Split id3.c/h into metadata.c/h and metadata/mp3.c. Updated all references. Moved mp3data.c/h from firmware to apps.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18814 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Björn Stenberg 2008-10-15 06:38:51 +00:00
parent a18ef2efd3
commit 51b45d5602
51 changed files with 440 additions and 466 deletions

View file

@ -7,7 +7,6 @@ abrepeat.c
bookmark.c
debug_menu.c
filetypes.c
id3.c
language.c
main.c
menu.c
@ -31,6 +30,7 @@ menus/recording_menu.c
menus/settings_menu.c
menus/sound_menu.c
misc.c
mp3data.c
onplay.c
playlist.c
playlist_catalog.c
@ -130,6 +130,7 @@ eq_arm.S
#endif
#endif
metadata.c
metadata/mp3.c
#if CONFIG_CODEC == SWCODEC
metadata/metadata_common.c
metadata/aiff.c

View file

@ -36,7 +36,7 @@
#include "config.h"
#include "kernel.h"
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "audio.h"
#ifdef RB_PROFILE
#include "profile.h"

View file

@ -25,7 +25,7 @@
#include "codecs.h"
#include "dsp.h"
#include "codeclib.h"
#include "id3.h"
#include "metadata.h"
long mem_ptr;
long bufsize;

View file

@ -25,7 +25,7 @@
#include <stdbool.h>
#include "screens.h"
#include "file.h"
#include "id3.h"
#include "metadata.h"
#define MAX_NAME 80 /* Max length of information strings */
#define MAX_TRACKS 99 /* Max number of tracks in a cuesheet */

View file

@ -23,7 +23,7 @@
#include "screen_access.h"
#include "statusbar.h"
#include "id3.h"
#include "metadata.h"
/* constants used in line_type and as refresh_mode for wps_refresh */
#define WPS_REFRESH_STATIC 1 /* line doesn't change over time */

View file

@ -27,7 +27,7 @@
#include "sound.h"
#include "settings.h"
#if CONFIG_CODEC == SWCODEC
#include "id3.h"
#include "metadata.h"
#endif
#include "icons.h"
#include "powermgmt.h"

View file

@ -1,246 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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.
*
****************************************************************************/
#ifndef ID3_H
#define ID3_H
#include <stdbool.h>
#include "config.h"
#include "file.h"
#define ID3V2_BUF_SIZE 300
/* Audio file types. */
/* NOTE: The values of the AFMT_* items are used for the %fc tag in the WPS
- so new entries MUST be added to the end to maintain compatibility.
*/
enum
{
AFMT_UNKNOWN = 0, /* Unknown file format */
/* start formats */
AFMT_MPA_L1, /* MPEG Audio layer 1 */
AFMT_MPA_L2, /* MPEG Audio layer 2 */
AFMT_MPA_L3, /* MPEG Audio layer 3 */
#if CONFIG_CODEC == SWCODEC
AFMT_AIFF, /* Audio Interchange File Format */
AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */
AFMT_OGG_VORBIS, /* Ogg Vorbis */
AFMT_FLAC, /* FLAC */
AFMT_MPC, /* Musepack */
AFMT_A52, /* A/52 (aka AC3) audio */
AFMT_WAVPACK, /* WavPack */
AFMT_ALAC, /* Apple Lossless Audio Codec */
AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */
AFMT_SHN, /* Shorten */
AFMT_SID, /* SID File Format */
AFMT_ADX, /* ADX File Format */
AFMT_NSF, /* NESM (NES Sound Format) */
AFMT_SPEEX, /* Ogg Speex speech */
AFMT_SPC, /* SPC700 save state */
AFMT_APE, /* Monkey's Audio (APE) */
AFMT_WMA, /* WMAV1/V2 in ASF */
AFMT_MOD, /* Amiga MOD File Format */
AFMT_SAP, /* Amiga 8Bit SAP Format */
#endif
/* add new formats at any index above this line to have a sensible order -
specified array index inits are used */
/* format arrays defined in id3.c */
AFMT_NUM_CODECS,
#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
/* masks to decompose parts */
CODEC_AFMT_MASK = 0x0fff,
CODEC_TYPE_MASK = 0x7000,
/* switch for specifying codec type when requesting a filename */
CODEC_TYPE_DECODER = (0 << 12), /* default */
CODEC_TYPE_ENCODER = (1 << 12),
#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */
};
#if CONFIG_CODEC == SWCODEC
#define CODEC_EXTENSION "codec"
#ifdef HAVE_RECORDING
#define ENCODER_SUFFIX "_enc"
enum rec_format_indexes
{
__REC_FORMAT_START_INDEX = -1,
/* start formats */
REC_FORMAT_PCM_WAV,
REC_FORMAT_AIFF,
REC_FORMAT_WAVPACK,
REC_FORMAT_MPA_L3,
/* add new formats at any index above this line to have a sensible order -
specified array index inits are used
REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range
REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes
*/
REC_NUM_FORMATS,
REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV,
REC_FORMAT_CFG_NUM_BITS = 2
};
#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3"
/* get REC_FORMAT_* corresponding AFMT_* */
extern const int rec_format_afmt[REC_NUM_FORMATS];
/* get AFMT_* corresponding REC_FORMAT_* */
extern const int afmt_rec_format[AFMT_NUM_CODECS];
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, root_fname, enc_root_fname, ext_list }
#else /* !HAVE_RECORDING */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, root_fname, ext_list }
#endif /* HAVE_RECORDING */
#else /* !SWCODEC */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, ext_list }
#endif /* CONFIG_CODEC == SWCODEC */
/* record describing the audio format */
struct afmt_entry
{
char label[8]; /* format label */
#if CONFIG_CODEC == SWCODEC
char *codec_root_fn; /* root codec filename (sans _enc and .codec) */
#ifdef HAVE_RECORDING
char *codec_enc_root_fn; /* filename of encoder codec */
#endif
#endif
char *ext_list; /* double NULL terminated extension
list for type with the first as
the default for recording */
};
/* database of labels and codecs. add formats per above enum */
extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS];
struct mp3entry {
char path[MAX_PATH];
char* title;
char* artist;
char* album;
char* genre_string;
char* disc_string;
char* track_string;
char* year_string;
char* composer;
char* comment;
char* albumartist;
char* grouping;
int discnum;
int tracknum;
int version;
int layer;
int year;
unsigned char id3version;
unsigned int codectype;
unsigned int bitrate;
unsigned long frequency;
unsigned long id3v2len;
unsigned long id3v1len;
unsigned long first_frame_offset; /* Byte offset to first real MP3 frame.
Used for skipping leading garbage to
avoid gaps between tracks. */
unsigned long vbr_header_pos;
unsigned long filesize; /* without headers; in bytes */
unsigned long length; /* song length in ms */
unsigned long elapsed; /* ms played */
int lead_trim; /* Number of samples to skip at the beginning */
int tail_trim; /* Number of samples to remove from the end */
/* Added for Vorbis */
unsigned long samples; /* number of samples in track */
/* MP3 stream specific info */
unsigned long frame_count; /* number of frames in the file (if VBR) */
/* Used for A52/AC3 */
unsigned long bytesperframe; /* number of bytes per frame (if CBR) */
/* Xing VBR fields */
bool vbr;
bool has_toc; /* True if there is a VBR header in the file */
unsigned char toc[100]; /* table of contents */
/* these following two fields are used for local buffering */
char id3v2buf[ID3V2_BUF_SIZE];
char id3v1buf[4][92];
/* resume related */
unsigned long offset; /* bytes played */
int index; /* playlist index */
/* runtime database fields */
long tagcache_idx; /* 0=invalid, otherwise idx+1 */
int rating;
int score;
long playcount;
long lastplayed;
long playtime;
/* replaygain support */
#if CONFIG_CODEC == SWCODEC
char* track_gain_string;
char* album_gain_string;
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
long album_gain;
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
long album_peak;
#endif
/* Cuesheet support */
int cuesheet_type; /* 0: none, 1: external, 2: embedded */
/* Musicbrainz Track ID */
char* mb_track_id;
};
enum {
ID3_VER_1_0 = 1,
ID3_VER_1_1,
ID3_VER_2_2,
ID3_VER_2_3,
ID3_VER_2_4
};
bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename);
bool mp3info(struct mp3entry *entry, const char *filename);
char* id3_get_num_genre(unsigned int genre_num);
int getid3v2len(int fd);
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
#endif

View file

@ -55,7 +55,7 @@
#endif
#include "splash.h"
#if CONFIG_CODEC == SWCODEC
#include "id3.h"
#include "metadata.h"
#include "dsp.h"
#include "menus/eq_menu.h"
#ifdef HAVE_RECORDING

View file

@ -24,23 +24,126 @@
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "playback.h"
#include "debug.h"
#include "logf.h"
#include "cuesheet.h"
#include "metadata.h"
#include "metadata/metadata_parsers.h"
#if CONFIG_CODEC == SWCODEC
/* For trailing tag stripping */
#include "buffering.h"
#include "metadata/metadata_common.h"
#include "metadata/metadata_parsers.h"
#endif /* CONFIG_CODEC == SWCODEC */
const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
{
/* Unknown file format */
[AFMT_UNKNOWN] =
AFMT_ENTRY("???", NULL, NULL, NULL ),
/* MPEG Audio layer 1 */
[AFMT_MPA_L1] =
AFMT_ENTRY("MP1", "mpa", NULL, "mp1\0" ),
/* MPEG Audio layer 2 */
[AFMT_MPA_L2] =
AFMT_ENTRY("MP2", "mpa", NULL, "mpa\0mp2\0" ),
/* MPEG Audio layer 3 */
[AFMT_MPA_L3] =
AFMT_ENTRY("MP3", "mpa", "mp3_enc", "mp3\0" ),
#if CONFIG_CODEC == SWCODEC
/* Audio Interchange File Format */
[AFMT_AIFF] =
AFMT_ENTRY("AIFF", "aiff", "aiff_enc", "aiff\0aif\0"),
/* Uncompressed PCM in a WAV file */
[AFMT_PCM_WAV] =
AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ),
/* Ogg Vorbis */
[AFMT_OGG_VORBIS] =
AFMT_ENTRY("Ogg", "vorbis", NULL, "ogg\0" ),
/* FLAC */
[AFMT_FLAC] =
AFMT_ENTRY("FLAC", "flac", NULL, "flac\0" ),
/* Musepack */
[AFMT_MPC] =
AFMT_ENTRY("MPC", "mpc", NULL, "mpc\0" ),
/* A/52 (aka AC3) audio */
[AFMT_A52] =
AFMT_ENTRY("AC3", "a52", NULL, "a52\0ac3\0" ),
/* WavPack */
[AFMT_WAVPACK] =
AFMT_ENTRY("WV", "wavpack", "wavpack_enc", "wv\0" ),
/* Apple Lossless Audio Codec */
[AFMT_ALAC] =
AFMT_ENTRY("ALAC", "alac", NULL, "m4a\0m4b\0" ),
/* Advanced Audio Coding in M4A container */
[AFMT_AAC] =
AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ),
/* Shorten */
[AFMT_SHN] =
AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ),
/* SID File Format */
[AFMT_SID] =
AFMT_ENTRY("SID", "sid", NULL, "sid\0" ),
/* ADX File Format */
[AFMT_ADX] =
AFMT_ENTRY("ADX", "adx", NULL, "adx\0" ),
/* NESM (NES Sound Format) */
[AFMT_NSF] =
AFMT_ENTRY("NSF", "nsf", NULL, "nsf\0nsfe\0" ),
/* Speex File Format */
[AFMT_SPEEX] =
AFMT_ENTRY("Speex","speex", NULL, "spx\0" ),
/* SPC700 Save State */
[AFMT_SPC] =
AFMT_ENTRY("SPC", "spc", NULL, "spc\0" ),
/* APE (Monkey's Audio) */
[AFMT_APE] =
AFMT_ENTRY("APE", "ape", NULL, "ape\0mac\0" ),
/* WMA (WMAV1/V2 in ASF) */
[AFMT_WMA] =
AFMT_ENTRY("WMA", "wma", NULL, "wma\0wmv\0asf\0" ),
/* Amiga MOD File */
[AFMT_MOD] =
AFMT_ENTRY("MOD", "mod", NULL, "mod\0" ),
/* Amiga SAP File */
[AFMT_SAP] =
AFMT_ENTRY("SAP", "asap", NULL, "sap\0" ),
#endif
};
#if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING)
/* get REC_FORMAT_* corresponding AFMT_* */
const int rec_format_afmt[REC_NUM_FORMATS] =
{
/* give AFMT_UNKNOWN by default */
[0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN,
/* add new entries below this line */
[REC_FORMAT_AIFF] = AFMT_AIFF,
[REC_FORMAT_MPA_L3] = AFMT_MPA_L3,
[REC_FORMAT_WAVPACK] = AFMT_WAVPACK,
[REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV,
};
/* get AFMT_* corresponding REC_FORMAT_* */
const int afmt_rec_format[AFMT_NUM_CODECS] =
{
/* give -1 by default */
[0 ... AFMT_NUM_CODECS-1] = -1,
/* add new entries below this line */
[AFMT_AIFF] = REC_FORMAT_AIFF,
[AFMT_MPA_L3] = REC_FORMAT_MPA_L3,
[AFMT_WAVPACK] = REC_FORMAT_WAVPACK,
[AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV,
};
#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
/* Simple file type probing by looking at the filename extension. */
unsigned int probe_file_format(const char *filename)
@ -78,6 +181,23 @@ unsigned int probe_file_format(const char *filename)
return AFMT_UNKNOWN;
}
/* Note, that this returns false for successful, true for error! */
bool mp3info(struct mp3entry *entry, const char *filename)
{
int fd;
bool result;
fd = open(filename, O_RDONLY);
if (fd < 0)
return true;
result = !get_metadata(entry, fd, filename);
close(fd);
return result;
}
/* Get metadata for track - return false if parsing showed problems with the
* file that would prevent playback.
*/
@ -314,3 +434,50 @@ void strip_tags(int handle_id)
bufcuttail(handle_id, len);
}
#endif /* CONFIG_CODEC == SWCODEC */
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig)
{
long offset;
if (orig > dest)
offset = - ((size_t)orig - (size_t)dest);
else
offset = (size_t)dest - (size_t)orig;
if (entry->title)
entry->title += offset;
if (entry->artist)
entry->artist += offset;
if (entry->album)
entry->album += offset;
if (entry->genre_string && !id3_is_genre_string(entry->genre_string))
/* Don't adjust that if it points to an entry of the "genres" array */
entry->genre_string += offset;
if (entry->track_string)
entry->track_string += offset;
if (entry->disc_string)
entry->disc_string += offset;
if (entry->year_string)
entry->year_string += offset;
if (entry->composer)
entry->composer += offset;
if (entry->comment)
entry->comment += offset;
if (entry->albumartist)
entry->albumartist += offset;
if (entry->grouping)
entry->grouping += offset;
#if CONFIG_CODEC == SWCODEC
if (entry->track_gain_string)
entry->track_gain_string += offset;
if (entry->album_gain_string)
entry->album_gain_string += offset;
#endif
if (entry->mb_track_id)
entry->mb_track_id += offset;
}
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig)
{
memcpy(dest, orig, sizeof(struct mp3entry));
adjust_mp3entry(dest, dest, orig);
}

