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 */