📄 id3.c
字号:
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <unistd.h>
#include "id3.h"
#include "player_lib.h"
#define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \
((long)(b1 & 0x7F) << (2*7)) | \
((long)(b2 & 0x7F) << (1*7)) | \
((long)(b3 & 0x7F) << (0*7)))
#define BYTES2INT(b0,b1,b2,b3) (((long)(b0 & 0xFF) << (3*8)) | \
((long)(b1 & 0xFF) << (2*8)) | \
((long)(b2 & 0xFF) << (1*8)) | \
((long)(b3 & 0xFF) << (0*8)))
#ifndef MIN
#define MIN(a, b) (((a)<(b))?(a):(b))
#endif
/* parse numeric value from string */
static int parsetracknum( struct id3info* entry, char* tag, int bufferpos )
{
entry->tracknum = atoi( tag );
return bufferpos;
}
/* parse numeric value from string */
static int parseyearnum( struct id3info* entry, char* tag, int bufferpos )
{
entry->year = atoi( tag );
return bufferpos;
}
/* parse numeric genre from string, version 2.2 and 2.3 */
static int parsegenre( struct id3info* entry, char* tag, int bufferpos )
{
if(entry->id3version >= ID3_VER_2_4) {
/* In version 2.4 and up, there are no parentheses, and the genre frame
is a list of strings, either numbers or text. */
/* Is it a number? */
if(isdigit(tag[0])) {
entry->genre = atoi( tag );
entry->genre_string = 0;
return tag - entry->id3v2buf;
} else {
entry->genre_string = tag;
entry->genre = 0xff;
return bufferpos;
}
} else {
if( tag[0] == '(' && tag[1] != '(' ) {
entry->genre = atoi( tag + 1 );
entry->genre_string = 0;
entry->genre_string = id3_get_genre( entry );
return tag - entry->id3v2buf;
}
else {
entry->genre_string = tag;
entry->genre = 0xff;
return bufferpos;
}
}
}
/* Structure for ID3 Tag extraction information */
struct tag_resolver {
const char* tag;
int tag_length;
size_t offset;
int (*ppFunc)(struct id3info*, char* tag, int bufferpos);
};
static const struct tag_resolver taglist[] = {
{ "TPE1", 4, offsetof(struct id3info, artist), NULL },
{ "TP1", 3, offsetof(struct id3info, artist), NULL },
{ "TIT2", 4, offsetof(struct id3info, title), NULL },
{ "TT2", 3, offsetof(struct id3info, title), NULL },
{ "TALB", 4, offsetof(struct id3info, album), NULL },
{ "TAL", 3, offsetof(struct id3info, album), NULL },
{ "TRK", 3, offsetof(struct id3info, track_string), &parsetracknum },
{ "TRCK", 4, offsetof(struct id3info, track_string), &parsetracknum },
{ "TYER", 4, offsetof(struct id3info, year_string), &parseyearnum },
{ "TYE", 3, offsetof(struct id3info, year_string), &parseyearnum },
{ "TCOM", 4, offsetof(struct id3info, composer), NULL },
{ "TCON", 4, offsetof(struct id3info, genre_string), &parsegenre },
{ "TCO", 3, offsetof(struct id3info, genre_string), &parsegenre },
};
#define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0])))
static bool global_ff_found;
static int getid3v2len(int fd)
{
char buf[6];
int offset;
/* Make sure file has a ID3 tag */
if((-1 == ide_lseek(fd, 0, SEEK_SET)) ||
(ide_read(fd, buf, 6) != 6) ||
(strncmp(buf, "ID3", strlen("ID3")) != 0))
offset = 0;
/* Now check what the ID3v2 size field says */
else
if(ide_read(fd, buf, 4) != 4)
offset = 0;
else
offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
DEBUGF("ID3V2 Length: 0x%x\n", offset);
return offset;
}
static int unsynchronize(char* tag, int len, bool *ff_found)
{
int i;
unsigned char c;
unsigned char *rp, *wp;
wp = rp = tag;
rp = (unsigned char *)tag;
for(i = 0;i < len;i++) {
/* Read the next byte and write it back, but don't increment the
write pointer */
c = *rp++;
*wp = c;
if(*ff_found) {
/* Increment the write pointer if it isn't an unsynch pattern */
if(c != 0)
wp++;
*ff_found = false;
} else {
if(c == 0xff)
*ff_found = true;
wp++;
}
}
return (long)wp - (long)tag;
}
static int unsynchronize_frame(char* tag, int len)
{
bool ff_found = false;
return unsynchronize(tag, len, &ff_found);
}
static int read_unsynched(int fd, void *buf, int len)
{
int i;
int rc;
int remaining = len;
char *wp;
char *rp;
wp = buf;
while(remaining) {
rp = wp;
rc = read(fd, rp, remaining);
if(rc < 0)
return rc;
i = unsynchronize(wp, remaining, &global_ff_found);
remaining -= i;
wp += i;
}
return len;
}
static int unicode_munge(char* string, int *len) {
long tmp;
bool le = false;
int i;
char *str = string;
char *outstr = string;
bool bom = false;
int outlen;
if(str[0] > 0x03) {
/* Plain old string */
return 0;
}
/* Type 0x00 is ordinary ISO 8859-1 */
if(str[0] == 0x00) {
int i = --(*len);
/* We must move the string to the left */
while (i--) {
string[0] = string[1];
string++;
}
return 0;
}
/* Unicode with or without BOM */
if(str[0] == 0x01 || str[0] == 0x02) {
(*len)--;
str++;
i = 0;
/* Handle frames with more than one string (needed for TXXX frames).
*/
do {
tmp = BYTES2INT(0, 0, str[0], str[1]);
/* Now check if there is a BOM (zero-width non-breaking space, 0xfeff)
and if it is in little or big endian format */
if(tmp == 0xfffe) { /* Little endian? */
bom = true;
le = true;
str += 2;
(*len)-=2;
}
if(tmp == 0xfeff) { /* Big endian? */
bom = true;
str += 2;
(*len)-=2;
}
/* If there is no BOM (which is a specification violation),
let's try to guess it. If one of the bytes is 0x00, it is
probably the most significant one. */
if(!bom) {
if(str[1] == 0)
le = true;
}
outlen = *len / 2;
do {
if(le) {
if(str[1])
outstr[i++] = '.';
else
outstr[i++] = str[0];
} else {
if(str[0])
outstr[i++] = '.';
else
outstr[i++] = str[1];
}
str += 2;
} while((str[0] || str[1]) && (i < outlen));
str += 2;
outstr[i++] = 0; /* Terminate the string */
} while(i < outlen);
*len = i - 1;
return 0;
}
/* If we come here, the string was of an unsupported type */
*len = 1;
outstr[0] = 0;
return -1;
}
static int skip_unsynched(int fd, int len)
{
int rc;
int remaining = len;
int rlen;
char buf[32];
while(remaining) {
rlen = MIN(sizeof(buf), (unsigned int)remaining);
rc = read(fd, buf, rlen);
if(rc < 0)
return rc;
remaining -= unsynchronize(buf, rlen, &global_ff_found);
}
return len;
}
static const char* const genres[] = {
"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
"Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
"Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
"Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
"Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
"Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock",
"Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
"Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
"Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
"Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
"Native American", "Cabaret", "New Wave", "Psychadelic", "Rave",
"Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
"Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock"
};
char* id3_get_genre(const struct id3info* id3)
{
if( id3->genre_string )
return id3->genre_string ;
if (id3->genre < sizeof(genres)/sizeof(char*))
return (char*)genres[id3->genre];
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -