2002-04-23 08:43:04 +00:00
|
|
|
|
/***************************************************************************
|
|
|
|
|
* __________ __ ___.
|
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2002 by Daniel Stenberg
|
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
|
* 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.
|
2002-04-23 08:43:04 +00:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2007-02-09 10:06:53 +00:00
|
|
|
|
* by David H<EFBFBD>deman. It has since been extended and enhanced pretty much by
|
2002-08-19 14:15:33 +00:00
|
|
|
|
* all sorts of friendly Rockbox people.
|
|
|
|
|
*
|
2002-04-23 08:43:04 +00:00
|
|
|
|
*/
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2003-06-04 15:09:35 +00:00
|
|
|
|
/* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach
|
|
|
|
|
*/
|
2002-04-23 08:43:04 +00:00
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2010-05-15 09:15:24 +00:00
|
|
|
|
#include <string.h>
|
2002-05-13 12:29:34 +00:00
|
|
|
|
#include <stdbool.h>
|
2010-05-15 09:24:38 +00:00
|
|
|
|
#include "string-extra.h"
|
2005-06-18 16:24:27 +00:00
|
|
|
|
#include "config.h"
|
2002-05-08 15:36:18 +00:00
|
|
|
|
#include "file.h"
|
2007-10-29 18:02:41 +00:00
|
|
|
|
#include "logf.h"
|
2002-04-30 12:42:37 +00:00
|
|
|
|
|
2004-01-04 00:37:30 +00:00
|
|
|
|
#include "system.h"
|
2010-05-15 09:15:24 +00:00
|
|
|
|
#include "metadata.h"
|
|
|
|
|
#include "mp3data.h"
|
2009-10-02 09:14:17 +00:00
|
|
|
|
#include "metadata_common.h"
|
2010-05-15 09:15:24 +00:00
|
|
|
|
#include "metadata_parsers.h"
|
2002-04-23 08:43:04 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-08-19 14:15:33 +00:00
|
|
|
|
* Calculates the length (in milliseconds) of an MP3 file.
|
2002-04-23 08:43:04 +00:00
|
|
|
|
*
|
|
|
|
|
* Modified to only use integers.
|
|
|
|
|
*
|
|
|
|
|
* Arguments: file - the file to calculate the length upon
|
|
|
|
|
* entry - the entry to update with the length
|
|
|
|
|
*
|
2006-07-20 10:35:20 +00:00
|
|
|
|
* Returns: the song length in milliseconds,
|
2002-08-22 07:59:31 +00:00
|
|
|
|
* 0 means that it couldn't be calculated
|
2002-04-23 08:43:04 +00:00
|
|
|
|
*/
|
2002-07-04 22:10:43 +00:00
|
|
|
|
static int getsonglength(int fd, struct mp3entry *entry)
|
2002-04-23 08:43:04 +00:00
|
|
|
|
{
|
2005-09-10 12:28:16 +00:00
|
|
|
|
unsigned long filetime = 0;
|
2003-03-10 14:55:31 +00:00
|
|
|
|
struct mp3info info;
|
2005-09-10 12:28:16 +00:00
|
|
|
|
long bytecount;
|
|
|
|
|
|
2006-07-20 10:35:20 +00:00
|
|
|
|
/* Start searching after ID3v2 header */
|
2002-04-30 12:42:37 +00:00
|
|
|
|
if(-1 == lseek(fd, entry->id3v2len, SEEK_SET))
|
2002-08-22 07:59:31 +00:00
|
|
|
|
return 0;
|
2002-08-19 14:15:33 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
bytecount = get_mp3file_info(fd, &info);
|
2002-04-23 08:43:04 +00:00
|
|
|
|
|
2007-10-29 18:02:41 +00:00
|
|
|
|
logf("Space between ID3V2 tag and first audio frame: 0x%lx bytes",
|
2003-03-10 14:55:31 +00:00
|
|
|
|
bytecount);
|
2002-07-18 02:08:23 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
if(bytecount < 0)
|
2002-07-03 21:04:09 +00:00
|
|
|
|
return -1;
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
bytecount += entry->id3v2len;
|
2002-04-23 08:43:04 +00:00
|
|
|
|
|
2006-01-08 08:58:58 +00:00
|
|
|
|
/* Validate byte count, in case the file has been edited without
|
|
|
|
|
* updating the header.
|
|
|
|
|
*/
|
|
|
|
|
if (info.byte_count)
|
|
|
|
|
{
|
2006-07-20 10:35:20 +00:00
|
|
|
|
const unsigned long expected = entry->filesize - entry->id3v1len
|
2006-01-08 08:58:58 +00:00
|
|
|
|
- entry->id3v2len;
|
|
|
|
|
const unsigned long diff = MAX(10240, info.byte_count / 20);
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
|
|
|
|
if ((info.byte_count > expected + diff)
|
2006-01-08 08:58:58 +00:00
|
|
|
|
|| (info.byte_count < expected - diff))
|
|
|
|
|
{
|
2007-10-29 18:02:41 +00:00
|
|
|
|
logf("Note: info.byte_count differs from expected value by "
|
|
|
|
|
"%ld bytes", labs((long) (expected - info.byte_count)));
|
2006-01-08 08:58:58 +00:00
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
entry->bitrate = info.bitrate;
|
2003-03-13 19:12:05 +00:00
|
|
|
|
entry->frequency = info.frequency;
|
|
|
|
|
entry->version = info.version;
|
|
|
|
|
entry->layer = info.layer;
|
2005-06-18 16:24:27 +00:00
|
|
|
|
switch(entry->layer) {
|
2005-08-29 21:15:27 +00:00
|
|
|
|
#if CONFIG_CODEC==SWCODEC
|
2005-06-18 16:24:27 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2002-07-10 13:17:31 +00:00
|
|
|
|
|
2002-07-03 21:04:09 +00:00
|
|
|
|
/* If the file time hasn't been established, this may be a fixed
|
|
|
|
|
rate MP3, so just use the default formula */
|
2003-03-10 14:55:31 +00:00
|
|
|
|
|
|
|
|
|
filetime = info.file_time;
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2002-07-03 21:04:09 +00:00
|
|
|
|
if(filetime == 0)
|
|
|
|
|
{
|
2006-09-19 18:27:43 +00:00
|
|
|
|
/* Prevent a division by zero */
|
|
|
|
|
if (info.bitrate < 8)
|
|
|
|
|
filetime = 0;
|
|
|
|
|
else
|
|
|
|
|
filetime = (entry->filesize - bytecount) / (info.bitrate / 8);
|
2005-09-09 07:19:41 +00:00
|
|
|
|
/* 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. */
|
2002-07-03 21:04:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-07-05 19:55:40 +00:00
|
|
|
|
entry->frame_count = info.frame_count;
|
2005-09-09 07:19:41 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
entry->vbr = info.is_vbr;
|
|
|
|
|
entry->has_toc = info.has_toc;
|
2005-07-05 19:55:40 +00:00
|
|
|
|
|
2007-02-17 20:49:54 +00:00
|
|
|
|
#if CONFIG_CODEC==SWCODEC
|
2009-10-02 09:14:17 +00:00
|
|
|
|
if (!entry->lead_trim)
|
|
|
|
|
entry->lead_trim = info.enc_delay;
|
|
|
|
|
if (!entry->tail_trim)
|
|
|
|
|
entry->tail_trim = info.enc_padding;
|
2007-02-15 22:55:22 +00:00
|
|
|
|
#endif
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
memcpy(entry->toc, info.toc, sizeof(info.toc));
|
|
|
|
|
|
2003-03-10 18:25:40 +00:00
|
|
|
|
entry->vbr_header_pos = info.vbr_header_pos;
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2002-10-27 23:07:26 +00:00
|
|
|
|
/* Update the seek point for the first playable frame */
|
|
|
|
|
entry->first_frame_offset = bytecount;
|
2007-10-29 18:02:41 +00:00
|
|
|
|
logf("First frame is at %lx", entry->first_frame_offset);
|
2002-10-27 23:07:26 +00:00
|
|
|
|
|
2002-07-03 21:04:09 +00:00
|
|
|
|
return filetime;
|
2002-04-23 08:43:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc)
|
|
|
|
|
* about an MP3 file and updates it's entry accordingly.
|
|
|
|
|
*
|
2006-10-25 16:57:53 +00:00
|
|
|
|
Note, that this returns true for successful, false for error! */
|
2007-09-19 10:40:55 +00:00
|
|
|
|
bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename)
|
2002-04-23 08:43:04 +00:00
|
|
|
|
{
|
2005-08-29 21:15:27 +00:00
|
|
|
|
#if CONFIG_CODEC != SWCODEC
|
2002-06-25 11:39:22 +00:00
|
|
|
|
memset(entry, 0, sizeof(struct mp3entry));
|
2005-06-19 08:29:23 +00:00
|
|
|
|
#endif
|
2005-12-06 13:27:15 +00:00
|
|
|
|
|
2009-07-14 13:57:45 +00:00
|
|
|
|
strlcpy(entry->path, filename, sizeof(entry->path));
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2002-04-23 08:43:04 +00:00
|
|
|
|
entry->title = NULL;
|
2003-03-18 00:45:27 +00:00
|
|
|
|
entry->filesize = filesize(fd);
|
2002-06-26 14:51:09 +00:00
|
|
|
|
entry->id3v2len = getid3v2len(fd);
|
2002-06-26 21:11:29 +00:00
|
|
|
|
entry->tracknum = 0;
|
2007-08-03 10:00:42 +00:00
|
|
|
|
entry->discnum = 0;
|
2002-06-26 21:11:29 +00:00
|
|
|
|
|
2007-09-19 10:40:55 +00:00
|
|
|
|
if (entry->id3v2len)
|
2002-04-30 12:42:37 +00:00
|
|
|
|
setid3v2title(fd, entry);
|
2007-08-14 11:56:14 +00:00
|
|
|
|
int len = getsonglength(fd, entry);
|
|
|
|
|
if (len < 0)
|
|
|
|
|
return false;
|
|
|
|
|
entry->length = len;
|
2002-04-23 08:43:04 +00:00
|
|
|
|
|
2003-03-10 14:55:31 +00:00
|
|
|
|
/* Subtract the meta information from the file size to get
|
|
|
|
|
the true size of the MP3 stream */
|
|
|
|
|
entry->filesize -= entry->first_frame_offset;
|
2006-07-20 10:35:20 +00:00
|
|
|
|
|
2007-09-19 10:40:55 +00:00
|
|
|
|
/* only seek to end of file if no id3v2 tags were found */
|
|
|
|
|
if (!entry->id3v2len) {
|
2010-05-15 21:57:17 +00:00
|
|
|
|
setid3v1title(fd, entry);
|
2002-12-03 15:38:39 +00:00
|
|
|
|
}
|
2002-04-30 12:42:37 +00:00
|
|
|
|
|
2002-08-22 07:59:31 +00:00
|
|
|
|
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! */
|
2006-10-25 16:57:53 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|