diff options
Diffstat (limited to 'apps/metadata')
-rw-r--r-- | apps/metadata/a52.c | 2 | ||||
-rw-r--r-- | apps/metadata/adx.c | 2 | ||||
-rw-r--r-- | apps/metadata/aiff.c | 2 | ||||
-rw-r--r-- | apps/metadata/ape.c | 2 | ||||
-rw-r--r-- | apps/metadata/asap.c | 2 | ||||
-rw-r--r-- | apps/metadata/asf.c | 2 | ||||
-rw-r--r-- | apps/metadata/flac.c | 2 | ||||
-rw-r--r-- | apps/metadata/metadata_common.c | 3 | ||||
-rw-r--r-- | apps/metadata/metadata_common.h | 2 | ||||
-rw-r--r-- | apps/metadata/metadata_parsers.h | 6 | ||||
-rw-r--r-- | apps/metadata/mod.c | 2 | ||||
-rw-r--r-- | apps/metadata/monkeys.c | 2 | ||||
-rw-r--r-- | apps/metadata/mp3.c | 1183 | ||||
-rw-r--r-- | apps/metadata/mp4.c | 2 | ||||
-rw-r--r-- | apps/metadata/mpc.c | 2 | ||||
-rw-r--r-- | apps/metadata/ogg.c | 2 | ||||
-rw-r--r-- | apps/metadata/sid.c | 2 | ||||
-rw-r--r-- | apps/metadata/spc.c | 2 | ||||
-rw-r--r-- | apps/metadata/vorbis.c | 2 | ||||
-rw-r--r-- | apps/metadata/wave.c | 2 | ||||
-rw-r--r-- | apps/metadata/wavpack.c | 2 |
21 files changed, 1208 insertions, 20 deletions
diff --git a/apps/metadata/a52.c b/apps/metadata/a52.c index bcfd3c7b51..c35b32dba3 100644 --- a/apps/metadata/a52.c +++ b/apps/metadata/a52.c | |||
@@ -19,7 +19,7 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "id3.h" | 22 | #include "metadata.h" |
23 | #include "logf.h" | 23 | #include "logf.h" |
24 | 24 | ||
25 | #include "metadata_parsers.h" | 25 | #include "metadata_parsers.h" |
diff --git a/apps/metadata/adx.c b/apps/metadata/adx.c index c5da0deeeb..a903a6d053 100644 --- a/apps/metadata/adx.c +++ b/apps/metadata/adx.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "debug.h" | 31 | #include "debug.h" |
diff --git a/apps/metadata/aiff.c b/apps/metadata/aiff.c index 74e2465523..cb18e9249a 100644 --- a/apps/metadata/aiff.c +++ b/apps/metadata/aiff.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | 31 | ||
diff --git a/apps/metadata/ape.c b/apps/metadata/ape.c index 7e9100a50a..dcb359799c 100644 --- a/apps/metadata/ape.c +++ b/apps/metadata/ape.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "structec.h" | 31 | #include "structec.h" |
diff --git a/apps/metadata/asap.c b/apps/metadata/asap.c index 9bd615afd1..128a18d642 100644 --- a/apps/metadata/asap.c +++ b/apps/metadata/asap.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
diff --git a/apps/metadata/asf.c b/apps/metadata/asf.c index 255a0bcba4..611cc2aca3 100644 --- a/apps/metadata/asf.c +++ b/apps/metadata/asf.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | #include <inttypes.h> | 26 | #include <inttypes.h> |
27 | 27 | ||
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "replaygain.h" | 29 | #include "replaygain.h" |
30 | #include "debug.h" | 30 | #include "debug.h" |
31 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
diff --git a/apps/metadata/flac.c b/apps/metadata/flac.c index 286d356003..a50649e54a 100644 --- a/apps/metadata/flac.c +++ b/apps/metadata/flac.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "logf.h" | 31 | #include "logf.h" |
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c index e4df874cff..94ff212cea 100644 --- a/apps/metadata/metadata_common.c +++ b/apps/metadata/metadata_common.c | |||
@@ -25,8 +25,9 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | ||
30 | #include "replaygain.h" | 31 | #include "replaygain.h" |
31 | 32 | ||
32 | /* Skip an ID3v2 tag if it can be found. We assume the tag is located at the | 33 | /* Skip an ID3v2 tag if it can be found. We assume the tag is located at the |
diff --git a/apps/metadata/metadata_common.h b/apps/metadata/metadata_common.h index 3d9a0759c6..f57690af91 100644 --- a/apps/metadata/metadata_common.h +++ b/apps/metadata/metadata_common.h | |||
@@ -18,7 +18,7 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "id3.h" | 21 | #include "metadata.h" |
22 | 22 | ||
23 | #ifdef ROCKBOX_BIG_ENDIAN | 23 | #ifdef ROCKBOX_BIG_ENDIAN |
24 | #define IS_BIG_ENDIAN 1 | 24 | #define IS_BIG_ENDIAN 1 |
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h index 00ad112e54..1521f1301d 100644 --- a/apps/metadata/metadata_parsers.h +++ b/apps/metadata/metadata_parsers.h | |||
@@ -18,7 +18,11 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "id3.h" | 21 | |
22 | char* id3_get_num_genre(unsigned int genre_num); | ||
23 | bool id3_is_genre_string(const char *string); | ||
24 | int getid3v2len(int fd); | ||
25 | bool get_mp3_metadata(int fd, struct mp3entry* id3, const char *filename); | ||
22 | 26 | ||
23 | bool get_adx_metadata(int fd, struct mp3entry* id3); | 27 | bool get_adx_metadata(int fd, struct mp3entry* id3); |
24 | bool get_aiff_metadata(int fd, struct mp3entry* id3); | 28 | bool get_aiff_metadata(int fd, struct mp3entry* id3); |
diff --git a/apps/metadata/mod.c b/apps/metadata/mod.c index 38adeea06a..e10090499e 100644 --- a/apps/metadata/mod.c +++ b/apps/metadata/mod.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
diff --git a/apps/metadata/monkeys.c b/apps/metadata/monkeys.c index d59e7ee2b4..1cacff13af 100644 --- a/apps/metadata/monkeys.c +++ b/apps/metadata/monkeys.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | 31 | ||
diff --git a/apps/metadata/mp3.c b/apps/metadata/mp3.c new file mode 100644 index 0000000000..8c85c8916f --- /dev/null +++ b/apps/metadata/mp3.c | |||
@@ -0,0 +1,1183 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | /* | ||
22 | * Parts of this code has been stolen from the Ample project and was written | ||
23 | * by David H�deman. It has since been extended and enhanced pretty much by | ||
24 | * all sorts of friendly Rockbox people. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <errno.h> | ||
35 | #include <stdbool.h> | ||
36 | #include <stddef.h> | ||
37 | #include <ctype.h> | ||
38 | #include "config.h" | ||
39 | #include "file.h" | ||
40 | #include "logf.h" | ||
41 | |||
42 | #include "mp3data.h" | ||
43 | #include "system.h" | ||
44 | #include "replaygain.h" | ||
45 | #include "rbunicode.h" | ||
46 | |||
47 | static unsigned long unsync(unsigned long b0, | ||
48 | unsigned long b1, | ||
49 | unsigned long b2, | ||
50 | unsigned long b3) | ||
51 | { | ||
52 | return (((long)(b0 & 0x7F) << (3*7)) | | ||
53 | ((long)(b1 & 0x7F) << (2*7)) | | ||
54 | ((long)(b2 & 0x7F) << (1*7)) | | ||
55 | ((long)(b3 & 0x7F) << (0*7))); | ||
56 | } | ||
57 | |||
58 | static const char* const genres[] = { | ||
59 | "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", | ||
60 | "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", | ||
61 | "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", | ||
62 | "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", | ||
63 | "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", | ||
64 | "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", | ||
65 | "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", | ||
66 | "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", | ||
67 | "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", | ||
68 | "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", | ||
69 | "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", | ||
70 | "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", | ||
71 | "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", | ||
72 | |||
73 | /* winamp extensions */ | ||
74 | "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", | ||
75 | "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", | ||
76 | "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", | ||
77 | "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", | ||
78 | "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", | ||
79 | "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", | ||
80 | "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", | ||
81 | "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", | ||
82 | "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", | ||
83 | "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", | ||
84 | "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", | ||
85 | "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", | ||
86 | "Synthpop" | ||
87 | }; | ||
88 | |||
89 | char* id3_get_num_genre(unsigned int genre_num) | ||
90 | { | ||
91 | if (genre_num < sizeof(genres)/sizeof(char*)) | ||
92 | return (char*)genres[genre_num]; | ||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | /* True if the string is from the "genres" array */ | ||
97 | bool id3_is_genre_string(const char *string) | ||
98 | { | ||
99 | return ( string >= genres[0] && | ||
100 | string <= genres[sizeof(genres)/sizeof(char*) - 1] ); | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | HOW TO ADD ADDITIONAL ID3 VERSION 2 TAGS | ||
105 | Code and comments by Thomas Paul Diffenbach | ||
106 | |||
107 | To add another ID3v2 Tag, do the following: | ||
108 | 1. add a char* named for the tag to struct mp3entry in id3.h, | ||
109 | (I (tpd) prefer to use char* rather than ints, even for what seems like | ||
110 | numerical values, for cases where a number won't do, e.g., | ||
111 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" | ||
112 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" | ||
113 | Text is more flexible, and as the main use of id3 data is to | ||
114 | display it, converting it to an int just means reconverting to | ||
115 | display it, at a runtime cost.) | ||
116 | |||
117 | 2. If any special processing beyond copying the tag value from the Id3 | ||
118 | block to the struct mp3entry is rrequired (such as converting to an | ||
119 | int), write a function to perform this special processing. | ||
120 | |||
121 | This function's prototype must match that of | ||
122 | typedef tagPostProcessFunc, that is it must be: | ||
123 | int func( struct mp3entry*, char* tag, int bufferpos ) | ||
124 | the first argument is a pointer to the current mp3entry structure the | ||
125 | second argument is a pointer to the null terminated string value of the | ||
126 | tag found the third argument is the offset of the next free byte in the | ||
127 | mp3entry's buffer your function should return the corrected offset; if | ||
128 | you don't lengthen or shorten the tag string, you can return the third | ||
129 | argument unchanged. | ||
130 | |||
131 | Unless you have a good reason no to, make the function static. | ||
132 | TO JUST COPY THE TAG NO SPECIAL PROCESSING FUNCTION IS NEEDED. | ||
133 | |||
134 | 3. add one or more entries to the tagList array, using the format: | ||
135 | char* ID3 Tag symbolic name -- see the ID3 specification for these, | ||
136 | sizeof() that name minus 1, | ||
137 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), | ||
138 | pointer to your special processing function or NULL | ||
139 | if you need no special processing | ||
140 | flag indicating if this tag is binary or textual | ||
141 | Many ID3 symbolic names come in more than one form. You can add both | ||
142 | forms, each referencing the same variable in struct mp3entry. | ||
143 | If both forms are present, the last found will be used. | ||
144 | Note that the offset can be zero, in which case no entry will be set | ||
145 | in the mp3entry struct; the frame is still read into the buffer and | ||
146 | the special processing function is called (several times, if there | ||
147 | are several frames with the same name). | ||
148 | |||
149 | 4. Alternately, use the TAG_LIST_ENTRY macro with | ||
150 | ID3 tag symbolic name, | ||
151 | variable in struct mp3entry, | ||
152 | special processing function address | ||
153 | |||
154 | 5. Add code to wps-display.c function get_tag to assign a printf-like | ||
155 | format specifier for the tag */ | ||
156 | |||
157 | /* Structure for ID3 Tag extraction information */ | ||
158 | struct tag_resolver { | ||
159 | const char* tag; | ||
160 | int tag_length; | ||
161 | size_t offset; | ||
162 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); | ||
163 | bool binary; | ||
164 | }; | ||
165 | |||
166 | static bool global_ff_found; | ||
167 | |||
168 | static int unsynchronize(char* tag, int len, bool *ff_found) | ||
169 | { | ||
170 | int i; | ||
171 | unsigned char c; | ||
172 | unsigned char *rp, *wp; | ||
173 | |||
174 | wp = rp = (unsigned char *)tag; | ||
175 | |||
176 | rp = (unsigned char *)tag; | ||
177 | for(i = 0;i < len;i++) { | ||
178 | /* Read the next byte and write it back, but don't increment the | ||
179 | write pointer */ | ||
180 | c = *rp++; | ||
181 | *wp = c; | ||
182 | if(*ff_found) { | ||
183 | /* Increment the write pointer if it isn't an unsynch pattern */ | ||
184 | if(c != 0) | ||
185 | wp++; | ||
186 | *ff_found = false; | ||
187 | } else { | ||
188 | if(c == 0xff) | ||
189 | *ff_found = true; | ||
190 | wp++; | ||
191 | } | ||
192 | } | ||
193 | return (long)wp - (long)tag; | ||
194 | } | ||
195 | |||
196 | static int unsynchronize_frame(char* tag, int len) | ||
197 | { | ||
198 | bool ff_found = false; | ||
199 | |||
200 | return unsynchronize(tag, len, &ff_found); | ||
201 | } | ||
202 | |||
203 | static int read_unsynched(int fd, void *buf, int len) | ||
204 | { | ||
205 | int i; | ||
206 | int rc; | ||
207 | int remaining = len; | ||
208 | char *wp; | ||
209 | char *rp; | ||
210 | |||
211 | wp = buf; | ||
212 | |||
213 | while(remaining) { | ||
214 | rp = wp; | ||
215 | rc = read(fd, rp, remaining); | ||
216 | if(rc <= 0) | ||
217 | return rc; | ||
218 | |||
219 | i = unsynchronize(wp, remaining, &global_ff_found); | ||
220 | remaining -= i; | ||
221 | wp += i; | ||
222 | } | ||
223 | |||
224 | return len; | ||
225 | } | ||
226 | |||
227 | static int skip_unsynched(int fd, int len) | ||
228 | { | ||
229 | int rc; | ||
230 | int remaining = len; | ||
231 | int rlen; | ||
232 | char buf[32]; | ||
233 | |||
234 | while(remaining) { | ||
235 | rlen = MIN(sizeof(buf), (unsigned int)remaining); | ||
236 | rc = read(fd, buf, rlen); | ||
237 | if(rc <= 0) | ||
238 | return rc; | ||
239 | |||
240 | remaining -= unsynchronize(buf, rlen, &global_ff_found); | ||
241 | } | ||
242 | |||
243 | return len; | ||
244 | } | ||
245 | |||
246 | /* parse numeric value from string */ | ||
247 | static int parsetracknum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
248 | { | ||
249 | entry->tracknum = atoi( tag ); | ||
250 | return bufferpos; | ||
251 | } | ||
252 | |||
253 | /* parse numeric value from string */ | ||
254 | static int parsediscnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
255 | { | ||
256 | entry->discnum = atoi( tag ); | ||
257 | return bufferpos; | ||
258 | } | ||
259 | |||
260 | /* parse numeric value from string */ | ||
261 | static int parseyearnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
262 | { | ||
263 | entry->year = atoi( tag ); | ||
264 | return bufferpos; | ||
265 | } | ||
266 | |||
267 | /* parse numeric genre from string, version 2.2 and 2.3 */ | ||
268 | static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) | ||
269 | { | ||
270 | if(entry->id3version >= ID3_VER_2_4) { | ||
271 | /* In version 2.4 and up, there are no parentheses, and the genre frame | ||
272 | is a list of strings, either numbers or text. */ | ||
273 | |||
274 | /* Is it a number? */ | ||
275 | if(isdigit(tag[0])) { | ||
276 | entry->genre_string = id3_get_num_genre(atoi( tag )); | ||
277 | return tag - entry->id3v2buf; | ||
278 | } else { | ||
279 | entry->genre_string = tag; | ||
280 | return bufferpos; | ||
281 | } | ||
282 | } else { | ||
283 | if( tag[0] == '(' && tag[1] != '(' ) { | ||
284 | entry->genre_string = id3_get_num_genre(atoi( tag + 1 )); | ||
285 | return tag - entry->id3v2buf; | ||
286 | } | ||
287 | else { | ||
288 | entry->genre_string = tag; | ||
289 | return bufferpos; | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | #if CONFIG_CODEC == SWCODEC | ||
295 | /* parse user defined text, looking for replaygain information. */ | ||
296 | static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | ||
297 | { | ||
298 | char* value = NULL; | ||
299 | int desc_len = strlen(tag); | ||
300 | int value_len = 0; | ||
301 | |||
302 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
303 | /* At least part of the value was read, so we can safely try to | ||
304 | * parse it | ||
305 | */ | ||
306 | value = tag + desc_len + 1; | ||
307 | value_len = parse_replaygain(tag, value, entry, tag, | ||
308 | bufferpos - (tag - entry->id3v2buf)); | ||
309 | } | ||
310 | |||
311 | return tag - entry->id3v2buf + value_len; | ||
312 | } | ||
313 | |||
314 | /* parse RVA2 binary data and convert to replaygain information. */ | ||
315 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) | ||
316 | { | ||
317 | int desc_len = strlen(tag); | ||
318 | int start_pos = tag - entry->id3v2buf; | ||
319 | int end_pos = start_pos + desc_len + 5; | ||
320 | int value_len = 0; | ||
321 | unsigned char* value = tag + desc_len + 1; | ||
322 | |||
323 | /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel | ||
324 | * type is master volume. | ||
325 | */ | ||
326 | if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos | ||
327 | && *value++ == 1) { | ||
328 | long gain = 0; | ||
329 | long peak = 0; | ||
330 | long peakbits; | ||
331 | long peakbytes; | ||
332 | bool album = false; | ||
333 | |||
334 | /* The RVA2 specification is unclear on some things (id string and | ||
335 | * peak volume), but this matches how Quod Libet use them. | ||
336 | */ | ||
337 | |||
338 | gain = (int16_t) ((value[0] << 8) | value[1]); | ||
339 | value += 2; | ||
340 | peakbits = *value++; | ||
341 | peakbytes = (peakbits + 7) / 8; | ||
342 | |||
343 | /* Only use the topmost 24 bits for peak volume */ | ||
344 | if (peakbytes > 3) { | ||
345 | peakbytes = 3; | ||
346 | } | ||
347 | |||
348 | /* Make sure the peak bits were read */ | ||
349 | if (end_pos + peakbytes < bufferpos) { | ||
350 | long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8; | ||
351 | |||
352 | for ( ; peakbytes; peakbytes--) { | ||
353 | peak <<= 8; | ||
354 | peak += *value++; | ||
355 | } | ||
356 | |||
357 | peak <<= shift; | ||
358 | |||
359 | if (peakbits > 24) { | ||
360 | peak += *value >> (8 - shift); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | if (strcasecmp(tag, "album") == 0) { | ||
365 | album = true; | ||
366 | } else if (strcasecmp(tag, "track") != 0) { | ||
367 | /* Only accept non-track values if we don't have any previous | ||
368 | * value. | ||
369 | */ | ||
370 | if (entry->track_gain != 0) { | ||
371 | return start_pos; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | value_len = parse_replaygain_int(album, gain, peak * 2, entry, | ||
376 | tag, sizeof(entry->id3v2buf) - start_pos); | ||
377 | } | ||
378 | |||
379 | return start_pos + value_len; | ||
380 | } | ||
381 | #endif | ||
382 | |||
383 | static int parsembtid( struct mp3entry* entry, char* tag, int bufferpos ) | ||
384 | { | ||
385 | char* value = NULL; | ||
386 | int desc_len = strlen(tag); | ||
387 | /*DEBUGF("MBID len: %d\n", desc_len);*/ | ||
388 | int value_len = 0; | ||
389 | |||
390 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) | ||
391 | { | ||
392 | value = tag + desc_len + 1; | ||
393 | |||
394 | if (strcasecmp(tag, "http://musicbrainz.org") == 0) | ||
395 | { | ||
396 | /* Musicbrainz track IDs are always 36 chars long plus null */ | ||
397 | value_len = 37; | ||
398 | |||
399 | entry->mb_track_id = value; | ||
400 | |||
401 | /*DEBUGF("ENTRY: %s LEN: %d\n", entry->mb_track_id, strlen(entry->mb_track_id));*/ | ||
402 | } | ||
403 | } | ||
404 | |||
405 | return tag - entry->id3v2buf + value_len; | ||
406 | } | ||
407 | |||
408 | static const struct tag_resolver taglist[] = { | ||
409 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, false }, | ||
410 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL, false }, | ||
411 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL, false }, | ||
412 | { "TT2", 3, offsetof(struct mp3entry, title), NULL, false }, | ||
413 | { "TALB", 4, offsetof(struct mp3entry, album), NULL, false }, | ||
414 | { "TAL", 3, offsetof(struct mp3entry, album), NULL, false }, | ||
415 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
416 | { "TPOS", 4, offsetof(struct mp3entry, disc_string), &parsediscnum, false }, | ||
417 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
418 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
419 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
420 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
421 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false }, | ||
422 | { "TPE2", 4, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
423 | { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
424 | { "TIT1", 4, offsetof(struct mp3entry, grouping), NULL, false }, | ||
425 | { "TT1", 3, offsetof(struct mp3entry, grouping), NULL, false }, | ||
426 | { "COMM", 4, offsetof(struct mp3entry, comment), NULL, false }, | ||
427 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
428 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
429 | #if CONFIG_CODEC == SWCODEC | ||
430 | { "TXXX", 4, 0, &parseuser, false }, | ||
431 | { "RVA2", 4, 0, &parserva2, true }, | ||
432 | #endif | ||
433 | { "UFID", 4, 0, &parsembtid, false }, | ||
434 | }; | ||
435 | |||
436 | #define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0]))) | ||
437 | |||
438 | /* Get the length of an ID3 string in the given encoding. Returns the length | ||
439 | * in bytes, including end nil, or -1 if the encoding is unknown. | ||
440 | */ | ||
441 | static int unicode_len(char encoding, const void* string) | ||
442 | { | ||
443 | int len = 0; | ||
444 | |||
445 | if (encoding == 0x01 || encoding == 0x02) { | ||
446 | char first; | ||
447 | const char *s = string; | ||
448 | /* string might be unaligned, so using short* can crash on ARM and SH1 */ | ||
449 | do { | ||
450 | first = *s++; | ||
451 | } while ((first | *s++) != 0); | ||
452 | |||
453 | len = s - (const char*) string; | ||
454 | } else { | ||
455 | len = strlen((char*) string) + 1; | ||
456 | } | ||
457 | |||
458 | return len; | ||
459 | } | ||
460 | |||
461 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 | ||
462 | string. If it is, we convert it to a UTF-8 string. If it's not unicode, | ||
463 | we convert from the default codepage */ | ||
464 | static int unicode_munge(char* string, char* utf8buf, int *len) { | ||
465 | long tmp; | ||
466 | bool le = false; | ||
467 | int i = 0; | ||
468 | unsigned char *str = (unsigned char *)string; | ||
469 | int templen = 0; | ||
470 | unsigned char* utf8 = (unsigned char *)utf8buf; | ||
471 | |||
472 | switch (str[0]) { | ||
473 | case 0x00: /* Type 0x00 is ordinary ISO 8859-1 */ | ||
474 | str++; | ||
475 | (*len)--; | ||
476 | utf8 = iso_decode(str, utf8, -1, *len); | ||
477 | *utf8 = 0; | ||
478 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
479 | break; | ||
480 | |||
481 | case 0x01: /* Unicode with or without BOM */ | ||
482 | case 0x02: | ||
483 | (*len)--; | ||
484 | str++; | ||
485 | |||
486 | /* Handle frames with more than one string | ||
487 | (needed for TXXX frames).*/ | ||
488 | do { | ||
489 | tmp = bytes2int(0, 0, str[0], str[1]); | ||
490 | |||
491 | /* Now check if there is a BOM | ||
492 | (zero-width non-breaking space, 0xfeff) | ||
493 | and if it is in little or big endian format */ | ||
494 | if(tmp == 0xfffe) { /* Little endian? */ | ||
495 | le = true; | ||
496 | str += 2; | ||
497 | (*len)-=2; | ||
498 | } else if(tmp == 0xfeff) { /* Big endian? */ | ||
499 | str += 2; | ||
500 | (*len)-=2; | ||
501 | } else | ||
502 | /* If there is no BOM (which is a specification violation), | ||
503 | let's try to guess it. If one of the bytes is 0x00, it is | ||
504 | probably the most significant one. */ | ||
505 | if(str[1] == 0) | ||
506 | le = true; | ||
507 | |||
508 | do { | ||
509 | if(le) | ||
510 | utf8 = utf16LEdecode(str, utf8, 1); | ||
511 | else | ||
512 | utf8 = utf16BEdecode(str, utf8, 1); | ||
513 | |||
514 | str+=2; | ||
515 | i += 2; | ||
516 | } while((str[0] || str[1]) && (i < *len)); | ||
517 | |||
518 | *utf8++ = 0; /* Terminate the string */ | ||
519 | templen += (strlen(&utf8buf[templen]) + 1); | ||
520 | str += 2; | ||
521 | i+=2; | ||
522 | } while(i < *len); | ||
523 | *len = templen - 1; | ||
524 | break; | ||
525 | |||
526 | case 0x03: /* UTF-8 encoded string */ | ||
527 | for(i=0; i < *len; i++) | ||
528 | utf8[i] = str[i+1]; | ||
529 | (*len)--; | ||
530 | break; | ||
531 | |||
532 | default: /* Plain old string */ | ||
533 | utf8 = iso_decode(str, utf8, -1, *len); | ||
534 | *utf8 = 0; | ||
535 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
536 | break; | ||
537 | } | ||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Sets the title of an MP3 entry based on its ID3v1 tag. | ||
543 | * | ||
544 | * Arguments: file - the MP3 file to scen for a ID3v1 tag | ||
545 | * entry - the entry to set the title in | ||
546 | * | ||
547 | * Returns: true if a title was found and created, else false | ||
548 | */ | ||
549 | static bool setid3v1title(int fd, struct mp3entry *entry) | ||
550 | { | ||
551 | unsigned char buffer[128]; | ||
552 | static const char offsets[] = {3, 33, 63, 97, 93, 125, 127}; | ||
553 | int i, j; | ||
554 | unsigned char* utf8; | ||
555 | |||
556 | if (-1 == lseek(fd, -128, SEEK_END)) | ||
557 | return false; | ||
558 | |||
559 | if (read(fd, buffer, sizeof buffer) != sizeof buffer) | ||
560 | return false; | ||
561 | |||
562 | if (strncmp((char *)buffer, "TAG", 3)) | ||
563 | return false; | ||
564 | |||
565 | entry->id3v1len = 128; | ||
566 | entry->id3version = ID3_VER_1_0; | ||
567 | |||
568 | for (i=0; i < (int)sizeof offsets; i++) { | ||
569 | unsigned char* ptr = (unsigned char *)buffer + offsets[i]; | ||
570 | |||
571 | switch(i) { | ||
572 | case 0: | ||
573 | case 1: | ||
574 | case 2: | ||
575 | /* kill trailing space in strings */ | ||
576 | for (j=29; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
577 | ptr[j] = 0; | ||
578 | /* convert string to utf8 */ | ||
579 | utf8 = (unsigned char *)entry->id3v1buf[i]; | ||
580 | utf8 = iso_decode(ptr, utf8, -1, 30); | ||
581 | /* make sure string is terminated */ | ||
582 | *utf8 = 0; | ||
583 | break; | ||
584 | |||
585 | case 3: | ||
586 | /* kill trailing space in strings */ | ||
587 | for (j=27; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
588 | ptr[j] = 0; | ||
589 | /* convert string to utf8 */ | ||
590 | utf8 = (unsigned char *)entry->id3v1buf[3]; | ||
591 | utf8 = iso_decode(ptr, utf8, -1, 28); | ||
592 | /* make sure string is terminated */ | ||
593 | *utf8 = 0; | ||
594 | break; | ||
595 | |||
596 | case 4: | ||
597 | ptr[4] = 0; | ||
598 | entry->year = atoi((char *)ptr); | ||
599 | break; | ||
600 | |||
601 | case 5: | ||
602 | /* id3v1.1 uses last two bytes of comment field for track | ||
603 | number: first must be 0 and second is track num */ | ||
604 | if (!ptr[0] && ptr[1]) { | ||
605 | entry->tracknum = ptr[1]; | ||
606 | entry->id3version = ID3_VER_1_1; | ||
607 | } | ||
608 | break; | ||
609 | |||
610 | case 6: | ||
611 | /* genre */ | ||
612 | entry->genre_string = id3_get_num_genre(ptr[0]); | ||
613 | break; | ||
614 | } | ||
615 | } | ||
616 | |||
617 | entry->title = entry->id3v1buf[0]; | ||
618 | entry->artist = entry->id3v1buf[1]; | ||
619 | entry->album = entry->id3v1buf[2]; | ||
620 | entry->comment = entry->id3v1buf[3]; | ||
621 | |||
622 | return true; | ||
623 | } | ||
624 | |||
625 | |||
626 | /* | ||
627 | * Sets the title of an MP3 entry based on its ID3v2 tag. | ||
628 | * | ||
629 | * Arguments: file - the MP3 file to scan for a ID3v2 tag | ||
630 | * entry - the entry to set the title in | ||
631 | * | ||
632 | * Returns: true if a title was found and created, else false | ||
633 | */ | ||
634 | static void setid3v2title(int fd, struct mp3entry *entry) | ||
635 | { | ||
636 | int minframesize; | ||
637 | int size; | ||
638 | long bufferpos = 0, totframelen, framelen; | ||
639 | char header[10]; | ||
640 | char tmp[4]; | ||
641 | unsigned char version; | ||
642 | char *buffer = entry->id3v2buf; | ||
643 | int bytesread = 0; | ||
644 | int buffersize = sizeof(entry->id3v2buf); | ||
645 | unsigned char global_flags; | ||
646 | int flags; | ||
647 | int skip; | ||
648 | bool global_unsynch = false; | ||
649 | bool unsynch = false; | ||
650 | int i, j; | ||
651 | int rc; | ||
652 | |||
653 | global_ff_found = false; | ||
654 | |||
655 | /* Bail out if the tag is shorter than 10 bytes */ | ||
656 | if(entry->id3v2len < 10) | ||
657 | return; | ||
658 | |||
659 | /* Read the ID3 tag version from the header */ | ||
660 | lseek(fd, 0, SEEK_SET); | ||
661 | if(10 != read(fd, header, 10)) | ||
662 | return; | ||
663 | |||
664 | /* Get the total ID3 tag size */ | ||
665 | size = entry->id3v2len - 10; | ||
666 | |||
667 | version = header[3]; | ||
668 | switch ( version ) { | ||
669 | case 2: | ||
670 | version = ID3_VER_2_2; | ||
671 | minframesize = 8; | ||
672 | break; | ||
673 | |||
674 | case 3: | ||
675 | version = ID3_VER_2_3; | ||
676 | minframesize = 12; | ||
677 | break; | ||
678 | |||
679 | case 4: | ||
680 | version = ID3_VER_2_4; | ||
681 | minframesize = 12; | ||
682 | break; | ||
683 | |||
684 | default: | ||
685 | /* unsupported id3 version */ | ||
686 | return; | ||
687 | } | ||
688 | entry->id3version = version; | ||
689 | entry->tracknum = entry->year = entry->discnum = 0; | ||
690 | entry->title = entry->artist = entry->album = NULL; /* FIXME incomplete */ | ||
691 | |||
692 | global_flags = header[5]; | ||
693 | |||
694 | /* Skip the extended header if it is present */ | ||
695 | if(global_flags & 0x40) { | ||
696 | if(version == ID3_VER_2_3) { | ||
697 | if(10 != read(fd, header, 10)) | ||
698 | return; | ||
699 | /* The 2.3 extended header size doesn't include the header size | ||
700 | field itself. Also, it is not unsynched. */ | ||
701 | framelen = | ||
702 | bytes2int(header[0], header[1], header[2], header[3]) + 4; | ||
703 | |||
704 | /* Skip the rest of the header */ | ||
705 | lseek(fd, framelen - 10, SEEK_CUR); | ||
706 | } | ||
707 | |||
708 | if(version >= ID3_VER_2_4) { | ||
709 | if(4 != read(fd, header, 4)) | ||
710 | return; | ||
711 | |||
712 | /* The 2.4 extended header size does include the entire header, | ||
713 | so here we can just skip it. This header is unsynched. */ | ||
714 | framelen = unsync(header[0], header[1], | ||
715 | header[2], header[3]); | ||
716 | |||
717 | lseek(fd, framelen - 4, SEEK_CUR); | ||
718 | } | ||
719 | } | ||
720 | |||
721 | /* Is unsynchronization applied? */ | ||
722 | if(global_flags & 0x80) { | ||
723 | global_unsynch = true; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * We must have at least minframesize bytes left for the | ||
728 | * remaining frames to be interesting | ||
729 | */ | ||
730 | while (size >= minframesize && bufferpos < buffersize - 1) { | ||
731 | flags = 0; | ||
732 | |||
733 | /* Read frame header and check length */ | ||
734 | if(version >= ID3_VER_2_3) { | ||
735 | if(global_unsynch && version <= ID3_VER_2_3) | ||
736 | rc = read_unsynched(fd, header, 10); | ||
737 | else | ||
738 | rc = read(fd, header, 10); | ||
739 | if(rc != 10) | ||
740 | return; | ||
741 | /* Adjust for the 10 bytes we read */ | ||
742 | size -= 10; | ||
743 | |||
744 | flags = bytes2int(0, 0, header[8], header[9]); | ||
745 | |||
746 | if (version >= ID3_VER_2_4) { | ||
747 | framelen = unsync(header[4], header[5], | ||
748 | header[6], header[7]); | ||
749 | } else { | ||
750 | /* version .3 files don't use synchsafe ints for | ||
751 | * size */ | ||
752 | framelen = bytes2int(header[4], header[5], | ||
753 | header[6], header[7]); | ||
754 | } | ||
755 | } else { | ||
756 | if(6 != read(fd, header, 6)) | ||
757 | return; | ||
758 | /* Adjust for the 6 bytes we read */ | ||
759 | size -= 6; | ||
760 | |||
761 | framelen = bytes2int(0, header[3], header[4], header[5]); | ||
762 | } | ||
763 | |||
764 | logf("framelen = %ld", framelen); | ||
765 | if(framelen == 0){ | ||
766 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) | ||
767 | return; | ||
768 | else | ||
769 | continue; | ||
770 | } | ||
771 | |||
772 | unsynch = false; | ||
773 | |||
774 | if(flags) | ||
775 | { | ||
776 | skip = 0; | ||
777 | |||
778 | if (version >= ID3_VER_2_4) { | ||
779 | if(flags & 0x0040) { /* Grouping identity */ | ||
780 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
781 | framelen--; | ||
782 | } | ||
783 | } else { | ||
784 | if(flags & 0x0020) { /* Grouping identity */ | ||
785 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
786 | framelen--; | ||
787 | } | ||
788 | } | ||
789 | |||
790 | if(flags & 0x000c) /* Compression or encryption */ | ||
791 | { | ||
792 | /* Skip it */ | ||
793 | size -= framelen; | ||
794 | lseek(fd, framelen, SEEK_CUR); | ||
795 | continue; | ||
796 | } | ||
797 | |||
798 | if(flags & 0x0002) /* Unsynchronization */ | ||
799 | unsynch = true; | ||
800 | |||
801 | if (version >= ID3_VER_2_4) { | ||
802 | if(flags & 0x0001) { /* Data length indicator */ | ||
803 | if(4 != read(fd, tmp, 4)) | ||
804 | return; | ||
805 | |||
806 | /* We don't need the data length */ | ||
807 | framelen -= 4; | ||
808 | } | ||
809 | } | ||
810 | } | ||
811 | |||
812 | /* Keep track of the remaining frame size */ | ||
813 | totframelen = framelen; | ||
814 | |||
815 | /* If the frame is larger than the remaining buffer space we try | ||
816 | to read as much as would fit in the buffer */ | ||
817 | if(framelen >= buffersize - bufferpos) | ||
818 | framelen = buffersize - bufferpos - 1; | ||
819 | |||
820 | logf("id3v2 frame: %.4s", header); | ||
821 | |||
822 | /* Check for certain frame headers | ||
823 | |||
824 | 'size' is the amount of frame bytes remaining. We decrement it by | ||
825 | the amount of bytes we read. If we fail to read as many bytes as | ||
826 | we expect, we assume that we can't read from this file, and bail | ||
827 | out. | ||
828 | |||
829 | For each frame. we will iterate over the list of supported tags, | ||
830 | and read the tag into entry's buffer. All tags will be kept as | ||
831 | strings, for cases where a number won't do, e.g., YEAR: "circa | ||
832 | 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: | ||
833 | "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more | ||
834 | flexible, and as the main use of id3 data is to display it, | ||
835 | converting it to an int just means reconverting to display it, at a | ||
836 | runtime cost. | ||
837 | |||
838 | For tags that the current code does convert to ints, a post | ||
839 | processing function will be called via a pointer to function. */ | ||
840 | |||
841 | for (i=0; i<TAGLIST_SIZE; i++) { | ||
842 | const struct tag_resolver* tr = &taglist[i]; | ||
843 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) | ||
844 | : NULL; | ||
845 | char* tag; | ||
846 | |||
847 | /* Only ID3_VER_2_2 uses frames with three-character names. */ | ||
848 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) | ||
849 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { | ||
850 | continue; | ||
851 | } | ||
852 | |||
853 | /* Note that parser functions sometimes set *ptag to NULL, so | ||
854 | * the "!*ptag" check here doesn't always have the desired | ||
855 | * effect. Should the parser functions (parsegenre in | ||
856 | * particular) be updated to handle the case of being called | ||
857 | * multiple times, or should the "*ptag" check be removed? | ||
858 | */ | ||
859 | if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { | ||
860 | |||
861 | /* found a tag matching one in tagList, and not yet filled */ | ||
862 | tag = buffer + bufferpos; | ||
863 | |||
864 | if(global_unsynch && version <= ID3_VER_2_3) | ||
865 | bytesread = read_unsynched(fd, tag, framelen); | ||
866 | else | ||
867 | bytesread = read(fd, tag, framelen); | ||
868 | |||
869 | if( bytesread != framelen ) | ||
870 | return; | ||
871 | |||
872 | size -= bytesread; | ||
873 | |||
874 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | ||
875 | bytesread = unsynchronize_frame(tag, bytesread); | ||
876 | |||
877 | /* the COMM frame has a 3 char field to hold an ISO-639-1 | ||
878 | * language string and an optional short description; | ||
879 | * remove them so unicode_munge can work correctly | ||
880 | */ | ||
881 | |||
882 | if(!memcmp( header, "COMM", 4 )) { | ||
883 | int offset; | ||
884 | /* ignore comments with iTunes 7 soundcheck/gapless data */ | ||
885 | if(!strncmp(tag+4, "iTun", 4)) | ||
886 | break; | ||
887 | offset = 3 + unicode_len(*tag, tag + 4); | ||
888 | if(bytesread > offset) { | ||
889 | bytesread -= offset; | ||
890 | memmove(tag + 1, tag + 1 + offset, bytesread - 1); | ||
891 | } | ||
892 | } | ||
893 | |||
894 | /* Attempt to parse Unicode string only if the tag contents | ||
895 | aren't binary */ | ||
896 | if(!tr->binary) { | ||
897 | /* UTF-8 could potentially be 3 times larger */ | ||
898 | /* so we need to create a new buffer */ | ||
899 | char utf8buf[(3 * bytesread) + 1]; | ||
900 | |||
901 | unicode_munge( tag, utf8buf, &bytesread ); | ||
902 | |||
903 | if(bytesread >= buffersize - bufferpos) | ||
904 | bytesread = buffersize - bufferpos - 1; | ||
905 | |||
906 | for (j = 0; j < bytesread; j++) | ||
907 | tag[j] = utf8buf[j]; | ||
908 | |||
909 | /* remove trailing spaces */ | ||
910 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | ||
911 | bytesread--; | ||
912 | } | ||
913 | |||
914 | tag[bytesread] = 0; | ||
915 | bufferpos += bytesread + 1; | ||
916 | |||
917 | if (ptag) | ||
918 | *ptag = tag; | ||
919 | |||
920 | if( tr->ppFunc ) | ||
921 | bufferpos = tr->ppFunc(entry, tag, bufferpos); | ||
922 | |||
923 | /* Seek to the next frame */ | ||
924 | if(framelen < totframelen) | ||
925 | lseek(fd, totframelen - framelen, SEEK_CUR); | ||
926 | break; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | if( i == TAGLIST_SIZE ) { | ||
931 | /* no tag in tagList was found, or it was a repeat. | ||
932 | skip it using the total size */ | ||
933 | |||
934 | if(global_unsynch && version <= ID3_VER_2_3) { | ||
935 | size -= skip_unsynched(fd, totframelen); | ||
936 | } else { | ||
937 | size -= totframelen; | ||
938 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) | ||
939 | return; | ||
940 | } | ||
941 | } | ||
942 | } | ||
943 | } | ||
944 | |||
945 | /* | ||
946 | * Calculates the size of the ID3v2 tag. | ||
947 | * | ||
948 | * Arguments: file - the file to search for a tag. | ||
949 | * | ||
950 | * Returns: the size of the tag or 0 if none was found | ||
951 | */ | ||
952 | int getid3v2len(int fd) | ||
953 | { | ||
954 | char buf[6]; | ||
955 | int offset; | ||
956 | |||
957 | /* Make sure file has a ID3 tag */ | ||
958 | if((-1 == lseek(fd, 0, SEEK_SET)) || | ||
959 | (read(fd, buf, 6) != 6) || | ||
960 | (strncmp(buf, "ID3", strlen("ID3")) != 0)) | ||
961 | offset = 0; | ||
962 | |||
963 | /* Now check what the ID3v2 size field says */ | ||
964 | else | ||
965 | if(read(fd, buf, 4) != 4) | ||
966 | offset = 0; | ||
967 | else | ||
968 | offset = unsync(buf[0], buf[1], buf[2], buf[3]) + 10; | ||
969 | |||
970 | logf("ID3V2 Length: 0x%x", offset); | ||
971 | return offset; | ||
972 | } | ||
973 | |||
974 | /* | ||
975 | * Calculates the length (in milliseconds) of an MP3 file. | ||
976 | * | ||
977 | * Modified to only use integers. | ||
978 | * | ||
979 | * Arguments: file - the file to calculate the length upon | ||
980 | * entry - the entry to update with the length | ||
981 | * | ||
982 | * Returns: the song length in milliseconds, | ||
983 | * 0 means that it couldn't be calculated | ||
984 | */ | ||
985 | static int getsonglength(int fd, struct mp3entry *entry) | ||
986 | { | ||
987 | unsigned long filetime = 0; | ||
988 | struct mp3info info; | ||
989 | long bytecount; | ||
990 | |||
991 | /* Start searching after ID3v2 header */ | ||
992 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) | ||
993 | return 0; | ||
994 | |||
995 | bytecount = get_mp3file_info(fd, &info); | ||
996 | |||
997 | logf("Space between ID3V2 tag and first audio frame: 0x%lx bytes", | ||
998 | bytecount); | ||
999 | |||
1000 | if(bytecount < 0) | ||
1001 | return -1; | ||
1002 | |||
1003 | bytecount += entry->id3v2len; | ||
1004 | |||
1005 | /* Validate byte count, in case the file has been edited without | ||
1006 | * updating the header. | ||
1007 | */ | ||
1008 | if (info.byte_count) | ||
1009 | { | ||
1010 | const unsigned long expected = entry->filesize - entry->id3v1len | ||
1011 | - entry->id3v2len; | ||
1012 | const unsigned long diff = MAX(10240, info.byte_count / 20); | ||
1013 | |||
1014 | if ((info.byte_count > expected + diff) | ||
1015 | || (info.byte_count < expected - diff)) | ||
1016 | { | ||
1017 | logf("Note: info.byte_count differs from expected value by " | ||
1018 | "%ld bytes", labs((long) (expected - info.byte_count))); | ||
1019 | info.byte_count = 0; | ||
1020 | info.frame_count = 0; | ||
1021 | info.file_time = 0; | ||
1022 | info.enc_padding = 0; | ||
1023 | |||
1024 | /* Even if the bitrate was based on "known bad" values, it | ||
1025 | * should still be better for VBR files than using the bitrate | ||
1026 | * of the first audio frame. | ||
1027 | */ | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | entry->bitrate = info.bitrate; | ||
1032 | entry->frequency = info.frequency; | ||
1033 | entry->version = info.version; | ||
1034 | entry->layer = info.layer; | ||
1035 | switch(entry->layer) { | ||
1036 | #if CONFIG_CODEC==SWCODEC | ||
1037 | case 0: | ||
1038 | entry->codectype=AFMT_MPA_L1; | ||
1039 | break; | ||
1040 | #endif | ||
1041 | case 1: | ||
1042 | entry->codectype=AFMT_MPA_L2; | ||
1043 | break; | ||
1044 | case 2: | ||
1045 | entry->codectype=AFMT_MPA_L3; | ||
1046 | break; | ||
1047 | } | ||
1048 | |||
1049 | /* If the file time hasn't been established, this may be a fixed | ||
1050 | rate MP3, so just use the default formula */ | ||
1051 | |||
1052 | filetime = info.file_time; | ||
1053 | |||
1054 | if(filetime == 0) | ||
1055 | { | ||
1056 | /* Prevent a division by zero */ | ||
1057 | if (info.bitrate < 8) | ||
1058 | filetime = 0; | ||
1059 | else | ||
1060 | filetime = (entry->filesize - bytecount) / (info.bitrate / 8); | ||
1061 | /* bitrate is in kbps so this delivers milliseconds. Doing bitrate / 8 | ||
1062 | * instead of filesize * 8 is exact, because mpeg audio bitrates are | ||
1063 | * always multiples of 8, and it avoids overflows. */ | ||
1064 | } | ||
1065 | |||
1066 | entry->frame_count = info.frame_count; | ||
1067 | |||
1068 | entry->vbr = info.is_vbr; | ||
1069 | entry->has_toc = info.has_toc; | ||
1070 | |||
1071 | #if CONFIG_CODEC==SWCODEC | ||
1072 | entry->lead_trim = info.enc_delay; | ||
1073 | entry->tail_trim = info.enc_padding; | ||
1074 | #endif | ||
1075 | |||
1076 | memcpy(entry->toc, info.toc, sizeof(info.toc)); | ||
1077 | |||
1078 | entry->vbr_header_pos = info.vbr_header_pos; | ||
1079 | |||
1080 | /* Update the seek point for the first playable frame */ | ||
1081 | entry->first_frame_offset = bytecount; | ||
1082 | logf("First frame is at %lx", entry->first_frame_offset); | ||
1083 | |||
1084 | return filetime; | ||
1085 | } | ||
1086 | |||
1087 | /* | ||
1088 | * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) | ||
1089 | * about an MP3 file and updates it's entry accordingly. | ||
1090 | * | ||
1091 | Note, that this returns true for successful, false for error! */ | ||
1092 | bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename) | ||
1093 | { | ||
1094 | #if CONFIG_CODEC != SWCODEC | ||
1095 | memset(entry, 0, sizeof(struct mp3entry)); | ||
1096 | #endif | ||
1097 | |||
1098 | strncpy(entry->path, filename, sizeof(entry->path)); | ||
1099 | |||
1100 | entry->title = NULL; | ||
1101 | entry->filesize = filesize(fd); | ||
1102 | entry->id3v2len = getid3v2len(fd); | ||
1103 | entry->tracknum = 0; | ||
1104 | entry->discnum = 0; | ||
1105 | |||
1106 | if (entry->id3v2len) | ||
1107 | setid3v2title(fd, entry); | ||
1108 | int len = getsonglength(fd, entry); | ||
1109 | if (len < 0) | ||
1110 | return false; | ||
1111 | entry->length = len; | ||
1112 | |||
1113 | /* Subtract the meta information from the file size to get | ||
1114 | the true size of the MP3 stream */ | ||
1115 | entry->filesize -= entry->first_frame_offset; | ||
1116 | |||
1117 | /* only seek to end of file if no id3v2 tags were found */ | ||
1118 | if (!entry->id3v2len) { | ||
1119 | setid3v1title(fd, entry); | ||
1120 | } | ||
1121 | |||
1122 | if(!entry->length || (entry->filesize < 8 )) | ||
1123 | /* no song length or less than 8 bytes is hereby considered to be an | ||
1124 | invalid mp3 and won't be played by us! */ | ||
1125 | return false; | ||
1126 | |||
1127 | return true; | ||
1128 | } | ||
1129 | |||
1130 | #ifdef DEBUG_STANDALONE | ||
1131 | |||
1132 | char *secs2str(int ms) | ||
1133 | { | ||
1134 | static char buffer[32]; | ||
1135 | int secs = ms/1000; | ||
1136 | ms %= 1000; | ||
1137 | snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100); | ||
1138 | return buffer; | ||
1139 | } | ||
1140 | |||
1141 | int main(int argc, char **argv) | ||
1142 | { | ||
1143 | int i; | ||
1144 | for(i=1; i<argc; i++) { | ||
1145 | struct mp3entry mp3; | ||
1146 | mp3.album = "Bogus"; | ||
1147 | if(mp3info(&mp3, argv[i], false)) { | ||
1148 | printf("Failed to get %s\n", argv[i]); | ||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1152 | printf("****** File: %s\n" | ||
1153 | " Title: %s\n" | ||
1154 | " Artist: %s\n" | ||
1155 | " Album: %s\n" | ||
1156 | " Genre: %s (%d) \n" | ||
1157 | " Composer: %s\n" | ||
1158 | " Year: %s (%d)\n" | ||
1159 | " Track: %s (%d)\n" | ||
1160 | " Length: %s / %d s\n" | ||
1161 | " Bitrate: %d\n" | ||
1162 | " Frequency: %d\n", | ||
1163 | argv[i], | ||
1164 | mp3.title?mp3.title:"<blank>", | ||
1165 | mp3.artist?mp3.artist:"<blank>", | ||
1166 | mp3.album?mp3.album:"<blank>", | ||
1167 | mp3.genre_string?mp3.genre_string:"<blank>", | ||
1168 | mp3.genre, | ||
1169 | mp3.composer?mp3.composer:"<blank>", | ||
1170 | mp3.year_string?mp3.year_string:"<blank>", | ||
1171 | mp3.year, | ||
1172 | mp3.track_string?mp3.track_string:"<blank>", | ||
1173 | mp3.tracknum, | ||
1174 | secs2str(mp3.length), | ||
1175 | mp3.length/1000, | ||
1176 | mp3.bitrate, | ||
1177 | mp3.frequency); | ||
1178 | } | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | #endif | ||
diff --git a/apps/metadata/mp4.c b/apps/metadata/mp4.c index 493bc48da7..803f82f4f3 100644 --- a/apps/metadata/mp4.c +++ b/apps/metadata/mp4.c | |||
@@ -26,7 +26,7 @@ | |||
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "errno.h" | 28 | #include "errno.h" |
29 | #include "id3.h" | 29 | #include "metadata.h" |
30 | #include "metadata_common.h" | 30 | #include "metadata_common.h" |
31 | #include "metadata_parsers.h" | 31 | #include "metadata_parsers.h" |
32 | #include "logf.h" | 32 | #include "logf.h" |
diff --git a/apps/metadata/mpc.c b/apps/metadata/mpc.c index dd83515246..5ab1241153 100644 --- a/apps/metadata/mpc.c +++ b/apps/metadata/mpc.c | |||
@@ -22,7 +22,7 @@ | |||
22 | #include <string.h> | 22 | #include <string.h> |
23 | #include <inttypes.h> | 23 | #include <inttypes.h> |
24 | #include "system.h" | 24 | #include "system.h" |
25 | #include "id3.h" | 25 | #include "metadata.h" |
26 | #include "metadata_common.h" | 26 | #include "metadata_common.h" |
27 | #include "metadata_parsers.h" | 27 | #include "metadata_parsers.h" |
28 | #include "logf.h" | 28 | #include "logf.h" |
diff --git a/apps/metadata/ogg.c b/apps/metadata/ogg.c index edb55f53a3..cd4c85f46e 100644 --- a/apps/metadata/ogg.c +++ b/apps/metadata/ogg.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "logf.h" | 31 | #include "logf.h" |
diff --git a/apps/metadata/sid.c b/apps/metadata/sid.c index 8741ce6dcb..bab7233752 100644 --- a/apps/metadata/sid.c +++ b/apps/metadata/sid.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
diff --git a/apps/metadata/spc.c b/apps/metadata/spc.c index 094fcce96d..786c678c4c 100644 --- a/apps/metadata/spc.c +++ b/apps/metadata/spc.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "debug.h" | 31 | #include "debug.h" |
diff --git a/apps/metadata/vorbis.c b/apps/metadata/vorbis.c index 19b791570b..cfaa7158f1 100644 --- a/apps/metadata/vorbis.c +++ b/apps/metadata/vorbis.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "structec.h" | 31 | #include "structec.h" |
diff --git a/apps/metadata/wave.c b/apps/metadata/wave.c index 229d61599e..cf676f89b0 100644 --- a/apps/metadata/wave.c +++ b/apps/metadata/wave.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | 31 | ||
diff --git a/apps/metadata/wavpack.c b/apps/metadata/wavpack.c index c695203292..a5a342bf04 100644 --- a/apps/metadata/wavpack.c +++ b/apps/metadata/wavpack.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "system.h" | 27 | #include "system.h" |
28 | #include "id3.h" | 28 | #include "metadata.h" |
29 | #include "metadata_common.h" | 29 | #include "metadata_common.h" |
30 | #include "metadata_parsers.h" | 30 | #include "metadata_parsers.h" |
31 | #include "logf.h" | 31 | #include "logf.h" |