diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2003-03-10 14:55:31 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2003-03-10 14:55:31 +0000 |
commit | a039091187f40d018b6353b8c13de7a01d3a6fe0 (patch) | |
tree | 08f7eb86e86e2c61f4d36f9c91731cb93252ba84 /firmware | |
parent | 22cbe938feb48895d7488449835d3ee577399057 (diff) | |
download | rockbox-a039091187f40d018b6353b8c13de7a01d3a6fe0.tar.gz rockbox-a039091187f40d018b6353b8c13de7a01d3a6fe0.zip |
New ID3 and MP3 stream parser, plus not-yet-ready Xing header generation code
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3410 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/id3.h | 9 | ||||
-rw-r--r-- | firmware/export/mp3data.h | 66 | ||||
-rw-r--r-- | firmware/export/mpeg.h | 1 | ||||
-rw-r--r-- | firmware/id3.c | 371 | ||||
-rw-r--r-- | firmware/mp3data.c | 664 | ||||
-rw-r--r-- | firmware/mpeg.c | 320 |
6 files changed, 1008 insertions, 423 deletions
diff --git a/firmware/export/id3.h b/firmware/export/id3.h index 55ce002c2e..30be4bf883 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h | |||
@@ -39,15 +39,18 @@ struct mp3entry { | |||
39 | unsigned int first_frame_offset; /* Byte offset to first real MP3 frame. | 39 | unsigned int first_frame_offset; /* Byte offset to first real MP3 frame. |
40 | Used for skipping leading garbage to | 40 | Used for skipping leading garbage to |
41 | avoid gaps between tracks. */ | 41 | avoid gaps between tracks. */ |
42 | unsigned int xing_header_pos; | ||
42 | unsigned int filesize; /* in bytes */ | 43 | unsigned int filesize; /* in bytes */ |
43 | unsigned int length; /* song length */ | 44 | unsigned int length; /* song length */ |
44 | unsigned int elapsed; /* ms played */ | 45 | unsigned int elapsed; /* ms played */ |
46 | |||
47 | /* MP3 stream specific info */ | ||
45 | long bpf; /* bytes per frame */ | 48 | long bpf; /* bytes per frame */ |
46 | long tpf; /* time per frame */ | 49 | long tpf; /* time per frame */ |
47 | 50 | ||
48 | /* Xing VBR fields */ | 51 | /* Xing VBR fields */ |
49 | bool vbr; | 52 | bool vbr; |
50 | unsigned char vbrflags; | 53 | bool has_toc; /* True if there is a VBR header in the file */ |
51 | unsigned char toc[100];/* table of contents */ | 54 | unsigned char toc[100];/* table of contents */ |
52 | 55 | ||
53 | /* these following two fields are used for local buffering */ | 56 | /* these following two fields are used for local buffering */ |
@@ -59,10 +62,6 @@ struct mp3entry { | |||
59 | int index; /* playlist index */ | 62 | int index; /* playlist index */ |
60 | }; | 63 | }; |
61 | 64 | ||
62 | #define VBR_FRAMES_FLAG 0x01 | ||
63 | #define VBR_BYTES_FLAG 0x02 | ||
64 | #define VBR_TOC_FLAG 0x04 | ||
65 | |||
66 | enum { | 65 | enum { |
67 | ID3_VER_1_0 = 1, | 66 | ID3_VER_1_0 = 1, |
68 | ID3_VER_1_1, | 67 | ID3_VER_1_1, |
diff --git a/firmware/export/mp3data.h b/firmware/export/mp3data.h new file mode 100644 index 0000000000..a1018ebaa2 --- /dev/null +++ b/firmware/export/mp3data.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _MP3DATA_H_ | ||
21 | #define _MP3DATA_H_ | ||
22 | |||
23 | #define MPEG_VERSION2_5 0 | ||
24 | #define MPEG_VERSION1 1 | ||
25 | #define MPEG_VERSION2 2 | ||
26 | |||
27 | struct mp3info { | ||
28 | /* Standard MP3 frame header fields */ | ||
29 | int version; | ||
30 | int layer; | ||
31 | bool protection; | ||
32 | int bitrate; | ||
33 | int frequency; | ||
34 | int padding; | ||
35 | int channel_mode; | ||
36 | int mode_extension; | ||
37 | int emphasis; | ||
38 | int frame_size; /* Frame size in bytes */ | ||
39 | int frame_time; /* Frame duration in milliseconds */ | ||
40 | |||
41 | bool is_vbr; /* True if the file is VBR */ | ||
42 | bool has_toc; /* True if there is a VBR header in the file */ | ||
43 | bool is_xing_vbr; /* True if the VBR header is of Xing type */ | ||
44 | bool is_vbri_vbr; /* True if the VBR header is of VBRI type */ | ||
45 | unsigned char toc[100]; | ||
46 | int frame_count; /* Number of frames in the file (if VBR) */ | ||
47 | int byte_count; /* File size in bytes */ | ||
48 | int file_time; /* Length of the whole file in milliseconds */ | ||
49 | int xing_header_pos; | ||
50 | }; | ||
51 | |||
52 | /* Xing header information */ | ||
53 | #define VBR_FRAMES_FLAG 0x01 | ||
54 | #define VBR_BYTES_FLAG 0x02 | ||
55 | #define VBR_TOC_FLAG 0x04 | ||
56 | |||
57 | |||
58 | unsigned long find_next_frame(int fd, int *offset, int max_offset, unsigned long last_header); | ||
59 | int get_mp3file_info(int fd, struct mp3info *info); | ||
60 | int count_mp3_frames(int fd, int startpos, int filesize, | ||
61 | void (*progressfunc)(int)); | ||
62 | int create_xing_header(int fd, int startpos, int filesize, | ||
63 | unsigned char *buf, int num_frames, | ||
64 | void (*progressfunc)(int)); | ||
65 | |||
66 | #endif | ||
diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h index 6b44363f8f..9ce03daa7d 100644 --- a/firmware/export/mpeg.h +++ b/firmware/export/mpeg.h | |||
@@ -90,6 +90,7 @@ unsigned long mpeg_num_recorded_bytes(void); | |||
90 | #endif | 90 | #endif |
91 | void mpeg_get_debugdata(struct mpeg_debug *dbgdata); | 91 | void mpeg_get_debugdata(struct mpeg_debug *dbgdata); |
92 | void mpeg_set_buffer_margin(int seconds); | 92 | void mpeg_set_buffer_margin(int seconds); |
93 | int mpeg_create_xing_header(char *filename, void (*progressfunc)(int)); | ||
93 | 94 | ||
94 | #define SOUND_VOLUME 0 | 95 | #define SOUND_VOLUME 0 |
95 | #define SOUND_BASS 1 | 96 | #define SOUND_BASS 1 |
diff --git a/firmware/id3.c b/firmware/id3.c index 8e8a60eb71..6aeafc4749 100644 --- a/firmware/id3.c +++ b/firmware/id3.c | |||
@@ -22,9 +22,6 @@ | |||
22 | * by David Härdeman. It has since been extended and enhanced pretty much by | 22 | * by David Härdeman. It has since been extended and enhanced pretty much by |
23 | * all sorts of friendly Rockbox people. | 23 | * all sorts of friendly Rockbox people. |
24 | * | 24 | * |
25 | * A nice reference for MPEG header info: | ||
26 | * http://rockbox.haxx.se/docs/mpeghdr.html | ||
27 | * | ||
28 | */ | 25 | */ |
29 | 26 | ||
30 | #include <stdio.h> | 27 | #include <stdio.h> |
@@ -37,6 +34,7 @@ | |||
37 | #include "atoi.h" | 34 | #include "atoi.h" |
38 | 35 | ||
39 | #include "id3.h" | 36 | #include "id3.h" |
37 | #include "mp3data.h" | ||
40 | 38 | ||
41 | #define UNSYNC(b0,b1,b2,b3) (((b0 & 0x7F) << (3*7)) | \ | 39 | #define UNSYNC(b0,b1,b2,b3) (((b0 & 0x7F) << (3*7)) | \ |
42 | ((b1 & 0x7F) << (2*7)) | \ | 40 | ((b1 & 0x7F) << (2*7)) | \ |
@@ -48,38 +46,6 @@ | |||
48 | ((b2 & 0xFF) << (1*8)) | \ | 46 | ((b2 & 0xFF) << (1*8)) | \ |
49 | ((b3 & 0xFF) << (0*8))) | 47 | ((b3 & 0xFF) << (0*8))) |
50 | 48 | ||
51 | /* Table of bitrates for MP3 files, all values in kilo. | ||
52 | * Indexed by version, layer and value of bit 15-12 in header. | ||
53 | */ | ||
54 | const int bitrate_table[2][3][16] = | ||
55 | { | ||
56 | { | ||
57 | {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, | ||
58 | {0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0}, | ||
59 | {0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0} | ||
60 | }, | ||
61 | { | ||
62 | {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0}, | ||
63 | {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}, | ||
64 | {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0} | ||
65 | } | ||
66 | }; | ||
67 | |||
68 | /* Table of samples per frame for MP3 files. | ||
69 | * Indexed by layer. Multiplied with 1000. | ||
70 | */ | ||
71 | const int bs[4] = {0, 384000, 1152000, 1152000}; | ||
72 | |||
73 | /* Table of sample frequency for MP3 files. | ||
74 | * Indexed by version and layer. | ||
75 | */ | ||
76 | const int freqtab[][4] = | ||
77 | { | ||
78 | {11025, 12000, 8000, 0}, /* MPEG version 2.5 */ | ||
79 | {44100, 48000, 32000, 0}, /* MPEG Version 1 */ | ||
80 | {22050, 24000, 16000, 0}, /* MPEG version 2 */ | ||
81 | }; | ||
82 | |||
83 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 | 49 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 |
84 | string. If it is, we attempt to convert it to a 8-bit ASCII string | 50 | string. If it is, we attempt to convert it to a 8-bit ASCII string |
85 | (for valid 8-bit ASCII characters). If it's not unicode, we leave | 51 | (for valid 8-bit ASCII characters). If it's not unicode, we leave |
@@ -168,6 +134,7 @@ static bool setid3v1title(int fd, struct mp3entry *entry) | |||
168 | if (strncmp(buffer, "TAG", 3)) | 134 | if (strncmp(buffer, "TAG", 3)) |
169 | return false; | 135 | return false; |
170 | 136 | ||
137 | entry->id3v1len = 128; | ||
171 | entry->id3version = ID3_VER_1_0; | 138 | entry->id3version = ID3_VER_1_0; |
172 | 139 | ||
173 | for (i=0; i < (int)sizeof offsets; i++) { | 140 | for (i=0; i < (int)sizeof offsets; i++) { |
@@ -239,6 +206,8 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
239 | char *tracknum = NULL; | 206 | char *tracknum = NULL; |
240 | int bytesread = 0; | 207 | int bytesread = 0; |
241 | int buffersize = sizeof(entry->id3v2buf); | 208 | int buffersize = sizeof(entry->id3v2buf); |
209 | int flags; | ||
210 | int skip; | ||
242 | 211 | ||
243 | /* Bail out if the tag is shorter than 10 bytes */ | 212 | /* Bail out if the tag is shorter than 10 bytes */ |
244 | if(entry->id3v2len < 10) | 213 | if(entry->id3v2len < 10) |
@@ -275,17 +244,34 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
275 | } | 244 | } |
276 | entry->id3version = version; | 245 | entry->id3version = version; |
277 | 246 | ||
247 | /* Skip the extended header if it is present */ | ||
248 | if(version >= ID3_VER_2_4) { | ||
249 | if(header[5] & 0x40) { | ||
250 | if(4 != read(fd, header, 4)) | ||
251 | return; | ||
252 | |||
253 | framelen = UNSYNC(header[0], header[1], | ||
254 | header[2], header[3]); | ||
255 | |||
256 | lseek(fd, framelen - 4, SEEK_CUR); | ||
257 | } | ||
258 | } | ||
259 | |||
278 | /* | 260 | /* |
279 | * We must have at least minframesize bytes left for the | 261 | * We must have at least minframesize bytes left for the |
280 | * remaining frames to be interesting | 262 | * remaining frames to be interesting |
281 | */ | 263 | */ |
282 | while(size > minframesize) { | 264 | while(size > minframesize) { |
265 | flags = 0; | ||
266 | |||
283 | /* Read frame header and check length */ | 267 | /* Read frame header and check length */ |
284 | if(version >= ID3_VER_2_3) { | 268 | if(version >= ID3_VER_2_3) { |
285 | if(10 != read(fd, header, 10)) | 269 | if(10 != read(fd, header, 10)) |
286 | return; | 270 | return; |
287 | /* Adjust for the 10 bytes we read */ | 271 | /* Adjust for the 10 bytes we read */ |
288 | size -= 10; | 272 | size -= 10; |
273 | |||
274 | flags = BYTES2INT(0, 0, header[8], header[9]); | ||
289 | 275 | ||
290 | if (version >= ID3_VER_2_4) { | 276 | if (version >= ID3_VER_2_4) { |
291 | framelen = UNSYNC(header[4], header[5], | 277 | framelen = UNSYNC(header[4], header[5], |
@@ -311,6 +297,33 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
311 | if(framelen == 0) | 297 | if(framelen == 0) |
312 | return; | 298 | return; |
313 | 299 | ||
300 | if(flags) | ||
301 | { | ||
302 | skip = 0; | ||
303 | |||
304 | if(flags & 0x0040) /* Grouping identity */ | ||
305 | skip++; | ||
306 | |||
307 | if(flags & 0x000e) /* Compression, encryption or | ||
308 | unsynchronization */ | ||
309 | { | ||
310 | /* Skip it using the total size in case | ||
311 | it was truncated */ | ||
312 | size -= totframelen; | ||
313 | lseek(fd, totframelen, SEEK_CUR); | ||
314 | continue; | ||
315 | } | ||
316 | |||
317 | if(flags & 0x0001) /* Data length indicator */ | ||
318 | skip += 4; | ||
319 | |||
320 | if(skip) | ||
321 | { | ||
322 | lseek(fd, skip, SEEK_CUR); | ||
323 | framelen -= skip; | ||
324 | } | ||
325 | } | ||
326 | |||
314 | /* If the frame is larger than the remaining buffer space we try | 327 | /* If the frame is larger than the remaining buffer space we try |
315 | to read as much as would fit in the buffer */ | 328 | to read as much as would fit in the buffer */ |
316 | if(framelen >= buffersize - bufferpos) | 329 | if(framelen >= buffersize - bufferpos) |
@@ -404,6 +417,7 @@ static int getid3v2len(int fd) | |||
404 | else | 417 | else |
405 | offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10; | 418 | offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10; |
406 | 419 | ||
420 | DEBUGF("ID3V2 Length: 0x%x\n", offset); | ||
407 | return offset; | 421 | return offset; |
408 | } | 422 | } |
409 | 423 | ||
@@ -419,29 +433,6 @@ static int getfilesize(int fd) | |||
419 | return size; | 433 | return size; |
420 | } | 434 | } |
421 | 435 | ||
422 | /* check if 'head' is a valid mp3 frame header */ | ||
423 | static bool mp3frameheader(unsigned long head) | ||
424 | { | ||
425 | if ((head & 0xffe00000) != 0xffe00000) /* bad sync? */ | ||
426 | return false; | ||
427 | if (!((head >> 17) & 3)) /* no layer? */ | ||
428 | return false; | ||
429 | if (((head >> 12) & 0xf) == 0xf) /* bad bitrate? */ | ||
430 | return false; | ||
431 | if (!((head >> 12) & 0xf)) /* no bitrate? */ | ||
432 | return false; | ||
433 | if (((head >> 10) & 0x3) == 0x3) /* bad sample rate? */ | ||
434 | return false; | ||
435 | if (((head >> 19) & 1) == 1 && | ||
436 | ((head >> 17) & 3) == 3 && | ||
437 | ((head >> 16) & 1) == 1) | ||
438 | return false; | ||
439 | if ((head & 0xffff0000) == 0xfffe0000) | ||
440 | return false; | ||
441 | |||
442 | return true; | ||
443 | } | ||
444 | |||
445 | /* | 436 | /* |
446 | * Calculates the length (in milliseconds) of an MP3 file. | 437 | * Calculates the length (in milliseconds) of an MP3 file. |
447 | * | 438 | * |
@@ -456,263 +447,48 @@ static bool mp3frameheader(unsigned long head) | |||
456 | static int getsonglength(int fd, struct mp3entry *entry) | 447 | static int getsonglength(int fd, struct mp3entry *entry) |
457 | { | 448 | { |
458 | unsigned int filetime = 0; | 449 | unsigned int filetime = 0; |
459 | unsigned long header=0; | 450 | struct mp3info info; |
460 | unsigned char tmp; | ||
461 | unsigned char frame[156]; | ||
462 | unsigned char* xing; | ||
463 | |||
464 | enum { | ||
465 | MPEG_VERSION2_5, | ||
466 | MPEG_VERSION1, | ||
467 | MPEG_VERSION2 | ||
468 | } version; | ||
469 | int layer; | ||
470 | int bitindex; | ||
471 | int bitrate; | ||
472 | int freqindex; | ||
473 | int frequency; | ||
474 | int chmode; | ||
475 | int bytecount; | 451 | int bytecount; |
476 | int bytelimit; | 452 | |
477 | int bittable; /* which bitrate table to use */ | ||
478 | bool header_found = false; | ||
479 | |||
480 | long bpf; | ||
481 | long tpf; | ||
482 | |||
483 | /* Start searching after ID3v2 header */ | 453 | /* Start searching after ID3v2 header */ |
484 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) | 454 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) |
485 | return 0; | 455 | return 0; |
486 | |||
487 | /* Fill up header with first 24 bits */ | ||
488 | for(version = 0; version < 3; version++) { | ||
489 | header <<= 8; | ||
490 | if(!read(fd, &tmp, 1)) | ||
491 | return 0; | ||
492 | header |= tmp; | ||
493 | } | ||
494 | |||
495 | /* Loop trough file until we find a frame header */ | ||
496 | bytecount = entry->id3v2len - 1; | ||
497 | bytelimit = entry->id3v2len + 0x20000; | ||
498 | restart: | ||
499 | do { | ||
500 | header <<= 8; | ||
501 | if(!read(fd, &tmp, 1)) | ||
502 | return 0; | ||
503 | header |= tmp; | ||
504 | 456 | ||
505 | /* Quit if we haven't found a valid header within 128K */ | 457 | bytecount = get_mp3file_info(fd, &info); |
506 | bytecount++; | ||
507 | if(bytecount > bytelimit) | ||
508 | return 0; | ||
509 | } while(!mp3frameheader(header)); | ||
510 | |||
511 | /* | ||
512 | * Some files are filled with garbage in the beginning, | ||
513 | * if the bitrate index of the header is binary 1111 | ||
514 | * that is a good indicator | ||
515 | */ | ||
516 | if((header & 0xF000) == 0xF000) | ||
517 | goto restart; | ||
518 | |||
519 | /* MPEG Audio Version */ | ||
520 | switch((header & 0x180000) >> 19) { | ||
521 | case 0: | ||
522 | /* MPEG version 2.5 is not an official standard */ | ||
523 | version = MPEG_VERSION2_5; | ||
524 | bittable = MPEG_VERSION2; /* use the V2 bit rate table */ | ||
525 | break; | ||
526 | 458 | ||
527 | case 2: | 459 | DEBUGF("Space between ID3V2 tag and first audio frame: 0x%x bytes\n", |
528 | /* MPEG version 2 (ISO/IEC 13818-3) */ | 460 | bytecount); |
529 | version = MPEG_VERSION2; | ||
530 | bittable = MPEG_VERSION2; | ||
531 | break; | ||
532 | 461 | ||
533 | case 3: | 462 | if(bytecount < 0) |
534 | /* MPEG version 1 (ISO/IEC 11172-3) */ | ||
535 | version = MPEG_VERSION1; | ||
536 | bittable = MPEG_VERSION1; | ||
537 | break; | ||
538 | default: | ||
539 | goto restart; | ||
540 | } | ||
541 | |||
542 | /* Layer */ | ||
543 | switch((header & 0x060000) >> 17) { | ||
544 | case 1: | ||
545 | layer = 3; | ||
546 | break; | ||
547 | case 2: | ||
548 | layer = 2; | ||
549 | break; | ||
550 | case 3: | ||
551 | layer = 1; | ||
552 | break; | ||
553 | default: | ||
554 | goto restart; | ||
555 | } | ||
556 | |||
557 | /* Bitrate */ | ||
558 | bitindex = (header & 0xF000) >> 12; | ||
559 | bitrate = bitrate_table[bittable-1][layer-1][bitindex]; | ||
560 | if(bitrate == 0) | ||
561 | goto restart; | ||
562 | |||
563 | /* Sampling frequency */ | ||
564 | freqindex = (header & 0x0C00) >> 10; | ||
565 | frequency = freqtab[version][freqindex]; | ||
566 | if(frequency == 0) | ||
567 | goto restart; | ||
568 | |||
569 | #ifdef DEBUG_VERBOSE | ||
570 | DEBUGF( "Version %i, lay %i, biti %i, bitr %i, freqi %i, freq %i, chmode %d\n", | ||
571 | version, layer, bitindex, bitrate, freqindex, frequency, chmode); | ||
572 | #endif | ||
573 | entry->version = version; | ||
574 | entry->layer = layer; | ||
575 | entry->frequency = frequency; | ||
576 | |||
577 | /* Calculate bytes per frame, calculation depends on layer */ | ||
578 | switch(layer) { | ||
579 | case 1: | ||
580 | bpf = bitrate_table[bittable - 1][layer - 1][bitindex]; | ||
581 | bpf *= 48000; | ||
582 | bpf /= freqtab[version][freqindex] << (bittable - 1); | ||
583 | break; | ||
584 | case 2: | ||
585 | case 3: | ||
586 | bpf = bitrate_table[bittable - 1][layer - 1][bitindex]; | ||
587 | bpf *= 144000; | ||
588 | bpf /= freqtab[version][freqindex] << (bittable - 1); | ||
589 | break; | ||
590 | default: | ||
591 | bpf = 1; | ||
592 | } | ||
593 | |||
594 | /* Calculate time per frame */ | ||
595 | tpf = bs[layer] / (freqtab[version][freqindex] << (bittable - 1)); | ||
596 | |||
597 | entry->bpf = bpf; | ||
598 | entry->tpf = tpf; | ||
599 | |||
600 | /* OK, we have found a frame. Let's see if it has a Xing header */ | ||
601 | if(read(fd, frame, sizeof frame) < 0) | ||
602 | return -1; | 463 | return -1; |
464 | |||
465 | bytecount += entry->id3v2len; | ||
603 | 466 | ||
604 | /* Channel mode (stereo/mono) */ | 467 | entry->bitrate = info.bitrate; |
605 | chmode = (header & 0xc0) >> 6; | ||
606 | |||
607 | /* calculate position of Xing VBR header */ | ||
608 | if ( version == 1 ) { | ||
609 | if ( chmode == 3 ) /* mono */ | ||
610 | xing = frame + 17; | ||
611 | else | ||
612 | xing = frame + 32; | ||
613 | } | ||
614 | else { | ||
615 | if ( chmode == 3 ) /* mono */ | ||
616 | xing = frame + 9; | ||
617 | else | ||
618 | xing = frame + 17; | ||
619 | } | ||
620 | |||
621 | if (xing[0] == 'X' && | ||
622 | xing[1] == 'i' && | ||
623 | xing[2] == 'n' && | ||
624 | xing[3] == 'g') | ||
625 | { | ||
626 | int i = 8; /* Where to start parsing info */ | ||
627 | |||
628 | /* Yes, it is a VBR file */ | ||
629 | entry->vbr = true; | ||
630 | entry->vbrflags = xing[7]; | ||
631 | |||
632 | if (entry->vbrflags & VBR_FRAMES_FLAG) /* Is the frame count there? */ | ||
633 | { | ||
634 | int framecount = (xing[i] << 24) | (xing[i+1] << 16) | | ||
635 | (xing[i+2] << 8) | xing[i+3]; | ||
636 | |||
637 | filetime = framecount * tpf; | ||
638 | i += 4; | ||
639 | } | ||
640 | |||
641 | if (entry->vbrflags & VBR_BYTES_FLAG) /* is byte count there? */ | ||
642 | { | ||
643 | int bytecount = (xing[i] << 24) | (xing[i+1] << 16) | | ||
644 | (xing[i+2] << 8) | xing[i+3]; | ||
645 | |||
646 | bitrate = bytecount * 8 / filetime; | ||
647 | i += 4; | ||
648 | } | ||
649 | |||
650 | if (entry->vbrflags & VBR_TOC_FLAG) /* is table-of-contents there? */ | ||
651 | { | ||
652 | memcpy( entry->toc, xing+i, 100 ); | ||
653 | } | ||
654 | |||
655 | /* Make sure we skip this frame in playback */ | ||
656 | bytecount += bpf; | ||
657 | |||
658 | header_found = true; | ||
659 | } | ||
660 | |||
661 | if (xing[0] == 'V' && | ||
662 | xing[1] == 'B' && | ||
663 | xing[2] == 'R' && | ||
664 | xing[3] == 'I') | ||
665 | { | ||
666 | int framecount; | ||
667 | int bytecount; | ||
668 | |||
669 | /* Yes, it is a FhG VBR file */ | ||
670 | entry->vbr = true; | ||
671 | entry->vbrflags = 0; | ||
672 | |||
673 | bytecount = (xing[10] << 24) | (xing[11] << 16) | | ||
674 | (xing[12] << 8) | xing[13]; | ||
675 | |||
676 | framecount = (xing[14] << 24) | (xing[15] << 16) | | ||
677 | (xing[16] << 8) | xing[17]; | ||
678 | |||
679 | filetime = framecount * tpf; | ||
680 | bitrate = bytecount * 8 / filetime; | ||
681 | |||
682 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
683 | |||
684 | /* Make sure we skip this frame in playback */ | ||
685 | bytecount += bpf; | ||
686 | |||
687 | header_found = true; | ||
688 | } | ||
689 | |||
690 | /* Is it a LAME Info frame? */ | ||
691 | if (xing[0] == 'I' && | ||
692 | xing[1] == 'n' && | ||
693 | xing[2] == 'f' && | ||
694 | xing[3] == 'o') | ||
695 | { | ||
696 | /* Make sure we skip this frame in playback */ | ||
697 | bytecount += bpf; | ||
698 | |||
699 | header_found = true; | ||
700 | } | ||
701 | |||
702 | |||
703 | entry->bitrate = bitrate; | ||
704 | 468 | ||
705 | /* If the file time hasn't been established, this may be a fixed | 469 | /* If the file time hasn't been established, this may be a fixed |
706 | rate MP3, so just use the default formula */ | 470 | rate MP3, so just use the default formula */ |
471 | |||
472 | filetime = info.file_time; | ||
473 | |||
707 | if(filetime == 0) | 474 | if(filetime == 0) |
708 | { | 475 | { |
709 | /* | 476 | /* |
710 | * Now song length is | 477 | * Now song length is |
711 | * ((filesize)/(bytes per frame))*(time per frame) | 478 | * ((filesize)/(bytes per frame))*(time per frame) |
712 | */ | 479 | */ |
713 | filetime = entry->filesize/bpf*tpf; | 480 | filetime = entry->filesize/info.frame_size*info.frame_time; |
714 | } | 481 | } |
715 | 482 | ||
483 | entry->tpf = info.frame_time; | ||
484 | entry->bpf = info.frame_size; | ||
485 | |||
486 | entry->vbr = info.is_vbr; | ||
487 | entry->has_toc = info.has_toc; | ||
488 | memcpy(entry->toc, info.toc, sizeof(info.toc)); | ||
489 | |||
490 | entry->xing_header_pos = info.xing_header_pos; | ||
491 | |||
716 | /* Update the seek point for the first playable frame */ | 492 | /* Update the seek point for the first playable frame */ |
717 | entry->first_frame_offset = bytecount; | 493 | entry->first_frame_offset = bytecount; |
718 | DEBUGF("First frame is at %x\n", entry->first_frame_offset); | 494 | DEBUGF("First frame is at %x\n", entry->first_frame_offset); |
@@ -720,7 +496,6 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
720 | return filetime; | 496 | return filetime; |
721 | } | 497 | } |
722 | 498 | ||
723 | |||
724 | /* | 499 | /* |
725 | * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) | 500 | * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) |
726 | * about an MP3 file and updates it's entry accordingly. | 501 | * about an MP3 file and updates it's entry accordingly. |
@@ -750,6 +525,10 @@ bool mp3info(struct mp3entry *entry, char *filename) | |||
750 | setid3v2title(fd, entry); | 525 | setid3v2title(fd, entry); |
751 | entry->length = getsonglength(fd, entry); | 526 | entry->length = getsonglength(fd, entry); |
752 | 527 | ||
528 | /* Subtract the meta information from the file size to get | ||
529 | the true size of the MP3 stream */ | ||
530 | entry->filesize -= entry->first_frame_offset; | ||
531 | |||
753 | /* only seek to end of file if no id3v2 tags were found */ | 532 | /* only seek to end of file if no id3v2 tags were found */ |
754 | if (!entry->id3v2len) { | 533 | if (!entry->id3v2len) { |
755 | if(!entry->title) | 534 | if(!entry->title) |
diff --git a/firmware/mp3data.c b/firmware/mp3data.c new file mode 100644 index 0000000000..8d925041ce --- /dev/null +++ b/firmware/mp3data.c | |||
@@ -0,0 +1,664 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | /* | ||
21 | * Parts of this code has been stolen from the Ample project and was written | ||
22 | * by David Härdeman. It has since been extended and enhanced pretty much by | ||
23 | * all sorts of friendly Rockbox people. | ||
24 | * | ||
25 | * A nice reference for MPEG header info: | ||
26 | * http://rockbox.haxx.se/docs/mpeghdr.html | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <stdbool.h> | ||
34 | #include "debug.h" | ||
35 | #include "mp3data.h" | ||
36 | #include "file.h" | ||
37 | |||
38 | #define DEBUG_VERBOSE | ||
39 | |||
40 | #define BYTES2INT(b1,b2,b3,b4) (((b1 & 0xFF) << (3*8)) | \ | ||
41 | ((b2 & 0xFF) << (2*8)) | \ | ||
42 | ((b3 & 0xFF) << (1*8)) | \ | ||
43 | ((b4 & 0xFF) << (0*8))) | ||
44 | |||
45 | #define SYNC_MASK (0x7ff << 21) | ||
46 | #define VERSION_MASK (3 << 19) | ||
47 | #define LAYER_MASK (3 << 17) | ||
48 | #define PROTECTION_MASK (1 << 16) | ||
49 | #define BITRATE_MASK (0xf << 12) | ||
50 | #define SAMPLERATE_MASK (3 << 10) | ||
51 | #define PADDING_MASK (1 << 9) | ||
52 | #define PRIVATE_MASK (1 << 8) | ||
53 | #define CHANNELMODE_MASK (3 << 6) | ||
54 | #define MODE_EXT_MASK (3 << 4) | ||
55 | #define COPYRIGHT_MASK (1 << 3) | ||
56 | #define ORIGINAL_MASK (1 << 2) | ||
57 | #define EMPHASIS_MASK 3 | ||
58 | |||
59 | /* Table of bitrates for MP3 files, all values in kilo. | ||
60 | * Indexed by version, layer and value of bit 15-12 in header. | ||
61 | */ | ||
62 | const int bitrate_table[2][3][16] = | ||
63 | { | ||
64 | { | ||
65 | {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, | ||
66 | {0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0}, | ||
67 | {0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0} | ||
68 | }, | ||
69 | { | ||
70 | {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0}, | ||
71 | {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}, | ||
72 | {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0} | ||
73 | } | ||
74 | }; | ||
75 | |||
76 | /* Table of samples per frame for MP3 files. | ||
77 | * Indexed by layer. Multiplied with 1000. | ||
78 | */ | ||
79 | const int bs[3] = {384000, 1152000, 1152000}; | ||
80 | |||
81 | /* Table of sample frequency for MP3 files. | ||
82 | * Indexed by version and layer. | ||
83 | */ | ||
84 | |||
85 | const int freqtab[][4] = | ||
86 | { | ||
87 | {11025, 12000, 8000, 0}, /* MPEG version 2.5 */ | ||
88 | {44100, 48000, 32000, 0}, /* MPEG Version 1 */ | ||
89 | {22050, 24000, 16000, 0}, /* MPEG version 2 */ | ||
90 | }; | ||
91 | |||
92 | /* check if 'head' is a valid mp3 frame header */ | ||
93 | static bool is_mp3frameheader(unsigned long head) | ||
94 | { | ||
95 | if ((head & SYNC_MASK) != (unsigned long)SYNC_MASK) /* bad sync? */ | ||
96 | return false; | ||
97 | if ((head & VERSION_MASK) == (1 << 19)) /* bad version? */ | ||
98 | return false; | ||
99 | if (!(head & LAYER_MASK)) /* no layer? */ | ||
100 | return false; | ||
101 | if ((head & BITRATE_MASK) == BITRATE_MASK) /* bad bitrate? */ | ||
102 | return false; | ||
103 | if (!(head & BITRATE_MASK)) /* no bitrate? */ | ||
104 | return false; | ||
105 | if ((head & SAMPLERATE_MASK) == SAMPLERATE_MASK) /* bad sample rate? */ | ||
106 | return false; | ||
107 | if (((head >> 19) & 1) == 1 && | ||
108 | ((head >> 17) & 3) == 3 && | ||
109 | ((head >> 16) & 1) == 1) | ||
110 | return false; | ||
111 | if ((head & 0xffff0000) == 0xfffe0000) | ||
112 | return false; | ||
113 | |||
114 | return true; | ||
115 | } | ||
116 | |||
117 | static bool mp3headerinfo(struct mp3info *info, unsigned long header) | ||
118 | { | ||
119 | int bittable = 0; | ||
120 | int bitindex; | ||
121 | int freqindex; | ||
122 | |||
123 | /* MPEG Audio Version */ | ||
124 | switch(header & VERSION_MASK) { | ||
125 | case 0: | ||
126 | /* MPEG version 2.5 is not an official standard */ | ||
127 | info->version = MPEG_VERSION2_5; | ||
128 | bittable = MPEG_VERSION2 - 1; /* use the V2 bit rate table */ | ||
129 | break; | ||
130 | |||
131 | case (1 << 19): | ||
132 | return false; | ||
133 | |||
134 | case (2 << 19): | ||
135 | /* MPEG version 2 (ISO/IEC 13818-3) */ | ||
136 | info->version = MPEG_VERSION2; | ||
137 | bittable = MPEG_VERSION2 - 1; | ||
138 | break; | ||
139 | |||
140 | case (3 << 19): | ||
141 | /* MPEG version 1 (ISO/IEC 11172-3) */ | ||
142 | info->version = MPEG_VERSION1; | ||
143 | bittable = MPEG_VERSION1 - 1; | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | switch(header & LAYER_MASK) { | ||
148 | case 0: | ||
149 | return false; | ||
150 | case (1 << 17): | ||
151 | info->layer = 2; | ||
152 | break; | ||
153 | case (2 << 17): | ||
154 | info->layer = 1; | ||
155 | break; | ||
156 | case (3 << 17): | ||
157 | info->layer = 0; | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | info->protection = (header & PROTECTION_MASK)?true:false; | ||
162 | |||
163 | /* Bitrate */ | ||
164 | bitindex = (header & 0xf000) >> 12; | ||
165 | info->bitrate = bitrate_table[bittable][info->layer][bitindex]; | ||
166 | if(info->bitrate == 0) | ||
167 | return false; | ||
168 | |||
169 | /* Sampling frequency */ | ||
170 | freqindex = (header & 0x0C00) >> 10; | ||
171 | info->frequency = freqtab[info->version][freqindex]; | ||
172 | if(info->frequency == 0) | ||
173 | return false; | ||
174 | |||
175 | info->padding = (header & 0x0200)?1:0; | ||
176 | |||
177 | /* Calculate number of bytes, calculation depends on layer */ | ||
178 | switch(info->layer) { | ||
179 | case 0: | ||
180 | info->frame_size = info->bitrate * 48000; | ||
181 | info->frame_size /= | ||
182 | freqtab[info->version][freqindex] << bittable; | ||
183 | break; | ||
184 | case 1: | ||
185 | case 2: | ||
186 | info->frame_size = info->bitrate * 144000; | ||
187 | info->frame_size /= | ||
188 | freqtab[info->version][freqindex] << bittable; | ||
189 | break; | ||
190 | default: | ||
191 | info->frame_size = 1; | ||
192 | } | ||
193 | |||
194 | info->frame_size += info->padding; | ||
195 | |||
196 | /* Calculate time per frame */ | ||
197 | info->frame_time = bs[info->layer] / | ||
198 | (freqtab[info->version][freqindex] << bittable); | ||
199 | |||
200 | info->channel_mode = (header & 0xc0) >> 6; | ||
201 | info->mode_extension = (header & 0x30) >> 4; | ||
202 | info->emphasis = header & 3; | ||
203 | |||
204 | #ifdef DEBUG_VERBOSE | ||
205 | DEBUGF( "Header: %08x, Ver %d, lay %d, bitr %d, freq %d, " | ||
206 | "chmode %d, mode_ext %d, emph %d, bytes: %d time: %d\n", | ||
207 | header, info->version, info->layer, info->bitrate, info->frequency, | ||
208 | info->channel_mode, info->mode_extension, | ||
209 | info->emphasis, info->frame_size, info->frame_time); | ||
210 | #endif | ||
211 | return true; | ||
212 | } | ||
213 | |||
214 | unsigned long find_next_frame(int fd, int *offset, int max_offset, unsigned long last_header) | ||
215 | { | ||
216 | unsigned long header=0; | ||
217 | unsigned char tmp; | ||
218 | int i; | ||
219 | |||
220 | int pos = 0; | ||
221 | |||
222 | /* We remember the last header we found, to use as a template to see if | ||
223 | the header we find has the same frequency, layer etc */ | ||
224 | last_header &= 0xffff0c00; | ||
225 | |||
226 | /* Fill up header with first 24 bits */ | ||
227 | for(i = 0; i < 3; i++) { | ||
228 | header <<= 8; | ||
229 | if(!read(fd, &tmp, 1)) | ||
230 | return 0; | ||
231 | header |= tmp; | ||
232 | pos++; | ||
233 | } | ||
234 | |||
235 | do { | ||
236 | header <<= 8; | ||
237 | if(!read(fd, &tmp, 1)) | ||
238 | return 0; | ||
239 | header |= tmp; | ||
240 | pos++; | ||
241 | if(max_offset > 0 && pos > max_offset) | ||
242 | return 0; | ||
243 | } while(!is_mp3frameheader(header) || | ||
244 | (last_header?((header & 0xffff0c00) != last_header):false)); | ||
245 | |||
246 | *offset = pos - 4; | ||
247 | |||
248 | #ifdef DEBUG | ||
249 | if(*offset) | ||
250 | DEBUGF("Warning: skipping %d bytes of garbage\n", *offset); | ||
251 | #endif | ||
252 | |||
253 | return header; | ||
254 | } | ||
255 | |||
256 | #ifdef SIMULATOR | ||
257 | unsigned char mp3buf[0x100000]; | ||
258 | unsigned char mp3end[1]; | ||
259 | #else | ||
260 | extern unsigned char mp3buf[]; | ||
261 | extern unsigned char mp3end[]; | ||
262 | #endif | ||
263 | static int fnf_read_index; | ||
264 | static int fnf_buf_len; | ||
265 | |||
266 | static int fd; | ||
267 | |||
268 | static int buf_getbyte(unsigned char *c) | ||
269 | { | ||
270 | if(fnf_read_index < fnf_buf_len) | ||
271 | { | ||
272 | *c = mp3buf[fnf_read_index++]; | ||
273 | return 1; | ||
274 | } | ||
275 | else | ||
276 | { | ||
277 | fnf_buf_len = read(fd, mp3buf, mp3end - mp3buf); | ||
278 | if(fnf_buf_len < 0) | ||
279 | return -1; | ||
280 | |||
281 | fnf_read_index = 0; | ||
282 | |||
283 | if(fnf_buf_len > 0) | ||
284 | { | ||
285 | *c = mp3buf[fnf_read_index++]; | ||
286 | return 1; | ||
287 | } | ||
288 | else | ||
289 | return 0; | ||
290 | } | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int buf_seek(int len) | ||
295 | { | ||
296 | fnf_read_index += len; | ||
297 | if(fnf_read_index > fnf_buf_len) | ||
298 | { | ||
299 | len = fnf_read_index - fnf_buf_len; | ||
300 | |||
301 | fnf_buf_len = read(fd, mp3buf, mp3end - mp3buf); | ||
302 | if(fnf_buf_len < 0) | ||
303 | return -1; | ||
304 | |||
305 | fnf_read_index = 0; | ||
306 | fnf_read_index += len; | ||
307 | } | ||
308 | |||
309 | if(fnf_read_index > fnf_buf_len) | ||
310 | { | ||
311 | return -1; | ||
312 | } | ||
313 | else | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static void buf_init(void) | ||
318 | { | ||
319 | fnf_buf_len = 0; | ||
320 | fnf_read_index = 0; | ||
321 | } | ||
322 | |||
323 | unsigned long buf_find_next_frame(int *offset, int max_offset, | ||
324 | unsigned long last_header) | ||
325 | { | ||
326 | unsigned long header=0; | ||
327 | unsigned char tmp; | ||
328 | int i; | ||
329 | |||
330 | int pos = 0; | ||
331 | |||
332 | /* We remember the last header we found, to use as a template to see if | ||
333 | the header we find has the same frequency, layer etc */ | ||
334 | last_header &= 0xffff0c00; | ||
335 | |||
336 | /* Fill up header with first 24 bits */ | ||
337 | for(i = 0; i < 3; i++) { | ||
338 | header <<= 8; | ||
339 | if(!buf_getbyte(&tmp)) | ||
340 | return 0; | ||
341 | header |= tmp; | ||
342 | pos++; | ||
343 | } | ||
344 | |||
345 | do { | ||
346 | header <<= 8; | ||
347 | if(!buf_getbyte(&tmp)) | ||
348 | return 0; | ||
349 | header |= tmp; | ||
350 | pos++; | ||
351 | if(max_offset > 0 && pos > max_offset) | ||
352 | return 0; | ||
353 | } while(!is_mp3frameheader(header) || | ||
354 | (last_header?((header & 0xffff0c00) != last_header):false)); | ||
355 | |||
356 | *offset = pos - 4; | ||
357 | |||
358 | #ifdef DEBUG | ||
359 | if(*offset) | ||
360 | DEBUGF("Warning: skipping %d bytes of garbage\n", *offset); | ||
361 | #endif | ||
362 | |||
363 | return header; | ||
364 | } | ||
365 | |||
366 | int get_mp3file_info(int fd, struct mp3info *info) | ||
367 | { | ||
368 | unsigned char frame[1024]; | ||
369 | unsigned char *vbrheader; | ||
370 | unsigned long header; | ||
371 | int bytecount; | ||
372 | int num_offsets; | ||
373 | int frames_per_entry; | ||
374 | int i; | ||
375 | int offset; | ||
376 | int j; | ||
377 | int tmp; | ||
378 | |||
379 | header = find_next_frame(fd, &bytecount, 0x20000, 0); | ||
380 | /* Quit if we haven't found a valid header within 128K */ | ||
381 | if(header == 0) | ||
382 | return -1; | ||
383 | |||
384 | memset(info, 0, sizeof(struct mp3info)); | ||
385 | if(!mp3headerinfo(info, header)) | ||
386 | return -2; | ||
387 | |||
388 | /* OK, we have found a frame. Let's see if it has a Xing header */ | ||
389 | if(read(fd, frame, info->frame_size-4) < 0) | ||
390 | return -3; | ||
391 | |||
392 | /* calculate position of VBR header */ | ||
393 | if ( info->version == MPEG_VERSION1 ) { | ||
394 | if (info->channel_mode == 3) /* mono */ | ||
395 | vbrheader = frame + 17; | ||
396 | else | ||
397 | vbrheader = frame + 32; | ||
398 | } | ||
399 | else { | ||
400 | if (info->channel_mode == 3) /* mono */ | ||
401 | vbrheader = frame + 9; | ||
402 | else | ||
403 | vbrheader = frame + 17; | ||
404 | } | ||
405 | |||
406 | if (vbrheader[0] == 'X' && | ||
407 | vbrheader[1] == 'i' && | ||
408 | vbrheader[2] == 'n' && | ||
409 | vbrheader[3] == 'g') | ||
410 | { | ||
411 | int i = 8; /* Where to start parsing info */ | ||
412 | |||
413 | DEBUGF("Xing header\n"); | ||
414 | |||
415 | /* Remember where in the file the Xing header is */ | ||
416 | info->xing_header_pos = lseek(fd, 0, SEEK_CUR) - info->frame_size; | ||
417 | |||
418 | /* We want to skip the Xing frame when playing the stream */ | ||
419 | bytecount += info->frame_size; | ||
420 | |||
421 | /* Now get the next frame to find out the real info about | ||
422 | the mp3 stream */ | ||
423 | header = find_next_frame(fd, &tmp, 0x20000, 0); | ||
424 | if(header == 0) | ||
425 | return -4; | ||
426 | |||
427 | if(!mp3headerinfo(info, header)) | ||
428 | return -5; | ||
429 | |||
430 | /* Yes, it is a VBR file */ | ||
431 | info->is_vbr = true; | ||
432 | info->is_xing_vbr = true; | ||
433 | |||
434 | if(vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ | ||
435 | { | ||
436 | info->frame_count = BYTES2INT(vbrheader[i], vbrheader[i+1], | ||
437 | vbrheader[i+2], vbrheader[i+3]); | ||
438 | info->file_time = info->frame_count * info->frame_time; | ||
439 | i += 4; | ||
440 | } | ||
441 | |||
442 | if(vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */ | ||
443 | { | ||
444 | info->byte_count = BYTES2INT(vbrheader[i], vbrheader[i+1], | ||
445 | vbrheader[i+2], vbrheader[i+3]); | ||
446 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
447 | i += 4; | ||
448 | } | ||
449 | |||
450 | if(vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ | ||
451 | { | ||
452 | memcpy( info->toc, vbrheader+i, 100 ); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | if (vbrheader[0] == 'V' && | ||
457 | vbrheader[1] == 'B' && | ||
458 | vbrheader[2] == 'R' && | ||
459 | vbrheader[3] == 'I') | ||
460 | { | ||
461 | DEBUGF("VBRI header\n"); | ||
462 | |||
463 | /* We want to skip the VBRI frame when playing the stream */ | ||
464 | bytecount += info->frame_size; | ||
465 | |||
466 | /* Now get the next frame to find out the real info about | ||
467 | the mp3 stream */ | ||
468 | header = find_next_frame(fd, &bytecount, 0x20000, 0); | ||
469 | if(header == 0) | ||
470 | return -6; | ||
471 | |||
472 | if(!mp3headerinfo(info, header)) | ||
473 | return -7; | ||
474 | |||
475 | DEBUGF("%04x: %04x %04x ", 0, header >> 16, header & 0xffff); | ||
476 | for(i = 4;i < (int)sizeof(frame)-4;i+=2) { | ||
477 | if(i % 16 == 0) { | ||
478 | DEBUGF("\n%04x: ", i-4); | ||
479 | } | ||
480 | DEBUGF("%04x ", (frame[i-4] << 8) | frame[i-4+1]); | ||
481 | } | ||
482 | |||
483 | DEBUGF("\n"); | ||
484 | |||
485 | /* Yes, it is a FhG VBR file */ | ||
486 | info->is_vbr = true; | ||
487 | info->is_vbri_vbr = true; | ||
488 | info->has_toc = false; /* We don't parse the TOC (yet) */ | ||
489 | |||
490 | info->byte_count = BYTES2INT(vbrheader[10], vbrheader[11], | ||
491 | vbrheader[12], vbrheader[13]); | ||
492 | info->frame_count = BYTES2INT(vbrheader[14], vbrheader[15], | ||
493 | vbrheader[16], vbrheader[17]); | ||
494 | |||
495 | info->file_time = info->frame_count * info->frame_time; | ||
496 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
497 | |||
498 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
499 | num_offsets = BYTES2INT(0, 0, vbrheader[18], vbrheader[19]); | ||
500 | frames_per_entry = BYTES2INT(0, 0, vbrheader[24], vbrheader[25]); | ||
501 | DEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n", | ||
502 | info->bitrate, info->frame_size, info->frame_size); | ||
503 | DEBUGF("Frame count: %x\n", info->frame_count); | ||
504 | DEBUGF("Byte count: %x\n", info->byte_count); | ||
505 | DEBUGF("Offsets: %d\n", num_offsets); | ||
506 | DEBUGF("Frames/entry: %d\n", frames_per_entry); | ||
507 | |||
508 | offset = 0; | ||
509 | |||
510 | for(i = 0;i < num_offsets;i++) | ||
511 | { | ||
512 | j = BYTES2INT(0, 0, vbrheader[26+i*2], vbrheader[27+i*2]); | ||
513 | offset += j; | ||
514 | DEBUGF("%03d: %x (%x)\n", i, offset - bytecount, j); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | /* Is it a LAME Info frame? */ | ||
519 | if (vbrheader[0] == 'I' && | ||
520 | vbrheader[1] == 'n' && | ||
521 | vbrheader[2] == 'f' && | ||
522 | vbrheader[3] == 'o') | ||
523 | { | ||
524 | /* Make sure we skip this frame in playback */ | ||
525 | bytecount += info->frame_size; | ||
526 | } | ||
527 | |||
528 | return bytecount; | ||
529 | } | ||
530 | |||
531 | /* This is an MP3 header, 128kbit/s, 44.1kHz, with silence */ | ||
532 | static const unsigned char xing_frame_header[] = { | ||
533 | 0xff, 0xfa, 0x90, 0x64, 0x86, 0x1f | ||
534 | }; | ||
535 | |||
536 | static const char cooltext[] = "Rockbox rocks"; | ||
537 | |||
538 | static void int2bytes(unsigned char *buf, int val) | ||
539 | { | ||
540 | buf[0] = (val >> 24) & 0xff; | ||
541 | buf[1] = (val >> 16) & 0xff; | ||
542 | buf[2] = (val >> 8) & 0xff; | ||
543 | buf[3] = val & 0xff; | ||
544 | } | ||
545 | |||
546 | int count_mp3_frames(int fd, int startpos, int filesize, | ||
547 | void (*progressfunc)(int)) | ||
548 | { | ||
549 | unsigned long header = 0; | ||
550 | struct mp3info info; | ||
551 | int num_frames; | ||
552 | int bytes; | ||
553 | int cnt; | ||
554 | int progress_chunk = filesize / 50; /* Max is 50%, in 1% increments */ | ||
555 | int progress_cnt = 0; | ||
556 | |||
557 | if(lseek(fd, startpos, SEEK_SET) < 0) | ||
558 | return -1; | ||
559 | |||
560 | buf_init(); | ||
561 | |||
562 | /* Find out the total number of frames */ | ||
563 | num_frames = 0; | ||
564 | cnt = 0; | ||
565 | |||
566 | while((header = buf_find_next_frame(&bytes, -1, header))) { | ||
567 | mp3headerinfo(&info, header); | ||
568 | buf_seek(info.frame_size-4); | ||
569 | num_frames++; | ||
570 | if(progressfunc) | ||
571 | { | ||
572 | cnt += bytes + info.frame_size; | ||
573 | if(cnt > progress_chunk) | ||
574 | { | ||
575 | progress_cnt++; | ||
576 | progressfunc(progress_cnt); | ||
577 | cnt = 0; | ||
578 | } | ||
579 | } | ||
580 | } | ||
581 | DEBUGF("Total number of frames: %d\n", num_frames); | ||
582 | |||
583 | return num_frames; | ||
584 | } | ||
585 | |||
586 | int create_xing_header(int fd, int startpos, int filesize, | ||
587 | unsigned char *buf, int num_frames, | ||
588 | void (*progressfunc)(int)) | ||
589 | { | ||
590 | unsigned long header = 0; | ||
591 | struct mp3info info; | ||
592 | int pos, last_pos; | ||
593 | int i, j; | ||
594 | int bytes; | ||
595 | int filepos; | ||
596 | int tocentry; | ||
597 | int x; | ||
598 | |||
599 | DEBUGF("create_xing_header()\n"); | ||
600 | |||
601 | /* Create the frame header */ | ||
602 | memset(buf, 0, 417); | ||
603 | memcpy(buf, xing_frame_header, 6); | ||
604 | |||
605 | lseek(fd, startpos, SEEK_SET); | ||
606 | buf_init(); | ||
607 | |||
608 | buf[36] = 'X'; | ||
609 | buf[36+1] = 'i'; | ||
610 | buf[36+2] = 'n'; | ||
611 | buf[36+3] = 'g'; | ||
612 | int2bytes(&buf[36+4], (VBR_FRAMES_FLAG | VBR_BYTES_FLAG | VBR_TOC_FLAG)); | ||
613 | int2bytes(&buf[36+8], num_frames); | ||
614 | int2bytes(&buf[36+12], filesize - startpos); | ||
615 | |||
616 | /* Generate filepos table */ | ||
617 | last_pos = 0; | ||
618 | filepos = 0; | ||
619 | header = 0; | ||
620 | x = 0; | ||
621 | for(i = 0;i < 100;i++) { | ||
622 | /* Calculate the absolute frame number for this seek point */ | ||
623 | pos = i * num_frames / 100; | ||
624 | |||
625 | /* Advance from the last seek point to this one */ | ||
626 | for(j = 0;j < pos - last_pos;j++) | ||
627 | { | ||
628 | DEBUGF("fpos: %x frame no: %x ", filepos, x++); | ||
629 | header = buf_find_next_frame(&bytes, -1, header); | ||
630 | mp3headerinfo(&info, header); | ||
631 | buf_seek(info.frame_size-4); | ||
632 | filepos += info.frame_size; | ||
633 | } | ||
634 | |||
635 | if(progressfunc) | ||
636 | { | ||
637 | progressfunc(50 + i/2); | ||
638 | } | ||
639 | |||
640 | tocentry = filepos * 256 / filesize; | ||
641 | |||
642 | DEBUGF("Pos %d: %d relpos: %d filepos: %x tocentry: %x\n", | ||
643 | i, pos, pos-last_pos, filepos, tocentry); | ||
644 | |||
645 | /* Fill in the TOC entry */ | ||
646 | buf[36+16+i] = tocentry; | ||
647 | |||
648 | last_pos = pos; | ||
649 | } | ||
650 | |||
651 | memcpy(buf+152, cooltext, sizeof(cooltext)); | ||
652 | |||
653 | #ifdef DEBUG | ||
654 | for(i = 0;i < 417;i++) | ||
655 | { | ||
656 | if(i && !(i % 16)) | ||
657 | DEBUGF("\n"); | ||
658 | |||
659 | DEBUGF("%02x ", buf[i]); | ||
660 | } | ||
661 | #endif | ||
662 | |||
663 | return 0; | ||
664 | } | ||
diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 9470e7d84b..92f11e1b84 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "string.h" | 26 | #include "string.h" |
27 | #include <kernel.h> | 27 | #include <kernel.h> |
28 | #include "thread.h" | 28 | #include "thread.h" |
29 | #include "mp3data.h" | ||
29 | #ifndef SIMULATOR | 30 | #ifndef SIMULATOR |
30 | #include "i2c.h" | 31 | #include "i2c.h" |
31 | #include "mas.h" | 32 | #include "mas.h" |
@@ -302,7 +303,7 @@ static void remove_all_tags(void) | |||
302 | static void set_elapsed(struct mp3entry* id3) | 303 | static void set_elapsed(struct mp3entry* id3) |
303 | { | 304 | { |
304 | if ( id3->vbr ) { | 305 | if ( id3->vbr ) { |
305 | if ( id3->vbrflags & VBR_TOC_FLAG ) { | 306 | if ( id3->has_toc ) { |
306 | /* calculate elapsed time using TOC */ | 307 | /* calculate elapsed time using TOC */ |
307 | int i; | 308 | int i; |
308 | unsigned int remainder, plen, relpos, nextpos; | 309 | unsigned int remainder, plen, relpos, nextpos; |
@@ -1482,7 +1483,7 @@ static void mpeg_thread(void) | |||
1482 | 1483 | ||
1483 | if (id3->vbr) | 1484 | if (id3->vbr) |
1484 | { | 1485 | { |
1485 | if (id3->vbrflags & VBR_TOC_FLAG) | 1486 | if (id3->has_toc) |
1486 | { | 1487 | { |
1487 | /* Use the TOC to find the new position */ | 1488 | /* Use the TOC to find the new position */ |
1488 | unsigned int percent, remainder; | 1489 | unsigned int percent, remainder; |
@@ -1528,10 +1529,10 @@ static void mpeg_thread(void) | |||
1528 | transition properly to the next song */ | 1529 | transition properly to the next song */ |
1529 | newpos = id3->filesize - id3->id3v1len - 1; | 1530 | newpos = id3->filesize - id3->id3v1len - 1; |
1530 | } | 1531 | } |
1531 | else if (newpos < (int)id3->id3v2len) | 1532 | else if (newpos < (int)id3->first_frame_offset) |
1532 | { | 1533 | { |
1533 | /* skip past id3v2 tag */ | 1534 | /* skip past id3v2 tag and other leading garbage */ |
1534 | newpos = id3->id3v2len; | 1535 | newpos = id3->first_frame_offset; |
1535 | } | 1536 | } |
1536 | 1537 | ||
1537 | if (mpeg_file >= 0) | 1538 | if (mpeg_file >= 0) |
@@ -1720,7 +1721,7 @@ static void mpeg_thread(void) | |||
1720 | t2 = current_tick; | 1721 | t2 = current_tick; |
1721 | DEBUGF("time: %d\n", t2 - t1); | 1722 | DEBUGF("time: %d\n", t2 - t1); |
1722 | DEBUGF("R: %x\n", len); | 1723 | DEBUGF("R: %x\n", len); |
1723 | 1724 | ||
1724 | /* Now make sure that we don't feed the MAS with ID3V1 | 1725 | /* Now make sure that we don't feed the MAS with ID3V1 |
1725 | data */ | 1726 | data */ |
1726 | if (len < amount_to_read) | 1727 | if (len < amount_to_read) |
@@ -1734,19 +1735,19 @@ static void mpeg_thread(void) | |||
1734 | { | 1735 | { |
1735 | if(tagptr >= mp3buflen) | 1736 | if(tagptr >= mp3buflen) |
1736 | tagptr -= mp3buflen; | 1737 | tagptr -= mp3buflen; |
1737 | 1738 | ||
1738 | if(mp3buf[tagptr] != tag[i]) | 1739 | if(mp3buf[tagptr] != tag[i]) |
1739 | taglen = 0; | 1740 | taglen = 0; |
1740 | 1741 | ||
1741 | tagptr++; | 1742 | tagptr++; |
1742 | } | 1743 | } |
1743 | 1744 | ||
1744 | if(taglen) | 1745 | if(taglen) |
1745 | { | 1746 | { |
1746 | /* Skip id3v1 tag */ | 1747 | /* Skip id3v1 tag */ |
1747 | DEBUGF("Skipping ID3v1 tag\n"); | 1748 | DEBUGF("Skipping ID3v1 tag\n"); |
1748 | len -= taglen; | 1749 | len -= taglen; |
1749 | 1750 | ||
1750 | /* The very rare case when the entire tag | 1751 | /* The very rare case when the entire tag |
1751 | wasn't read in this read() call must be | 1752 | wasn't read in this read() call must be |
1752 | taken care of */ | 1753 | taken care of */ |
@@ -1819,131 +1820,135 @@ static void mpeg_thread(void) | |||
1819 | } | 1820 | } |
1820 | else | 1821 | else |
1821 | { | 1822 | { |
1822 | /* This doesn't look neccessary... | 1823 | queue_wait(&mpeg_queue, &ev); |
1823 | yield(); | 1824 | switch(ev.id) |
1824 | if(!queue_empty(&mpeg_queue)) | 1825 | { |
1825 | {*/ | 1826 | case MPEG_RECORD: |
1826 | queue_wait(&mpeg_queue, &ev); | 1827 | DEBUGF("Recording...\n"); |
1827 | switch(ev.id) | 1828 | reset_mp3_buffer(); |
1828 | { | ||
1829 | case MPEG_RECORD: | ||
1830 | DEBUGF("Recording...\n"); | ||
1831 | reset_mp3_buffer(); | ||
1832 | start_recording(); | ||
1833 | demand_irq_enable(true); | ||
1834 | mpeg_file = creat(recording_filename, O_WRONLY); | ||
1835 | |||
1836 | if(mpeg_file < 0) | ||
1837 | panicf("recfile: %d", mpeg_file); | ||
1838 | |||
1839 | close(mpeg_file); | ||
1840 | mpeg_file = -1; | ||
1841 | break; | ||
1842 | |||
1843 | case MPEG_STOP: | ||
1844 | DEBUGF("MPEG_STOP\n"); | ||
1845 | demand_irq_enable(false); | ||
1846 | stop_recording(); | ||
1847 | |||
1848 | /* Save the remaining data in the buffer */ | ||
1849 | stop_pending = true; | ||
1850 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | ||
1851 | break; | ||
1852 | 1829 | ||
1853 | case MPEG_STOP_DONE: | 1830 | /* Advance the write pointer 4096+417 bytes to make |
1854 | DEBUGF("MPEG_STOP_DONE\n"); | 1831 | room for an ID3 tag plus a VBR header */ |
1832 | mp3buf_write = 4096+417; | ||
1833 | memset(mp3buf, 0, 4096+417); | ||
1855 | 1834 | ||
1856 | if(mpeg_file >= 0) | 1835 | start_recording(); |
1857 | close(mpeg_file); | 1836 | demand_irq_enable(true); |
1858 | mpeg_file = -1; | 1837 | |
1838 | mpeg_file = creat(recording_filename, O_WRONLY); | ||
1839 | |||
1840 | if(mpeg_file < 0) | ||
1841 | panicf("recfile: %d", mpeg_file); | ||
1859 | 1842 | ||
1843 | |||
1844 | close(mpeg_file); | ||
1845 | |||
1846 | mpeg_file = -1; | ||
1847 | break; | ||
1848 | |||
1849 | case MPEG_STOP: | ||
1850 | DEBUGF("MPEG_STOP\n"); | ||
1851 | demand_irq_enable(false); | ||
1852 | stop_recording(); | ||
1853 | |||
1854 | /* Save the remaining data in the buffer */ | ||
1855 | stop_pending = true; | ||
1856 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | ||
1857 | break; | ||
1858 | |||
1859 | case MPEG_STOP_DONE: | ||
1860 | DEBUGF("MPEG_STOP_DONE\n"); | ||
1861 | |||
1862 | if(mpeg_file >= 0) | ||
1863 | close(mpeg_file); | ||
1864 | mpeg_file = -1; | ||
1865 | |||
1860 | #ifdef DEBUG1 | 1866 | #ifdef DEBUG1 |
1867 | { | ||
1868 | int i; | ||
1869 | for(i = 0;i < 512;i++) | ||
1861 | { | 1870 | { |
1862 | int i; | 1871 | DEBUGF("%d - %d us (%d bytes)\n", |
1863 | for(i = 0;i < 512;i++) | 1872 | timing_info[i*2], |
1864 | { | 1873 | (timing_info[i*2+1] & 0xffff) * |
1865 | DEBUGF("%d - %d us (%d bytes)\n", | 1874 | 10000 / 13824, |
1866 | timing_info[i*2], | 1875 | timing_info[i*2+1] >> 16); |
1867 | (timing_info[i*2+1] & 0xffff) * | ||
1868 | 10000 / 13824, | ||
1869 | timing_info[i*2+1] >> 16); | ||
1870 | } | ||
1871 | } | 1876 | } |
1877 | } | ||
1872 | #endif | 1878 | #endif |
1873 | mpeg_stop_done = true; | 1879 | mpeg_stop_done = true; |
1874 | break; | 1880 | break; |
1875 | 1881 | ||
1876 | case MPEG_SAVE_DATA: | 1882 | case MPEG_SAVE_DATA: |
1877 | amount_to_save = mp3buf_write - mp3buf_read; | 1883 | amount_to_save = mp3buf_write - mp3buf_read; |
1878 | 1884 | ||
1879 | /* If the result is negative, the write index has | 1885 | /* If the result is negative, the write index has |
1880 | wrapped */ | 1886 | wrapped */ |
1881 | if(amount_to_save < 0) | 1887 | if(amount_to_save < 0) |
1882 | { | 1888 | { |
1883 | amount_to_save += mp3buflen; | 1889 | amount_to_save += mp3buflen; |
1884 | } | 1890 | } |
1885 | 1891 | ||
1886 | DEBUGF("r: %x w: %x\n", mp3buf_read, mp3buf_write); | 1892 | DEBUGF("r: %x w: %x\n", mp3buf_read, mp3buf_write); |
1887 | DEBUGF("ats: %x\n", amount_to_save); | 1893 | DEBUGF("ats: %x\n", amount_to_save); |
1888 | /* Save data only if the buffer is getting full, | 1894 | /* Save data only if the buffer is getting full, |
1889 | or if we should stop recording */ | 1895 | or if we should stop recording */ |
1890 | if(amount_to_save) | 1896 | if(amount_to_save) |
1897 | { | ||
1898 | if(mp3buflen - amount_to_save < MPEG_LOW_WATER || | ||
1899 | stop_pending) | ||
1891 | { | 1900 | { |
1892 | if(mp3buflen - amount_to_save < MPEG_LOW_WATER || | 1901 | int rc; |
1893 | stop_pending) | 1902 | |
1894 | { | 1903 | /* Only save up to the end of the buffer */ |
1895 | int rc; | 1904 | writelen = MIN(amount_to_save, |
1896 | 1905 | mp3buflen - mp3buf_read); | |
1897 | /* Only save up to the end of the buffer */ | 1906 | |
1898 | writelen = MIN(amount_to_save, | 1907 | DEBUGF("wrl: %x\n", writelen); |
1899 | mp3buflen - mp3buf_read); | 1908 | mpeg_file = open(recording_filename, |
1900 | 1909 | O_WRONLY| O_APPEND); | |
1901 | DEBUGF("wrl: %x\n", writelen); | 1910 | if(mpeg_file < 0) |
1902 | mpeg_file = open(recording_filename, | 1911 | panicf("rec open: %d", mpeg_file); |
1903 | O_WRONLY| O_APPEND); | 1912 | |
1904 | if(mpeg_file < 0) | 1913 | rc = write(mpeg_file, mp3buf + mp3buf_read, |
1905 | panicf("rec open: %d", mpeg_file); | 1914 | writelen); |
1906 | 1915 | ||
1907 | rc = write(mpeg_file, mp3buf + mp3buf_read, | 1916 | if(rc < 0) |
1908 | writelen); | 1917 | panicf("rec wrt: %d", rc); |
1909 | 1918 | ||
1910 | if(rc < 0) | 1919 | rc = close(mpeg_file); |
1911 | panicf("rec wrt: %d", rc); | 1920 | if(rc < 0) |
1912 | 1921 | panicf("rec cls: %d", rc); | |
1913 | rc = close(mpeg_file); | 1922 | |
1914 | if(rc < 0) | 1923 | mpeg_file = -1; |
1915 | panicf("rec cls: %d", rc); | 1924 | DEBUGF("rc: %x\n", rc); |
1916 | 1925 | ||
1917 | mpeg_file = -1; | 1926 | mp3buf_read += amount_to_save; |
1918 | DEBUGF("rc: %x\n", rc); | 1927 | if(mp3buf_read >= mp3buflen) |
1919 | 1928 | mp3buf_read = 0; | |
1920 | mp3buf_read += amount_to_save; | 1929 | |
1921 | if(mp3buf_read >= mp3buflen) | 1930 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); |
1922 | mp3buf_read = 0; | ||
1923 | |||
1924 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | ||
1925 | } | ||
1926 | else | ||
1927 | { | ||
1928 | saving = false; | ||
1929 | } | ||
1930 | } | 1931 | } |
1931 | else | 1932 | else |
1932 | { | 1933 | { |
1933 | /* We have saved all data, | ||
1934 | time to stop for real */ | ||
1935 | if(stop_pending) | ||
1936 | queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); | ||
1937 | saving = false; | 1934 | saving = false; |
1938 | } | 1935 | } |
1939 | break; | 1936 | } |
1940 | 1937 | else | |
1941 | case MPEG_INIT_PLAYBACK: | 1938 | { |
1942 | init_playback(); | 1939 | /* We have saved all data, |
1943 | init_playback_done = true; | 1940 | time to stop for real */ |
1944 | break; | 1941 | if(stop_pending) |
1945 | } | 1942 | queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); |
1946 | /*}*/ | 1943 | saving = false; |
1944 | } | ||
1945 | break; | ||
1946 | |||
1947 | case MPEG_INIT_PLAYBACK: | ||
1948 | init_playback(); | ||
1949 | init_playback_done = true; | ||
1950 | break; | ||
1951 | } | ||
1947 | } | 1952 | } |
1948 | #endif | 1953 | #endif |
1949 | } | 1954 | } |
@@ -2981,3 +2986,74 @@ void mpeg_init(int volume, int bass, int treble, int balance, int loudness, | |||
2981 | dbg_cnt2us(0); | 2986 | dbg_cnt2us(0); |
2982 | #endif | 2987 | #endif |
2983 | } | 2988 | } |
2989 | |||
2990 | int d_1; | ||
2991 | int d_2; | ||
2992 | |||
2993 | int mpeg_create_xing_header(char *filename, void (*progressfunc)(int)) | ||
2994 | { | ||
2995 | struct mp3entry entry; | ||
2996 | char xingbuf[417]; | ||
2997 | int fd; | ||
2998 | int rc; | ||
2999 | int flen; | ||
3000 | int num_frames; | ||
3001 | int fpos; | ||
3002 | |||
3003 | if(progressfunc) | ||
3004 | progressfunc(0); | ||
3005 | |||
3006 | rc = mp3info(&entry, filename); | ||
3007 | if(rc < 0) | ||
3008 | return rc * 10 - 1; | ||
3009 | |||
3010 | fd = open(filename, O_RDWR); | ||
3011 | if(fd < 0) | ||
3012 | return fd * 10 - 2; | ||
3013 | |||
3014 | flen = lseek(fd, 0, SEEK_END); | ||
3015 | |||
3016 | d_1 = entry.first_frame_offset; | ||
3017 | d_2 = entry.filesize; | ||
3018 | |||
3019 | if(progressfunc) | ||
3020 | progressfunc(0); | ||
3021 | |||
3022 | num_frames = count_mp3_frames(fd, entry.first_frame_offset, | ||
3023 | flen, | ||
3024 | progressfunc); | ||
3025 | |||
3026 | create_xing_header(fd, entry.first_frame_offset, | ||
3027 | flen, xingbuf, num_frames, progressfunc); | ||
3028 | |||
3029 | /* Try to fit the Xing header first in the stream. Replace the existing | ||
3030 | Xing header if there is one, else see if there is room between the | ||
3031 | ID3 tag and the first MP3 frame. */ | ||
3032 | if(entry.xing_header_pos) | ||
3033 | { | ||
3034 | /* Reuse existing Xing header */ | ||
3035 | fpos = entry.xing_header_pos; | ||
3036 | } | ||
3037 | else | ||
3038 | { | ||
3039 | /* Any room between ID3 tag and first MP3 frame? */ | ||
3040 | if(entry.first_frame_offset - entry.id3v2len > 417) | ||
3041 | { | ||
3042 | fpos = entry.first_frame_offset - 417; | ||
3043 | } | ||
3044 | else | ||
3045 | { | ||
3046 | close(fd); | ||
3047 | return -3; | ||
3048 | } | ||
3049 | } | ||
3050 | |||
3051 | lseek(fd, fpos, SEEK_SET); | ||
3052 | write(fd, xingbuf, 417); | ||
3053 | close(fd); | ||
3054 | |||
3055 | if(progressfunc) | ||
3056 | progressfunc(100); | ||
3057 | |||
3058 | return 0; | ||
3059 | } | ||