summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Peskett <rockbox@peskett.co.uk>2011-12-16 10:09:41 +0000
committerNick Peskett <rockbox@peskett.co.uk>2011-12-16 10:09:41 +0000
commit02fd314a0b426d6d445e2c9b167681ade6b0c1d2 (patch)
treead6100925dadaae0a69b8ec87b03eb8c1c5b23ab
parent014003afac4e6ab5f132df25e0e92106ed21607a (diff)
downloadrockbox-02fd314a0b426d6d445e2c9b167681ade6b0c1d2.tar.gz
rockbox-02fd314a0b426d6d445e2c9b167681ade6b0c1d2.zip
FS #12419 : Support for embedded cuesheets.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31321 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/cuesheet.c127
-rw-r--r--apps/cuesheet.h15
-rw-r--r--apps/metadata.c4
-rw-r--r--apps/metadata.h16
-rw-r--r--apps/metadata/id3tags.c34
-rw-r--r--apps/metadata/vorbis.c20
-rw-r--r--apps/mpeg.c6
-rw-r--r--apps/playback.c6
-rw-r--r--manual/configure_rockbox/playback_options.tex8
9 files changed, 192 insertions, 44 deletions
diff --git a/apps/cuesheet.c b/apps/cuesheet.c
index 935af60898..ab4063a66a 100644
--- a/apps/cuesheet.c
+++ b/apps/cuesheet.c
@@ -42,21 +42,29 @@
42 42
43#define CUE_DIR ROCKBOX_DIR "/cue" 43#define CUE_DIR ROCKBOX_DIR "/cue"
44 44
45bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path) 45bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cue_file)
46{ 46{
47 /* DEBUGF("look for cue file\n"); */ 47 /* DEBUGF("look for cue file\n"); */
48 48
49 char cuepath[MAX_PATH]; 49 char cuepath[MAX_PATH];
50 char *dot, *slash; 50 char *dot, *slash;
51 51
52 slash = strrchr(trackpath, '/'); 52 if (track_id3->embed_cuesheet.present)
53 if (!slash)
54 { 53 {
55 found_cue_path = NULL; 54 cue_file->pos = track_id3->embed_cuesheet.pos;
56 return false; 55 cue_file->size = track_id3->embed_cuesheet.size;
56 cue_file->encoding = track_id3->embed_cuesheet.encoding;
57 strlcpy(cue_file->path, track_id3->path, MAX_PATH);
58 return true;
57 } 59 }
58 60
59 strlcpy(cuepath, trackpath, MAX_PATH); 61 cue_file->pos = 0;
62 cue_file->size = 0;
63 cue_file->path[0] = '\0';
64 slash = strrchr(track_id3->path, '/');
65 if (!slash)
66 return false;
67 strlcpy(cuepath, track_id3->path, MAX_PATH);
60 dot = strrchr(cuepath, '.'); 68 dot = strrchr(cuepath, '.');
61 strcpy(dot, ".cue"); 69 strcpy(dot, ".cue");
62 70
@@ -67,15 +75,10 @@ bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path)
67 char *dot = strrchr(cuepath, '.'); 75 char *dot = strrchr(cuepath, '.');
68 strcpy(dot, ".cue"); 76 strcpy(dot, ".cue");
69 if (!file_exists(cuepath)) 77 if (!file_exists(cuepath))
70 {
71 if (found_cue_path)
72 found_cue_path = NULL;
73 return false; 78 return false;
74 }
75 } 79 }
76 80
77 if (found_cue_path) 81 strlcpy(cue_file->path, cuepath, MAX_PATH);
78 strlcpy(found_cue_path, cuepath, MAX_PATH);
79 return true; 82 return true;
80} 83}
81 84
@@ -99,29 +102,81 @@ static char *get_string(const char *line)
99 return start; 102 return start;
100} 103}
101 104
102/* parse cuesheet "file" and store the information in "cue" */ 105/* parse cuesheet "cue_file" and store the information in "cue" */
103bool parse_cuesheet(char *file, struct cuesheet *cue) 106bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
104{ 107{
105 char line[MAX_PATH]; 108 char line[MAX_PATH];
106 char *s; 109 char *s;
107 bool utf8 = false; 110 unsigned char char_enc = CHAR_ENC_ISO_8859_1;
111 bool is_embedded = false;
112 int line_len;
113 int bytes_left = 0;
114 int read_bytes = MAX_PATH;
115 unsigned char utf16_buf[MAX_PATH];
116
117 int fd = open(cue_file->path, O_RDONLY, 0644);
118 if(fd < 0)
119 return false;
120 if (cue_file->pos > 0)
121 {
122 is_embedded = true;
123 lseek(fd, cue_file->pos, SEEK_SET);
124 bytes_left = cue_file->size;
125 char_enc = cue_file->encoding;
126 }
108 127
109 int fd = open_utf8(file,O_RDONLY); 128 /* Look for a Unicode BOM */
110 if (fd < 0) 129 unsigned char bom_read = 0;
130 read(fd, line, 3);
131 if(!memcmp(line, "\xef\xbb\xbf", 3))
111 { 132 {
112 /* couln't open the file */ 133 char_enc = CHAR_ENC_UTF_8;
113 return false; 134 bom_read = 3;
135 }
136 else if(!memcmp(line, "\xff\xfe", 2))
137 {
138 char_enc = CHAR_ENC_UTF_16_LE;
139 bom_read = 2;
140 }
141 else if(!memcmp(line, "\xfe\xff", 2))
142 {
143 char_enc = CHAR_ENC_UTF_16_BE;
144 bom_read = 2;
145 }
146 if (bom_read < 3 )
147 lseek(fd, cue_file->pos + bom_read, SEEK_SET);
148 if (is_embedded)
149 {
150 if (bom_read > 0)
151 bytes_left -= bom_read;
152 if (read_bytes > bytes_left)
153 read_bytes = bytes_left;
114 } 154 }
115 if(lseek(fd, 0, SEEK_CUR) > 0)
116 utf8 = true;
117 155
118 /* Initialization */ 156 /* Initialization */
119 memset(cue, 0, sizeof(struct cuesheet)); 157 memset(cue, 0, sizeof(struct cuesheet));
120 strcpy(cue->path, file); 158 strcpy(cue->path, cue_file->path);
121 cue->curr_track = cue->tracks; 159 cue->curr_track = cue->tracks;
122 160
123 while ( read_line(fd,line,MAX_PATH) && cue->track_count < MAX_TRACKS ) 161 while ((line_len = read_line(fd, line, read_bytes)) > 0
162 && cue->track_count < MAX_TRACKS )
124 { 163 {
164 if (char_enc == CHAR_ENC_UTF_16_LE)
165 {
166 s = utf16LEdecode(line, utf16_buf, line_len);
167 /* terminate the string at the newline */
168 *s = '\0';
169 strcpy(line, utf16_buf);
170 /* chomp the trailing 0 after the newline */
171 lseek(fd, 1, SEEK_CUR);
172 line_len++;
173 }
174 else if (char_enc == CHAR_ENC_UTF_16_BE)
175 {
176 s = utf16BEdecode(line, utf16_buf, line_len);
177 *s = '\0';
178 strcpy(line, utf16_buf);
179 }
125 s = skip_whitespace(line); 180 s = skip_whitespace(line);
126 181
127 if (!strncmp(s, "TRACK", 5)) 182 if (!strncmp(s, "TRACK", 5))
@@ -169,9 +224,10 @@ bool parse_cuesheet(char *file, struct cuesheet *cue)
169 224
170 if (dest) 225 if (dest)
171 { 226 {
172 if (!utf8) 227 if (char_enc == CHAR_ENC_ISO_8859_1)
173 { 228 {
174 dest = iso_decode(string, dest, -1, MIN(strlen(string), MAX_NAME)); 229 dest = iso_decode(string, dest, -1,
230 MIN(strlen(string), MAX_NAME));
175 *dest = '\0'; 231 *dest = '\0';
176 } 232 }
177 else 233 else
@@ -180,6 +236,14 @@ bool parse_cuesheet(char *file, struct cuesheet *cue)
180 } 236 }
181 } 237 }
182 } 238 }
239 if (is_embedded)
240 {
241 bytes_left -= line_len;
242 if (bytes_left <= 0)
243 break;
244 if (bytes_left < read_bytes)
245 read_bytes = bytes_left;
246 }
183 } 247 }
184 close(fd); 248 close(fd);
185 249
@@ -256,7 +320,7 @@ void browse_cuesheet(struct cuesheet *cue)
256 bool done = false; 320 bool done = false;
257 int sel; 321 int sel;
258 char title[MAX_PATH]; 322 char title[MAX_PATH];
259 char cuepath[MAX_PATH]; 323 struct cuesheet_file cue_file;
260 struct mp3entry *id3 = audio_current_track(); 324 struct mp3entry *id3 = audio_current_track();
261 325
262 snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title); 326 snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title);
@@ -283,8 +347,8 @@ void browse_cuesheet(struct cuesheet *cue)
283 id3 = audio_current_track(); 347 id3 = audio_current_track();
284 if (id3 && *id3->path && strcmp(id3->path, "No file!")) 348 if (id3 && *id3->path && strcmp(id3->path, "No file!"))
285 { 349 {
286 look_for_cuesheet_file(id3->path, cuepath); 350 look_for_cuesheet_file(id3, &cue_file);
287 if (id3->cuesheet && !strcmp(cue->path, cuepath)) 351 if (id3->cuesheet && !strcmp(cue->path, cue_file.path))
288 { 352 {
289 sel = gui_synclist_get_sel_pos(&lists); 353 sel = gui_synclist_get_sel_pos(&lists);
290 seek(cue->tracks[sel/2].offset); 354 seek(cue->tracks[sel/2].offset);
@@ -300,11 +364,16 @@ void browse_cuesheet(struct cuesheet *cue)
300bool display_cuesheet_content(char* filename) 364bool display_cuesheet_content(char* filename)
301{ 365{
302 size_t bufsize = 0; 366 size_t bufsize = 0;
367 struct cuesheet_file cue_file;
303 struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize); 368 struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
304 if (!cue || bufsize < sizeof(struct cuesheet)) 369 if (!cue || bufsize < sizeof(struct cuesheet))
305 return false; 370 return false;
306 371
307 if (!parse_cuesheet(filename, cue)) 372 strlcpy(cue_file.path, filename, MAX_PATH);
373 cue_file.pos = 0;
374 cue_file.size = 0;
375
376 if (!parse_cuesheet(&cue_file, cue))
308 return false; 377 return false;
309 378
310 browse_cuesheet(cue); 379 browse_cuesheet(cue);
diff --git a/apps/cuesheet.h b/apps/cuesheet.h
index e8d77ca3a9..31841dacf6 100644
--- a/apps/cuesheet.h
+++ b/apps/cuesheet.h
@@ -51,11 +51,18 @@ struct cuesheet {
51 struct cue_track_info *curr_track; 51 struct cue_track_info *curr_track;
52}; 52};
53 53
54/* looks if there is a cuesheet file that has a name matching "trackpath" */ 54struct cuesheet_file {
55bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path); 55 char path[MAX_PATH];
56 int size;
57 off_t pos;
58 enum character_encoding encoding;
59};
60
61/* looks if there is a cuesheet file with a name matching path of "track_id3" */
62bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cue_file);
56 63
57/* parse cuesheet "file" and store the information in "cue" */ 64/* parse cuesheet_file "cue_file" and store the information in "cue" */
58bool parse_cuesheet(char *file, struct cuesheet *cue); 65bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue);
59 66
60/* reads a cuesheet to find the audio track associated to it */ 67/* reads a cuesheet to find the audio track associated to it */
61bool get_trackname_from_cuesheet(char *filename, char *buf); 68bool get_trackname_from_cuesheet(char *filename, char *buf);
diff --git a/apps/metadata.c b/apps/metadata.c
index 7479de105f..898436781b 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -438,6 +438,10 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
438 /* Take our best guess at the codec type based on file extension */ 438 /* Take our best guess at the codec type based on file extension */
439 id3->codectype = probe_file_format(trackname); 439 id3->codectype = probe_file_format(trackname);
440 440
441 /* default values for embedded cuesheets */
442 id3->embed_cuesheet.present = false;
443 id3->embed_cuesheet.pos = 0;
444
441 entry = &audio_formats[id3->codectype]; 445 entry = &audio_formats[id3->codectype];
442 446
443 /* Load codec specific track tag information and confirm the codec type. */ 447 /* Load codec specific track tag information and confirm the codec type. */
diff --git a/apps/metadata.h b/apps/metadata.h
index 3676bd8e24..0c6768d3d9 100644
--- a/apps/metadata.h
+++ b/apps/metadata.h
@@ -217,6 +217,21 @@ struct mp3_albumart {
217}; 217};
218#endif 218#endif
219 219
220enum character_encoding {
221 CHAR_ENC_ISO_8859_1 = 1,
222 CHAR_ENC_UTF_8,
223 CHAR_ENC_UTF_16_LE,
224 CHAR_ENC_UTF_16_BE,
225};
226
227/* cache embedded cuesheet details */
228struct embed_cuesheet {
229 bool present;
230 int size;
231 off_t pos;
232 enum character_encoding encoding;
233};
234
220struct mp3entry { 235struct mp3entry {
221 char path[MAX_PATH]; 236 char path[MAX_PATH];
222 char* title; 237 char* title;
@@ -307,6 +322,7 @@ struct mp3entry {
307#endif 322#endif
308 323
309 /* Cuesheet support */ 324 /* Cuesheet support */
325 struct embed_cuesheet embed_cuesheet;
310 struct cuesheet *cuesheet; 326 struct cuesheet *cuesheet;
311 327
312 /* Musicbrainz Track ID */ 328 /* Musicbrainz Track ID */
diff --git a/apps/metadata/id3tags.c b/apps/metadata/id3tags.c
index dcf71f71bf..e9b59e012a 100644
--- a/apps/metadata/id3tags.c
+++ b/apps/metadata/id3tags.c
@@ -995,6 +995,40 @@ void setid3v2title(int fd, struct mp3entry *entry)
995 if(bytesread >= buffersize - bufferpos) 995 if(bytesread >= buffersize - bufferpos)
996 bytesread = buffersize - bufferpos - 1; 996 bytesread = buffersize - bufferpos - 1;
997 997
998 if ( /* Is it an embedded cuesheet? */
999 (tr->tag_length == 4 && !memcmp(header, "TXXX", 4)) &&
1000 (bytesread >= 14 && !strncmp(utf8buf, "CUESHEET", 8))
1001 ) {
1002 unsigned char char_enc = 0;
1003 /* 0CUESHEET0 = 10 bytes */
1004 unsigned char cuesheet_offset = 10;
1005 switch (tag[0]) {
1006 case 0x00:
1007 char_enc = CHAR_ENC_ISO_8859_1;
1008 break;
1009 case 0x01:
1010 char_enc = CHAR_ENC_UTF_16_LE;
1011 cuesheet_offset += cuesheet_offset+1;
1012 break;
1013 case 0x02:
1014 char_enc = CHAR_ENC_UTF_16_BE;
1015 cuesheet_offset += cuesheet_offset+1;
1016 break;
1017 case 0x03:
1018 char_enc = CHAR_ENC_UTF_8;
1019 break;
1020 }
1021 if (char_enc > 0) {
1022 entry->embed_cuesheet.present = true;
1023 entry->embed_cuesheet.pos = lseek(fd, 0, SEEK_CUR)
1024 - framelen + cuesheet_offset;
1025 entry->embed_cuesheet.size = totframelen
1026 - cuesheet_offset;
1027 entry->embed_cuesheet.encoding = char_enc;
1028 }
1029 break;
1030 }
1031
998 for (j = 0; j < bytesread; j++) 1032 for (j = 0; j < bytesread; j++)
999 tag[j] = utf8buf[j]; 1033 tag[j] = utf8buf[j];
1000 1034
diff --git a/apps/metadata/vorbis.c b/apps/metadata/vorbis.c
index f6d3af1cef..29848daa19 100644
--- a/apps/metadata/vorbis.c
+++ b/apps/metadata/vorbis.c
@@ -341,15 +341,29 @@ long read_vorbis_tags(int fd, struct mp3entry *id3,
341 } 341 }
342 342
343 len -= read_len; 343 len -= read_len;
344 read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len);
344 345
345 if (file_read_string(&file, id3->path, sizeof(id3->path), -1, len) < 0) 346 if (read_len < 0)
346 { 347 {
347 return 0; 348 return 0;
348 } 349 }
349 350
350 logf("Vorbis comment %d: %s=%s", i, name, id3->path); 351 logf("Vorbis comment %d: %s=%s", i, name, id3->path);
351 len = parse_tag(name, id3->path, id3, buf, buf_remaining, 352
352 TAGTYPE_VORBIS); 353 /* Is it an embedded cuesheet? */
354 if (!strcasecmp(name, "CUESHEET"))
355 {
356 id3->embed_cuesheet.present = true;
357 id3->embed_cuesheet.pos = lseek(file.fd, 0, SEEK_CUR) - read_len;
358 id3->embed_cuesheet.size = len;
359 id3->embed_cuesheet.encoding = CHAR_ENC_UTF_8;
360 }
361 else
362 {
363 len = parse_tag(name, id3->path, id3, buf, buf_remaining,
364 TAGTYPE_VORBIS);
365 }
366
353 buf += len; 367 buf += len;
354 buf_remaining -= len; 368 buf_remaining -= len;
355 } 369 }
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 698695b72d..4fbc40ff1e 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -2170,10 +2170,10 @@ struct mp3entry* audio_current_track(void)
2170 if (!checked_for_cuesheet && curr_cuesheet && id3->cuesheet == NULL) 2170 if (!checked_for_cuesheet && curr_cuesheet && id3->cuesheet == NULL)
2171 { 2171 {
2172 checked_for_cuesheet = true; /* only check once per track */ 2172 checked_for_cuesheet = true; /* only check once per track */
2173 char cuepath[MAX_PATH]; 2173 struct cuesheet_file cue_file;
2174 2174
2175 if (look_for_cuesheet_file(id3->path, cuepath) && 2175 if (look_for_cuesheet_file(id3, &cue_file)) &&
2176 parse_cuesheet(cuepath, curr_cuesheet)) 2176 parse_cuesheet(&cue_file, curr_cuesheet))
2177 { 2177 {
2178 id3->cuesheet = curr_cuesheet; 2178 id3->cuesheet = curr_cuesheet;
2179 } 2179 }
diff --git a/apps/playback.c b/apps/playback.c
index 2739118aeb..36fbd88832 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -1485,12 +1485,12 @@ static bool audio_load_cuesheet(struct track_info *info,
1485 /* If error other than a full buffer, then mark it "unsupported" to 1485 /* If error other than a full buffer, then mark it "unsupported" to
1486 avoid reloading attempt */ 1486 avoid reloading attempt */
1487 int hid = ERR_UNSUPPORTED_TYPE; 1487 int hid = ERR_UNSUPPORTED_TYPE;
1488 char cuepath[MAX_PATH]; 1488 struct cuesheet_file cue_file;
1489 1489
1490#ifdef HAVE_IO_PRIORITY 1490#ifdef HAVE_IO_PRIORITY
1491 buf_back_off_storage(true); 1491 buf_back_off_storage(true);
1492#endif 1492#endif
1493 if (look_for_cuesheet_file(track_id3->path, cuepath)) 1493 if (look_for_cuesheet_file(track_id3, &cue_file))
1494 { 1494 {
1495 hid = bufalloc(NULL, sizeof (struct cuesheet), TYPE_CUESHEET); 1495 hid = bufalloc(NULL, sizeof (struct cuesheet), TYPE_CUESHEET);
1496 1496
@@ -1499,7 +1499,7 @@ static bool audio_load_cuesheet(struct track_info *info,
1499 void *cuesheet = NULL; 1499 void *cuesheet = NULL;
1500 bufgetdata(hid, sizeof (struct cuesheet), &cuesheet); 1500 bufgetdata(hid, sizeof (struct cuesheet), &cuesheet);
1501 1501
1502 if (parse_cuesheet(cuepath, (struct cuesheet *)cuesheet)) 1502 if (parse_cuesheet(&cue_file, (struct cuesheet *)cuesheet))
1503 { 1503 {
1504 /* Indicate cuesheet is present (while track remains 1504 /* Indicate cuesheet is present (while track remains
1505 buffered) */ 1505 buffered) */
diff --git a/manual/configure_rockbox/playback_options.tex b/manual/configure_rockbox/playback_options.tex
index ec2d99b8da..b3a5c82ee6 100644
--- a/manual/configure_rockbox/playback_options.tex
+++ b/manual/configure_rockbox/playback_options.tex
@@ -269,10 +269,14 @@ you to configure settings related to audio playback.
269 effect. 269 effect.
270 270
271 Cuesheet files should have the same file name as the audio file they 271 Cuesheet files should have the same file name as the audio file they
272 reference, except with the extension \fname{.cue}. This file can either reside in 272 reference, except with the extension \fname{.cue}. This file can either
273 the same directory as the audio file (checked first), or within the 273 reside in the same directory as the audio file (checked first), or within the
274 \fname{.rockbox/cue} directory. 274 \fname{.rockbox/cue} directory.
275 275
276 The contents of a cuesheet file can also be embedded within the metadata of
277 an audio file. There is currently support for the FLAC tag/ Vorbis comment
278 \emph{CUESHEET} or the ID3v2 \emph{TXXX CUESHEET} tag.
279
276\section{Skip Length}\index{Skip Length} 280\section{Skip Length}\index{Skip Length}
277 Designed to speed up navigation when listening to long audio tracks, 281 Designed to speed up navigation when listening to long audio tracks,
278 \setting{Skip Length} changes the behaviour of 282 \setting{Skip Length} changes the behaviour of