summaryrefslogtreecommitdiff
path: root/firmware/id3.c
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2003-06-04 15:09:35 +0000
committerBjörn Stenberg <bjorn@haxx.se>2003-06-04 15:09:35 +0000
commit8498a48496579aecc1645604f49d931d4f35ff7f (patch)
tree6caebfc5fe29e6c155d236cab5ceff17e1a05bf6 /firmware/id3.c
parent7bc69aa084fe3d0577303fbf6cf1569a19cde390 (diff)
downloadrockbox-8498a48496579aecc1645604f49d931d4f35ff7f.tar.gz
rockbox-8498a48496579aecc1645604f49d931d4f35ff7f.zip
Generalized id3v2 parsing code. Added support for the composer frame and free-form genre and tracknum frames. (Patch #706111 by Thomas Paul Diffenbach)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3727 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/id3.c')
-rw-r--r--firmware/id3.c256
1 files changed, 163 insertions, 93 deletions
diff --git a/firmware/id3.c b/firmware/id3.c
index f797ed891b..9205c17d8c 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -23,12 +23,16 @@
23 * all sorts of friendly Rockbox people. 23 * all sorts of friendly Rockbox people.
24 * 24 *
25 */ 25 */
26
27 /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach
28 */
26 29
27#include <stdio.h> 30#include <stdio.h>
28#include <stdlib.h> 31#include <stdlib.h>
29#include <string.h> 32#include <string.h>
30#include <errno.h> 33#include <errno.h>
31#include <stdbool.h> 34#include <stdbool.h>
35#include <stddef.h>
32#include "file.h" 36#include "file.h"
33#include "debug.h" 37#include "debug.h"
34#include "atoi.h" 38#include "atoi.h"
@@ -46,6 +50,99 @@
46 ((b2 & 0xFF) << (1*8)) | \ 50 ((b2 & 0xFF) << (1*8)) | \
47 ((b3 & 0xFF) << (0*8))) 51 ((b3 & 0xFF) << (0*8)))
48 52
53/*
54 HOW TO ADD ADDITIONAL ID3 VERSION 2 TAGS
55 Code and comments by Thomas Paul Diffenbach
56
57 To add another ID3v2 Tag, do the following:
58 1. add a char* named for the tag to struct mp3entry in id3.h,
59 (I (tpd) prefer to use char* rather than ints, even for what seems like
60 numerical values, for cases where a number won't do, e.g.,
61 YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969"
62 TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name"
63 Text is more flexible, and as the main use of id3 data is to
64 display it, converting it to an int just means reconverting to
65 display it, at a runtime cost.)
66
67 2. If any special processing beyond copying the tag value from the Id3
68 block to the struct mp3entry is rrequired (such as converting to an
69 int), write a function to perform this special processing.
70
71 This function's prototype must match that of
72 typedef tagPostProcessFunc, that is it must be:
73 int func( struct mp3entry*, char* tag, int bufferpos )
74 the first argument is a pointer to the current mp3entry structure the
75 second argument is a pointer to the null terminated string value of the
76 tag found the third argument is the offset of the next free byte in the
77 mp3entry's buffer your function should return the corrected offset; if
78 you don't lengthen or shorten the tag string, you can return the third
79 argument unchanged.
80
81 Unless you have a good reason no to, make the function static.
82 TO JUST COPY THE TAG NO SPECIAL PROCESSING FUNCTION IS NEEDED.
83
84 3. add one or more entries to the tagList array, using the format:
85 char* ID3 Tag symbolic name -- see the ID3 specification for these,
86 sizeof() that name minus 1,
87 offsetof( struct mp3entry, variable_name_in_struct_mp3entry ),
88 pointer to your special processing function or NULL
89 if you need no special processing
90 Many ID3 symbolic names come in more than one form. You can add both
91 forms, each referencing the same variable in struct mp3entry.
92 If both forms are present, the last found will be used.
93
94 4. Alternately, use the TAG_LIST_ENTRY macro with
95 ID3 tag symbolic name,
96 variable in struct mp3entry,
97 special processing function address
98
99 5. Add code to wps-display.c function get_tag to assign a printf-like
100 format specifier for the tag */
101
102/* Structure for ID3 Tag extraction information */
103struct tag_resolver {
104 const char* tag;
105 int tag_length;
106 size_t offset;
107 int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos);
108};
109
110/* parse numeric value from string */
111static int parsenum( struct mp3entry* entry, char* tag, int bufferpos )
112{
113 entry->tracknum = atoi( tag );
114 return bufferpos;
115}
116
117/* parse numeric genre from string */
118static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos )
119{
120 if( tag[ 1 ] == '(' && tag[ 2 ] != '(' ) {
121 entry->genre = atoi( tag + 2 );
122 entry->genre_string = 0;
123 return tag - entry->id3v2buf;
124 }
125 else {
126 entry->genre = 0xFF;
127 return bufferpos;
128 }
129}
130
131static struct tag_resolver taglist[] = {
132 { "TPE1", 4, offsetof(struct mp3entry, artist), NULL },
133 { "TP1", 3, offsetof(struct mp3entry, artist), NULL },
134 { "TIT2", 4, offsetof(struct mp3entry, title), NULL },
135 { "TT2", 3, offsetof(struct mp3entry, title), NULL },
136 { "TALB", 4, offsetof(struct mp3entry, album), NULL },
137 { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsenum },
138 { "TYER", 4, offsetof(struct mp3entry, year_string), &parsenum },
139 { "TYR", 3, offsetof(struct mp3entry, year_string), &parsenum },
140 { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre },
141 { "TCOM", 5, offsetof(struct mp3entry, composer), NULL }
142};
143
144#define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0])))
145
49/* Checks to see if the passed in string is a 16-bit wide Unicode v2 146/* Checks to see if the passed in string is a 16-bit wide Unicode v2
50 string. If it is, we attempt to convert it to a 8-bit ASCII string 147 string. If it is, we attempt to convert it to a 8-bit ASCII string
51 (for valid 8-bit ASCII characters). If it's not unicode, we leave 148 (for valid 8-bit ASCII characters). If it's not unicode, we leave
@@ -186,24 +283,6 @@ static bool setid3v1title(int fd, struct mp3entry *entry)
186 return true; 283 return true;
187} 284}
188 285
189static int read_frame(int fd, unsigned char *buf, char **destptr, int framelen)
190{
191 int bytesread;
192
193 bytesread = read(fd, buf, framelen);
194 if(bytesread < 0)
195 return bytesread * 10 - 1;
196
197 if(bytesread < framelen)
198 return -1;
199
200 *destptr = buf;
201 if(unicode_munge(destptr, &bytesread) < 0)
202 return -2;
203
204 (*destptr)[bytesread] = '\0';
205 return bytesread + 1;
206}
207 286
208/* 287/*
209 * Sets the title of an MP3 entry based on its ID3v2 tag. 288 * Sets the title of an MP3 entry based on its ID3v2 tag.
@@ -221,12 +300,12 @@ static void setid3v2title(int fd, struct mp3entry *entry)
221 char header[10]; 300 char header[10];
222 unsigned char version; 301 unsigned char version;
223 char *buffer = entry->id3v2buf; 302 char *buffer = entry->id3v2buf;
224 char *tmp = NULL;
225 int bytesread = 0; 303 int bytesread = 0;
226 int buffersize = sizeof(entry->id3v2buf); 304 int buffersize = sizeof(entry->id3v2buf);
227 int flags; 305 int flags;
228 int skip; 306 int skip;
229 307 int i;
308
230 /* Bail out if the tag is shorter than 10 bytes */ 309 /* Bail out if the tag is shorter than 10 bytes */
231 if(entry->id3v2len < 10) 310 if(entry->id3v2len < 10)
232 return; 311 return;
@@ -263,7 +342,7 @@ static void setid3v2title(int fd, struct mp3entry *entry)
263 entry->id3version = version; 342 entry->id3version = version;
264 entry->tracknum = entry->year = entry->genre = 0; 343 entry->tracknum = entry->year = entry->genre = 0;
265 entry->title = entry->artist = entry->album = NULL; 344 entry->title = entry->artist = entry->album = NULL;
266 345
267 /* Skip the extended header if it is present */ 346 /* Skip the extended header if it is present */
268 if(version >= ID3_VER_2_4) { 347 if(version >= ID3_VER_2_4) {
269 if(header[5] & 0x40) { 348 if(header[5] & 0x40) {
@@ -281,7 +360,7 @@ static void setid3v2title(int fd, struct mp3entry *entry)
281 * We must have at least minframesize bytes left for the 360 * We must have at least minframesize bytes left for the
282 * remaining frames to be interesting 361 * remaining frames to be interesting
283 */ 362 */
284 while(size > minframesize) { 363 while(size > minframesize ) {
285 flags = 0; 364 flags = 0;
286 365
287 /* Read frame header and check length */ 366 /* Read frame header and check length */
@@ -353,78 +432,56 @@ static void setid3v2title(int fd, struct mp3entry *entry)
353 432
354 DEBUGF("id3v2 frame: %.4s\n", header); 433 DEBUGF("id3v2 frame: %.4s\n", header);
355 434
356 /* Check for certain frame headers */ 435 /* Check for certain frame headers
357 if (!entry->artist &&
358 (!strncmp(header, "TPE1", strlen("TPE1")) ||
359 !strncmp(header, "TP1", strlen("TP1")))) {
360 bytesread = read_frame(fd, buffer + bufferpos,
361 &entry->artist, framelen);
362 if(bytesread < 0)
363 return;
364
365 bufferpos += bytesread;
366 size -= framelen;
367 }
368 else if (!entry->title &&
369 (!strncmp(header, "TIT2", strlen("TIT2")) ||
370 !strncmp(header, "TT2", strlen("TT2")))) {
371 bytesread = read_frame(fd, buffer + bufferpos,
372 &entry->title, framelen);
373 if(bytesread < 0)
374 return;
375
376 bufferpos += bytesread;
377 size -= framelen;
378 }
379 else if( !entry->album &&
380 !strncmp(header, "TALB", strlen("TALB"))) {
381 bytesread = read_frame(fd, buffer + bufferpos,
382 &entry->album, framelen);
383 if(bytesread < 0)
384 return;
385
386 bufferpos += bytesread;
387 size -= framelen;
388 }
389 else if (!entry->tracknum &&
390 !strncmp(header, "TRCK", strlen("TRCK"))) {
391 bytesread = read_frame(fd, buffer + bufferpos,
392 &tmp, framelen);
393 if(bytesread < 0)
394 return;
395
396 entry->tracknum = atoi(tmp);
397 436
398 size -= framelen; 437 'size' is the amount of frame bytes remaining. We decrement it by
399 } 438 the amount of bytes we read. If we fail to read as many bytes as
400 else if (!entry->year && 439 we expect, we assume that we can't read from this file, and bail
401 (!strncmp(header, "TYER", 4) || 440 out.
402 !strncmp(header, "TYR", 3))) { 441
403 bytesread = read_frame(fd, buffer + bufferpos, 442 For each frame. we will iterate over the list of supported tags,
404 &tmp, framelen); 443 and read the tag into entry's buffer. All tags will be kept as
405 if(bytesread < 0) 444 strings, for cases where a number won't do, e.g., YEAR: "circa
406 return; 445 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK:
407 446 "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more
408 entry->year = atoi(tmp); 447 flexible, and as the main use of id3 data is to display it,
409 size -= bytesread; 448 converting it to an int just means reconverting to display it, at a
410 } 449 runtime cost.
411 else if (!entry->genre && 450
412 !strncmp(header, "TCON", 4)) { 451 For tags that the current code does convert to ints, a post
413 char* ptr = buffer + bufferpos; 452 processing function will be called via a pointer to function. */
414 bytesread = read(fd, ptr, framelen); 453
415 if(bytesread < 0 || bytesread < framelen) 454 for (i=0; i<TAGLIST_SIZE; i++) {
416 return; 455 struct tag_resolver* tr = &taglist[i];
456 char** ptag = (char**) (((char*)entry) + tr->offset);
457 char* tag;
417 458
418 if (ptr[1] == '(' && ptr[2] != '(') 459 if( !*ptag && !memcmp( header, tr->tag, tr->tag_length ) ) {
419 entry->genre = atoi(ptr+2); 460
420 bufferpos += bytesread + 1; 461 /* found a tag matching one in tagList, and not yet filled */
421 size -= bytesread; 462 bytesread = read(fd, buffer + bufferpos, framelen);
463 if( bytesread != framelen )
464 return;
465
466 size -= bytesread;
467 *ptag = buffer + bufferpos;
468 unicode_munge( ptag, &bytesread );
469 tag = *ptag;
470 tag[bytesread + 1] = 0;
471 bufferpos += bytesread + 2;
472 if( tr->ppFunc )
473 bufferpos = tr->ppFunc(entry, tag, bufferpos);
474 break;
475 }
422 } 476 }
423 else { 477
424 /* Unknown frame, skip it using the total size in case 478 if( i == TAGLIST_SIZE ) {
425 it was truncated */ 479 /* no tag in tagList was found, or it was a repeat.
480 skip it using the total size */
481
426 size -= totframelen; 482 size -= totframelen;
427 lseek(fd, totframelen, SEEK_CUR); 483 if( lseek(fd, totframelen, SEEK_CUR) == -1 )
484 return;
428 } 485 }
429 } 486 }
430} 487}
@@ -448,10 +505,11 @@ static int getid3v2len(int fd)
448 offset = 0; 505 offset = 0;
449 506
450 /* Now check what the ID3v2 size field says */ 507 /* Now check what the ID3v2 size field says */
451 else if(read(fd, buf, 4) != 4)
452 offset = 0;
453 else 508 else
454 offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10; 509 if(read(fd, buf, 4) != 4)
510 offset = 0;
511 else
512 offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
455 513
456 DEBUGF("ID3V2 Length: 0x%x\n", offset); 514 DEBUGF("ID3V2 Length: 0x%x\n", offset);
457 return offset; 515 return offset;
@@ -588,6 +646,7 @@ int main(int argc, char **argv)
588 int i; 646 int i;
589 for(i=1; i<argc; i++) { 647 for(i=1; i<argc; i++) {
590 struct mp3entry mp3; 648 struct mp3entry mp3;
649 mp3.album = "Bogus";
591 if(mp3info(&mp3, argv[i])) { 650 if(mp3info(&mp3, argv[i])) {
592 printf("Failed to get %s\n", argv[i]); 651 printf("Failed to get %s\n", argv[i]);
593 return 0; 652 return 0;
@@ -597,6 +656,10 @@ int main(int argc, char **argv)
597 " Title: %s\n" 656 " Title: %s\n"
598 " Artist: %s\n" 657 " Artist: %s\n"
599 " Album: %s\n" 658 " Album: %s\n"
659 " Genre: %s (%d) \n"
660 " Composer: %s\n"
661 " Year: %s (%d)\n"
662 " Track: %s (%d)\n"
600 " Length: %s / %d s\n" 663 " Length: %s / %d s\n"
601 " Bitrate: %d\n" 664 " Bitrate: %d\n"
602 " Frequency: %d\n", 665 " Frequency: %d\n",
@@ -604,6 +667,13 @@ int main(int argc, char **argv)
604 mp3.title?mp3.title:"<blank>", 667 mp3.title?mp3.title:"<blank>",
605 mp3.artist?mp3.artist:"<blank>", 668 mp3.artist?mp3.artist:"<blank>",
606 mp3.album?mp3.album:"<blank>", 669 mp3.album?mp3.album:"<blank>",
670 mp3.genre_string?mp3.genre_string:"<blank>",
671 mp3.genre,
672 mp3.composer?mp3.composer:"<blank>",
673 mp3.year_string?mp3.year_string:"<blank>",
674 mp3.year,
675 mp3.track_string?mp3.track_string:"<blank>",
676 mp3.tracknum,
607 secs2str(mp3.length), 677 secs2str(mp3.length),
608 mp3.length/1000, 678 mp3.length/1000,
609 mp3.bitrate, 679 mp3.bitrate,