/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 by Daniel Stenberg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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�deman. It has since been extended and enhanced pretty much by * all sorts of friendly Rockbox people. * */ /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach */ #include #include #include #include #include "string-extra.h" #include "config.h" #include "logf.h" #include "platform.h" #include "metadata.h" #include "mp3data.h" #include "metadata_common.h" #include "metadata_parsers.h" /* * Calculates the length (in milliseconds) of an MP3 file. * * Modified to only use integers. * * Arguments: file - the file to calculate the length upon * entry - the entry to update with the length * * Returns: the song length in milliseconds, * 0 means that it couldn't be calculated */ static int getsonglength(int fd, struct mp3entry *entry) { unsigned long filetime = 0; struct mp3info info; long bytecount; /* Start searching after ID3v2 header */ if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) return 0; bytecount = get_mp3file_info(fd, &info); logf("Space between ID3V2 tag and first audio frame: 0x%lx bytes", bytecount); if(bytecount < 0) return -1; bytecount += entry->id3v2len; /* Validate byte count, in case the file has been edited without * updating the header. */ if (info.byte_count) { const unsigned long expected = entry->filesize - entry->id3v1len - entry->id3v2len; const unsigned long diff = MAX(10240, info.byte_count / 20); if ((info.byte_count > expected + diff) || (info.byte_count < expected - diff)) { logf("Note: info.byte_count differs from expected value by " "%ld bytes", labs((long) (expected - info.byte_count))); info.byte_count = 0; info.frame_count = 0; info.file_time = 0; info.enc_padding = 0; /* Even if the bitrate was based on "known bad" values, it * should still be better for VBR files than using the bitrate * of the first audio frame. */ } } entry->bitrate = info.bitrate; entry->frequency = info.frequency; entry->layer = info.layer; switch(entry->layer) { #if CONFIG_CODEC==SWCODEC case 0: entry->codectype=AFMT_MPA_L1; break; #endif case 1: entry->codectype=AFMT_MPA_L2; break; case 2: entry->codectype=AFMT_MPA_L3; break; } /* 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) { /* Prevent a division by zero */ if (info.bitrate < 8) filetime = 0; else filetime = (entry->filesize - bytecount) / (info.bitrate / 8); /* bitrate is in kbps so this delivers milliseconds. Doing bitrate / 8 * instead of filesize * 8 is exact, because mpeg audio bitrates are * always multiples of 8, and it avoids overflows. */ } entry->frame_count = info.frame_count; entry->vbr = info.is_vbr; entry->has_toc = info.has_toc; #if CONFIG_CODEC==SWCODEC if (!entry->lead_trim) entry->lead_trim = info.enc_delay; if (!entry->tail_trim) entry->tail_trim = info.enc_padding; #endif memcpy(entry->toc, info.toc, sizeof(info.toc)); /* Update the seek point for the first playable frame */ entry->first_frame_offset = bytecount; logf("First frame is at %lx", entry->first_frame_offset); return filetime; } /* * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) * about an MP3 file and updates it's entry accordingly. * Note, that this returns true for successful, false for error! */ bool get_mp3_metadata(int fd, struct mp3entry *entry) { entry->title = NULL; entry->filesize = filesize(fd); entry->id3v2len = getid3v2len(fd); entry->tracknum = 0; entry->discnum = 0; if (entry->id3v2len) setid3v2title(fd, entry); int len = getsonglength(fd, entry); if (len < 0) return false; entry->length = len; /* 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) { setid3v1title(fd, entry); } if(!entry->length || (entry->filesize < 8 )) /* no song length or less than 8 bytes is hereby considered to be an invalid mp3 and won't be played by us! */ return false; return true; }