View file

@ -23,11 +23,228 @@
#define _METADATA_H
#include <stdbool.h>
#include "file.h"
#include "config.h"
#include "id3.h"
/* Audio file types. */
/* NOTE: The values of the AFMT_* items are used for the %fc tag in the WPS
- so new entries MUST be added to the end to maintain compatibility.
*/
enum
{
AFMT_UNKNOWN = 0, /* Unknown file format */
/* start formats */
AFMT_MPA_L1, /* MPEG Audio layer 1 */
AFMT_MPA_L2, /* MPEG Audio layer 2 */
AFMT_MPA_L3, /* MPEG Audio layer 3 */
#if CONFIG_CODEC == SWCODEC
AFMT_AIFF, /* Audio Interchange File Format */
AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */
AFMT_OGG_VORBIS, /* Ogg Vorbis */
AFMT_FLAC, /* FLAC */
AFMT_MPC, /* Musepack */
AFMT_A52, /* A/52 (aka AC3) audio */
AFMT_WAVPACK, /* WavPack */
AFMT_ALAC, /* Apple Lossless Audio Codec */
AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */
AFMT_SHN, /* Shorten */
AFMT_SID, /* SID File Format */
AFMT_ADX, /* ADX File Format */
AFMT_NSF, /* NESM (NES Sound Format) */
AFMT_SPEEX, /* Ogg Speex speech */
AFMT_SPC, /* SPC700 save state */
AFMT_APE, /* Monkey's Audio (APE) */
AFMT_WMA, /* WMAV1/V2 in ASF */
AFMT_MOD, /* Amiga MOD File Format */
AFMT_SAP, /* Amiga 8Bit SAP Format */
#endif
/* add new formats at any index above this line to have a sensible order -
specified array index inits are used */
/* format arrays defined in id3.c */
AFMT_NUM_CODECS,
#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
/* masks to decompose parts */
CODEC_AFMT_MASK = 0x0fff,
CODEC_TYPE_MASK = 0x7000,
/* switch for specifying codec type when requesting a filename */
CODEC_TYPE_DECODER = (0 << 12), /* default */
CODEC_TYPE_ENCODER = (1 << 12),
#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */
};
#if CONFIG_CODEC == SWCODEC
#define CODEC_EXTENSION "codec"
#ifdef HAVE_RECORDING
#define ENCODER_SUFFIX "_enc"
enum rec_format_indexes
{
__REC_FORMAT_START_INDEX = -1,
/* start formats */
REC_FORMAT_PCM_WAV,
REC_FORMAT_AIFF,
REC_FORMAT_WAVPACK,
REC_FORMAT_MPA_L3,
/* add new formats at any index above this line to have a sensible order -
specified array index inits are used
REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range
REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes
*/
REC_NUM_FORMATS,
REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV,
REC_FORMAT_CFG_NUM_BITS = 2
};
#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3"
/* get REC_FORMAT_* corresponding AFMT_* */
extern const int rec_format_afmt[REC_NUM_FORMATS];
/* get AFMT_* corresponding REC_FORMAT_* */
extern const int afmt_rec_format[AFMT_NUM_CODECS];
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, root_fname, enc_root_fname, ext_list }
#else /* !HAVE_RECORDING */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, root_fname, ext_list }
#endif /* HAVE_RECORDING */
#else /* !SWCODEC */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
{ label, ext_list }
#endif /* CONFIG_CODEC == SWCODEC */
/** Database of audio formats **/
/* record describing the audio format */
struct afmt_entry
{
char label[8]; /* format label */
#if CONFIG_CODEC == SWCODEC
char *codec_root_fn; /* root codec filename (sans _enc and .codec) */
#ifdef HAVE_RECORDING
char *codec_enc_root_fn; /* filename of encoder codec */
#endif
#endif
char *ext_list; /* double NULL terminated extension
list for type with the first as
the default for recording */
};
/* database of labels and codecs. add formats per above enum */
extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS];
#define ID3V2_BUF_SIZE 300
enum {
ID3_VER_1_0 = 1,
ID3_VER_1_1,
ID3_VER_2_2,
ID3_VER_2_3,
ID3_VER_2_4
};
struct mp3entry {
char path[MAX_PATH];
char* title;
char* artist;
char* album;
char* genre_string;
char* disc_string;
char* track_string;
char* year_string;
char* composer;
char* comment;
char* albumartist;
char* grouping;
int discnum;
int tracknum;
int version;
int layer;
int year;
unsigned char id3version;
unsigned int codectype;
unsigned int bitrate;
unsigned long frequency;
unsigned long id3v2len;
unsigned long id3v1len;
unsigned long first_frame_offset; /* Byte offset to first real MP3 frame.
Used for skipping leading garbage to
avoid gaps between tracks. */
unsigned long vbr_header_pos;
unsigned long filesize; /* without headers; in bytes */
unsigned long length; /* song length in ms */
unsigned long elapsed; /* ms played */
int lead_trim; /* Number of samples to skip at the beginning */
int tail_trim; /* Number of samples to remove from the end */
/* Added for Vorbis */
unsigned long samples; /* number of samples in track */
/* MP3 stream specific info */
unsigned long frame_count; /* number of frames in the file (if VBR) */
/* Used for A52/AC3 */
unsigned long bytesperframe; /* number of bytes per frame (if CBR) */
/* Xing VBR fields */
bool vbr;
bool has_toc; /* True if there is a VBR header in the file */
unsigned char toc[100]; /* table of contents */
/* these following two fields are used for local buffering */
char id3v2buf[ID3V2_BUF_SIZE];
char id3v1buf[4][92];
/* resume related */
unsigned long offset; /* bytes played */
int index; /* playlist index */
/* runtime database fields */
long tagcache_idx; /* 0=invalid, otherwise idx+1 */
int rating;
int score;
long playcount;
long lastplayed;
long playtime;
/* replaygain support */
#if CONFIG_CODEC == SWCODEC
char* track_gain_string;
char* album_gain_string;
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
long album_gain;
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
long album_peak;
#endif
/* Cuesheet support */
int cuesheet_type; /* 0: none, 1: external, 2: embedded */
/* Musicbrainz Track ID */
char* mb_track_id;
};
unsigned int probe_file_format(const char *filename);
bool get_metadata(struct mp3entry* id3, int fd, const char* trackname);
bool mp3info(struct mp3entry *entry, const char *filename);
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
#if CONFIG_CODEC == SWCODEC
void strip_tags(int handle_id);
#endif

View file

@ -19,7 +19,7 @@
*
****************************************************************************/
#include "id3.h"
#include "metadata.h"
#include "logf.h"
#include "metadata_parsers.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "debug.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "structec.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"

