summaryrefslogtreecommitdiff
path: root/lib/rbcodec/metadata/mp3.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/metadata/mp3.c')
-rw-r--r--lib/rbcodec/metadata/mp3.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/lib/rbcodec/metadata/mp3.c b/lib/rbcodec/metadata/mp3.c
new file mode 100644
index 0000000..feb1a52
--- /dev/null
+++ b/lib/rbcodec/metadata/mp3.c
@@ -0,0 +1,193 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "string-extra.h"
+#include "config.h"
+#include "file.h"
+#include "logf.h"
+
+#include "system.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;
+}