From 3ad485b15a51e92a23540429b64e89656555bba6 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Tue, 14 Jun 2005 22:27:57 +0000 Subject: 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 --- apps/SOURCES | 1 + apps/metadata.c | 342 +++++++++++++++++++++++++++++++++++++++++++++++++++++ apps/metadata.h | 31 +++++ apps/playback.c | 357 +++----------------------------------------------------- apps/playback.h | 29 ++++- 5 files changed, 419 insertions(+), 341 deletions(-) create mode 100644 apps/metadata.c create mode 100644 apps/metadata.h (limited to 'apps') diff --git a/apps/SOURCES b/apps/SOURCES index 5e58816d4e..d6ea2c9491 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -50,4 +50,5 @@ recorder/recording.c #endif #ifdef IRIVER_H100 playback.c +metadata.c #endif diff --git a/apps/metadata.c b/apps/metadata.c new file mode 100644 index 0000000000..35ed900efb --- /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 +#include +#include +#include + +#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 0000000000..79ca2a21a9 --- /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 0828d58ce0..d0b2882204 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 codecbufused; 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 @@ void codec_configure_callback(int setting, void *value) } } -/* 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 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) 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 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) 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 4cdece75c9..1df283e6b8 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 +#include +#include + +#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 */ -- cgit v1.2.3