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/mp3data.c | |
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/mp3data.c')
-rw-r--r-- | firmware/mp3data.c | 664 |
1 files changed, 664 insertions, 0 deletions
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 | } | ||