View file

@ -25,7 +25,7 @@
#include <ctype.h>
#include <inttypes.h>
#include "id3.h"
#include "metadata.h"
#include "replaygain.h"
#include "debug.h"
#include "rbunicode.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"

View file

@ -25,8 +25,9 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "replaygain.h"
/* Skip an ID3v2 tag if it can be found. We assume the tag is located at the

View file

@ -18,7 +18,7 @@
* KIND, either express or implied.
*
****************************************************************************/
#include "id3.h"
#include "metadata.h"
#ifdef ROCKBOX_BIG_ENDIAN
#define IS_BIG_ENDIAN 1

View file

@ -18,7 +18,11 @@
* KIND, either express or implied.
*
****************************************************************************/
#include "id3.h"
char* id3_get_num_genre(unsigned int genre_num);
bool id3_is_genre_string(const char *string);
int getid3v2len(int fd);
bool get_mp3_metadata(int fd, struct mp3entry* id3, const char *filename);
bool get_adx_metadata(int fd, struct mp3entry* id3);
bool get_aiff_metadata(int fd, struct mp3entry* id3);

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"

View file

@ -39,117 +39,11 @@
#include "file.h"
#include "logf.h"
#include "id3.h"
#include "mp3data.h"
#include "system.h"
#include "replaygain.h"
#include "rbunicode.h"
/** Database of audio formats **/
const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
{
/* Unknown file format */
[AFMT_UNKNOWN] =
AFMT_ENTRY("???", NULL, NULL, NULL ),
/* MPEG Audio layer 1 */
[AFMT_MPA_L1] =
AFMT_ENTRY("MP1", "mpa", NULL, "mp1\0" ),
/* MPEG Audio layer 2 */
[AFMT_MPA_L2] =
AFMT_ENTRY("MP2", "mpa", NULL, "mpa\0mp2\0" ),
/* MPEG Audio layer 3 */
[AFMT_MPA_L3] =
AFMT_ENTRY("MP3", "mpa", "mp3_enc", "mp3\0" ),
#if CONFIG_CODEC == SWCODEC
/* Audio Interchange File Format */
[AFMT_AIFF] =
AFMT_ENTRY("AIFF", "aiff", "aiff_enc", "aiff\0aif\0"),
/* Uncompressed PCM in a WAV file */
[AFMT_PCM_WAV] =
AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ),
/* Ogg Vorbis */
[AFMT_OGG_VORBIS] =
AFMT_ENTRY("Ogg", "vorbis", NULL, "ogg\0" ),
/* FLAC */
[AFMT_FLAC] =
AFMT_ENTRY("FLAC", "flac", NULL, "flac\0" ),
/* Musepack */
[AFMT_MPC] =
AFMT_ENTRY("MPC", "mpc", NULL, "mpc\0" ),
/* A/52 (aka AC3) audio */
[AFMT_A52] =
AFMT_ENTRY("AC3", "a52", NULL, "a52\0ac3\0" ),
/* WavPack */
[AFMT_WAVPACK] =
AFMT_ENTRY("WV", "wavpack", "wavpack_enc", "wv\0" ),
/* Apple Lossless Audio Codec */
[AFMT_ALAC] =
AFMT_ENTRY("ALAC", "alac", NULL, "m4a\0m4b\0" ),
/* Advanced Audio Coding in M4A container */
[AFMT_AAC] =
AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ),
/* Shorten */
[AFMT_SHN] =
AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ),
/* SID File Format */
[AFMT_SID] =
AFMT_ENTRY("SID", "sid", NULL, "sid\0" ),
/* ADX File Format */
[AFMT_ADX] =
AFMT_ENTRY("ADX", "adx", NULL, "adx\0" ),
/* NESM (NES Sound Format) */
[AFMT_NSF] =
AFMT_ENTRY("NSF", "nsf", NULL, "nsf\0nsfe\0" ),
/* Speex File Format */
[AFMT_SPEEX] =
AFMT_ENTRY("Speex","speex", NULL, "spx\0" ),
/* SPC700 Save State */
[AFMT_SPC] =
AFMT_ENTRY("SPC", "spc", NULL, "spc\0" ),
/* APE (Monkey's Audio) */
[AFMT_APE] =
AFMT_ENTRY("APE", "ape", NULL, "ape\0mac\0" ),
/* WMA (WMAV1/V2 in ASF) */
[AFMT_WMA] =
AFMT_ENTRY("WMA", "wma", NULL, "wma\0wmv\0asf\0" ),
/* Amiga MOD File */
[AFMT_MOD] =
AFMT_ENTRY("MOD", "mod", NULL, "mod\0" ),
/* Amiga SAP File */
[AFMT_SAP] =
AFMT_ENTRY("SAP", "asap", NULL, "sap\0" ),
#endif
};
#if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING)
/* get REC_FORMAT_* corresponding AFMT_* */
const int rec_format_afmt[REC_NUM_FORMATS] =
{
/* give AFMT_UNKNOWN by default */
[0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN,
/* add new entries below this line */
[REC_FORMAT_AIFF] = AFMT_AIFF,
[REC_FORMAT_MPA_L3] = AFMT_MPA_L3,
[REC_FORMAT_WAVPACK] = AFMT_WAVPACK,
[REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV,
};
/* get AFMT_* corresponding REC_FORMAT_* */
const int afmt_rec_format[AFMT_NUM_CODECS] =
{
/* give -1 by default */
[0 ... AFMT_NUM_CODECS-1] = -1,
/* add new entries below this line */
[AFMT_AIFF] = REC_FORMAT_AIFF,
[AFMT_MPA_L3] = REC_FORMAT_MPA_L3,
[AFMT_WAVPACK] = REC_FORMAT_WAVPACK,
[AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV,
};
#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
/****/
static unsigned long unsync(unsigned long b0,
unsigned long b1,
unsigned long b2,
@ -200,7 +94,7 @@ char* id3_get_num_genre(unsigned int genre_num)
}
/* True if the string is from the "genres" array */
static bool id3_is_genre_string(const char *string)
bool id3_is_genre_string(const char *string)
{
return ( string >= genres[0] &&
string <= genres[sizeof(genres)/sizeof(char*) - 1] );
@ -1233,70 +1127,6 @@ bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename)
return true;
}
/* Note, that this returns false for successful, true for error! */
bool mp3info(struct mp3entry *entry, const char *filename)
{
int fd;
bool result;
fd = open(filename, O_RDONLY);
if (fd < 0)
return true;
result = !get_mp3_metadata(fd, entry, filename);
close(fd);
return result;
}
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig)
{
long offset;
if (orig > dest)
offset = - ((size_t)orig - (size_t)dest);
else
offset = (size_t)dest - (size_t)orig;
if (entry->title)
entry->title += offset;
if (entry->artist)
entry->artist += offset;
if (entry->album)
entry->album += offset;
if (entry->genre_string && !id3_is_genre_string(entry->genre_string))
/* Don't adjust that if it points to an entry of the "genres" array */
entry->genre_string += offset;
if (entry->track_string)
entry->track_string += offset;
if (entry->disc_string)
entry->disc_string += offset;
if (entry->year_string)
entry->year_string += offset;
if (entry->composer)
entry->composer += offset;
if (entry->comment)
entry->comment += offset;
if (entry->albumartist)
entry->albumartist += offset;
if (entry->grouping)
entry->grouping += offset;
#if CONFIG_CODEC == SWCODEC
if (entry->track_gain_string)
entry->track_gain_string += offset;
if (entry->album_gain_string)
entry->album_gain_string += offset;
#endif
if (entry->mb_track_id)
entry->mb_track_id += offset;
}
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig)
{
memcpy(dest, orig, sizeof(struct mp3entry));
adjust_mp3entry(dest, dest, orig);
}
#ifdef DEBUG_STANDALONE
char *secs2str(int ms)

