summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2003-03-10 14:55:31 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2003-03-10 14:55:31 +0000
commita039091187f40d018b6353b8c13de7a01d3a6fe0 (patch)
tree08f7eb86e86e2c61f4d36f9c91731cb93252ba84 /firmware
parent22cbe938feb48895d7488449835d3ee577399057 (diff)
downloadrockbox-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.h9
-rw-r--r--firmware/export/mp3data.h66
-rw-r--r--firmware/export/mpeg.h1
-rw-r--r--firmware/id3.c371
-rw-r--r--firmware/mp3data.c664
-rw-r--r--firmware/mpeg.c320
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 {
unsigned int first_frame_offset; /* Byte offset to first real MP3 frame.
Used for skipping leading garbage to
avoid gaps between tracks. */
+ unsigned int xing_header_pos;
unsigned int filesize; /* in bytes */
unsigned int length; /* song length */
unsigned int elapsed; /* ms played */
+
+ /* MP3 stream specific info */
long bpf; /* bytes per frame */
long tpf; /* time per frame */
/* Xing VBR fields */
bool vbr;
- unsigned char vbrflags;
+ bool has_toc; /* True if there is a VBR header in the file */
unsigned char toc[100];/* table of contents */
/* these following two fields are used for local buffering */
@@ -59,10 +62,6 @@ struct mp3entry {
int index; /* playlist index */
};
-#define VBR_FRAMES_FLAG 0x01
-#define VBR_BYTES_FLAG 0x02
-#define VBR_TOC_FLAG 0x04
-
enum {
ID3_VER_1_0 = 1,
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Linus Nielsen Feltzing
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef _MP3DATA_H_
+#define _MP3DATA_H_
+
+#define MPEG_VERSION2_5 0
+#define MPEG_VERSION1 1
+#define MPEG_VERSION2 2
+
+struct mp3info {
+ /* Standard MP3 frame header fields */
+ int version;
+ int layer;
+ bool protection;
+ int bitrate;
+ int frequency;
+ int padding;
+ int channel_mode;
+ int mode_extension;
+ int emphasis;
+ int frame_size; /* Frame size in bytes */
+ int frame_time; /* Frame duration in milliseconds */
+
+ bool is_vbr; /* True if the file is VBR */
+ bool has_toc; /* True if there is a VBR header in the file */
+ bool is_xing_vbr; /* True if the VBR header is of Xing type */
+ bool is_vbri_vbr; /* True if the VBR header is of VBRI type */
+ unsigned char toc[100];
+ int frame_count; /* Number of frames in the file (if VBR) */
+ int byte_count; /* File size in bytes */
+ int file_time; /* Length of the whole file in milliseconds */
+ int xing_header_pos;
+};
+
+/* Xing header information */
+#define VBR_FRAMES_FLAG 0x01
+#define VBR_BYTES_FLAG 0x02
+#define VBR_TOC_FLAG 0x04
+
+
+unsigned long find_next_frame(int fd, int *offset, int max_offset, unsigned long last_header);
+int get_mp3file_info(int fd, struct mp3info *info);
+int count_mp3_frames(int fd, int startpos, int filesize,
+ void (*progressfunc)(int));
+int create_xing_header(int fd, int startpos, int filesize,
+ unsigned char *buf, int num_frames,
+ void (*progressfunc)(int));
+
+#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);
#endif
void mpeg_get_debugdata(struct mpeg_debug *dbgdata);
void mpeg_set_buffer_margin(int seconds);
+int mpeg_create_xing_header(char *filename, void (*progressfunc)(int));
#define SOUND_VOLUME 0
#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 @@
* 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 <stdio.h>
@@ -37,6 +34,7 @@
#include "atoi.h"
#include "id3.h"
+#include "mp3data.h"
#define UNSYNC(b0,b1,b2,b3) (((b0 & 0x7F) << (3*7)) | \
((b1 & 0x7F) << (2*7)) | \
@@ -48,38 +46,6 @@
((b2 & 0xFF) << (1*8)) | \
((b3 & 0xFF) << (0*8)))
-/* 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[4] = {0, 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 */
-};
-
/* Checks to see if the passed in string is a 16-bit wide Unicode v2
string. If it is, we attempt to convert it to a 8-bit ASCII string
(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)
if (strncmp(buffer, "TAG", 3))
return false;
+ entry->id3v1len = 128;
entry->id3version = ID3_VER_1_0;
for (i=0; i < (int)sizeof offsets; i++) {
@@ -239,6 +206,8 @@ static void setid3v2title(int fd, struct mp3entry *entry)
char *tracknum = NULL;
int bytesread = 0;
int buffersize = sizeof(entry->id3v2buf);
+ int flags;
+ int skip;
/* Bail out if the tag is shorter than 10 bytes */
if(entry->id3v2len < 10)
@@ -275,17 +244,34 @@ static void setid3v2title(int fd, struct mp3entry *entry)
}
entry->id3version = version;
+ /* Skip the extended header if it is present */
+ if(version >= ID3_VER_2_4) {
+ if(header[5] & 0x40) {
+ if(4 != read(fd, header, 4))
+ return;
+
+ framelen = UNSYNC(header[0], header[1],
+ header[2], header[3]);
+
+ lseek(fd, framelen - 4, SEEK_CUR);
+ }
+ }
+
/*
* We must have at least minframesize bytes left for the
* remaining frames to be interesting
*/
while(size > minframesize) {
+ flags = 0;
+
/* Read frame header and check length */
if(version >= ID3_VER_2_3) {
if(10 != read(fd, header, 10))
return;
/* Adjust for the 10 bytes we read */
size -= 10;
+
+ flags = BYTES2INT(0, 0, header[8], header[9]);
if (version >= ID3_VER_2_4) {
framelen = UNSYNC(header[4], header[5],
@@ -311,6 +297,33 @@ static void setid3v2title(int fd, struct mp3entry *entry)
if(framelen == 0)
return;
+ if(flags)
+ {
+ skip = 0;
+
+ if(flags & 0x0040) /* Grouping identity */
+ skip++;
+
+ if(flags & 0x000e) /* Compression, encryption or
+ unsynchronization */
+ {
+ /* Skip it using the total size in case
+ it was truncated */
+ size -= totframelen;
+ lseek(fd, totframelen, SEEK_CUR);
+ continue;
+ }
+
+ if(flags & 0x0001) /* Data length indicator */
+ skip += 4;
+
+ if(skip)
+ {
+ lseek(fd, skip, SEEK_CUR);
+ framelen -= skip;
+ }
+ }
+
/* If the frame is larger than the remaining buffer space we try
to read as much as would fit in the buffer */
if(framelen >= buffersize - bufferpos)
@@ -404,6 +417,7 @@ static int getid3v2len(int fd)
else
offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
+ DEBUGF("ID3V2 Length: 0x%x\n", offset);
return offset;
}
@@ -419,29 +433,6 @@ static int getfilesize(int fd)
return size;
}
-/* check if 'head' is a valid mp3 frame header */
-static bool mp3frameheader(unsigned long head)
-{
- if ((head & 0xffe00000) != 0xffe00000) /* bad sync? */
- return false;
- if (!((head >> 17) & 3)) /* no layer? */
- return false;
- if (((head >> 12) & 0xf) == 0xf) /* bad bitrate? */
- return false;
- if (!((head >> 12) & 0xf)) /* no bitrate? */
- return false;
- if (((head >> 10) & 0x3) == 0x3) /* 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;
-}
-
/*
* Calculates the length (in milliseconds) of an MP3 file.
*
@@ -456,263 +447,48 @@ static bool mp3frameheader(unsigned long head)
static int getsonglength(int fd, struct mp3entry *entry)
{
unsigned int filetime = 0;
- unsigned long header=0;
- unsigned char tmp;
- unsigned char frame[156];
- unsigned char* xing;
-
- enum {
- MPEG_VERSION2_5,
- MPEG_VERSION1,
- MPEG_VERSION2
- } version;
- int layer;
- int bitindex;
- int bitrate;
- int freqindex;
- int frequency;
- int chmode;
+ struct mp3info info;
int bytecount;
- int bytelimit;
- int bittable; /* which bitrate table to use */
- bool header_found = false;
-
- long bpf;
- long tpf;
-
+
/* Start searching after ID3v2 header */
if(-1 == lseek(fd, entry->id3v2len, SEEK_SET))
return 0;
-
- /* Fill up header with first 24 bits */
- for(version = 0; version < 3; version++) {
- header <<= 8;
- if(!read(fd, &tmp, 1))
- return 0;
- header |= tmp;
- }
-
- /* Loop trough file until we find a frame header */
- bytecount = entry->id3v2len - 1;
- bytelimit = entry->id3v2len + 0x20000;
- restart:
- do {
- header <<= 8;
- if(!read(fd, &tmp, 1))
- return 0;
- header |= tmp;
- /* Quit if we haven't found a valid header within 128K */
- bytecount++;
- if(bytecount > bytelimit)
- return 0;
- } while(!mp3frameheader(header));
-
- /*
- * Some files are filled with garbage in the beginning,
- * if the bitrate index of the header is binary 1111
- * that is a good indicator
- */
- if((header & 0xF000) == 0xF000)
- goto restart;
-
- /* MPEG Audio Version */
- switch((header & 0x180000) >> 19) {
- case 0:
- /* MPEG version 2.5 is not an official standard */
- version = MPEG_VERSION2_5;
- bittable = MPEG_VERSION2; /* use the V2 bit rate table */
- break;
+ bytecount = get_mp3file_info(fd, &info);
- case 2:
- /* MPEG version 2 (ISO/IEC 13818-3) */
- version = MPEG_VERSION2;
- bittable = MPEG_VERSION2;
- break;
+ DEBUGF("Space between ID3V2 tag and first audio frame: 0x%x bytes\n",
+ bytecount);
- case 3:
- /* MPEG version 1 (ISO/IEC 11172-3) */
- version = MPEG_VERSION1;
- bittable = MPEG_VERSION1;
- break;
- default:
- goto restart;
- }
-
- /* Layer */
- switch((header & 0x060000) >> 17) {
- case 1:
- layer = 3;
- break;
- case 2:
- layer = 2;
- break;
- case 3:
- layer = 1;
- break;
- default:
- goto restart;
- }
-
- /* Bitrate */
- bitindex = (header & 0xF000) >> 12;
- bitrate = bitrate_table[bittable-1][layer-1][bitindex];
- if(bitrate == 0)
- goto restart;
-
- /* Sampling frequency */
- freqindex = (header & 0x0C00) >> 10;
- frequency = freqtab[version][freqindex];
- if(frequency == 0)
- goto restart;
-
-#ifdef DEBUG_VERBOSE
- DEBUGF( "Version %i, lay %i, biti %i, bitr %i, freqi %i, freq %i, chmode %d\n",
- version, layer, bitindex, bitrate, freqindex, frequency, chmode);
-#endif
- entry->version = version;
- entry->layer = layer;
- entry->frequency = frequency;
-
- /* Calculate bytes per frame, calculation depends on layer */
- switch(layer) {
- case 1:
- bpf = bitrate_table[bittable - 1][layer - 1][bitindex];
- bpf *= 48000;
- bpf /= freqtab[version][freqindex] << (bittable - 1);
- break;
- case 2:
- case 3:
- bpf = bitrate_table[bittable - 1][layer - 1][bitindex];
- bpf *= 144000;
- bpf /= freqtab[version][freqindex] << (bittable - 1);
- break;
- default:
- bpf = 1;
- }
-
- /* Calculate time per frame */
- tpf = bs[layer] / (freqtab[version][freqindex] << (bittable - 1));
-
- entry->bpf = bpf;
- entry->tpf = tpf;
-
- /* OK, we have found a frame. Let's see if it has a Xing header */
- if(read(fd, frame, sizeof frame) < 0)
+ if(bytecount < 0)
return -1;
+
+ bytecount += entry->id3v2len;
- /* Channel mode (stereo/mono) */
- chmode = (header & 0xc0) >> 6;
-
- /* calculate position of Xing VBR header */
- if ( version == 1 ) {
- if ( chmode == 3 ) /* mono */
- xing = frame + 17;
- else
- xing = frame + 32;
- }
- else {
- if ( chmode == 3 ) /* mono */
- xing = frame + 9;
- else
- xing = frame + 17;
- }
-
- if (xing[0] == 'X' &&
- xing[1] == 'i' &&
- xing[2] == 'n' &&
- xing[3] == 'g')
- {
- int i = 8; /* Where to start parsing info */
-
- /* Yes, it is a VBR file */
- entry->vbr = true;
- entry->vbrflags = xing[7];
-
- if (entry->vbrflags & VBR_FRAMES_FLAG) /* Is the frame count there? */
- {
- int framecount = (xing[i] << 24) | (xing[i+1] << 16) |
- (xing[i+2] << 8) | xing[i+3];
-
- filetime = framecount * tpf;
- i += 4;
- }
-
- if (entry->vbrflags & VBR_BYTES_FLAG) /* is byte count there? */
- {
- int bytecount = (xing[i] << 24) | (xing[i+1] << 16) |
- (xing[i+2] << 8) | xing[i+3];
-
- bitrate = bytecount * 8 / filetime;
- i += 4;
- }
-
- if (entry->vbrflags & VBR_TOC_FLAG) /* is table-of-contents there? */
- {
- memcpy( entry->toc, xing+i, 100 );
- }
-
- /* Make sure we skip this frame in playback */
- bytecount += bpf;
-
- header_found = true;
- }
-
- if (xing[0] == 'V' &&
- xing[1] == 'B' &&
- xing[2] == 'R' &&
- xing[3] == 'I')
- {
- int framecount;
- int bytecount;
-
- /* Yes, it is a FhG VBR file */
- entry->vbr = true;
- entry->vbrflags = 0;
-
- bytecount = (xing[10] << 24) | (xing[11] << 16) |
- (xing[12] << 8) | xing[13];
-
- framecount = (xing[14] << 24) | (xing[15] << 16) |
- (xing[16] << 8) | xing[17];
-
- filetime = framecount * tpf;
- bitrate = bytecount * 8 / filetime;
-
- /* We don't parse the TOC, since we don't yet know how to (FIXME) */
-
- /* Make sure we skip this frame in playback */
- bytecount += bpf;
-
- header_found = true;
- }
-
- /* Is it a LAME Info frame? */
- if (xing[0] == 'I' &&
- xing[1] == 'n' &&
- xing[2] == 'f' &&
- xing[3] == 'o')
- {
- /* Make sure we skip this frame in playback */
- bytecount += bpf;
-
- header_found = true;
- }
-
-
- entry->bitrate = bitrate;
+ entry->bitrate = info.bitrate;
/* If the file time hasn't been established, this may be a fixed
rate MP3, so just use the default formula */
+
+ filetime = info.file_time;
+
if(filetime == 0)
{
/*
* Now song length is
* ((filesize)/(bytes per frame))*(time per frame)
*/
- filetime = entry->filesize/bpf*tpf;
+ filetime = entry->filesize/info.frame_size*info.frame_time;
}
+ entry->tpf = info.frame_time;
+ entry->bpf = info.frame_size;
+
+ entry->vbr = info.is_vbr;
+ entry->has_toc = info.has_toc;
+ memcpy(entry->toc, info.toc, sizeof(info.toc));
+
+ entry->xing_header_pos = info.xing_header_pos;
+
/* Update the seek point for the first playable frame */
entry->first_frame_offset = bytecount;
DEBUGF("First frame is at %x\n", entry->first_frame_offset);
@@ -720,7 +496,6 @@ static int getsonglength(int fd, struct mp3entry *entry)
return filetime;
}
-
/*
* Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc)
* about an MP3 file and updates it's entry accordingly.
@@ -750,6 +525,10 @@ bool mp3info(struct mp3entry *entry, char *filename)
setid3v2title(fd, entry);
entry->length = getsonglength(fd, entry);
+ /* Subtract the meta information from the file size to get
+ the true size of the MP3 stream */
+ entry->filesize -= entry->first_frame_offset;
+
/* only seek to end of file if no id3v2 tags were found */
if (!entry->id3v2len) {
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#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 fd;
+
+static int buf_getbyte(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 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 *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(&tmp))
+ return 0;
+ header |= tmp;
+ pos++;
+ }
+
+ do {
+ header <<= 8;
+ if(!buf_getbyte(&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[1024];
+ 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->xing_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]);
+ info->bitrate = info->byte_count * 8 / info->file_time;
+ i += 4;
+ }
+
+ 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;
+
+ 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(&bytes, -1, header))) {
+ mp3headerinfo(&info, header);
+ buf_seek(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);
+
+ return num_frames;
+}
+
+int create_xing_header(int fd, int startpos, int filesize,
+ unsigned char *buf, int num_frames,
+ void (*progressfunc)(int))
+{
+ unsigned long header = 0;
+ struct mp3info info;
+ int pos, last_pos;
+ int i, j;
+ int bytes;
+ int filepos;
+ int tocentry;
+ int x;
+
+ 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], (VBR_FRAMES_FLAG | VBR_BYTES_FLAG | VBR_TOC_FLAG));
+ int2bytes(&buf[36+8], num_frames);
+ int2bytes(&buf[36+12], filesize - startpos);
+
+ /* 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(&bytes, -1, header);
+ mp3headerinfo(&info, header);
+ buf_seek(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[36+16+i] = tocentry;
+
+ last_pos = pos;
+ }
+
+ memcpy(buf+152, cooltext, sizeof(cooltext));
+
+#ifdef DEBUG
+ for(i = 0;i < 417;i++)
+ {
+ if(i && !(i % 16))
+ DEBUGF("\n");
+
+ DEBUGF("%02x ", buf[i]);
+ }
+#endif
+
+ return 0;
+}
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 @@
#include "string.h"
#include <kernel.h>
#include "thread.h"
+#include "mp3data.h"
#ifndef SIMULATOR
#include "i2c.h"
#include "mas.h"
@@ -302,7 +303,7 @@ static void remove_all_tags(void)
static void set_elapsed(struct mp3entry* id3)
{
if ( id3->vbr ) {
- if ( id3->vbrflags & VBR_TOC_FLAG ) {
+ if ( id3->has_toc ) {
/* calculate elapsed time using TOC */
int i;
unsigned int remainder, plen, relpos, nextpos;
@@ -1482,7 +1483,7 @@ static void mpeg_thread(void)
if (id3->vbr)
{
- if (id3->vbrflags & VBR_TOC_FLAG)
+ if (id3->has_toc)
{
/* Use the TOC to find the new position */
unsigned int percent, remainder;
@@ -1528,10 +1529,10 @@ static void mpeg_thread(void)
transition properly to the next song */
newpos = id3->filesize - id3->id3v1len - 1;
}
- else if (newpos < (int)id3->id3v2len)
+ else if (newpos < (int)id3->first_frame_offset)
{
- /* skip past id3v2 tag */
- newpos = id3->id3v2len;
+ /* skip past id3v2 tag and other leading garbage */
+ newpos = id3->first_frame_offset;
}
if (mpeg_file >= 0)
@@ -1720,7 +1721,7 @@ static void mpeg_thread(void)
t2 = current_tick;
DEBUGF("time: %d\n", t2 - t1);
DEBUGF("R: %x\n", len);
-
+
/* Now make sure that we don't feed the MAS with ID3V1
data */
if (len < amount_to_read)
@@ -1734,19 +1735,19 @@ static void mpeg_thread(void)
{
if(tagptr >= mp3buflen)
tagptr -= mp3buflen;
-
+
if(mp3buf[tagptr] != tag[i])
taglen = 0;
-
+
tagptr++;
}
-
+
if(taglen)
{
/* Skip id3v1 tag */
DEBUGF("Skipping ID3v1 tag\n");
len -= taglen;
-
+
/* The very rare case when the entire tag
wasn't read in this read() call must be
taken care of */
@@ -1819,131 +1820,135 @@ static void mpeg_thread(void)
}
else
{
- /* This doesn't look neccessary...
- yield();
- if(!queue_empty(&mpeg_queue))
- {*/
- queue_wait(&mpeg_queue, &ev);
- switch(ev.id)
- {
- case MPEG_RECORD:
- DEBUGF("Recording...\n");
- reset_mp3_buffer();
- start_recording();
- demand_irq_enable(true);
- mpeg_file = creat(recording_filename, O_WRONLY);
-
- if(mpeg_file < 0)
- panicf("recfile: %d", mpeg_file);
-
- close(mpeg_file);
- mpeg_file = -1;
- break;
-
- case MPEG_STOP:
- DEBUGF("MPEG_STOP\n");
- demand_irq_enable(false);
- stop_recording();
-
- /* Save the remaining data in the buffer */
- stop_pending = true;
- queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
- break;
+ queue_wait(&mpeg_queue, &ev);
+ switch(ev.id)
+ {
+ case MPEG_RECORD:
+ DEBUGF("Recording...\n");
+ reset_mp3_buffer();
- case MPEG_STOP_DONE:
- DEBUGF("MPEG_STOP_DONE\n");
+ /* Advance the write pointer 4096+417 bytes to make
+ room for an ID3 tag plus a VBR header */
+ mp3buf_write = 4096+417;
+ memset(mp3buf, 0, 4096+417);
- if(mpeg_file >= 0)
- close(mpeg_file);
- mpeg_file = -1;
+ start_recording();
+ demand_irq_enable(true);
+
+ mpeg_file = creat(recording_filename, O_WRONLY);
+
+ if(mpeg_file < 0)
+ panicf("recfile: %d", mpeg_file);
+
+ close(mpeg_file);
+
+ mpeg_file = -1;
+ break;
+
+ case MPEG_STOP:
+ DEBUGF("MPEG_STOP\n");
+ demand_irq_enable(false);
+ stop_recording();
+
+ /* Save the remaining data in the buffer */
+ stop_pending = true;
+ queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
+ break;
+
+ case MPEG_STOP_DONE:
+ DEBUGF("MPEG_STOP_DONE\n");
+
+ if(mpeg_file >= 0)
+ close(mpeg_file);
+ mpeg_file = -1;
+
#ifdef DEBUG1
+ {
+ int i;
+ for(i = 0;i < 512;i++)
{
- int i;
- for(i = 0;i < 512;i++)
- {
- DEBUGF("%d - %d us (%d bytes)\n",
- timing_info[i*2],
- (timing_info[i*2+1] & 0xffff) *
- 10000 / 13824,
- timing_info[i*2+1] >> 16);
- }
+ DEBUGF("%d - %d us (%d bytes)\n",
+ timing_info[i*2],
+ (timing_info[i*2+1] & 0xffff) *
+ 10000 / 13824,
+ timing_info[i*2+1] >> 16);
}
+ }
#endif
- mpeg_stop_done = true;
- break;
-
- case MPEG_SAVE_DATA:
- amount_to_save = mp3buf_write - mp3buf_read;
-
- /* If the result is negative, the write index has
- wrapped */
- if(amount_to_save < 0)
- {
- amount_to_save += mp3buflen;
- }
-
- DEBUGF("r: %x w: %x\n", mp3buf_read, mp3buf_write);
- DEBUGF("ats: %x\n", amount_to_save);
- /* Save data only if the buffer is getting full,
- or if we should stop recording */
- if(amount_to_save)
+ mpeg_stop_done = true;
+ break;
+
+ case MPEG_SAVE_DATA:
+ amount_to_save = mp3buf_write - mp3buf_read;
+
+ /* If the result is negative, the write index has
+ wrapped */
+ if(amount_to_save < 0)
+ {
+ amount_to_save += mp3buflen;
+ }
+
+ DEBUGF("r: %x w: %x\n", mp3buf_read, mp3buf_write);
+ DEBUGF("ats: %x\n", amount_to_save);
+ /* Save data only if the buffer is getting full,
+ or if we should stop recording */
+ if(amount_to_save)
+ {
+ if(mp3buflen - amount_to_save < MPEG_LOW_WATER ||
+ stop_pending)
{
- if(mp3buflen - amount_to_save < MPEG_LOW_WATER ||
- stop_pending)
- {
- int rc;
-
- /* Only save up to the end of the buffer */
- writelen = MIN(amount_to_save,
- mp3buflen - mp3buf_read);
-
- DEBUGF("wrl: %x\n", writelen);
- mpeg_file = open(recording_filename,
- O_WRONLY| O_APPEND);
- if(mpeg_file < 0)
- panicf("rec open: %d", mpeg_file);
-
- rc = write(mpeg_file, mp3buf + mp3buf_read,
- writelen);
-
- if(rc < 0)
- panicf("rec wrt: %d", rc);
-
- rc = close(mpeg_file);
- if(rc < 0)
- panicf("rec cls: %d", rc);
-
- mpeg_file = -1;
- DEBUGF("rc: %x\n", rc);
-
- mp3buf_read += amount_to_save;
- if(mp3buf_read >= mp3buflen)
- mp3buf_read = 0;
-
- queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
- }
- else
- {
- saving = false;
- }
+ int rc;
+
+ /* Only save up to the end of the buffer */
+ writelen = MIN(amount_to_save,
+ mp3buflen - mp3buf_read);
+
+ DEBUGF("wrl: %x\n", writelen);
+ mpeg_file = open(recording_filename,
+ O_WRONLY| O_APPEND);
+ if(mpeg_file < 0)
+ panicf("rec open: %d", mpeg_file);
+
+ rc = write(mpeg_file, mp3buf + mp3buf_read,
+ writelen);
+
+ if(rc < 0)
+ panicf("rec wrt: %d", rc);
+
+ rc = close(mpeg_file);
+ if(rc < 0)
+ panicf("rec cls: %d", rc);
+
+ mpeg_file = -1;
+ DEBUGF("rc: %x\n", rc);
+
+ mp3buf_read += amount_to_save;
+ if(mp3buf_read >= mp3buflen)
+ mp3buf_read = 0;
+
+ queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
}
else
{
- /* We have saved all data,
- time to stop for real */
- if(stop_pending)
- queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
saving = false;
}
- break;
-
- case MPEG_INIT_PLAYBACK:
- init_playback();
- init_playback_done = true;
- break;
- }
- /*}*/
+ }
+ else
+ {
+ /* We have saved all data,
+ time to stop for real */
+ if(stop_pending)
+ queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
+ saving = false;
+ }
+ break;
+
+ case MPEG_INIT_PLAYBACK:
+ init_playback();
+ init_playback_done = true;
+ break;
+ }
}
#endif
}
@@ -2981,3 +2986,74 @@ void mpeg_init(int volume, int bass, int treble, int balance, int loudness,
dbg_cnt2us(0);
#endif
}
+
+int d_1;
+int d_2;
+
+int mpeg_create_xing_header(char *filename, void (*progressfunc)(int))
+{
+ struct mp3entry entry;
+ char xingbuf[417];
+ int fd;
+ int rc;
+ int flen;
+ int num_frames;
+ int fpos;
+
+ if(progressfunc)
+ progressfunc(0);
+
+ rc = mp3info(&entry, filename);
+ if(rc < 0)
+ return rc * 10 - 1;
+
+ fd = open(filename, O_RDWR);
+ if(fd < 0)
+ return fd * 10 - 2;
+
+ flen = lseek(fd, 0, SEEK_END);
+
+ d_1 = entry.first_frame_offset;
+ d_2 = entry.filesize;
+
+ if(progressfunc)
+ progressfunc(0);
+
+ num_frames = count_mp3_frames(fd, entry.first_frame_offset,
+ flen,
+ progressfunc);
+
+ create_xing_header(fd, entry.first_frame_offset,
+ flen, xingbuf, num_frames, progressfunc);
+
+ /* Try to fit the Xing header first in the stream. Replace the existing
+ Xing header if there is one, else see if there is room between the
+ ID3 tag and the first MP3 frame. */
+ if(entry.xing_header_pos)
+ {
+ /* Reuse existing Xing header */
+ fpos = entry.xing_header_pos;
+ }
+ else
+ {
+ /* Any room between ID3 tag and first MP3 frame? */
+ if(entry.first_frame_offset - entry.id3v2len > 417)
+ {
+ fpos = entry.first_frame_offset - 417;
+ }
+ else
+ {
+ close(fd);
+ return -3;
+ }
+ }
+
+ lseek(fd, fpos, SEEK_SET);
+ write(fd, xingbuf, 417);
+ close(fd);
+
+ if(progressfunc)
+ progressfunc(100);
+
+ return 0;
+}