Move metadata parsing code from playback.c into metadata.c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6714 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 5e58816..d6ea2c9 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -50,4 +50,5 @@
#endif
#ifdef IRIVER_H100
playback.c
+metadata.c
#endif
diff --git a/apps/metadata.c b/apps/metadata.c
new file mode 100644
index 0000000..35ed900
--- /dev/null
+++ b/apps/metadata.c
@@ -0,0 +1,342 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Dave Chapman
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "metadata.h"
+#include "mp3_playback.h"
+#include "mp3data.h"
+#include "logf.h"
+
+/* Simple file type probing by looking filename extension. */
+int probe_file_format(const char *filename)
+{
+ char *suffix;
+
+ suffix = strrchr(filename, '.');
+ if (suffix == NULL)
+ return AFMT_UNKNOWN;
+ suffix += 1;
+
+ if (!strcasecmp("mp1", suffix))
+ return AFMT_MPA_L1;
+ else if (!strcasecmp("mp2", suffix))
+ return AFMT_MPA_L2;
+ else if (!strcasecmp("mpa", suffix))
+ return AFMT_MPA_L2;
+ else if (!strcasecmp("mp3", suffix))
+ return AFMT_MPA_L3;
+ else if (!strcasecmp("ogg", suffix))
+ return AFMT_OGG_VORBIS;
+ else if (!strcasecmp("wav", suffix))
+ return AFMT_PCM_WAV;
+ else if (!strcasecmp("flac", suffix))
+ return AFMT_FLAC;
+ else if (!strcasecmp("mpc", suffix))
+ return AFMT_MPC;
+ else if (!strcasecmp("aac", suffix))
+ return AFMT_AAC;
+ else if (!strcasecmp("ape", suffix))
+ return AFMT_APE;
+ else if (!strcasecmp("wma", suffix))
+ return AFMT_WMA;
+ else if ((!strcasecmp("a52", suffix)) || (!strcasecmp("ac3", suffix)))
+ return AFMT_A52;
+ else if (!strcasecmp("rm", suffix))
+ return AFMT_REAL;
+ else if (!strcasecmp("wv", suffix))
+ return AFMT_WAVPACK;
+
+ return AFMT_UNKNOWN;
+
+}
+
+/* Get metadata for track - return false if parsing showed problems with the
+ file that would prevent playback. */
+
+bool get_metadata(struct track_info* track, int fd, const char* trackname,
+ bool v1first) {
+ unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
+ unsigned char* buf;
+ int i,j,eof;
+ int rc;
+
+ /* Load codec specific track tag information. */
+ switch (track->codectype) {
+ case AFMT_MPA_L2:
+ case AFMT_MPA_L3:
+ /* Should check the return value. */
+ mp3info(&track->id3, trackname, v1first);
+ lseek(fd, 0, SEEK_SET);
+
+ /* This is too slow to execute on some files. */
+ get_mp3file_info(fd, &track->mp3data);
+ lseek(fd, 0, SEEK_SET);
+
+ /*
+ logf("T:%s", track->id3.title);
+ logf("L:%d", track->id3.length);
+ logf("O:%d", track->id3.first_frame_offset);
+ logf("F:%d", track->id3.frequency);
+ */
+ track->taginfo_ready = true;
+ break ;
+
+ case AFMT_PCM_WAV:
+ /* Use the trackname part of the id3 structure as a temporary buffer */
+ buf=track->id3.path;
+
+ lseek(fd, 0, SEEK_SET);
+
+ rc = read(fd, buf, 44);
+ if (rc < 44) {
+ return false;
+ }
+
+ if ((memcmp(buf,"RIFF",4)!=0) ||
+ (memcmp(&buf[8],"WAVEfmt",7)!=0)) {
+ logf("%s is not a WAV file\n",trackname);
+ return(false);
+ }
+
+ /* FIX: Correctly parse WAV header - we assume canonical
+ 44-byte header */
+
+ bitspersample=buf[34];
+ channels=buf[22];
+
+ if ((bitspersample!=16) || (channels != 2)) {
+ logf("Unsupported WAV file - %d bitspersample, %d channels\n",
+ bitspersample,channels);
+ return(false);
+ }
+
+ bytespersample=((bitspersample/8)*channels);
+ numbytes=(buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24));
+ totalsamples=numbytes/bytespersample;
+
+ track->id3.vbr=false; /* All WAV files are CBR */
+ track->id3.filesize=filesize(fd);
+ track->id3.frequency=buf[24]|(buf[25]<<8)|(buf[26]<<16)|(buf[27]<<24);
+
+ /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
+ track->id3.length=(totalsamples/track->id3.frequency)*1000;
+ track->id3.bitrate=(track->id3.frequency*bytespersample)/(1000/8);
+
+ lseek(fd, 0, SEEK_SET);
+ strncpy(track->id3.path,trackname,sizeof(track->id3.path));
+ track->taginfo_ready = true;
+
+ break;
+
+ case AFMT_FLAC:
+ /* A simple parser to read vital metadata from a FLAC file - length, frequency, bitrate etc. */
+ /* This code should either be moved to a seperate file, or discarded in favour of the libFLAC code */
+ /* The FLAC stream specification can be found at http://flac.sourceforge.net/format.html#stream */
+
+ /* Use the trackname part of the id3 structure as a temporary buffer */
+ buf=track->id3.path;
+
+ lseek(fd, 0, SEEK_SET);
+
+ rc = read(fd, buf, 4);
+ if (rc < 4) {
+ return false;
+ }
+
+ if (memcmp(buf,"fLaC",4)!=0) {
+ logf("%s is not a FLAC file\n",trackname);
+ return(false);
+ }
+
+ while (1) {
+ rc = read(fd, buf, 4);
+ i = (buf[1]<<16)|(buf[2]<<8)|buf[3]; /* The length of the block */
+
+ if ((buf[0]&0x7f)==0) { /* 0 is the STREAMINFO block */
+ rc = read(fd, buf, i); /* FIXME: Don't trust the value of i */
+ if (rc < 0) {
+ return false;
+ }
+ track->id3.vbr=true; /* All FLAC files are VBR */
+ track->id3.filesize=filesize(fd);
+
+ track->id3.frequency=(buf[10]<<12)|(buf[11]<<4)|((buf[12]&0xf0)>>4);
+
+ /* NOT NEEDED: bitspersample=(((buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1; */
+
+ /* totalsamples is a 36-bit field, but we assume <= 32 bits are used */
+ totalsamples=(buf[14]<<24)|(buf[15]<<16)|(buf[16]<<8)|buf[17];
+
+ /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
+ track->id3.length=(totalsamples/track->id3.frequency)*1000;
+ track->id3.bitrate=(filesize(fd)*8)/track->id3.length;
+ } else if ((buf[0]&0x7f)==4) { /* 4 is the VORBIS_COMMENT block */
+
+ /* The next i bytes of the file contain the VORBIS COMMENTS - just skip them for now. */
+ lseek(fd, i, SEEK_CUR);
+
+ } else {
+ if (buf[0]&0x80) { /* If we have reached the last metadata block, abort. */
+ break;
+ } else {
+ lseek(fd, i, SEEK_CUR); /* Skip to next metadata block */
+ }
+ }
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ strncpy(track->id3.path,trackname,sizeof(track->id3.path));
+ track->taginfo_ready = true;
+ break;
+
+ case AFMT_OGG_VORBIS:
+ /* A simple parser to read vital metadata from an Ogg Vorbis file */
+
+ /* An Ogg File is split into pages, each starting with the string
+ "OggS". Each page has a timestamp (in PCM samples) referred to as
+ the "granule position".
+
+ An Ogg Vorbis has the following structure:
+ 1) Identification header (containing samplerate, numchannels, etc)
+ 2) Comment header - containing the Vorbis Comments
+ 3) Setup header - containing codec setup information
+ 4) Many audio packets...
+
+ */
+
+ /* Use the trackname part of the id3 structure as a temporary buffer */
+ buf=track->id3.path;
+
+ lseek(fd, 0, SEEK_SET);
+
+ rc = read(fd, buf, 58);
+ if (rc < 4) {
+ return false;
+ }
+
+ if ((memcmp(buf,"OggS",4)!=0) || (memcmp(&buf[29],"vorbis",6)!=0)) {
+ logf("%s is not an Ogg Vorbis file\n",trackname);
+ return(false);
+ }
+
+ /* Ogg stores integers in little-endian format. */
+ track->id3.filesize=filesize(fd);
+ track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24);
+ channels=buf[39];
+
+ /* We now need to search for the last page in the file - identified by
+ by ('O','g','g','S',0) and retrieve totalsamples */
+
+ lseek(fd, -32*1024, SEEK_END);
+ eof=0;
+ j=0; /* The number of bytes currently in buffer */
+ i=0;
+ totalsamples=0;
+ while (!eof) {
+ rc = read(fd, &buf[j], MAX_PATH-j);
+ if (rc <= 0) {
+ eof=1;
+ } else {
+ j+=rc;
+ }
+ /* Inefficient (but simple) search */
+ i=0;
+ while (i < (j-5)) {
+ if (memcmp(&buf[i],"OggS",5)==0) {
+ if (i < (j-10)) {
+ totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
+ j=0; /* We can discard the rest of the buffer */
+ } else {
+ break;
+ }
+ } else {
+ i++;
+ }
+ }
+ if (i < (j-5)) {
+ /* Move OggS to start of buffer */
+ while(i>0) buf[i--]=buf[j--];
+ } else {
+ j=0;
+ }
+ }
+
+ track->id3.length=(totalsamples/track->id3.frequency)*1000;
+
+ /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */
+ track->id3.bitrate=(filesize(fd)*8)/track->id3.length;
+ track->id3.vbr=true;
+
+ lseek(fd, 0, SEEK_SET);
+ strncpy(track->id3.path,trackname,sizeof(track->id3.path));
+ track->taginfo_ready = true;
+ break;
+
+ case AFMT_WAVPACK:
+ /* A simple parser to read basic information from a WavPack file.
+ * This will fail on WavPack files that don't have the WavPack header
+ * as the first thing (i.e. self-extracting WavPack files) or WavPack
+ * files that have so much extra RIFF data stored in the first block
+ * that they don't have samples (very rare, I would think).
+ */
+
+ /* Use the trackname part of the id3 structure as a temporary buffer */
+ buf=track->id3.path;
+
+ lseek(fd, 0, SEEK_SET);
+
+ rc = read(fd, buf, 32);
+ if (rc < 32) {
+ return false;
+ }
+
+ if (memcmp (buf, "wvpk", 4) != 0 || buf [9] != 4 || buf [8] < 2) {
+ logf ("%s is not a WavPack file\n", trackname);
+ return (false);
+ }
+
+ track->id3.vbr = true; /* All WavPack files are VBR */
+ track->id3.filesize = filesize (fd);
+ track->id3.frequency = 44100;
+
+ if ((buf [20] | buf [21] | buf [22] | buf [23]) &&
+ (buf [12] & buf [13] & buf [14] & buf [15]) != 0xff) {
+ totalsamples = (buf[15] << 24) | (buf[14] << 16) | (buf[13] << 8) | buf[12];
+ track->id3.length = (totalsamples + 220) / 441 * 10;
+ track->id3.bitrate = filesize (fd) /
+ (track->id3.length / 8);
+ }
+
+ lseek (fd, 0, SEEK_SET);
+ strncpy (track->id3.path, trackname, sizeof (track->id3.path));
+ track->taginfo_ready = true;
+ break;
+
+ /* If we don't know how to read the metadata, just store the filename */
+ default:
+ strncpy(track->id3.path,trackname,sizeof(track->id3.path));
+ track->taginfo_ready = true;
+ break;
+ }
+
+ return true;
+}
diff --git a/apps/metadata.h b/apps/metadata.h
new file mode 100644
index 0000000..79ca2a2
--- /dev/null
+++ b/apps/metadata.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Dave Chapman
+ *
+ * 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 _METADATA_H
+#define _METADATA_H
+
+#include "playback.h"
+
+int probe_file_format(const char *filename);
+bool get_metadata(struct track_info* track, int fd, const char* trackname,
+ bool v1first);
+
+#endif
+
+
diff --git a/apps/playback.c b/apps/playback.c
index 0828d58..d0b2882 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -58,6 +58,7 @@
#include "bookmark.h"
#include "misc.h"
#include "sound.h"
+#include "metadata.h"
static volatile bool playing;
static volatile bool paused;
@@ -124,23 +125,6 @@
static volatile int buf_ridx;
static volatile int buf_widx;
-#define MAX_TRACK 10
-struct track_info {
- struct mp3entry id3; /* TAG metadata */
- struct mp3info mp3data; /* MP3 metadata */
- char *codecbuf; /* Pointer to codec buffer */
- size_t codecsize; /* Codec length in bytes */
- int codectype; /* Codec type (example AFMT_MPA_L3) */
-
- off_t filerem; /* Remaining bytes of file NOT in buffer */
- off_t filesize; /* File total length */
- off_t filepos; /* Read position of file for next buffer fill */
- off_t start_pos; /* Position to first bytes of file in buffer */
- volatile int available; /* Available bytes to read from buffer */
- bool taginfo_ready; /* Is metadata read */
- int playlist_offset; /* File location in playlist */
-};
-
/* Track information (count in file buffer, read/write indexes for
track ring structure. */
static int track_count;
@@ -394,49 +378,6 @@
}
}
-/* Simple file type probing by looking filename extension. */
-int probe_file_format(const char *filename)
-{
- char *suffix;
-
- suffix = strrchr(filename, '.');
- if (suffix == NULL)
- return AFMT_UNKNOWN;
- suffix += 1;
-
- if (!strcasecmp("mp1", suffix))
- return AFMT_MPA_L1;
- else if (!strcasecmp("mp2", suffix))
- return AFMT_MPA_L2;
- else if (!strcasecmp("mpa", suffix))
- return AFMT_MPA_L2;
- else if (!strcasecmp("mp3", suffix))
- return AFMT_MPA_L3;
- else if (!strcasecmp("ogg", suffix))
- return AFMT_OGG_VORBIS;
- else if (!strcasecmp("wav", suffix))
- return AFMT_PCM_WAV;
- else if (!strcasecmp("flac", suffix))
- return AFMT_FLAC;
- else if (!strcasecmp("mpc", suffix))
- return AFMT_MPC;
- else if (!strcasecmp("aac", suffix))
- return AFMT_AAC;
- else if (!strcasecmp("ape", suffix))
- return AFMT_APE;
- else if (!strcasecmp("wma", suffix))
- return AFMT_WMA;
- else if ((!strcasecmp("a52", suffix)) || (!strcasecmp("ac3", suffix)))
- return AFMT_A52;
- else if (!strcasecmp("rm", suffix))
- return AFMT_REAL;
- else if (!strcasecmp("wv", suffix))
- return AFMT_WAVPACK;
-
- return AFMT_UNKNOWN;
-
-}
-
void yield_codecs(void)
{
yield();
@@ -620,10 +561,6 @@
off_t size;
int rc, i;
int copy_n;
- /* Used by the metadata parsers */
- unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
- unsigned char* buf;
- int j,eof;
if (track_count >= MAX_TRACK)
return false;
@@ -673,283 +610,25 @@
close(fd);
return false;
}
-
- /* Load codec specific track tag information. */
- switch (tracks[track_widx].codectype) {
- case AFMT_MPA_L2:
- case AFMT_MPA_L3:
- /* Should check the return value. */
- mp3info(&tracks[track_widx].id3, trackname, v1first);
- lseek(fd, 0, SEEK_SET);
- /* This is too slow to execute on some files. */
- get_mp3file_info(fd, &tracks[track_widx].mp3data);
- if (offset) {
- lseek(fd, offset, SEEK_SET);
- tracks[track_widx].id3.offset = offset;
- mp3_set_elapsed(&tracks[track_widx].id3);
- tracks[track_widx].filepos = offset;
- tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
- ci.curpos = offset;
- tracks[track_widx].start_pos = offset;
- } else {
- lseek(fd, 0, SEEK_SET);
- }
- /*
- logf("T:%s", tracks[track_widx].id3.title);
- logf("L:%d", tracks[track_widx].id3.length);
- logf("O:%d", tracks[track_widx].id3.first_frame_offset);
- logf("F:%d", tracks[track_widx].id3.frequency);
- */
- tracks[track_widx].taginfo_ready = true;
- break ;
- case AFMT_PCM_WAV:
- /* Use the trackname part of the id3 structure as a temporary buffer */
- buf=tracks[track_widx].id3.path;
-
- lseek(fd, 0, SEEK_SET);
-
- rc = read(fd, buf, 44);
- if (rc < 44) {
- close(fd);
- return false;
- }
-
- if ((memcmp(buf,"RIFF",4)!=0) ||
- (memcmp(&buf[8],"WAVEfmt",7)!=0)) {
- logf("%s is not a WAV file\n",trackname);
- close(fd);
- return(false);
- }
-
- /* FIX: Correctly parse WAV header - we assume canonical
- 44-byte header */
-
- bitspersample=buf[34];
- channels=buf[22];
-
- if ((bitspersample!=16) || (channels != 2)) {
- logf("Unsupported WAV file - %d bitspersample, %d channels\n",
- bitspersample,channels);
- close(fd);
- return(false);
- }
-
- bytespersample=((bitspersample/8)*channels);
- numbytes=(buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24));
- totalsamples=numbytes/bytespersample;
-
- tracks[track_widx].id3.vbr=false; /* All WAV files are CBR */
- tracks[track_widx].id3.filesize=filesize(fd);
- tracks[track_widx].id3.frequency=buf[24]|(buf[25]<<8)|(buf[26]<<16)|(buf[27]<<24);
-
- /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
- tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
- tracks[track_widx].id3.bitrate=(tracks[track_widx].id3.frequency*bytespersample)/(1000/8);
-
- lseek(fd, 0, SEEK_SET);
- strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
- tracks[track_widx].taginfo_ready = true;
-
- break;
-
- case AFMT_FLAC:
- /* A simple parser to read vital metadata from a FLAC file - length, frequency, bitrate etc. */
- /* This code should either be moved to a seperate file, or discarded in favour of the libFLAC code */
- /* The FLAC stream specification can be found at http://flac.sourceforge.net/format.html#stream */
-
- /* Use the trackname part of the id3 structure as a temporary buffer */
- buf=tracks[track_widx].id3.path;
-
- lseek(fd, 0, SEEK_SET);
-
- rc = read(fd, buf, 4);
- if (rc < 4) {
- close(fd);
- return false;
- }
-
- if (memcmp(buf,"fLaC",4)!=0) {
- logf("%s is not a FLAC file\n",trackname);
- close(fd);
- return(false);
- }
-
- while (1) {
- rc = read(fd, buf, 4);
- i = (buf[1]<<16)|(buf[2]<<8)|buf[3]; /* The length of the block */
-
- if ((buf[0]&0x7f)==0) { /* 0 is the STREAMINFO block */
- rc = read(fd, buf, i); /* FIXME: Don't trust the value of i */
- if (rc < 0) {
- close(fd);
- return false;
- }
- tracks[track_widx].id3.vbr=true; /* All FLAC files are VBR */
- tracks[track_widx].id3.filesize=filesize(fd);
-
- tracks[track_widx].id3.frequency=(buf[10]<<12)|(buf[11]<<4)|((buf[12]&0xf0)>>4);
-
- /* NOT NEEDED: bitspersample=(((buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1; */
-
- /* totalsamples is a 36-bit field, but we assume <= 32 bits are used */
- totalsamples=(buf[14]<<24)|(buf[15]<<16)|(buf[16]<<8)|buf[17];
-
- /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
- tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
- tracks[track_widx].id3.bitrate=(filesize(fd)*8)/tracks[track_widx].id3.length;
- } else if ((buf[0]&0x7f)==4) { /* 4 is the VORBIS_COMMENT block */
-
- /* The next i bytes of the file contain the VORBIS COMMENTS - just skip them for now. */
- lseek(fd, i, SEEK_CUR);
-
- } else {
- if (buf[0]&0x80) { /* If we have reached the last metadata block, abort. */
- break;
- } else {
- lseek(fd, i, SEEK_CUR); /* Skip to next metadata block */
- }
- }
- }
-
- lseek(fd, 0, SEEK_SET);
- strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
- tracks[track_widx].taginfo_ready = true;
- break;
-
- case AFMT_OGG_VORBIS:
- /* A simple parser to read vital metadata from an Ogg Vorbis file */
-
- /* An Ogg File is split into pages, each starting with the string
- "OggS". Each page has a timestamp (in PCM samples) referred to as
- the "granule position".
-
- An Ogg Vorbis has the following structure:
- 1) Identification header (containing samplerate, numchannels, etc)
- 2) Comment header - containing the Vorbis Comments
- 3) Setup header - containing codec setup information
- 4) Many audio packets...
-
- */
-
- /* Use the trackname part of the id3 structure as a temporary buffer */
- buf=tracks[track_widx].id3.path;
-
- lseek(fd, 0, SEEK_SET);
-
- rc = read(fd, buf, 58);
- if (rc < 4) {
- close(fd);
- return false;
- }
-
- if ((memcmp(buf,"OggS",4)!=0) || (memcmp(&buf[29],"vorbis",6)!=0)) {
- logf("%s is not an Ogg Vorbis file\n",trackname);
- close(fd);
- return(false);
- }
-
- /* Ogg stores integers in little-endian format. */
- tracks[track_widx].id3.filesize=filesize(fd);
- tracks[track_widx].id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24);
- channels=buf[39];
-
- /* We now need to search for the last page in the file - identified by
- by ('O','g','g','S',0) and retrieve totalsamples */
-
- lseek(fd, -32*1024, SEEK_END);
- eof=0;
- j=0; /* The number of bytes currently in buffer */
- i=0;
- totalsamples=0;
- while (!eof) {
- rc = read(fd, &buf[j], MAX_PATH-j);
- if (rc <= 0) {
- eof=1;
- } else {
- j+=rc;
- }
- /* Inefficient (but simple) search */
- i=0;
- while (i < (j-5)) {
- if (memcmp(&buf[i],"OggS",5)==0) {
- if (i < (j-10)) {
- totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
- j=0; /* We can discard the rest of the buffer */
- } else {
- break;
- }
- } else {
- i++;
- }
- }
- if (i < (j-5)) {
- /* Move OggS to start of buffer */
- while(i>0) buf[i--]=buf[j--];
- } else {
- j=0;
- }
- }
-
- tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
-
- /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */
- tracks[track_widx].id3.bitrate=(filesize(fd)*8)/tracks[track_widx].id3.length;
- tracks[track_widx].id3.vbr=true;
-
- lseek(fd, 0, SEEK_SET);
- strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
- tracks[track_widx].taginfo_ready = true;
- break;
-
- case AFMT_WAVPACK:
- /* A simple parser to read basic information from a WavPack file.
- * This will fail on WavPack files that don't have the WavPack header
- * as the first thing (i.e. self-extracting WavPack files) or WavPack
- * files that have so much extra RIFF data stored in the first block
- * that they don't have samples (very rare, I would think).
- */
-
- /* Use the trackname part of the id3 structure as a temporary buffer */
- buf=tracks[track_widx].id3.path;
-
- lseek(fd, 0, SEEK_SET);
-
- rc = read(fd, buf, 32);
- if (rc < 32) {
- close(fd);
- return false;
- }
-
- if (memcmp (buf, "wvpk", 4) != 0 || buf [9] != 4 || buf [8] < 2) {
- logf ("%s is not a WavPack file\n", trackname);
- close (fd);
- return (false);
- }
-
- tracks[track_widx].id3.vbr = true; /* All WavPack files are VBR */
- tracks[track_widx].id3.filesize = filesize (fd);
- tracks[track_widx].id3.frequency = 44100;
-
- if ((buf [20] | buf [21] | buf [22] | buf [23]) &&
- (buf [12] & buf [13] & buf [14] & buf [15]) != 0xff) {
- totalsamples = (buf[15] << 24) | (buf[14] << 16) | (buf[13] << 8) | buf[12];
- tracks[track_widx].id3.length = (totalsamples + 220) / 441 * 10;
- tracks[track_widx].id3.bitrate = filesize (fd) /
- (tracks[track_widx].id3.length / 8);
- }
-
- lseek (fd, 0, SEEK_SET);
- strncpy (tracks[track_widx].id3.path, trackname, sizeof (tracks[track_widx].id3.path));
- tracks[track_widx].taginfo_ready = true;
- break;
-
- /* If we don't know how to read the metadata, just store the filename */
- default:
- strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
- tracks[track_widx].taginfo_ready = true;
- break;
+ if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) {
+ close(fd);
+ return false;
}
+
+ /* Starting playback from an offset is only support in MPA at the moment */
+ if (offset > 0) {
+ if ((tracks[track_widx].codectype==AFMT_MPA_L2) ||
+ (tracks[track_widx].codectype==AFMT_MPA_L3)) {
+ lseek(fd, offset, SEEK_SET);
+ tracks[track_widx].id3.offset = offset;
+ mp3_set_elapsed(&tracks[track_widx].id3);
+ tracks[track_widx].filepos = offset;
+ tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
+ ci.curpos = offset;
+ tracks[track_widx].start_pos = offset;
+ }
+ }
if (start_play) {
track_count++;
diff --git a/apps/playback.h b/apps/playback.h
index 4cdece7..1df283e 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -17,8 +17,15 @@
*
****************************************************************************/
-#ifndef _AUDIO_H
-#define _AUDIO_H
+#ifndef _PLAYBACK_H
+#define _PLAYBACK_H
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+#include "id3.h"
+#include "mp3data.h"
/* Supported file types. */
#define AFMT_MPA_L1 0x0001 // MPEG Audio layer 1
@@ -45,6 +52,24 @@
/* Not yet implemented. */
#define CODEC_SET_AUDIOBUF_WATERMARK 4
+#define MAX_TRACK 10
+struct track_info {
+ struct mp3entry id3; /* TAG metadata */
+ struct mp3info mp3data; /* MP3 metadata */
+ char *codecbuf; /* Pointer to codec buffer */
+ size_t codecsize; /* Codec length in bytes */
+ int codectype; /* Codec type (example AFMT_MPA_L3) */
+
+ off_t filerem; /* Remaining bytes of file NOT in buffer */
+ off_t filesize; /* File total length */
+ off_t filepos; /* Read position of file for next buffer fill */
+ off_t start_pos; /* Position to first bytes of file in buffer */
+ volatile int available; /* Available bytes to read from buffer */
+ bool taginfo_ready; /* Is metadata read */
+ int playlist_offset; /* File location in playlist */
+};
+
+
/* Codec Interface */
struct codec_api {
off_t filesize; /* Total file length */