/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 by Daniel Stenberg * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* * Parts of this code has been stolen from the Ample project and was written * by David Härdeman. It has since been extended and enhanced pretty much by * all sorts of friendly Rockbox people. * * A nice reference for MPEG header info: * http://rockbox.haxx.se/docs/mpeghdr.html * */ #include #include #include #include #include "debug.h" #include "mp3data.h" #include "file.h" #define DEBUG_VERBOSE #define BYTES2INT(b1,b2,b3,b4) (((b1 & 0xFF) << (3*8)) | \ ((b2 & 0xFF) << (2*8)) | \ ((b3 & 0xFF) << (1*8)) | \ ((b4 & 0xFF) << (0*8))) #define SYNC_MASK (0x7ff << 21) #define VERSION_MASK (3 << 19) #define LAYER_MASK (3 << 17) #define PROTECTION_MASK (1 << 16) #define BITRATE_MASK (0xf << 12) #define SAMPLERATE_MASK (3 << 10) #define PADDING_MASK (1 << 9) #define PRIVATE_MASK (1 << 8) #define CHANNELMODE_MASK (3 << 6) #define MODE_EXT_MASK (3 << 4) #define COPYRIGHT_MASK (1 << 3) #define ORIGINAL_MASK (1 << 2) #define EMPHASIS_MASK 3 /* Table of bitrates for MP3 files, all values in kilo. * Indexed by version, layer and value of bit 15-12 in header. */ const int bitrate_table[2][3][16] = { { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, {0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0}, {0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0} }, { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0}, {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}, {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0} } }; /* Table of samples per frame for MP3 files. * Indexed by layer. Multiplied with 1000. */ const int bs[3] = {384000, 1152000, 1152000}; /* Table of sample frequency for MP3 files. * Indexed by version and layer. */ const int freqtab[][4] = { {11025, 12000, 8000, 0}, /* MPEG version 2.5 */ {44100, 48000, 32000, 0}, /* MPEG Version 1 */ {22050, 24000, 16000, 0}, /* MPEG version 2 */ }; /* check if 'head' is a valid mp3 frame header */ static bool is_mp3frameheader(unsigned long head) { if ((head & SYNC_MASK) != (unsigned long)SYNC_MASK) /* bad sync? */ return false; if ((head & VERSION_MASK) == (1 << 19)) /* bad version? */ return false; if (!(head & LAYER_MASK)) /* no layer? */ return false; if ((head & BITRATE_MASK) == BITRATE_MASK) /* bad bitrate? */ return false; if (!(head & BITRATE_MASK)) /* no bitrate? */ return false; if ((head & SAMPLERATE_MASK) == SAMPLERATE_MASK) /* bad sample rate? */ return false; if (((head >> 19) & 1) == 1 && ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1) return false; if ((head & 0xffff0000) == 0xfffe0000) return false; return true; } static bool mp3headerinfo(struct mp3info *info, unsigned long header) { int bittable = 0; int bitindex; int freqindex; /* MPEG Audio Version */ switch(header & VERSION_MASK) { case 0: /* MPEG version 2.5 is not an official standard */ info->version = MPEG_VERSION2_5; bittable = MPEG_VERSION2 - 1; /* use the V2 bit rate table */ break; case (1 << 19): return false; case (2 << 19): /* MPEG version 2 (ISO/IEC 13818-3) */ info->version = MPEG_VERSION2; bittable = MPEG_VERSION2 - 1; break; case (3 << 19): /* MPEG version 1 (ISO/IEC 11172-3) */ info->version = MPEG_VERSION1; bittable = MPEG_VERSION1 - 1; break; } switch(header & LAYER_MASK) { case 0: return false; case (1 << 17): info->layer = 2; break; case (2 << 17): info->layer = 1; break; case (3 << 17): info->layer = 0; break; } info->protection = (header & PROTECTION_MASK)?true:false; /* Bitrate */ bitindex = (header & 0xf000) >> 12; info->bitrate = bitrate_table[bittable][info->layer][bitindex]; if(info->bitrate == 0) return false; /* Sampling frequency */ freqindex = (header & 0x0C00) >> 10; info->frequency = freqtab[info->version][freqindex]; if(info->frequency == 0) return false; info->padding = (header & 0x0200)?1:0; /* Calculate number of bytes, calculation depends on layer */ switch(info->layer) { case 0: info->frame_size = info->bitrate * 48000; info->frame_size /= freqtab[info->version][freqindex] << bittable; break; case 1: case 2: info->frame_size = info->bitrate * 144000; info->frame_size /= freqtab[info->version][freqindex] << bittable; break; default: info->frame_size = 1; } info->frame_size += info->padding; /* Calculate time per frame */ info->frame_time = bs[info->layer] / (freqtab[info->version][freqindex] << bittable); info->channel_mode = (header & 0xc0) >> 6; info->mode_extension = (header & 0x30) >> 4; info->emphasis = header & 3; #ifdef DEBUG_VERBOSE DEBUGF( "Header: %08x, Ver %d, lay %d, bitr %d, freq %d, " "chmode %d, mode_ext %d, emph %d, bytes: %d time: %d\n", header, info->version, info->layer, info->bitrate, info->frequency, info->channel_mode, info->mode_extension, info->emphasis, info->frame_size, info->frame_time); #endif return true; } unsigned long find_next_frame(int fd, int *offset, int max_offset, unsigned long last_header) { unsigned long header=0; unsigned char tmp; int i; int pos = 0; /* We remember the last header we found, to use as a template to see if the header we find has the same frequency, layer etc */ last_header &= 0xffff0c00; /* Fill up header with first 24 bits */ for(i = 0; i < 3; i++) { header <<= 8; if(!read(fd, &tmp, 1)) return 0; header |= tmp; pos++; } do { header <<= 8; if(!read(fd, &tmp, 1)) return 0; header |= tmp; pos++; if(max_offset > 0 && pos > max_offset) return 0; } while(!is_mp3frameheader(header) || (last_header?((header & 0xffff0c00) != last_header):false)); *offset = pos - 4; #ifdef DEBUG if(*offset) DEBUGF("Warning: skipping %d bytes of garbage\n", *offset); #endif return header; } #ifdef SIMULATOR unsigned char mp3buf[0x100000]; unsigned char mp3end[1]; #else extern unsigned char mp3buf[]; extern unsigned char mp3end[]; #endif static int fnf_read_index; static int fnf_buf_len; static int buf_getbyte(int fd, unsigned char *c) { if(fnf_read_index < fnf_buf_len) { *c = mp3buf[fnf_read_index++]; return 1; } else { fnf_buf_len = read(fd, mp3buf, mp3end - mp3buf); if(fnf_buf_len < 0) return -1; fnf_read_index = 0; if(fnf_buf_len > 0) { *c = mp3buf[fnf_read_index++]; return 1; } else return 0; } return 0; } static int buf_seek(int fd, int len) { fnf_read_index += len; if(fnf_read_index > fnf_buf_len) { len = fnf_read_index - fnf_buf_len; fnf_buf_len = read(fd, mp3buf, mp3end - mp3buf); if(fnf_buf_len < 0) return -1; fnf_read_index = 0; fnf_read_index += len; } if(fnf_read_index > fnf_buf_len) { return -1; } else return 0; } static void buf_init(void) { fnf_buf_len = 0; fnf_read_index = 0; } unsigned long buf_find_next_frame(int fd, int *offset, int max_offset, unsigned long last_header) { unsigned long header=0; unsigned char tmp; int i; int pos = 0; /* We remember the last header we found, to use as a template to see if the header we find has the same frequency, layer etc */ last_header &= 0xffff0c00; /* Fill up header with first 24 bits */ for(i = 0; i < 3; i++) { header <<= 8; if(!buf_getbyte(fd, &tmp)) return 0; header |= tmp; pos++; } do { header <<= 8; if(!buf_getbyte(fd, &tmp)) return 0; header |= tmp; pos++; if(max_offset > 0 && pos > max_offset) return 0; } while(!is_mp3frameheader(header) || (last_header?((header & 0xffff0c00) != last_header):false)); *offset = pos - 4; #ifdef DEBUG if(*offset) DEBUGF("Warning: skipping %d bytes of garbage\n", *offset); #endif return header; } int get_mp3file_info(int fd, struct mp3info *info) { unsigned char frame[1050]; unsigned char *vbrheader; unsigned long header; int bytecount; int num_offsets; int frames_per_entry; int i; int offset; int j; int tmp; header = find_next_frame(fd, &bytecount, 0x20000, 0); /* Quit if we haven't found a valid header within 128K */ if(header == 0) return -1; memset(info, 0, sizeof(struct mp3info)); if(!mp3headerinfo(info, header)) return -2; /* OK, we have found a frame. Let's see if it has a Xing header */ if(read(fd, frame, info->frame_size-4) < 0) return -3; /* calculate position of VBR header */ if ( info->version == MPEG_VERSION1 ) { if (info->channel_mode == 3) /* mono */ vbrheader = frame + 17; else vbrheader = frame + 32; } else { if (info->channel_mode == 3) /* mono */ vbrheader = frame + 9; else vbrheader = frame + 17; } if (vbrheader[0] == 'X' && vbrheader[1] == 'i' && vbrheader[2] == 'n' && vbrheader[3] == 'g') { int i = 8; /* Where to start parsing info */ DEBUGF("Xing header\n"); /* Remember where in the file the Xing header is */ info->vbr_header_pos = lseek(fd, 0, SEEK_CUR) - info->frame_size; /* We want to skip the Xing frame when playing the stream */ bytecount += info->frame_size; /* Now get the next frame to find out the real info about the mp3 stream */ header = find_next_frame(fd, &tmp, 0x20000, 0); if(header == 0) return -4; if(!mp3headerinfo(info, header)) return -5; /* Yes, it is a VBR file */ info->is_vbr = true; info->is_xing_vbr = true; if(vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ { info->frame_count = BYTES2INT(vbrheader[i], vbrheader[i+1], vbrheader[i+2], vbrheader[i+3]); info->file_time = info->frame_count * info->frame_time; i += 4; } if(vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */ { info->byte_count = BYTES2INT(vbrheader[i], vbrheader[i+1], vbrheader[i+2], vbrheader[i+3]); i += 4; } if(info->file_time && info->byte_count) info->bitrate = info->byte_count * 8 / info->file_time; else info->bitrate = 0; if(vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ { memcpy( info->toc, vbrheader+i, 100 ); } } if (vbrheader[0] == 'V' && vbrheader[1] == 'B' && vbrheader[2] == 'R' && vbrheader[3] == 'I') { DEBUGF("VBRI header\n"); /* We want to skip the VBRI frame when playing the stream */ bytecount += info->frame_size; /* Now get the next frame to find out the real info about the mp3 stream */ header = find_next_frame(fd, &bytecount, 0x20000, 0); if(header == 0) return -6; if(!mp3headerinfo(info, header)) return -7; DEBUGF("%04x: %04x %04x ", 0, header >> 16, header & 0xffff); for(i = 4;i < (int)sizeof(frame)-4;i+=2) { if(i % 16 == 0) { DEBUGF("\n%04x: ", i-4); } DEBUGF("%04x ", (frame[i-4] << 8) | frame[i-4+1]); } DEBUGF("\n"); /* Yes, it is a FhG VBR file */ info->is_vbr = true; info->is_vbri_vbr = true; info->has_toc = false; /* We don't parse the TOC (yet) */ info->byte_count = BYTES2INT(vbrheader[10], vbrheader[11], vbrheader[12], vbrheader[13]); info->frame_count = BYTES2INT(vbrheader[14], vbrheader[15], vbrheader[16], vbrheader[17]); info->file_time = info->frame_count * info->frame_time; info->bitrate = info->byte_count * 8 / info->file_time; /* We don't parse the TOC, since we don't yet know how to (FIXME) */ num_offsets = BYTES2INT(0, 0, vbrheader[18], vbrheader[19]); frames_per_entry = BYTES2INT(0, 0, vbrheader[24], vbrheader[25]); DEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n", info->bitrate, info->frame_size, info->frame_size); DEBUGF("Frame count: %x\n", info->frame_count); DEBUGF("Byte count: %x\n", info->byte_count); DEBUGF("Offsets: %d\n", num_offsets); DEBUGF("Frames/entry: %d\n", frames_per_entry); offset = 0; for(i = 0;i < num_offsets;i++) { j = BYTES2INT(0, 0, vbrheader[26+i*2], vbrheader[27+i*2]); offset += j; DEBUGF("%03d: %x (%x)\n", i, offset - bytecount, j); } } /* Is it a LAME Info frame? */ if (vbrheader[0] == 'I' && vbrheader[1] == 'n' && vbrheader[2] == 'f' && vbrheader[3] == 'o') { /* Make sure we skip this frame in playback */ bytecount += info->frame_size; } return bytecount; } /* This is an MP3 header, 128kbit/s, 44.1kHz, with silence */ static const unsigned char xing_frame_header[] = { 0xff, 0xfa, 0x90, 0x64, 0x86, 0x1f }; static const char cooltext[] = "Rockbox rocks"; static void int2bytes(unsigned char *buf, int val) { buf[0] = (val >> 24) & 0xff; buf[1] = (val >> 16) & 0xff; buf[2] = (val >> 8) & 0xff; buf[3] = val & 0xff; } int count_mp3_frames(int fd, int startpos, int filesize, void (*progressfunc)(int)) { unsigned long header = 0; struct mp3info info; int num_frames; int bytes; int cnt; int progress_chunk = filesize / 50; /* Max is 50%, in 1% increments */ int progress_cnt = 0; bool is_vbr = false; int last_bitrate = 0; if(lseek(fd, startpos, SEEK_SET) < 0) return -1; buf_init(); /* Find out the total number of frames */ num_frames = 0; cnt = 0; while((header = buf_find_next_frame(fd, &bytes, -1, header))) { mp3headerinfo(&info, header); /* See if this really is a VBR file */ if(last_bitrate && info.bitrate != last_bitrate) { is_vbr = true; } last_bitrate = info.bitrate; buf_seek(fd, info.frame_size-4); num_frames++; if(progressfunc) { cnt += bytes + info.frame_size; if(cnt > progress_chunk) { progress_cnt++; progressfunc(progress_cnt); cnt = 0; } } } DEBUGF("Total number of frames: %d\n", num_frames); if(is_vbr) return num_frames; else { DEBUGF("Not a VBR file\n"); return 0; } } int create_xing_header(int fd, int startpos, int filesize, unsigned char *buf, int num_frames, void (*progressfunc)(int), bool generate_toc) { unsigned long header = 0; struct mp3info info; int pos, last_pos; int i, j; int bytes; int filepos; int tocentry; int x; int index; DEBUGF("create_xing_header()\n"); /* Create the frame header */ memset(buf, 0, 417); memcpy(buf, xing_frame_header, 6); lseek(fd, startpos, SEEK_SET); buf_init(); buf[36] = 'X'; buf[36+1] = 'i'; buf[36+2] = 'n'; buf[36+3] = 'g'; int2bytes(&buf[36+4], ((num_frames?VBR_FRAMES_FLAG:0) | (filesize?VBR_BYTES_FLAG:0) | (generate_toc?VBR_TOC_FLAG:0))); index = 36+8; if(num_frames) { int2bytes(&buf[index], num_frames); index += 4; } if(filesize) { int2bytes(&buf[index], filesize - startpos); index += 4; } if(generate_toc) { /* Generate filepos table */ last_pos = 0; filepos = 0; header = 0; x = 0; for(i = 0;i < 100;i++) { /* Calculate the absolute frame number for this seek point */ pos = i * num_frames / 100; /* Advance from the last seek point to this one */ for(j = 0;j < pos - last_pos;j++) { DEBUGF("fpos: %x frame no: %x ", filepos, x++); header = buf_find_next_frame(fd, &bytes, -1, header); mp3headerinfo(&info, header); buf_seek(fd, info.frame_size-4); filepos += info.frame_size; } if(progressfunc) { progressfunc(50 + i/2); } tocentry = filepos * 256 / filesize; DEBUGF("Pos %d: %d relpos: %d filepos: %x tocentry: %x\n", i, pos, pos-last_pos, filepos, tocentry); /* Fill in the TOC entry */ buf[index + i] = tocentry; last_pos = pos; } } memcpy(buf + index + 100, cooltext, sizeof(cooltext)); #ifdef DEBUG for(i = 0;i < 417;i++) { if(i && !(i % 16)) DEBUGF("\n"); DEBUGF("%02x ", buf[i]); } #endif return 0; }