View file

@ -26,7 +26,7 @@
#include "system.h"
#include "errno.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"

View file

@ -22,7 +22,7 @@
#include <string.h>
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "debug.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "structec.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"

View file

@ -25,7 +25,7 @@
#include <inttypes.h>
#include "system.h"
#include "id3.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"

782
apps/mp3data.c Normal file
View file

@ -0,0 +1,782 @@
/***************************************************************************
* __________ __ ___.
* 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ärdeman. It has since been extended and enhanced pretty much by
* all sorts of friendly Rockbox people.
*
* A nice reference for MPEG header info:
* http://rockbox.haxx.se/docs/mpeghdr.html
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include "debug.h"
#include "logf.h"
#include "mp3data.h"
#include "file.h"
#include "buffer.h"
// #define DEBUG_VERBOSE
#define SYNC_MASK (0x7ffL << 21)
#define VERSION_MASK (3L << 19)
#define LAYER_MASK (3L << 17)
#define PROTECTION_MASK (1L << 16)
#define BITRATE_MASK (0xfL << 12)
#define SAMPLERATE_MASK (3L << 10)
#define PADDING_MASK (1L << 9)
#define PRIVATE_MASK (1L << 8)
#define CHANNELMODE_MASK (3L << 6)
#define MODE_EXT_MASK (3L << 4)
#define COPYRIGHT_MASK (1L << 3)
#define ORIGINAL_MASK (1L << 2)
#define EMPHASIS_MASK 3L
/* MPEG Version table, sorted by version index */
static const signed char version_table[4] = {
MPEG_VERSION2_5, -1, MPEG_VERSION2, MPEG_VERSION1
};
/* Bitrate table for mpeg audio, indexed by row index and birate index */
static const short bitrates[5][16] = {
{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, /* V1 L1 */
{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, /* V1 L2 */
{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}, /* V1 L3 */
{0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,0}, /* V2 L1 */
{0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0} /* V2 L2+L3 */
};
/* Bitrate pointer table, indexed by version and layer */
static const short *bitrate_table[3][3] =
{
{bitrates[0], bitrates[1], bitrates[2]},
{bitrates[3], bitrates[4], bitrates[4]},
{bitrates[3], bitrates[4], bitrates[4]}
};
/* Sampling frequency table, indexed by version and frequency index */
static const unsigned short freq_table[3][3] =
{
{44100, 48000, 32000}, /* MPEG Version 1 */
{22050, 24000, 16000}, /* MPEG version 2 */
{11025, 12000, 8000}, /* MPEG version 2.5 */
};
unsigned long bytes2int(unsigned long b0,
unsigned long b1,
unsigned long b2,
unsigned long b3)
{
return (((long)(b0 & 0xFF) << (3*8)) |
((long)(b1 & 0xFF) << (2*8)) |
((long)(b2 & 0xFF) << (1*8)) |
((long)(b3 & 0xFF) << (0*8)));
}
/* check if 'head' is a valid mp3 frame header */
static bool is_mp3frameheader(unsigned long head)
{
if ((head & SYNC_MASK) != (unsigned long)SYNC_MASK) /* bad sync? */
return false;
if ((head & VERSION_MASK) == (1L << 19)) /* bad version? */
return false;
if (!(head & LAYER_MASK)) /* no layer? */
return false;
#if CONFIG_CODEC != SWCODEC
/* The MAS can't decode layer 1, so treat layer 1 data as invalid */
if ((head & LAYER_MASK) == LAYER_MASK)
return false;
#endif
if ((head & BITRATE_MASK) == BITRATE_MASK) /* bad bitrate? */
return false;
if (!(head & BITRATE_MASK)) /* no bitrate? */
return false;
if ((head & SAMPLERATE_MASK) == SAMPLERATE_MASK) /* bad sample rate? */
return false;
return true;
}
static bool mp3headerinfo(struct mp3info *info, unsigned long header)
{
int bitindex, freqindex;
/* MPEG Audio Version */
if ((header & VERSION_MASK) >> 19 >= sizeof(version_table))
return false;
info->version = version_table[(header & VERSION_MASK) >> 19];
if (info->version < 0)
return false;
/* Layer */
info->layer = 3 - ((header & LAYER_MASK) >> 17);
if (info->layer == 3)
return false;
info->protection = (header & PROTECTION_MASK) ? true : false;
/* Bitrate */
bitindex = (header & BITRATE_MASK) >> 12;
info->bitrate = bitrate_table[info->version][info->layer][bitindex];
if(info->bitrate == 0)
return false;
/* Sampling frequency */
freqindex = (header & SAMPLERATE_MASK) >> 10;
if (freqindex == 3)
return false;
info->frequency = freq_table[info->version][freqindex];
info->padding = (header & PADDING_MASK) ? 1 : 0;
/* Calculate number of bytes, calculation depends on layer */
if (info->layer == 0) {
info->frame_samples = 384;
info->frame_size = (12000 * info->bitrate / info->frequency
+ info->padding) * 4;
}
else {
if ((info->version > MPEG_VERSION1) && (info->layer == 2))
info->frame_samples = 576;
else
info->frame_samples = 1152;
info->frame_size = (1000/8) * info->frame_samples * info->bitrate
/ info->frequency + info->padding;
}
/* Frametime fraction denominator */
if (freqindex != 0) { /* 48/32/24/16/12/8 kHz */
info->ft_den = 1; /* integer number of milliseconds */
}
else { /* 44.1/22.05/11.025 kHz */
if (info->layer == 0) /* layer 1 */
info->ft_den = 147;
else /* layer 2+3 */
info->ft_den = 49;
}
/* Frametime fraction numerator */
info->ft_num = 1000 * info->ft_den * info->frame_samples / info->frequency;
info->channel_mode = (header & CHANNELMODE_MASK) >> 6;
info->mode_extension = (header & MODE_EXT_MASK) >> 4;
info->emphasis = header & EMPHASIS_MASK;
#ifdef DEBUG_VERBOSE
DEBUGF( "Header: %08lx, Ver %d, lay %d, bitr %d, freq %ld, "
"chmode %d, mode_ext %d, emph %d, bytes: %d time: %d/%d\n",
header, info->version, info->layer+1, info->bitrate,
info->frequency, info->channel_mode, info->mode_extension,
info->emphasis, info->frame_size, info->ft_num, info->ft_den);
#endif
return true;
}
static unsigned long __find_next_frame(int fd, long *offset, long max_offset,
unsigned long last_header,
int(*getfunc)(int fd, unsigned char *c))
{
unsigned long header=0;
unsigned char tmp;
int i;
long pos = 0;
/* We remember the last header we found, to use as a template to see if
the header we find has the same frequency, layer etc */
last_header &= 0xffff0c00;
/* Fill up header with first 24 bits */
for(i = 0; i < 3; i++) {
header <<= 8;
if(!getfunc(fd, &tmp))
return 0;
header |= tmp;
pos++;
}
do {
header <<= 8;
if(!getfunc(fd, &tmp))
return 0;
header |= tmp;
pos++;
if(max_offset > 0 && pos > max_offset)
return 0;
} while(!is_mp3frameheader(header) ||
(last_header?((header & 0xffff0c00) != last_header):false));
*offset = pos - 4;
#if defined(DEBUG)
if(*offset)
DEBUGF("Warning: skipping %ld bytes of garbage\n", *offset);
#endif
return header;
}
static int fileread(int fd, unsigned char *c)
{
return read(fd, c, 1);
}
unsigned long find_next_frame(int fd, long *offset, long max_offset, unsigned long last_header)
{
return __find_next_frame(fd, offset, max_offset, last_header, fileread);
}
#ifndef __PCTOOL__
static int fnf_read_index;
static int fnf_buf_len;
static int buf_getbyte(int fd, unsigned char *c)
{
if(fnf_read_index < fnf_buf_len)
{
*c = audiobuf[fnf_read_index++];
return 1;
}
else
{
fnf_buf_len = read(fd, audiobuf, audiobufend - audiobuf);
if(fnf_buf_len < 0)
return -1;
fnf_read_index = 0;
if(fnf_buf_len > 0)
{
*c = audiobuf[fnf_read_index++];
return 1;
}
else
return 0;
}
return 0;
}
static int buf_seek(int fd, int len)
{
fnf_read_index += len;
if(fnf_read_index > fnf_buf_len)
{
len = fnf_read_index - fnf_buf_len;
fnf_buf_len = read(fd, audiobuf, audiobufend - audiobuf);
if(fnf_buf_len < 0)
return -1;
fnf_read_index = 0;
fnf_read_index += len;
}
if(fnf_read_index > fnf_buf_len)
{
return -1;
}
else
return 0;
}
static void buf_init(void)
{
fnf_buf_len = 0;
fnf_read_index = 0;
}
static unsigned long buf_find_next_frame(int fd, long *offset, long max_offset,
unsigned long last_header)
{
return __find_next_frame(fd, offset, max_offset, last_header, buf_getbyte);
}
static int audiobuflen;
static int mem_pos;
static int mem_cnt;
static int mem_maxlen;
static int mem_getbyte(int dummy, unsigned char *c)
{
dummy = dummy;
*c = audiobuf[mem_pos++];
if(mem_pos >= audiobuflen)
mem_pos = 0;
if(mem_cnt++ >= mem_maxlen)
return 0;
else
return 1;
}
unsigned long mem_find_next_frame(int startpos, long *offset, long max_offset,
unsigned long last_header)
{
audiobuflen = audiobufend - audiobuf;
mem_pos = startpos;
mem_cnt = 0;
mem_maxlen = max_offset;
return __find_next_frame(0, offset, max_offset, last_header, mem_getbyte);
}
#endif
int get_mp3file_info(int fd, struct mp3info *info)
{
unsigned char frame[1800];
unsigned char *vbrheader;
unsigned long header;
long bytecount;
int num_offsets;
int frames_per_entry;
int i;
long offset;
int j;
long tmp;
header = find_next_frame(fd, &bytecount, 0x20000, 0);
/* Quit if we haven't found a valid header within 128K */
if(header == 0)
return -1;
memset(info, 0, sizeof(struct mp3info));
#if CONFIG_CODEC==SWCODEC
/* These two are needed for proper LAME gapless MP3 playback */
info->enc_delay = -1;
info->enc_padding = -1;
#endif
if(!mp3headerinfo(info, header))
return -2;
/* OK, we have found a frame. Let's see if it has a Xing header */
if (info->frame_size-4 >= (int)sizeof(frame))
{
#if defined(DEBUG)
DEBUGF("Error: Invalid id3 header, frame_size: %d\n", info->frame_size);
#endif
return -8;
}
if(read(fd, frame, info->frame_size-4) < 0)
return -3;
/* calculate position of VBR header */
if ( info->version == MPEG_VERSION1 ) {
if (info->channel_mode == 3) /* mono */
vbrheader = frame + 17;
else
vbrheader = frame + 32;
}
else {
if (info->channel_mode == 3) /* mono */
vbrheader = frame + 9;
else
vbrheader = frame + 17;
}
if (!memcmp(vbrheader, "Xing", 4)
|| !memcmp(vbrheader, "Info", 4))
{
int i = 8; /* Where to start parsing info */
/* DEBUGF("Xing/Info header\n"); */
/* Remember where in the file the Xing header is */
info->vbr_header_pos = lseek(fd, 0, SEEK_CUR) - info->frame_size;
/* We want to skip the Xing frame when playing the stream */
bytecount += info->frame_size;
/* Now get the next frame to find out the real info about
the mp3 stream */
header = find_next_frame(fd, &tmp, 0x20000, 0);
if(header == 0)
return -4;
if(!mp3headerinfo(info, header))
return -5;
/* Is it a VBR file? */
info->is_vbr = info->is_xing_vbr = !memcmp(vbrheader, "Xing", 4);
if (vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */
{
info->frame_count = bytes2int(vbrheader[i], vbrheader[i+1],
vbrheader[i+2], vbrheader[i+3]);
if (info->frame_count <= ULONG_MAX / info->ft_num)
info->file_time = info->frame_count * info->ft_num / info->ft_den;
else
info->file_time = info->frame_count / info->ft_den * info->ft_num;
i += 4;
}
if (vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */
{
info->byte_count = bytes2int(vbrheader[i], vbrheader[i+1],
vbrheader[i+2], vbrheader[i+3]);
i += 4;
}
if (info->file_time && info->byte_count)
{
if (info->byte_count <= (ULONG_MAX/8))
info->bitrate = info->byte_count * 8 / info->file_time;
else
info->bitrate = info->byte_count / (info->file_time >> 3);
}
else
info->bitrate = 0;
if (vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */
{
info->has_toc = true;
memcpy( info->toc, vbrheader+i, 100 );
i += 100;
}
if (vbrheader[7] & VBR_QUALITY_FLAG)
{
/* We don't care about this, but need to skip it */
i += 4;
}
#if CONFIG_CODEC==SWCODEC
i += 21;
info->enc_delay = (vbrheader[i] << 4) | (vbrheader[i + 1] >> 4);
info->enc_padding = ((vbrheader[i + 1] & 0x0f) << 8) | vbrheader[i + 2];
/* TODO: This sanity checking is rather silly, seeing as how the LAME
header contains a CRC field that can be used to verify integrity. */
if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 &&
info->enc_padding >= 0 && info->enc_padding <= 2*1152))
{
/* Invalid data */
info->enc_delay = -1;
info->enc_padding = -1;
}
#endif
}
if (!memcmp(vbrheader, "VBRI", 4))
{
DEBUGF("VBRI header\n");
/* We want to skip the VBRI frame when playing the stream */
bytecount += info->frame_size;
/* Now get the next frame to find out the real info about
the mp3 stream */
header = find_next_frame(fd, &tmp, 0x20000, 0);
if(header == 0)
return -6;
bytecount += tmp;
if(!mp3headerinfo(info, header))
return -7;
DEBUGF("%04x: %04x %04x ", 0, (short)(header >> 16),
(short)(header & 0xffff));
for(i = 4;i < (int)sizeof(frame)-4;i+=2) {
if(i % 16 == 0) {
DEBUGF("\n%04x: ", i-4);
}
DEBUGF("%04x ", (frame[i-4] << 8) | frame[i-4+1]);
}
DEBUGF("\n");
/* Yes, it is a FhG VBR file */
info->is_vbr = true;
info->is_vbri_vbr = true;
info->has_toc = false; /* We don't parse the TOC (yet) */
info->byte_count = bytes2int(vbrheader[10], vbrheader[11],
vbrheader[12], vbrheader[13]);
info->frame_count = bytes2int(vbrheader[14], vbrheader[15],
vbrheader[16], vbrheader[17]);
if (info->frame_count <= ULONG_MAX / info->ft_num)
info->file_time = info->frame_count * info->ft_num / info->ft_den;
else
info->file_time = info->frame_count / info->ft_den * info->ft_num;
if (info->byte_count <= (ULONG_MAX/8))
info->bitrate = info->byte_count * 8 / info->file_time;
else
info->bitrate = info->byte_count / (info->file_time >> 3);
/* We don't parse the TOC, since we don't yet know how to (FIXME) */
num_offsets = bytes2int(0, 0, vbrheader[18], vbrheader[19]);
frames_per_entry = bytes2int(0, 0, vbrheader[24], vbrheader[25]);
DEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n",
info->bitrate, info->frame_size, info->frame_size);
DEBUGF("Frame count: %lx\n", info->frame_count);
DEBUGF("Byte count: %lx\n", info->byte_count);
DEBUGF("Offsets: %d\n", num_offsets);
DEBUGF("Frames/entry: %d\n", frames_per_entry);
offset = 0;
for(i = 0;i < num_offsets;i++)
{
j = bytes2int(0, 0, vbrheader[26+i*2], vbrheader[27+i*2]);
offset += j;
DEBUGF("%03d: %lx (%x)\n", i, offset - bytecount, j);
}
}
return bytecount;
}
#ifndef __PCTOOL__
static void long2bytes(unsigned char *buf, long val)
{
buf[0] = (val >> 24) & 0xff;
buf[1] = (val >> 16) & 0xff;
buf[2] = (val >> 8) & 0xff;
buf[3] = val & 0xff;
}
int count_mp3_frames(int fd, int startpos, int filesize,
void (*progressfunc)(int))
{
unsigned long header = 0;
struct mp3info info;
int num_frames;
long bytes;
int cnt;
long progress_chunk = filesize / 50; /* Max is 50%, in 1% increments */
int progress_cnt = 0;
bool is_vbr = false;
int last_bitrate = 0;
int header_template = 0;
if(lseek(fd, startpos, SEEK_SET) < 0)
return -1;
buf_init();
/* Find out the total number of frames */
num_frames = 0;
cnt = 0;
while((header = buf_find_next_frame(fd, &bytes, -1, header_template))) {
mp3headerinfo(&info, header);
if(!header_template)
header_template = header;
/* See if this really is a VBR file */
if(last_bitrate && info.bitrate != last_bitrate)
{
is_vbr = true;
}
last_bitrate = info.bitrate;
buf_seek(fd, info.frame_size-4);
num_frames++;
if(progressfunc)
{
cnt += bytes + info.frame_size;
if(cnt > progress_chunk)
{
progress_cnt++;
progressfunc(progress_cnt);
cnt = 0;
}
}
}
DEBUGF("Total number of frames: %d\n", num_frames);
if(is_vbr)
return num_frames;
else
{
DEBUGF("Not a VBR file\n");
return 0;
}
}
static const char cooltext[] = "Rockbox - rocks your box";
/* buf needs to be the audio buffer with TOC generation enabled,
and at least MAX_XING_HEADER_SIZE bytes otherwise */
int create_xing_header(int fd, long startpos, long filesize,
unsigned char *buf, unsigned long num_frames,
unsigned long rec_time, unsigned long header_template,
void (*progressfunc)(int), bool generate_toc)
{
struct mp3info info;
unsigned char toc[100];
unsigned long header = 0;
unsigned long xing_header_template = header_template;
unsigned long filepos;
long pos, last_pos;
long j;
long bytes;
int i;
int index;
DEBUGF("create_xing_header()\n");
if(generate_toc)
{
lseek(fd, startpos, SEEK_SET);
buf_init();
/* Generate filepos table */
last_pos = 0;
filepos = 0;
header = 0;
for(i = 0;i < 100;i++) {
/* Calculate the absolute frame number for this seek point */
pos = i * num_frames / 100;
/* Advance from the last seek point to this one */
for(j = 0;j < pos - last_pos;j++)
{
header = buf_find_next_frame(fd, &bytes, -1, header_template);
filepos += bytes;
mp3headerinfo(&info, header);
buf_seek(fd, info.frame_size-4);
filepos += info.frame_size;
if(!header_template)
header_template = header;
}
/* Save a header for later use if header_template is empty.
We only save one header, and we want to save one in the
middle of the stream, just in case the first and the last
headers are corrupt. */
if(!xing_header_template && i == 1)
xing_header_template = header;
if(progressfunc)
{
progressfunc(50 + i/2);
}
/* Fill in the TOC entry */
/* each toc is a single byte indicating how many 256ths of the
* way through the file, is that percent of the way through the
* song. the easy method, filepos*256/filesize, chokes when
* the upper 8 bits of the file position are nonzero
* (i.e. files over 16mb in size).
*/
if (filepos > (ULONG_MAX/256))
{
/* instead of multiplying filepos by 256, we divide
* filesize by 256.
*/
toc[i] = filepos / (filesize >> 8);
}
else
{
toc[i] = filepos * 256 / filesize;
}
DEBUGF("Pos %d: %ld relpos: %ld filepos: %lx tocentry: %x\n",
i, pos, pos-last_pos, filepos, toc[i]);
last_pos = pos;
}
}
/* Use the template header and create a new one.
We ignore the Protection bit even if the rest of the stream is
protected. */
header = xing_header_template & ~(BITRATE_MASK|PROTECTION_MASK|PADDING_MASK);
header |= 8 << 12; /* This gives us plenty of space, 192..576 bytes */
if (!mp3headerinfo(&info, header))
return 0; /* invalid header */
if (num_frames == 0 && rec_time) {
/* estimate the number of frames based on the recording time */
if (rec_time <= ULONG_MAX / info.ft_den)
num_frames = rec_time * info.ft_den / info.ft_num;
else
num_frames = rec_time / info.ft_num * info.ft_den;
}
/* Clear the frame */
memset(buf, 0, MAX_XING_HEADER_SIZE);
/* Write the header to the buffer */
long2bytes(buf, header);
/* Calculate position of VBR header */
if (info.version == MPEG_VERSION1) {
if (info.channel_mode == 3) /* mono */
index = 21;
else
index = 36;
}
else {
if (info.channel_mode == 3) /* mono */
index = 13;
else
index = 21;
}
/* Create the Xing data */
memcpy(&buf[index], "Xing", 4);
long2bytes(&buf[index+4], (num_frames ? VBR_FRAMES_FLAG : 0)
| (filesize ? VBR_BYTES_FLAG : 0)
| (generate_toc ? VBR_TOC_FLAG : 0));
index += 8;
if(num_frames)
{
long2bytes(&buf[index], num_frames);
index += 4;
}
if(filesize)
{
long2bytes(&buf[index], filesize - startpos);
index += 4;
}
/* Copy the TOC */
memcpy(buf + index, toc, 100);
/* And some extra cool info */
memcpy(buf + index + 100, cooltext, sizeof(cooltext));
#ifdef DEBUG
for(i = 0;i < info.frame_size;i++)
{
if(i && !(i % 16))
DEBUGF("\n");
DEBUGF("%02x ", buf[i]);
}
#endif
return info.frame_size;
}
#endif

83
apps/mp3data.h Normal file
View file

@ -0,0 +1,83 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* 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.
*
****************************************************************************/
#ifndef _MP3DATA_H_
#define _MP3DATA_H_
#define MPEG_VERSION1 0
#define MPEG_VERSION2 1
#define MPEG_VERSION2_5 2
struct mp3info {
/* Standard MP3 frame header fields */
int version;
int layer;
bool protection;
int bitrate;
long frequency;
int padding;
int channel_mode;
int mode_extension;
int emphasis;
int frame_size; /* Frame size in bytes */
int frame_samples; /* Samples per frame */
int ft_num; /* Numerator of frametime in milliseconds */
int ft_den; /* Denominator of frametime in milliseconds */
bool is_vbr; /* True if the file is VBR */
bool has_toc; /* True if there is a VBR header in the file */
bool is_xing_vbr; /* True if the VBR header is of Xing type */
bool is_vbri_vbr; /* True if the VBR header is of VBRI type */
unsigned char toc[100];
unsigned long frame_count; /* Number of frames in the file (if VBR) */
unsigned long byte_count; /* File size in bytes */
unsigned long file_time; /* Length of the whole file in milliseconds */
unsigned long vbr_header_pos;
int enc_delay; /* Encoder delay, fetched from LAME header */
int enc_padding; /* Padded samples added to last frame. LAME header */
};
/* Xing header information */
#define VBR_FRAMES_FLAG 0x01
#define VBR_BYTES_FLAG 0x02
#define VBR_TOC_FLAG 0x04
#define VBR_QUALITY_FLAG 0x08
#define MAX_XING_HEADER_SIZE 576
unsigned long find_next_frame(int fd, long *offset, long max_offset,
unsigned long last_header);
unsigned long mem_find_next_frame(int startpos, long *offset, long max_offset,
unsigned long last_header);
int get_mp3file_info(int fd, struct mp3info *info);
int count_mp3_frames(int fd, int startpos, int filesize,
void (*progressfunc)(int));
int create_xing_header(int fd, long startpos, long filesize,
unsigned char *buf, unsigned long num_frames,
unsigned long rec_time, unsigned long header_template,
void (*progressfunc)(int), bool generate_toc);
extern unsigned long bytes2int(unsigned long b0,
unsigned long b1,
unsigned long b2,
unsigned long b3);
#endif

View file

@ -26,7 +26,7 @@
#include "debug.h"
#include "panic.h"
#include "id3.h"
#include "metadata.h"
#include "mpeg.h"
#include "audio.h"
#include "ata.h"

View file

@ -22,7 +22,7 @@
#define _MPEG_H_
#include <stdbool.h>
#include "id3.h"
#include "metadata.h"
#define MPEG_SWAP_CHUNKSIZE 0x2000
#define MPEG_HIGH_WATER 2 /* We leave 2 bytes empty because otherwise we

View file

@ -37,7 +37,7 @@
#include "kernel.h"
#include "keyboard.h"
#include "mp3data.h"
#include "id3.h"
#include "metadata.h"
#include "screens.h"
#include "tree.h"
#include "buffer.h"

View file

@ -25,7 +25,7 @@
#include <stdbool.h>
#include "file.h"
#include "kernel.h"
#include "id3.h"
#include "metadata.h"
#define PLAYLIST_ATTR_QUEUED 0x01
#define PLAYLIST_ATTR_INSERTED 0x02

View file

@ -52,7 +52,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#include "usb.h"
#include "font.h"
#include "lcd.h"
#include "id3.h"
#include "metadata.h"
#include "sound.h"
#include "mpeg.h"
#include "audio.h"

View file

@ -23,7 +23,7 @@
#include "sprintf.h"
#include "system.h"
#include "albumart.h"
#include "id3.h"
#include "metadata.h"
#include "gwps.h"
#include "buffering.h"
#include "dircache.h"

View file

@ -25,7 +25,7 @@
#ifdef HAVE_ALBUMART
#include <stdbool.h>
#include "id3.h"
#include "metadata.h"
#include "gwps.h"
/* Look for albumart bitmap in the same dir as the track and in its parent dir.

View file

@ -28,7 +28,7 @@
#include "settings.h"
#include "id3.h"
#include "metadata.h"
#include "icons.h"
const unsigned char bitmap_icons_5x8[][5] =

View file

@ -24,6 +24,7 @@
#ifndef PLUGIN
#include <lcd.h>
#include "metadata.h"
#ifdef HAVE_LCD_BITMAP
@ -88,7 +89,7 @@ enum Glyphs_4x8 {
extern const unsigned char bitmap_glyphs_4x8[Glyph_4x8Last][4];
#define BM_MPA_L3_M_WIDTH 6
#ifdef ID3_H
/* This enum is redundant but sort of in keeping with the style */
enum rec_format_18x8 {
Format_18x8_AIFF = REC_FORMAT_AIFF,
@ -98,7 +99,7 @@ enum rec_format_18x8 {
Format_18x8Last = REC_NUM_FORMATS
};
extern const unsigned char bitmap_formats_18x8[Format_18x8Last][18];
#endif /* ID3_H */
#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
extern const unsigned char bitmap_icons_5x8[Icon5x8Last][5];

View file

@ -30,7 +30,7 @@
#include "general.h"
#include "audio.h"
#include "sound.h"
#include "id3.h"
#include "metadata.h"
#ifdef HAVE_SPDIF_IN
#include "spdif.h"
#endif

View file

@ -27,7 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <system.h>
#include "id3.h"
#include "metadata.h"
#include "debug.h"
#include "replaygain.h"

View file

@ -22,7 +22,7 @@
#ifndef _REPLAYGAIN_H
#define _REPLAYGAIN_H
#include "id3.h"
#include "metadata.h"
long get_replaygain_int(long int_gain);
long parse_replaygain(const char* key, const char* value,

View file

@ -46,7 +46,7 @@
#include "action.h"
#include "talk.h"
#include "misc.h"
#include "id3.h"
#include "metadata.h"
#include "screens.h"
#include "debug.h"
#include "led.h"

View file

@ -27,7 +27,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
#include "sprintf.h"
#include "playback.h"
#include "logf.h"
#include "id3.h"
#include "metadata.h"
#include "kernel.h"
#include "audio.h"
#include "buffer.h"

View file

@ -69,7 +69,7 @@
#include "string.h"
#include "usb.h"
#include "metadata.h"
#include "id3.h"
#include "metadata.h"
#include "tagcache.h"
#include "buffer.h"
#include "crc32.h"

View file

@ -22,7 +22,7 @@
#ifndef _TAGCACHE_H
#define _TAGCACHE_H
#include "id3.h"
#include "metadata.h"
/**
Note: When adding new tags, make sure to update index_entry_ec in

View file

@ -36,7 +36,7 @@
#include "audio.h"
#include "lang.h"
#include "talk.h"
#include "id3.h"
#include "metadata.h"
#include "logf.h"
#include "bitswap.h"
#include "structec.h"