Build librbcodec with DSP and metadata.

All associated files are moved to /lib/rbcodec.

Change-Id: I572ddd2b8a996aae1e98c081d06b1ed356dce222
This commit is contained in:
Sean Bartell 2011-06-24 01:25:21 -04:00 committed by Nils Wallménius
parent 24bd9d5393
commit b5716df4cb
80 changed files with 97 additions and 112 deletions

103
lib/rbcodec/metadata/a52.c Normal file
View file

@ -0,0 +1,103 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include "metadata.h"
#include "logf.h"
#include "metadata_parsers.h"
static const unsigned short a52_bitrates[] =
{
32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
192, 224, 256, 320, 384, 448, 512, 576, 640
};
/* Only store frame sizes for 44.1KHz - others are simply multiples
of the bitrate */
static const unsigned short a52_441framesizes[] =
{
69 * 2, 70 * 2, 87 * 2, 88 * 2, 104 * 2, 105 * 2, 121 * 2,
122 * 2, 139 * 2, 140 * 2, 174 * 2, 175 * 2, 208 * 2, 209 * 2,
243 * 2, 244 * 2, 278 * 2, 279 * 2, 348 * 2, 349 * 2, 417 * 2,
418 * 2, 487 * 2, 488 * 2, 557 * 2, 558 * 2, 696 * 2, 697 * 2,
835 * 2, 836 * 2, 975 * 2, 976 * 2, 1114 * 2, 1115 * 2, 1253 * 2,
1254 * 2, 1393 * 2, 1394 * 2
};
bool get_a52_metadata(int fd, struct mp3entry *id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
unsigned long totalsamples;
int i;
if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 5) < 5))
{
return false;
}
if ((buf[0] != 0x0b) || (buf[1] != 0x77))
{
logf("not an A52/AC3 file\n");
return false;
}
i = buf[4] & 0x3e;
if (i > 36)
{
logf("A52: Invalid frmsizecod: %d\n",i);
return false;
}
id3->bitrate = a52_bitrates[i >> 1];
id3->vbr = false;
id3->filesize = filesize(fd);
switch (buf[4] & 0xc0)
{
case 0x00:
id3->frequency = 48000;
id3->bytesperframe=id3->bitrate * 2 * 2;
break;
case 0x40:
id3->frequency = 44100;
id3->bytesperframe = a52_441framesizes[i];
break;
case 0x80:
id3->frequency = 32000;
id3->bytesperframe = id3->bitrate * 3 * 2;
break;
default:
logf("A52: Invalid samplerate code: 0x%02x\n", buf[4] & 0xc0);
return false;
break;
}
/* One A52 frame contains 6 blocks, each containing 256 samples */
totalsamples = id3->filesize / id3->bytesperframe * 6 * 256;
id3->length = totalsamples / id3->frequency * 1000;
return true;
}

124
lib/rbcodec/metadata/adx.c Normal file
View file

@ -0,0 +1,124 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "debug.h"
bool get_adx_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char * buf = (unsigned char *)id3->path;
int chanstart, channels;
int looping = 0, start_adr = 0, end_adr = 0;
/* try to get the basic header */
if ((lseek(fd, 0, SEEK_SET) < 0)
|| (read(fd, buf, 0x38) < 0x38))
{
DEBUGF("lseek or read failed\n");
return false;
}
/* ADX starts with 0x80 */
if (buf[0] != 0x80) {
DEBUGF("get_adx_metadata: wrong first byte %c\n",buf[0]);
return false;
}
/* check for a reasonable offset */
chanstart = ((buf[2] << 8) | buf[3]) + 4;
if (chanstart > 4096) {
DEBUGF("get_adx_metadata: bad chanstart %i\n", chanstart);
return false;
}
/* check for a workable number of channels */
channels = buf[7];
if (channels != 1 && channels != 2) {
DEBUGF("get_adx_metadata: bad channel count %i\n",channels);
return false;
}
id3->frequency = get_long_be(&buf[8]);
/* 32 samples per 18 bytes */
id3->bitrate = id3->frequency * channels * 18 * 8 / 32 / 1000;
id3->length = get_long_be(&buf[12]) / id3->frequency * 1000;
id3->vbr = false;
id3->filesize = filesize(fd);
/* get loop info */
if (!memcmp(buf+0x10,"\x01\xF4\x03",3)) {
/* Soul Calibur 2 style (type 03) */
DEBUGF("get_adx_metadata: type 03 found\n");
/* check if header is too small for loop data */
if (chanstart-6 < 0x2c) looping=0;
else {
looping = get_long_be(&buf[0x18]);
end_adr = get_long_be(&buf[0x28]);
start_adr = get_long_be(&buf[0x1c])/32*channels*18+chanstart;
}
} else if (!memcmp(buf+0x10,"\x01\xF4\x04",3)) {
/* Standard (type 04) */
DEBUGF("get_adx_metadata: type 04 found\n");
/* check if header is too small for loop data */
if (chanstart-6 < 0x38) looping=0;
else {
looping = get_long_be(&buf[0x24]);
end_adr = get_long_be(&buf[0x34]);
start_adr = get_long_be(&buf[0x28])/32*channels*18+chanstart;
}
} else {
DEBUGF("get_adx_metadata: error, couldn't determine ADX type\n");
return false;
}
/* is file using encryption */
if (buf[0x13]==0x08) {
DEBUGF("get_adx_metadata: error, encrypted ADX not supported\n");
return false;
}
if (looping) {
/* 2 loops, 10 second fade */
id3->length = (start_adr-chanstart + 2*(end_adr-start_adr))
*8 / id3->bitrate + 10000;
}
/* try to get the channel header */
if ((lseek(fd, chanstart-6, SEEK_SET) < 0)
|| (read(fd, buf, 6) < 6))
{
return false;
}
/* check channel header */
if (memcmp(buf, "(c)CRI", 6) != 0) return false;
return true;
}

108
lib/rbcodec/metadata/aiff.c Normal file
View file

@ -0,0 +1,108 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "debug.h"
/* compressionType: AIFC QuickTime IMA ADPCM */
#define AIFC_FORMAT_QT_IMA_ADPCM "ima4"
bool get_aiff_metadata(int fd, struct mp3entry* id3)
{
unsigned char buf[512];
unsigned long numChannels = 0;
unsigned long numSampleFrames = 0;
unsigned long numbytes = 0;
bool is_aifc = false;
if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, &buf[0], 12) < 12) ||
(memcmp(&buf[0], "FORM", 4) != 0) || (memcmp(&buf[8], "AIF", 3) != 0) ||
(!(is_aifc = (buf[11] == 'C')) && buf[11] != 'F'))
{
return false;
}
while (read(fd, &buf[0], 8) == 8)
{
size_t size = get_long_be(&buf[4]); /* chunkSize */
if (memcmp(&buf[0], "SSND", 4) == 0)
{
numbytes = size - 8;
break; /* assume COMM was already read */
}
/* odd chunk sizes must be padded */
size += size & 1;
if (size > sizeof(buf))
{
DEBUGF("AIFF \"%4.4s\" chunk too large (%zd > %zd)",
(char*) &buf[0], size, sizeof(buf));
}
if (memcmp(&buf[0], "COMM", 4) == 0)
{
if (size > sizeof(buf) || read(fd, &buf[0], size) != (ssize_t)size)
return false;
numChannels = ((buf[0]<<8)|buf[1]);
numSampleFrames = get_long_be(&buf[2]);
/* sampleRate */
id3->frequency = get_long_be(&buf[10]);
id3->frequency >>= (16+14-buf[9]);
/* save format infos */
id3->bitrate = ((buf[6]<<8)|buf[7]) * numChannels * id3->frequency;
id3->bitrate /= 1000;
if (!is_aifc || memcmp(&buf[18], AIFC_FORMAT_QT_IMA_ADPCM, 4) != 0)
id3->length = ((int64_t) numSampleFrames * 1000) / id3->frequency;
else
{
/* QuickTime IMA ADPCM is 1block = 64 data for each channel */
id3->length = ((int64_t) numSampleFrames * 64000LL) / id3->frequency;
}
id3->vbr = false; /* AIFF files are CBR */
id3->filesize = filesize(fd);
}
else
{
/* skip chunk */
if (lseek(fd, size, SEEK_CUR) < 0)
return false;
}
}
return numbytes && numChannels;
}

182
lib/rbcodec/metadata/ape.c Normal file
View file

@ -0,0 +1,182 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "structec.h"
#define APETAG_HEADER_LENGTH 32
#define APETAG_HEADER_FORMAT "8llll8"
#define APETAG_ITEM_HEADER_FORMAT "ll"
#define APETAG_ITEM_TYPE_MASK 3
#ifdef HAVE_ALBUMART
/* The AA header consists of the pseudo filename "Album Cover (Front).ext"
* whereas ".ext" is the file extension. For now ".jpg" and ".png" are
* supported by this APE metadata parser. Therefore the length is 22. */
#define APETAG_AA_HEADER_LENGTH 22
#endif
struct apetag_header
{
char id[8];
long version;
long length;
long item_count;
long flags;
char reserved[8];
};
struct apetag_item_header
{
long length;
long flags;
};
/* Read the items in an APEV2 tag. Only looks for a tag at the end of a
* file. Returns true if a tag was found and fully read, false otherwise.
*/
bool read_ape_tags(int fd, struct mp3entry* id3)
{
struct apetag_header header;
if ((lseek(fd, -APETAG_HEADER_LENGTH, SEEK_END) < 0)
|| (ecread(fd, &header, 1, APETAG_HEADER_FORMAT, IS_BIG_ENDIAN)
!= APETAG_HEADER_LENGTH)
|| (memcmp(header.id, "APETAGEX", sizeof(header.id))))
{
return false;
}
if ((header.version == 2000) && (header.item_count > 0)
&& (header.length > APETAG_HEADER_LENGTH))
{
char *buf = id3->id3v2buf;
unsigned int buf_remaining = sizeof(id3->id3v2buf)
+ sizeof(id3->id3v1buf);
unsigned int tag_remaining = header.length - APETAG_HEADER_LENGTH;
int i;
if (lseek(fd, -header.length, SEEK_END) < 0)
{
return false;
}
for (i = 0; i < header.item_count; i++)
{
struct apetag_item_header item;
char name[TAG_NAME_LENGTH];
char value[TAG_VALUE_LENGTH];
long r;
if (tag_remaining < sizeof(item))
{
break;
}
if (ecread(fd, &item, 1, APETAG_ITEM_HEADER_FORMAT, IS_BIG_ENDIAN)
< (long) sizeof(item))
{
return false;
}
tag_remaining -= sizeof(item);
r = read_string(fd, name, sizeof(name), 0, tag_remaining);
if (r == -1)
{
return false;
}
tag_remaining -= r + item.length;
if ((item.flags & APETAG_ITEM_TYPE_MASK) == 0)
{
long len;
if (read_string(fd, value, sizeof(value), -1, item.length)
!= item.length)
{
return false;
}
len = parse_tag(name, value, id3, buf, buf_remaining,
TAGTYPE_APE);
buf += len;
buf_remaining -= len;
}
else
{
#ifdef HAVE_ALBUMART
if (strcasecmp(name, "cover art (front)") == 0)
{
/* Allow to read at least APETAG_AA_HEADER_LENGTH bytes. */
r = read_string(fd, name, sizeof(name), 0, APETAG_AA_HEADER_LENGTH);
if (r == -1)
{
return false;
}
/* Gather the album art format from the pseudo file name's ending. */
strcpy(name, name + strlen(name) - 4);
id3->albumart.type = AA_TYPE_UNKNOWN;
if (strcasecmp(name, ".jpg") == 0)
{
id3->albumart.type = AA_TYPE_JPG;
}
else if (strcasecmp(name, ".png") == 0)
{
id3->albumart.type = AA_TYPE_PNG;
}
/* Set the album art size and position. */
if (id3->albumart.type != AA_TYPE_UNKNOWN)
{
id3->albumart.pos = lseek(fd, 0, SEEK_CUR);
id3->albumart.size = item.length - r;
id3->has_embedded_albumart = true;
}
/* Seek back to this APE items begin. */
if (lseek(fd, -r, SEEK_CUR) < 0)
{
return false;
}
}
#endif
/* Seek to the next APE item. */
if (lseek(fd, item.length, SEEK_CUR) < 0)
{
return false;
}
}
}
}
return true;
}

254
lib/rbcodec/metadata/asap.c Normal file
View file

@ -0,0 +1,254 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2008 Dominik Wenger
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#include "debug.h"
#define MAX_SONGS 32
static bool parse_dec(int *retval, const char *p, int minval, int maxval)
{
int r = 0;
do {
char c = *p;
if (c >= '0' && c <= '9')
r = 10 * r + c - '0';
else
return false;
if (r > maxval)
return false;
} while (*++p != '\0');
if (r < minval)
return false;
*retval = r;
return true;
}
static bool parse_text(char *retval, const char *p)
{
int i;
if (*p != '"')
return false;
p++;
if (p[0] == '<' && p[1] == '?' && p[2] == '>' && p[3] == '"')
return true;
i = 0;
while (*p != '"') {
if (i >= 127)
return false;
if (*p == '\0')
return false;
retval[i++] = *p++;
}
retval[i] = '\0';
return true;
}
static int ASAP_ParseDuration(const char *s)
{
int r;
if (*s < '0' || *s > '9')
return -1;
r = *s++ - '0';
if (*s >= '0' && *s <= '9')
r = 10 * r + *s++ - '0';
if (*s == ':') {
s++;
if (*s < '0' || *s > '5')
return -1;
r = 60 * r + (*s++ - '0') * 10;
if (*s < '0' || *s > '9')
return -1;
r += *s++ - '0';
}
r *= 1000;
if (*s != '.')
return r;
s++;
if (*s < '0' || *s > '9')
return r;
r += 100 * (*s++ - '0');
if (*s < '0' || *s > '9')
return r;
r += 10 * (*s++ - '0');
if (*s < '0' || *s > '9')
return r;
r += *s - '0';
return r;
}
static bool read_asap_string(char* source, char** buf, char** buffer_end, char** dest)
{
if(parse_text(*buf,source) == false)
return false;
/* set dest pointer */
*dest = *buf;
/* move buf ptr */
*buf += strlen(*buf)+1;
/* check size */
if(*buf >= *buffer_end)
{
DEBUGF("Buffer full\n");
return false;
}
return true;
}
static bool parse_sap_header(int fd, struct mp3entry* id3, int file_len)
{
int module_index = 0;
int sap_signature = -1;
int duration_index = 0;
unsigned char cur_char = 0;
int i;
/* set defaults */
int numSongs = 1;
int defSong = 0;
int durations[MAX_SONGS];
for (i = 0; i < MAX_SONGS; i++)
durations[i] = -1;
/* use id3v2 buffer for our strings */
char* buffer = id3->id3v2buf;
char* buffer_end = id3->id3v2buf + ID3V2_BUF_SIZE;
/* parse file */
while (1)
{
char line[256];
char *p;
if (module_index + 8 >= file_len)
return false;
/* read a char */
read(fd,&cur_char,1);
/* end of header */
if (cur_char == 0xff)
break;
i = 0;
while (cur_char != 0x0d)
{
line[i++] = cur_char;
module_index++;
if (module_index >= file_len || (unsigned)i >= sizeof(line) - 1)
return false;
/* read a char */
read(fd,&cur_char,1);
}
if (++module_index >= file_len )
return false;
/* read a char */
read(fd,&cur_char,1);
if ( cur_char != 0x0a)
return false;
line[i] = '\0';
for (p = line; *p != '\0'; p++) {
if (*p == ' ') {
*p++ = '\0';
break;
}
}
/* parse tags */
if(strcmp(line, "SAP") == 0)
sap_signature = 1;
if (sap_signature == -1)
return false;
if (strcmp(line, "AUTHOR") == 0)
{
if(read_asap_string(p, &buffer, &buffer_end, &id3->artist) == false)
return false;
}
else if(strcmp(line, "NAME") == 0)
{
if(read_asap_string(p, &buffer, &buffer_end, &id3->title) == false)
return false;
}
else if(strcmp(line, "DATE") == 0)
{
if(read_asap_string(p, &buffer, &buffer_end, &id3->year_string) == false)
return false;
}
else if (strcmp(line, "SONGS") == 0)
{
if (parse_dec(&numSongs, p, 1, MAX_SONGS) == false )
return false;
}
else if (strcmp(line, "DEFSONG") == 0)
{
if (parse_dec(&defSong, p, 0, MAX_SONGS) == false)
return false;
}
else if (strcmp(line, "TIME") == 0)
{
int durationTemp = ASAP_ParseDuration(p);
if (durationTemp < 0 || duration_index >= MAX_SONGS)
return false;
durations[duration_index++] = durationTemp;
}
}
/* set length: */
int length = durations[defSong];
if (length < 0)
length = 180 * 1000;
id3->length = length;
lseek(fd, 0, SEEK_SET);
return true;
}
bool get_asap_metadata(int fd, struct mp3entry* id3)
{
int filelength = filesize(fd);
if(parse_sap_header(fd, id3, filelength) == false)
{
DEBUGF("parse sap header failed.\n");
return false;
}
id3->bitrate = 706;
id3->frequency = 44100;
id3->vbr = false;
id3->filesize = filelength;
id3->genre_string = id3_get_num_genre(36);
return true;
}

591
lib/rbcodec/metadata/asf.c Normal file
View file

@ -0,0 +1,591 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* $Id$
*
* Copyright (C) 2007 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "metadata.h"
#include "replaygain.h"
#include "debug.h"
#include "rbunicode.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "system.h"
#include <codecs/libasf/asf.h>
/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
struct guid_s {
uint32_t v1;
uint16_t v2;
uint16_t v3;
uint8_t v4[8];
};
typedef struct guid_s guid_t;
struct asf_object_s {
guid_t guid;
uint64_t size;
uint64_t datalen;
};
typedef struct asf_object_s asf_object_t;
static const guid_t asf_guid_null =
{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
/* top level object guids */
static const guid_t asf_guid_header =
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_data =
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_index =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
/* header level object guids */
static const guid_t asf_guid_file_properties =
{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_stream_properties =
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_content_description =
{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_extended_content_description =
{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
static const guid_t asf_guid_content_encryption =
{0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}};
static const guid_t asf_guid_extended_content_encryption =
{0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}};
/* stream type guids */
static const guid_t asf_guid_stream_type_audio =
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
{
if((guid1->v1 != guid2->v1) ||
(guid1->v2 != guid2->v2) ||
(guid1->v3 != guid2->v3) ||
(memcmp(guid1->v4, guid2->v4, 8))) {
return 0;
}
return 1;
}
/* Read the 16 byte GUID from a file */
static void asf_readGUID(int fd, guid_t* guid)
{
read_uint32le(fd, &guid->v1);
read_uint16le(fd, &guid->v2);
read_uint16le(fd, &guid->v3);
read(fd, guid->v4, 8);
}
static void asf_read_object_header(asf_object_t *obj, int fd)
{
asf_readGUID(fd, &obj->guid);
read_uint64le(fd, &obj->size);
obj->datalen = 0;
}
/* Parse an integer from the extended content object - we always
convert to an int, regardless of native format.
*/
static int asf_intdecode(int fd, int type, int length)
{
uint16_t tmp16;
uint32_t tmp32;
uint64_t tmp64;
if (type == 3) {
read_uint32le(fd, &tmp32);
lseek(fd,length - 4,SEEK_CUR);
return (int)tmp32;
} else if (type == 4) {
read_uint64le(fd, &tmp64);
lseek(fd,length - 8,SEEK_CUR);
return (int)tmp64;
} else if (type == 5) {
read_uint16le(fd, &tmp16);
lseek(fd,length - 2,SEEK_CUR);
return (int)tmp16;
}
return 0;
}
/* Decode a LE utf16 string from a disk buffer into a fixed-sized
utf8 buffer.
*/
static void asf_utf16LEdecode(int fd,
uint16_t utf16bytes,
unsigned char **utf8,
int* utf8bytes
)
{
unsigned long ucs;
int n;
unsigned char utf16buf[256];
unsigned char* utf16 = utf16buf;
unsigned char* newutf8;
n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16bytes -= n;
while (n > 0) {
/* Check for a surrogate pair */
if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) {
if (n < 4) {
/* Run out of utf16 bytes, read some more */
utf16buf[0] = utf16[0];
utf16buf[1] = utf16[1];
n = read(fd, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
n += 2;
}
if (n < 4) {
/* Truncated utf16 string, abort */
break;
}
ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18)
| utf16[2] | ((utf16[3] - 0xDC) << 8));
utf16 += 4;
n -= 4;
} else {
ucs = (utf16[0] | (utf16[1] << 8));
utf16 += 2;
n -= 2;
}
if (*utf8bytes > 6) {
newutf8 = utf8encode(ucs, *utf8);
*utf8bytes -= (newutf8 - *utf8);
*utf8 += (newutf8 - *utf8);
}
/* We have run out of utf16 bytes, read more if available */
if ((n == 0) && (utf16bytes > 0)) {
n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
}
}
*utf8[0] = 0;
--*utf8bytes;
if (utf16bytes > 0) {
/* Skip any remaining bytes */
lseek(fd, utf16bytes, SEEK_CUR);
}
return;
}
static int asf_parse_header(int fd, struct mp3entry* id3,
asf_waveformatex_t* wfx)
{
asf_object_t current;
asf_object_t header;
uint64_t datalen;
int i;
int fileprop = 0;
uint64_t play_duration;
uint16_t flags;
uint32_t subobjects;
uint8_t utf8buf[512];
int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
unsigned char* id3buf = (unsigned char*)id3->id3v2buf;
asf_read_object_header((asf_object_t *) &header, fd);
//DEBUGF("header.size=%d\n",(int)header.size);
if (header.size < 30) {
/* invalid size for header object */
return ASF_ERROR_OBJECT_SIZE;
}
read_uint32le(fd, &subobjects);
/* Two reserved bytes - do we need to read them? */
lseek(fd, 2, SEEK_CUR);
//DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects);
if (subobjects > 0) {
header.datalen = header.size - 30;
/* TODO: Check that we have datalen bytes left in the file */
datalen = header.datalen;
for (i=0; i<(int)subobjects; i++) {
//DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
if (datalen < 24) {
//DEBUGF("not enough data for reading object\n");
break;
}
asf_read_object_header(&current, fd);
if (current.size > datalen || current.size < 24) {
//DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
break;
}
if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
if (current.size < 104)
return ASF_ERROR_OBJECT_SIZE;
if (fileprop) {
/* multiple file properties objects not allowed */
return ASF_ERROR_INVALID_OBJECT;
}
fileprop = 1;
/* Get the number of logical packets - uint16_t at offset 31
* (Big endian byte order) */
lseek(fd, 31, SEEK_CUR);
read_uint16be(fd, &wfx->numpackets);
/* Now get the play duration - uint64_t at offset 40 */
lseek(fd, 7, SEEK_CUR);
read_uint64le(fd, &play_duration);
id3->length = play_duration / 10000;
//DEBUGF("****** length = %lums\n", id3->length);
/* Read the packet size - uint32_t at offset 68 */
lseek(fd, 20, SEEK_CUR);
read_uint32le(fd, &wfx->packet_size);
/* Skip bytes remaining in object */
lseek(fd, current.size - 24 - 72, SEEK_CUR);
} else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
guid_t guid;
uint32_t propdatalen;
if (current.size < 78)
return ASF_ERROR_OBJECT_SIZE;
#if 0
asf_byteio_getGUID(&guid, current->data);
datalen = asf_byteio_getDWLE(current->data + 40);
flags = asf_byteio_getWLE(current->data + 48);
#endif
asf_readGUID(fd, &guid);
lseek(fd, 24, SEEK_CUR);
read_uint32le(fd, &propdatalen);
lseek(fd, 4, SEEK_CUR);
read_uint16le(fd, &flags);
if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
//DEBUGF("Found stream properties for non audio stream, skipping\n");
lseek(fd,current.size - 24 - 50,SEEK_CUR);
} else if (wfx->audiostream == -1) {
lseek(fd, 4, SEEK_CUR);
//DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
if (propdatalen < 18) {
return ASF_ERROR_INVALID_LENGTH;
}
#if 0
if (asf_byteio_getWLE(data + 16) > datalen - 16) {
return ASF_ERROR_INVALID_LENGTH;
}
#endif
read_uint16le(fd, &wfx->codec_id);
read_uint16le(fd, &wfx->channels);
read_uint32le(fd, &wfx->rate);
read_uint32le(fd, &wfx->bitrate);
wfx->bitrate *= 8;
read_uint16le(fd, &wfx->blockalign);
read_uint16le(fd, &wfx->bitspersample);
read_uint16le(fd, &wfx->datalen);
/* Round bitrate to the nearest kbit */
id3->bitrate = (wfx->bitrate + 500) / 1000;
id3->frequency = wfx->rate;
if (wfx->codec_id == ASF_CODEC_ID_WMAV1) {
read(fd, wfx->data, 4);
lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR);
wfx->audiostream = flags&0x7f;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) {
read(fd, wfx->data, 6);
lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR);
wfx->audiostream = flags&0x7f;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) {
/* wma pro decoder needs the extra-data */
read(fd, wfx->data, wfx->datalen);
lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
wfx->audiostream = flags&0x7f;
/* Correct codectype to redirect playback to the proper .codec */
id3->codectype = AFMT_WMAPRO;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) {
read(fd, wfx->data, wfx->datalen);
lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
wfx->audiostream = flags&0x7f;
id3->codectype = AFMT_WMAVOICE;
} else {
DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n");
lseek(fd,current.size - 24 - 72,SEEK_CUR);
}
}
} else if (asf_guid_match(&current.guid, &asf_guid_content_description)) {
/* Object contains five 16-bit string lengths, followed by the five strings:
title, artist, copyright, description, rating
*/
uint16_t strlength[5];
int i;
//DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
/* Read the 5 string lengths - number of bytes included trailing zero */
for (i=0; i<5; i++) {
read_uint16le(fd, &strlength[i]);
//DEBUGF("strlength = %u\n",strlength[i]);
}
if (strlength[0] > 0) { /* 0 - Title */
id3->title = id3buf;
asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining);
}
if (strlength[1] > 0) { /* 1 - Artist */
id3->artist = id3buf;
asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining);
}
lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */
if (strlength[3] > 0) { /* 3 - description */
id3->comment = id3buf;
asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining);
}
lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */
} else if (asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
uint16_t count;
int i;
int bytesleft = current.size - 24;
//DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
read_uint16le(fd, &count);
bytesleft -= 2;
//DEBUGF("extended metadata count = %u\n",count);
for (i=0; i < count; i++) {
uint16_t length, type;
unsigned char* utf8 = utf8buf;
int utf8length = 512;
read_uint16le(fd, &length);
asf_utf16LEdecode(fd, length, &utf8, &utf8length);
bytesleft -= 2 + length;
read_uint16le(fd, &type);
read_uint16le(fd, &length);
if (!strcmp("WM/TrackNumber",utf8buf)) {
if (type == 0) {
id3->track_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
id3->tracknum = atoi(id3->track_string);
} else if ((type >=2) && (type <= 5)) {
id3->tracknum = asf_intdecode(fd, type, length);
} else {
lseek(fd, length, SEEK_CUR);
}
} else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) {
id3->genre_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) {
id3->album = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) {
id3->albumartist = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) {
id3->composer = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if (!strcmp("WM/Year", utf8buf)) {
if (type == 0) {
id3->year_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
id3->year = atoi(id3->year_string);
} else if ((type >=2) && (type <= 5)) {
id3->year = asf_intdecode(fd, type, length);
} else {
lseek(fd, length, SEEK_CUR);
}
} else if (!strncmp("replaygain_", utf8buf, 11)) {
char *value = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
parse_replaygain(utf8buf, value, id3);
} else if (!strcmp("MusicBrainz/Track Id", utf8buf)) {
id3->mb_track_id = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
#ifdef HAVE_ALBUMART
} else if (!strcmp("WM/Picture", utf8buf)) {
uint32_t datalength, strlength;
/* Expected is either "01 00 xx xx 03 yy yy yy yy" or
* "03 yy yy yy yy". xx is the size of the WM/Picture
* container in bytes. yy equals the raw data length of
* the embedded image. */
lseek(fd, -4, SEEK_CUR);
read(fd, &type, 1);
if (type == 1) {
lseek(fd, 3, SEEK_CUR);
read(fd, &type, 1);
/* In case the parsing will fail in the next step we
* might at least be able to skip the whole section. */
datalength = length - 1;
}
if (type == 3) {
/* Read the raw data length of the embedded image. */
read_uint32le(fd, &datalength);
/* Reset utf8 buffer */
utf8 = utf8buf;
utf8length = 512;
/* Gather the album art format, this string has a
* double zero-termination. */
asf_utf16LEdecode(fd, 32, &utf8, &utf8length);
strlength = (strlen(utf8buf) + 2) * 2;
lseek(fd, strlength-32, SEEK_CUR);
if (!strcmp("image/jpeg", utf8buf)) {
id3->albumart.type = AA_TYPE_JPG;
} else if (!strcmp("image/png", utf8buf)) {
id3->albumart.type = AA_TYPE_PNG;
} else {
id3->albumart.type = AA_TYPE_UNKNOWN;
}
/* Set the album art size and position. */
if (id3->albumart.type != AA_TYPE_UNKNOWN) {
id3->albumart.pos = lseek(fd, 0, SEEK_CUR);
id3->albumart.size = datalength;
id3->has_embedded_albumart = true;
}
}
lseek(fd, datalength, SEEK_CUR);
#endif
} else {
lseek(fd, length, SEEK_CUR);
}
bytesleft -= 4 + length;
}
lseek(fd, bytesleft, SEEK_CUR);
} else if (asf_guid_match(&current.guid, &asf_guid_content_encryption)
|| asf_guid_match(&current.guid, &asf_guid_extended_content_encryption)) {
//DEBUGF("File is encrypted\n");
return ASF_ERROR_ENCRYPTED;
} else {
//DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
lseek(fd,current.size - 24,SEEK_CUR);
}
//DEBUGF("Parsed object - size = %d\n",(int)current.size);
datalen -= current.size;
}
if (i != (int)subobjects || datalen != 0) {
//DEBUGF("header data doesn't match given subobject count\n");
return ASF_ERROR_INVALID_VALUE;
}
//DEBUGF("%d subobjects read successfully\n", i);
}
#if 0
tmp = asf_parse_header_validate(file, &header);
if (tmp < 0) {
/* header read ok but doesn't validate correctly */
return tmp;
}
#endif
//DEBUGF("header validated correctly\n");
return 0;
}
bool get_asf_metadata(int fd, struct mp3entry* id3)
{
int res;
asf_object_t obj;
asf_waveformatex_t wfx;
wfx.audiostream = -1;
res = asf_parse_header(fd, id3, &wfx);
if (res < 0) {
DEBUGF("ASF: parsing error - %d\n",res);
return false;
}
if (wfx.audiostream == -1) {
DEBUGF("ASF: No WMA streams found\n");
return false;
}
asf_read_object_header(&obj, fd);
if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
DEBUGF("ASF: No data object found\n");
return false;
}
/* Store the current file position - no need to parse the header
again in the codec. The +26 skips the rest of the data object
header.
*/
id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26;
id3->filesize = filesize(fd);
/* We copy the wfx struct to the MP3 TOC field in the id3 struct so
the codec doesn't need to parse the header object again */
memcpy(id3->toc, &wfx, sizeof(wfx));
return true;
}

105
lib/rbcodec/metadata/au.c Normal file
View file

@ -0,0 +1,105 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Yoshihisa Uchida
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
static const unsigned char bitspersamples[9] = {
0, /* encoding */
8, /* 1: G.711 MULAW */
8, /* 2: Linear PCM 8bit */
16, /* 3: Linear PCM 16bit */
24, /* 4: Linear PCM 24bit */
32, /* 5: Linear PCM 32bit */
32, /* 6: IEEE float 32bit */
64, /* 7: IEEE float 64bit */
/* encoding 8 - 26 unsupported. */
8, /* 27: G.711 ALAW */
};
static inline unsigned char get_au_bitspersample(unsigned int encoding)
{
if (encoding < 8)
return bitspersamples[encoding];
else if (encoding == 27)
return bitspersamples[8];
return 0;
}
bool get_au_metadata(int fd, struct mp3entry* id3)
{
/* temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
unsigned long numbytes = 0;
int offset;
id3->vbr = false; /* All Sun audio files are CBR */
id3->filesize = filesize(fd);
id3->length = 0;
lseek(fd, 0, SEEK_SET);
if ((read(fd, buf, 24) < 24) || (memcmp(buf, ".snd", 4) != 0))
{
/*
* no header
*
* frequency: 8000 Hz
* bits per sample: 8 bit
* channel: mono
*/
numbytes = id3->filesize;
id3->frequency = 8000;
id3->bitrate = 8;
}
else
{
/* parse header */
/* data offset */
offset = get_long_be(buf + 4);
if (offset < 24)
{
DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset);
return false;
}
/* data size */
numbytes = get_long_be(buf + 8);
if (numbytes == (uint32_t)0xffffffff)
numbytes = id3->filesize - offset;
id3->frequency = get_long_be(buf + 16);
id3->bitrate = get_au_bitspersample(get_long_be(buf + 12)) * get_long_be(buf + 20)
* id3->frequency / 1000;
}
/* Calculate track length [ms] */
if (id3->bitrate)
id3->length = (numbytes << 3) / id3->bitrate;
return true;
}

148
lib/rbcodec/metadata/ay.c Normal file
View file

@ -0,0 +1,148 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
/* Taken from blargg's Game_Music_Emu library */
typedef unsigned char byte;
/* AY file header */
enum { header_size = 0x14 };
struct header_t
{
byte tag[8];
byte vers;
byte player;
byte unused[2];
byte author[2];
byte comment[2];
byte max_track;
byte first_track;
byte track_info[2];
};
struct file_t {
struct header_t const* header;
byte const* tracks;
byte const* end; /* end of file data */
};
static int get_be16( const void *a )
{
return get_short_be( (void*) a );
}
/* Given pointer to 2-byte offset of data, returns pointer to data, or NULL if
* offset is 0 or there is less than min_size bytes of data available. */
static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )
{
int offset = (int16_t) get_be16( ptr );
int pos = ptr - (byte const*) file->header;
int size = file->end - (byte const*) file->header;
int limit = size - min_size;
if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )
return NULL;
return ptr + offset;
}
static const char *parse_header( byte const in [], int size, struct file_t* out )
{
if ( size < header_size )
return "wrong file type";
out->header = (struct header_t const*) in;
out->end = in + size;
struct header_t const* h = (struct header_t const*) in;
if ( memcmp( h->tag, "ZXAYEMUL", 8 ) )
return "wrong file type";
out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );
if ( !out->tracks )
return "missing track data";
return 0;
}
static void copy_ay_fields( struct file_t const* file, struct mp3entry* id3, int track )
{
int track_count = file->header->max_track + 1;
/* calculate track length based on number of subtracks */
if (track_count > 1) {
id3->length = file->header->max_track * 1000;
} else {
byte const* track_info = get_data( file, file->tracks + track * 4 + 2, 6 );
if (track_info)
id3->length = get_be16( track_info + 4 ) * (1000 / 50); /* frames to msec */
else id3->length = 120 * 1000;
}
if ( id3->length <= 0 )
id3->length = 120 * 1000; /* 2 minutes */
/* If meta info was found in the m3u skip next step */
if (id3->title && id3->title[0]) return;
/* If file has more than one track will
use file name as title */
char * tmp;
if (track_count <= 1) {
tmp = (char *) get_data( file, file->tracks + track * 4, 1 );
if ( tmp ) id3->title = tmp;
}
/* Author */
tmp = (char *) get_data( file, file->header->author, 1 );
if (tmp) id3->artist = tmp;
/* Comment */
tmp = (char *) get_data( file, file->header->comment, 1 );
if (tmp) id3->comment = tmp;
}
static bool parse_ay_header(int fd, struct mp3entry *id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->id3v2buf;
struct file_t file;
int read_bytes;
lseek(fd, 0, SEEK_SET);
if ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) < header_size)
return false;
buf [ID3V2_BUF_SIZE] = '\0';
if ( parse_header( buf, read_bytes, &file ) )
return false;
copy_ay_fields( &file, id3, 0 );
return true;
}
bool get_ay_metadata(int fd, struct mp3entry* id3)
{
char ay_type[8];
if ((lseek(fd, 0, SEEK_SET) < 0) ||
read(fd, ay_type, 8) < 8)
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
id3->bitrate = 706;
id3->frequency = 44100;
/* Make sure this is a ZX Ay file */
if (memcmp( ay_type, "ZXAYEMUL", 8 ) != 0)
return false;
return parse_ay_header(fd, id3);
}

127
lib/rbcodec/metadata/flac.c Normal file
View file

@ -0,0 +1,127 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
bool get_flac_metadata(int fd, struct mp3entry* id3)
{
/* A simple parser to read vital metadata from a FLAC file - length,
* frequency, bitrate etc. This code should either be moved to a
* seperate file, or discarded in favour of the libFLAC code.
* The FLAC stream specification can be found at
* http://flac.sourceforge.net/format.html#stream
*/
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
bool last_metadata = false;
bool rc = false;
if (!skip_id3v2(fd, id3) || (read(fd, buf, 4) < 4))
{
return rc;
}
if (memcmp(buf, "fLaC", 4) != 0)
{
return rc;
}
while (!last_metadata)
{
unsigned long i;
int type;
if (read(fd, buf, 4) < 0)
{
return rc;
}
last_metadata = buf[0] & 0x80;
type = buf[0] & 0x7f;
/* The length of the block */
i = (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (type == 0) /* 0 is the STREAMINFO block */
{
unsigned long totalsamples;
if (i >= sizeof(id3->path) || read(fd, buf, i) < 0)
{
return rc;
}
id3->vbr = true; /* All FLAC files are VBR */
id3->filesize = filesize(fd);
id3->frequency = (buf[10] << 12) | (buf[11] << 4)
| ((buf[12] & 0xf0) >> 4);
rc = true; /* Got vital metadata */
/* totalsamples is a 36-bit field, but we assume <= 32 bits are used */
totalsamples = get_long_be(&buf[14]);
if(totalsamples > 0)
{
/* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
id3->length = ((int64_t) totalsamples * 1000) / id3->frequency;
id3->bitrate = (id3->filesize * 8) / id3->length;
}
else if (totalsamples == 0)
{
id3->length = 0;
id3->bitrate = 0;
}
else
{
logf("flac length invalid!");
return false;
}
}
else if (type == 4) /* 4 is the VORBIS_COMMENT block */
{
/* The next i bytes of the file contain the VORBIS COMMENTS. */
if (read_vorbis_tags(fd, id3, i) == 0)
{
return rc;
}
}
else if (!last_metadata)
{
/* Skip to next metadata block */
if (lseek(fd, i, SEEK_CUR) < 0)
{
return rc;
}
}
}
return true;
}

View file

@ -0,0 +1,65 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
static bool parse_gbs_header(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, 112) < 112)
return false;
/* Calculate track length with number of subtracks */
id3->length = buf[4] * 1000;
/* If meta info was found in the m3u skip next step */
if (id3->title && id3->title[0]) return true;
char *p = id3->id3v2buf;
/* Some metadata entries have 32 bytes length */
/* Game */
memcpy(p, &buf[16], 32); *(p + 33) = '\0';
id3->title = p;
p += strlen(p)+1;
/* Artist */
memcpy(p, &buf[48], 32); *(p + 33) = '\0';
id3->artist = p;
p += strlen(p)+1;
/* Copyright */
memcpy(p, &buf[80], 32); *(p + 33) = '\0';
id3->album = p;
return true;
}
bool get_gbs_metadata(int fd, struct mp3entry* id3)
{
char gbs_type[3];
if ((lseek(fd, 0, SEEK_SET) < 0) ||
(read(fd, gbs_type, 3) < 3))
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
/* we only render 16 bits, 44.1KHz, Stereo */
id3->bitrate = 706;
id3->frequency = 44100;
/* Check for GBS magic */
if (memcmp( gbs_type, "GBS", 3 ) != 0)
return false;
return parse_gbs_header(fd, id3);
}

View file

@ -0,0 +1,39 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#include "plugin.h"
bool get_hes_metadata(int fd, struct mp3entry* id3)
{
/* Use the id3v2 buffer part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->id3v2buf;
int read_bytes;
if ((lseek(fd, 0, SEEK_SET) < 0)
|| ((read_bytes = read(fd, buf, 4)) < 4))
return false;
/* Verify this is a HES file */
if (memcmp(buf,"HESM",4) != 0)
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
/* we only render 16 bits, 44.1KHz, Stereo */
id3->bitrate = 706;
id3->frequency = 44100;
/* Set default track count (length)*/
id3->length = 255 * 1000;
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
static bool parse_kss_header(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, 0x20) < 0x20)
return false;
/* calculate track length with number of tracks */
id3->length = 0;
if (buf[14] == 0x10) {
id3->length = (get_short_le((void *)(buf + 26)) + 1) * 1000;
}
if (id3->length <= 0)
id3->length = 255 * 1000; /* 255 tracks */
return true;
}
bool get_kss_metadata(int fd, struct mp3entry* id3)
{
uint32_t kss_type;
if ((lseek(fd, 0, SEEK_SET) < 0) ||
read_uint32be(fd, &kss_type) != (int)sizeof(kss_type))
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
/* we only render 16 bits, 44.1KHz, Stereo */
id3->bitrate = 706;
id3->frequency = 44100;
/* Make sure this is an SGC file */
if (kss_type != FOURCC('K','S','C','C') && kss_type != FOURCC('K','S','S','X'))
return false;
return parse_kss_header(fd, id3);
}

View file

@ -0,0 +1,641 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "string-extra.h"
#include "debug.h"
#include "logf.h"
#include "settings.h"
#include "cuesheet.h"
#include "metadata.h"
#include "metadata_parsers.h"
#if CONFIG_CODEC == SWCODEC
/* For trailing tag stripping and base audio data types */
#include "buffering.h"
#include "metadata/metadata_common.h"
static bool get_shn_metadata(int fd, struct mp3entry *id3)
{
/* TODO: read the id3v2 header if it exists */
id3->vbr = true;
id3->filesize = filesize(fd);
return skip_id3v2(fd, id3);
}
static bool get_other_asap_metadata(int fd, struct mp3entry *id3)
{
id3->bitrate = 706;
id3->frequency = 44100;
id3->vbr = false;
id3->filesize = filesize(fd);
id3->genre_string = id3_get_num_genre(36);
return true;
}
#endif /* CONFIG_CODEC == SWCODEC */
bool write_metadata_log = false;
const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
{
/* Unknown file format */
[0 ... AFMT_NUM_CODECS-1] =
AFMT_ENTRY("???", NULL, NULL, NULL, "\0" ),
/* MPEG Audio layer 2 */
[AFMT_MPA_L2] =
AFMT_ENTRY("MP2", "mpa", NULL, get_mp3_metadata, "mpa\0mp2\0"),
#if CONFIG_CODEC != SWCODEC
/* MPEG Audio layer 3 on HWCODEC: .talk clips, no encoder */
[AFMT_MPA_L3] =
AFMT_ENTRY("MP3", "mpa", NULL, get_mp3_metadata, "mp3\0talk\0"),
#else /* CONFIG_CODEC == SWCODEC */
/* MPEG Audio layer 3 on SWCODEC */
[AFMT_MPA_L3] =
AFMT_ENTRY("MP3", "mpa", "mp3_enc", get_mp3_metadata, "mp3\0"),
/* MPEG Audio layer 1 */
[AFMT_MPA_L1] =
AFMT_ENTRY("MP1", "mpa", NULL, get_mp3_metadata, "mp1\0"),
/* Audio Interchange File Format */
[AFMT_AIFF] =
AFMT_ENTRY("AIFF", "aiff", "aiff_enc", get_aiff_metadata, "aiff\0aif\0"),
/* Uncompressed PCM in a WAV file OR ATRAC3 stream in WAV file (.at3) */
[AFMT_PCM_WAV] =
AFMT_ENTRY("WAV", "wav", "wav_enc", get_wave_metadata, "wav\0at3\0"),
/* Ogg Vorbis */
[AFMT_OGG_VORBIS] =
AFMT_ENTRY("Ogg", "vorbis", NULL, get_ogg_metadata, "ogg\0oga\0"),
/* FLAC */
[AFMT_FLAC] =
AFMT_ENTRY("FLAC", "flac", NULL, get_flac_metadata, "flac\0"),
/* Musepack SV7 */
[AFMT_MPC_SV7] =
AFMT_ENTRY("MPCv7", "mpc", NULL, get_musepack_metadata,"mpc\0"),
/* A/52 (aka AC3) audio */
[AFMT_A52] =
AFMT_ENTRY("AC3", "a52", NULL, get_a52_metadata, "a52\0ac3\0"),
/* WavPack */
[AFMT_WAVPACK] =
AFMT_ENTRY("WV","wavpack","wavpack_enc",get_wavpack_metadata,"wv\0"),
/* Apple Lossless Audio Codec */
[AFMT_MP4_ALAC] =
AFMT_ENTRY("ALAC", "alac", NULL, get_mp4_metadata, "m4a\0m4b\0"),
/* Advanced Audio Coding in M4A container */
[AFMT_MP4_AAC] =
AFMT_ENTRY("AAC", "aac", NULL, get_mp4_metadata, "mp4\0"),
/* Shorten */
[AFMT_SHN] =
AFMT_ENTRY("SHN","shorten", NULL, get_shn_metadata, "shn\0"),
/* SID File Format */
[AFMT_SID] =
AFMT_ENTRY("SID", "sid", NULL, get_sid_metadata, "sid\0"),
/* ADX File Format */
[AFMT_ADX] =
AFMT_ENTRY("ADX", "adx", NULL, get_adx_metadata, "adx\0"),
/* NESM (NES Sound Format) */
[AFMT_NSF] =
AFMT_ENTRY("NSF", "nsf", NULL, get_nsf_metadata, "nsf\0nsfe\0"),
/* Speex File Format */
[AFMT_SPEEX] =
AFMT_ENTRY("Speex", "speex",NULL, get_ogg_metadata, "spx\0"),
/* SPC700 Save State */
[AFMT_SPC] =
AFMT_ENTRY("SPC", "spc", NULL, get_spc_metadata, "spc\0"),
/* APE (Monkey's Audio) */
[AFMT_APE] =
AFMT_ENTRY("APE", "ape", NULL, get_monkeys_metadata,"ape\0mac\0"),
/* WMA (WMAV1/V2 in ASF) */
[AFMT_WMA] =
AFMT_ENTRY("WMA", "wma", NULL, get_asf_metadata,"wma\0wmv\0asf\0"),
/* WMA Professional in ASF */
[AFMT_WMAPRO] =
AFMT_ENTRY("WMAPro","wmapro",NULL, NULL, "wma\0wmv\0asf\0"),
/* Amiga MOD File */
[AFMT_MOD] =
AFMT_ENTRY("MOD", "mod", NULL, get_mod_metadata, "mod\0"),
/* Atari SAP File */
[AFMT_SAP] =
AFMT_ENTRY("SAP", "asap", NULL, get_asap_metadata, "sap\0"),
/* Cook in RM/RA */
[AFMT_RM_COOK] =
AFMT_ENTRY("Cook", "cook", NULL, get_rm_metadata,"rm\0ra\0rmvb\0"),
/* AAC in RM/RA */
[AFMT_RM_AAC] =
AFMT_ENTRY("RAAC", "raac", NULL, NULL, "rm\0ra\0rmvb\0"),
/* AC3 in RM/RA */
[AFMT_RM_AC3] =
AFMT_ENTRY("AC3", "a52_rm", NULL, NULL, "rm\0ra\0rmvb\0"),
/* ATRAC3 in RM/RA */
[AFMT_RM_ATRAC3] =
AFMT_ENTRY("ATRAC3","atrac3_rm",NULL, NULL, "rm\0ra\0rmvb\0"),
/* Atari CMC File */
[AFMT_CMC] =
AFMT_ENTRY("CMC", "asap", NULL, get_other_asap_metadata,"cmc\0"),
/* Atari CM3 File */
[AFMT_CM3] =
AFMT_ENTRY("CM3", "asap", NULL, get_other_asap_metadata,"cm3\0"),
/* Atari CMR File */
[AFMT_CMR] =
AFMT_ENTRY("CMR", "asap", NULL, get_other_asap_metadata,"cmr\0"),
/* Atari CMS File */
[AFMT_CMS] =
AFMT_ENTRY("CMS", "asap", NULL, get_other_asap_metadata,"cms\0"),
/* Atari DMC File */
[AFMT_DMC] =
AFMT_ENTRY("DMC", "asap", NULL, get_other_asap_metadata,"dmc\0"),
/* Atari DLT File */
[AFMT_DLT] =
AFMT_ENTRY("DLT", "asap", NULL, get_other_asap_metadata,"dlt\0"),
/* Atari MPT File */
[AFMT_MPT] =
AFMT_ENTRY("MPT", "asap", NULL, get_other_asap_metadata,"mpt\0"),
/* Atari MPD File */
[AFMT_MPD] =
AFMT_ENTRY("MPD", "asap", NULL, get_other_asap_metadata,"mpd\0"),
/* Atari RMT File */
[AFMT_RMT] =
AFMT_ENTRY("RMT", "asap", NULL, get_other_asap_metadata,"rmt\0"),
/* Atari TMC File */
[AFMT_TMC] =
AFMT_ENTRY("TMC", "asap", NULL, get_other_asap_metadata,"tmc\0"),
/* Atari TM8 File */
[AFMT_TM8] =
AFMT_ENTRY("TM8", "asap", NULL, get_other_asap_metadata,"tm8\0"),
/* Atari TM2 File */
[AFMT_TM2] =
AFMT_ENTRY("TM2", "asap", NULL, get_other_asap_metadata,"tm2\0"),
/* Atrac3 in Sony OMA Container */
[AFMT_OMA_ATRAC3] =
AFMT_ENTRY("ATRAC3","atrac3_oma",NULL, get_oma_metadata, "oma\0aa3\0"),
/* SMAF (Synthetic music Mobile Application Format) */
[AFMT_SMAF] =
AFMT_ENTRY("SMAF", "smaf", NULL, get_smaf_metadata, "mmf\0"),
/* Sun Audio file */
[AFMT_AU] =
AFMT_ENTRY("AU", "au", NULL, get_au_metadata, "au\0snd\0"),
/* VOX (Dialogic telephony file formats) */
[AFMT_VOX] =
AFMT_ENTRY("VOX", "vox", NULL, get_vox_metadata, "vox\0"),
/* Wave64 */
[AFMT_WAVE64] =
AFMT_ENTRY("WAVE64","wav64",NULL, get_wave64_metadata,"w64\0"),
/* True Audio */
[AFMT_TTA] =
AFMT_ENTRY("TTA", "tta", NULL, get_tta_metadata, "tta\0"),
/* WMA Voice in ASF */
[AFMT_WMAVOICE] =
AFMT_ENTRY("WMAVoice","wmavoice",NULL, NULL, "wma\0wmv\0"),
/* Musepack SV8 */
[AFMT_MPC_SV8] =
AFMT_ENTRY("MPCv8", "mpc", NULL, get_musepack_metadata,"mpc\0"),
/* Advanced Audio Coding High Efficiency in M4A container */
[AFMT_MP4_AAC_HE] =
AFMT_ENTRY("AAC-HE","aac", NULL, get_mp4_metadata, "mp4\0"),
/* AY (ZX Spectrum, Amstrad CPC Sound Format) */
[AFMT_AY] =
AFMT_ENTRY("AY", "ay", NULL, get_ay_metadata, "ay\0"),
/* GBS (Game Boy Sound Format) */
[AFMT_GBS] =
AFMT_ENTRY("GBS", "gbs", NULL, get_gbs_metadata, "gbs\0"),
/* HES (Hudson Entertainment System Sound Format) */
[AFMT_HES] =
AFMT_ENTRY("HES", "hes", NULL, get_hes_metadata, "hes\0"),
/* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */
[AFMT_SGC] =
AFMT_ENTRY("SGC", "sgc", NULL, get_sgc_metadata, "sgc\0"),
/* VGM (Video Game Music Format) */
[AFMT_VGM] =
AFMT_ENTRY("VGM", "vgm", NULL, get_vgm_metadata, "vgm\0vgz\0"),
/* KSS (MSX computer KSS Music File) */
[AFMT_KSS] =
AFMT_ENTRY("KSS", "kss", NULL, get_kss_metadata, "kss\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,
};
#if 0 /* Currently unused, left for reference and future use */
/* 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
#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
#if CONFIG_CODEC == SWCODEC
/* Get the canonical AFMT type */
int get_audio_base_codec_type(int type)
{
int base_type = type;
switch (type) {
case AFMT_MPA_L1:
case AFMT_MPA_L2:
case AFMT_MPA_L3:
base_type = AFMT_MPA_L3;
break;
case AFMT_MPC_SV7:
case AFMT_MPC_SV8:
base_type = AFMT_MPC_SV7;
break;
case AFMT_MP4_AAC:
case AFMT_MP4_AAC_HE:
base_type = AFMT_MP4_AAC;
break;
case AFMT_SAP:
case AFMT_CMC:
case AFMT_CM3:
case AFMT_CMR:
case AFMT_CMS:
case AFMT_DMC:
case AFMT_DLT:
case AFMT_MPT:
case AFMT_MPD:
case AFMT_RMT:
case AFMT_TMC:
case AFMT_TM8:
case AFMT_TM2:
base_type = AFMT_SAP;
break;
default:
break;
}
return base_type;
}
/* Get the basic audio type */
enum data_type get_audio_base_data_type(int afmt)
{
if ((unsigned)afmt >= AFMT_NUM_CODECS)
return TYPE_UNKNOWN;
switch (get_audio_base_codec_type(afmt))
{
case AFMT_NSF:
case AFMT_SPC:
case AFMT_SID:
case AFMT_MOD:
case AFMT_SAP:
case AFMT_AY:
case AFMT_GBS:
case AFMT_HES:
case AFMT_SGC:
case AFMT_VGM:
case AFMT_KSS:
/* Type must be allocated and loaded in its entirety onto
the buffer */
return TYPE_ATOMIC_AUDIO;
default:
/* Assume type may be loaded and discarded incrementally */
return TYPE_PACKET_AUDIO;
case AFMT_UNKNOWN:
/* Have no idea at all */
return TYPE_UNKNOWN;
}
}
/* Is the format allowed to buffer starting at some offset other than 0
or first frame only for resume purposes? */
bool format_buffers_with_offset(int afmt)
{
switch (afmt)
{
case AFMT_MPA_L1:
case AFMT_MPA_L2:
case AFMT_MPA_L3:
case AFMT_WAVPACK:
/* Format may be loaded at the first needed frame */
return true;
default:
/* Format must be loaded from the beginning of the file
(does not imply 'atomic', while 'atomic' implies 'no offset') */
return false;
}
}
#endif /* CONFIG_CODEC == SWCODEC */
/* Simple file type probing by looking at the filename extension. */
unsigned int probe_file_format(const char *filename)
{
char *suffix;
unsigned int i;
suffix = strrchr(filename, '.');
if (suffix == NULL)
{
return AFMT_UNKNOWN;
}
/* skip '.' */
suffix++;
for (i = 1; i < AFMT_NUM_CODECS; i++)
{
/* search extension list for type */
const char *ext = audio_formats[i].ext_list;
do
{
if (strcasecmp(suffix, ext) == 0)
{
return i;
}
ext += strlen(ext) + 1;
}
while (*ext != '\0');
}
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.
*/
bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
{
const struct afmt_entry *entry;
int logfd = 0;
DEBUGF("Read metadata for %s\n", trackname);
if (write_metadata_log)
{
logfd = open("/metadata.log", O_WRONLY | O_APPEND | O_CREAT, 0666);
if (logfd >= 0)
{
write(logfd, trackname, strlen(trackname));
write(logfd, "\n", 1);
close(logfd);
}
}
/* Clear the mp3entry to avoid having bogus pointers appear */
wipe_mp3entry(id3);
/* Take our best guess at the codec type based on file extension */
id3->codectype = probe_file_format(trackname);
/* default values for embedded cuesheets */
id3->has_embedded_cuesheet = false;
id3->embedded_cuesheet.pos = 0;
entry = &audio_formats[id3->codectype];
/* Load codec specific track tag information and confirm the codec type. */
if (!entry->parse_func)
{
DEBUGF("nothing to parse for %s (format %s)", trackname, entry->label);
return false;
}
if (!entry->parse_func(fd, id3))
{
DEBUGF("parsing %s failed (format: %s)", trackname, entry->label);
return false;
}
lseek(fd, 0, SEEK_SET);
strlcpy(id3->path, trackname, sizeof(id3->path));
/* We have successfully read the metadata from the file */
return true;
}
#ifndef __PCTOOL__
#if CONFIG_CODEC == SWCODEC
void strip_tags(int handle_id)
{
static const unsigned char tag[] = "TAG";
static const unsigned char apetag[] = "APETAGEX";
size_t len, version;
void *tail;
if (bufgettail(handle_id, 128, &tail) != 128)
return;
if (memcmp(tail, tag, 3) == 0)
{
/* Skip id3v1 tag */
logf("Cutting off ID3v1 tag");
bufcuttail(handle_id, 128);
}
/* Get a new tail, as the old one may have been cut */
if (bufgettail(handle_id, 32, &tail) != 32)
return;
/* Check for APE tag (look for the APE tag footer) */
if (memcmp(tail, apetag, 8) != 0)
return;
/* Read the version and length from the footer */
version = get_long_le(&((unsigned char *)tail)[8]);
len = get_long_le(&((unsigned char *)tail)[12]);
if (version == 2000)
len += 32; /* APEv2 has a 32 byte header */
/* Skip APE tag */
logf("Cutting off APE tag (%ldB)", len);
bufcuttail(handle_id, len);
}
#endif /* CONFIG_CODEC == SWCODEC */
#endif /* ! __PCTOOL__ */
#define MOVE_ENTRY(x) if (x) x += offset;
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);
MOVE_ENTRY(entry->title)
MOVE_ENTRY(entry->artist)
MOVE_ENTRY(entry->album)
if (entry->genre_string > (char*)orig &&
entry->genre_string < (char*)orig + sizeof(struct mp3entry))
/* Don't adjust that if it points to an entry of the "genres" array */
entry->genre_string += offset;
MOVE_ENTRY(entry->track_string)
MOVE_ENTRY(entry->disc_string)
MOVE_ENTRY(entry->year_string)
MOVE_ENTRY(entry->composer)
MOVE_ENTRY(entry->comment)
MOVE_ENTRY(entry->albumartist)
MOVE_ENTRY(entry->grouping)
MOVE_ENTRY(entry->mb_track_id)
}
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig)
{
memcpy(dest, orig, sizeof(struct mp3entry));
adjust_mp3entry(dest, dest, orig);
}
/* A shortcut to simplify the common task of clearing the struct */
void wipe_mp3entry(struct mp3entry *id3)
{
memset(id3, 0, sizeof (struct mp3entry));
}
#if CONFIG_CODEC == SWCODEC
/* Glean what is possible from the filename alone - does not parse metadata */
void fill_metadata_from_path(struct mp3entry *id3, const char *trackname)
{
char *p;
/* Clear the mp3entry to avoid having bogus pointers appear */
wipe_mp3entry(id3);
/* Find the filename portion of the path */
p = strrchr(trackname, '/');
strlcpy(id3->id3v2buf, p ? ++p : id3->path, ID3V2_BUF_SIZE);
/* Get the format from the extension and trim it off */
p = strrchr(id3->id3v2buf, '.');
if (p)
{
/* Might be wrong for container formats - should we bother? */
id3->codectype = probe_file_format(p);
if (id3->codectype != AFMT_UNKNOWN)
*p = '\0';
}
/* Set the filename as the title */
id3->title = id3->id3v2buf;
/* Copy the path info */
strlcpy(id3->path, trackname, sizeof (id3->path));
}
#endif /* CONFIG_CODEC == SWCODEC */
#ifndef __PCTOOL__
#ifdef HAVE_TAGCACHE
#if CONFIG_CODEC == SWCODEC
enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE };
bool autoresumable(struct mp3entry *id3)
{
char *endp, *path;
size_t len;
bool is_resumable;
if (id3->autoresumable) /* result cached? */
return id3->autoresumable == AUTORESUMABLE_TRUE;
is_resumable = false;
if (id3->path)
{
for (path = global_settings.autoresume_paths;
*path; /* search terms left? */
path++)
{
if (*path == ':') /* Skip empty search patterns */
continue;
/* FIXME: As soon as strcspn or strchrnul are made available in
the core, the following can be made more efficient. */
endp = strchr(path, ':');
if (endp)
len = endp - path;
else
len = strlen(path);
/* Note: At this point, len is always > 0 */
if (strncasecmp(id3->path, path, len) == 0)
{
/* Full directory-name matches only. Trailing '/' in
search path OK. */
if (id3->path[len] == '/' || id3->path[len - 1] == '/')
{
is_resumable = true;
break;
}
}
path += len - 1;
}
}
/* cache result */
id3->autoresumable =
is_resumable ? AUTORESUMABLE_TRUE : AUTORESUMABLE_FALSE;
logf("autoresumable: %s is%s resumable",
id3->path, is_resumable ? "" : " not");
return is_resumable;
}
#endif /* SWCODEC */
#endif /* HAVE_TAGCACHE */
#endif /* __PCTOOL__ */

View file

@ -0,0 +1,353 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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 _METADATA_H
#define _METADATA_H
#include <stdbool.h>
#include "config.h"
#include "file.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_SV7, /* Musepack SV7 */
AFMT_A52, /* A/52 (aka AC3) audio */
AFMT_WAVPACK, /* WavPack */
AFMT_MP4_ALAC, /* Apple Lossless Audio Codec */
AFMT_MP4_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_WMAPRO, /* WMA Professional in ASF */
AFMT_MOD, /* Amiga MOD File Format */
AFMT_SAP, /* Atari 8Bit SAP Format */
AFMT_RM_COOK, /* Cook in RM/RA */
AFMT_RM_AAC, /* AAC in RM/RA */
AFMT_RM_AC3, /* AC3 in RM/RA */
AFMT_RM_ATRAC3, /* ATRAC3 in RM/RA */
AFMT_CMC, /* Atari 8bit cmc format */
AFMT_CM3, /* Atari 8bit cm3 format */
AFMT_CMR, /* Atari 8bit cmr format */
AFMT_CMS, /* Atari 8bit cms format */
AFMT_DMC, /* Atari 8bit dmc format */
AFMT_DLT, /* Atari 8bit dlt format */
AFMT_MPT, /* Atari 8bit mpt format */
AFMT_MPD, /* Atari 8bit mpd format */
AFMT_RMT, /* Atari 8bit rmt format */
AFMT_TMC, /* Atari 8bit tmc format */
AFMT_TM8, /* Atari 8bit tm8 format */
AFMT_TM2, /* Atari 8bit tm2 format */
AFMT_OMA_ATRAC3, /* Atrac3 in Sony OMA container */
AFMT_SMAF, /* SMAF */
AFMT_AU, /* Sun Audio file */
AFMT_VOX, /* VOX */
AFMT_WAVE64, /* Wave64 */
AFMT_TTA, /* True Audio */
AFMT_WMAVOICE, /* WMA Voice in ASF */
AFMT_MPC_SV8, /* Musepack SV8 */
AFMT_MP4_AAC_HE, /* Advanced Audio Coding (AAC-HE) in M4A container */
AFMT_AY, /* AY (ZX Spectrum, Amstrad CPC Sound Format) */
AFMT_GBS, /* GBS (Game Boy Sound Format) */
AFMT_HES, /* HES (Hudson Entertainment System Sound Format) */
AFMT_SGC, /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */
AFMT_VGM, /* VGM (Video Game Music Format) */
AFMT_KSS, /* KSS (MSX computer KSS Music File) */
#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
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
#define CODEC_EXTENSION "so"
#define CODEC_PREFIX "lib"
#else
#define CODEC_EXTENSION "codec"
#define CODEC_PREFIX ""
#endif
#ifdef HAVE_RECORDING
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_* */
/* unused: extern const int afmt_rec_format[AFMT_NUM_CODECS]; */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \
{ label, root_fname, enc_root_fname, func, ext_list }
#else /* !HAVE_RECORDING */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \
{ label, root_fname, func, ext_list }
#endif /* HAVE_RECORDING */
#else /* !SWCODEC */
#define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \
{ label, func, ext_list }
#endif /* CONFIG_CODEC == SWCODEC */
/** Database of audio formats **/
/* record describing the audio format */
struct mp3entry;
struct afmt_entry
{
const char *label; /* format label */
#if CONFIG_CODEC == SWCODEC
const char *codec_root_fn; /* root codec filename (sans _enc and .codec) */
#ifdef HAVE_RECORDING
const char *codec_enc_root_fn; /* filename of encoder codec */
#endif
#endif
bool (*parse_func)(int fd, struct mp3entry *id3); /* return true on success */
const char *ext_list; /* 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];
#if MEMORYSIZE > 2
#define ID3V2_BUF_SIZE 900
#define ID3V2_MAX_ITEM_SIZE 240
#else
#define ID3V2_BUF_SIZE 300
#define ID3V2_MAX_ITEM_SIZE 90
#endif
enum {
ID3_VER_1_0 = 1,
ID3_VER_1_1,
ID3_VER_2_2,
ID3_VER_2_3,
ID3_VER_2_4
};
#ifdef HAVE_ALBUMART
enum mp3_aa_type {
AA_TYPE_UNSYNC = -1,
AA_TYPE_UNKNOWN,
AA_TYPE_BMP,
AA_TYPE_PNG,
AA_TYPE_JPG,
};
struct mp3_albumart {
enum mp3_aa_type type;
int size;
off_t pos;
};
#endif
enum character_encoding {
CHAR_ENC_ISO_8859_1 = 1,
CHAR_ENC_UTF_8,
CHAR_ENC_UTF_16_LE,
CHAR_ENC_UTF_16_BE,
};
/* cache embedded cuesheet details */
struct embedded_cuesheet {
int size;
off_t pos;
enum character_encoding encoding;
};
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 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 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, used by mp4 parser as well. */
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 */
/* Added for ATRAC3 */
unsigned int channels; /* Number of channels in the stream */
unsigned int extradata_size; /* Size (in bytes) of the codec's extradata from the container */
/* Added for AAC HE SBR */
bool needs_upsampling_correction; /* flag used by aac codec */
/* 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 */
#ifdef HAVE_TAGCACHE
unsigned char autoresumable; /* caches result of autoresumable() */
/* runtime database fields */
long tagcache_idx; /* 0=invalid, otherwise idx+1 */
int rating;
int score;
long playcount;
long lastplayed;
long playtime;
#endif
/* replaygain support */
#if CONFIG_CODEC == SWCODEC
long track_level; /* holds the level in dB * (1<<FP_BITS) */
long album_level;
long track_gain; /* s19.12 signed fixed point. 0 for no gain. */
long album_gain;
long track_peak; /* s19.12 signed fixed point. 0 for no peak. */
long album_peak;
#endif
#ifdef HAVE_ALBUMART
bool has_embedded_albumart;
struct mp3_albumart albumart;
#endif
/* Cuesheet support */
bool has_embedded_cuesheet;
struct embedded_cuesheet embedded_cuesheet;
struct cuesheet *cuesheet;
/* 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);
void wipe_mp3entry(struct mp3entry *id3);
#if CONFIG_CODEC == SWCODEC
void fill_metadata_from_path(struct mp3entry *id3, const char *trackname);
int get_audio_base_codec_type(int type);
void strip_tags(int handle_id);
enum data_type get_audio_base_data_type(int afmt);
bool format_buffers_with_offset(int afmt);
#endif
#ifdef HAVE_TAGCACHE
bool autoresumable(struct mp3entry *id3);
#endif
#endif

View file

@ -0,0 +1,374 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include "string-extra.h"
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "replaygain.h"
/* Read a string from the file. Read up to size bytes, or, if eos != -1,
* until the eos character is found (eos is not stored in buf, unless it is
* nil). Writes up to buf_size chars to buf, always terminating with a nil.
* Returns number of chars read or -1 on read error.
*/
long read_string(int fd, char* buf, long buf_size, int eos, long size)
{
long read_bytes = 0;
char c;
while (size != 0)
{
if (read(fd, &c, 1) != 1)
{
read_bytes = -1;
break;
}
read_bytes++;
size--;
if ((eos != -1) && (eos == (unsigned char) c))
{
break;
}
if (buf_size > 1)
{
*buf++ = c;
buf_size--;
}
}
*buf = 0;
return read_bytes;
}
/* Read an unsigned 8-bit integer from a file. */
int read_uint8(int fd, uint8_t* buf)
{
size_t n;
n = read(fd, (char*) buf, 1);
return n;
}
#ifdef ROCKBOX_LITTLE_ENDIAN
/* Read an unsigned 16-bit integer from a big-endian file. */
int read_uint16be(int fd, uint16_t* buf)
{
size_t n;
n = read(fd, (char*) buf, 2);
*buf = betoh16(*buf);
return n;
}
/* Read an unsigned 32-bit integer from a big-endian file. */
int read_uint32be(int fd, uint32_t* buf)
{
size_t n;
n = read(fd, (char*) buf, 4);
*buf = betoh32(*buf);
return n;
}
/* Read an unsigned 64-bit integer from a big-endian file. */
int read_uint64be(int fd, uint64_t* buf)
{
size_t n;
uint8_t data[8];
int i;
n = read(fd, data, 8);
for (i=0, *buf=0; i<=7; i++) {
*buf <<= 8;
*buf |= data[i];
}
return n;
}
#else
/* Read unsigned integers from a little-endian file. */
int read_uint16le(int fd, uint16_t* buf)
{
size_t n;
n = read(fd, (char*) buf, 2);
*buf = letoh16(*buf);
return n;
}
int read_uint32le(int fd, uint32_t* buf)
{
size_t n;
n = read(fd, (char*) buf, 4);
*buf = letoh32(*buf);
return n;
}
int read_uint64le(int fd, uint64_t* buf)
{
size_t n;
uint8_t data[8];
int i;
n = read(fd, data, 8);
for (i=7, *buf=0; i>=0; i--) {
*buf <<= 8;
*buf |= data[i];
}
return n;
}
#endif
/* Read an unaligned 64-bit little endian unsigned integer from buffer. */
uint64_t get_uint64_le(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24) | ((uint64_t)p[4] << 32) |
((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56);
}
/* Read an unaligned 32-bit little endian long from buffer. */
uint32_t get_long_le(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
/* Read an unaligned 16-bit little endian short from buffer. */
uint16_t get_short_le(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return p[0] | (p[1] << 8);
}
/* Read an unaligned 32-bit big endian long from buffer. */
uint32_t get_long_be(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
/* Read an unaligned 16-bit little endian short from buffer. */
uint16_t get_short_be(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return (p[0] << 8) | p[1];
}
/* Read an unaligned 32-bit little endian long from buffer. */
int32_t get_slong(void* buf)
{
unsigned char* p = (unsigned char*) buf;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
uint32_t get_itunes_int32(char* value, int count)
{
static const char hexdigits[] = "0123456789ABCDEF";
const char* c;
int r = 0;
while (count-- > 0)
{
while (isspace(*value))
{
value++;
}
while (*value && !isspace(*value))
{
value++;
}
}
while (isspace(*value))
{
value++;
}
while (*value && ((c = strchr(hexdigits, toupper(*value))) != NULL))
{
r = (r << 4) | (c - hexdigits);
value++;
}
return r;
}
/* Skip an ID3v2 tag if it can be found. We assume the tag is located at the
* start of the file, which should be true in all cases where we need to skip it.
* Returns true if successfully skipped or not skipped, and false if
* something went wrong while skipping.
*/
bool skip_id3v2(int fd, struct mp3entry *id3)
{
char buf[4];
read(fd, buf, 4);
if (memcmp(buf, "ID3", 3) == 0)
{
/* We have found an ID3v2 tag at the start of the file - find its
length and then skip it. */
if ((id3->first_frame_offset = getid3v2len(fd)) == 0)
return false;
if ((lseek(fd, id3->first_frame_offset, SEEK_SET) < 0))
return false;
return true;
} else {
lseek(fd, 0, SEEK_SET);
id3->first_frame_offset = 0;
return true;
}
}
/* Parse the tag (the name-value pair) and fill id3 and buffer accordingly.
* String values to keep are written to buf. Returns number of bytes written
* to buf (including end nil).
*/
long parse_tag(const char* name, char* value, struct mp3entry* id3,
char* buf, long buf_remaining, enum tagtype type)
{
long len = 0;
char** p;
if ((((strcasecmp(name, "track") == 0) && (type == TAGTYPE_APE)))
|| ((strcasecmp(name, "tracknumber") == 0) && (type == TAGTYPE_VORBIS)))
{
id3->tracknum = atoi(value);
p = &(id3->track_string);
}
else if (strcasecmp(name, "discnumber") == 0 || strcasecmp(name, "disc") == 0)
{
id3->discnum = atoi(value);
p = &(id3->disc_string);
}
else if (((strcasecmp(name, "year") == 0) && (type == TAGTYPE_APE))
|| ((strcasecmp(name, "date") == 0) && (type == TAGTYPE_VORBIS)))
{
/* Date's can be in any format in Vorbis. However most of them
* are in ISO8601 format so if we try and parse the first part
* of the tag as a number, we should get the year. If we get crap,
* then act like we never parsed it.
*/
id3->year = atoi(value);
if (id3->year < 1900)
{ /* yeah, not likely */
id3->year = 0;
}
p = &(id3->year_string);
}
else if (strcasecmp(name, "title") == 0)
{
p = &(id3->title);
}
else if (strcasecmp(name, "artist") == 0)
{
p = &(id3->artist);
}
else if (strcasecmp(name, "album") == 0)
{
p = &(id3->album);
}
else if (strcasecmp(name, "genre") == 0)
{
p = &(id3->genre_string);
}
else if (strcasecmp(name, "composer") == 0)
{
p = &(id3->composer);
}
else if (strcasecmp(name, "comment") == 0)
{
p = &(id3->comment);
}
else if (strcasecmp(name, "albumartist") == 0)
{
p = &(id3->albumartist);
}
else if (strcasecmp(name, "album artist") == 0)
{
p = &(id3->albumartist);
}
else if (strcasecmp(name, "ensemble") == 0)
{
p = &(id3->albumartist);
}
else if (strcasecmp(name, "grouping") == 0)
{
p = &(id3->grouping);
}
else if (strcasecmp(name, "content group") == 0)
{
p = &(id3->grouping);
}
else if (strcasecmp(name, "contentgroup") == 0)
{
p = &(id3->grouping);
}
else if (strcasecmp(name, "musicbrainz_trackid") == 0
|| strcasecmp(name, "http://musicbrainz.org") == 0 )
{
p = &(id3->mb_track_id);
}
else
{
parse_replaygain(name, value, id3);
p = NULL;
}
/* Do not overwrite already available metadata. Especially when reading
* tags with e.g. multiple genres / artists. This way only the first
* of multiple entries is used, all following are dropped. */
if (p!=NULL && *p==NULL)
{
len = strlen(value);
len = MIN(len, buf_remaining - 1);
len = MIN(len, ID3V2_MAX_ITEM_SIZE); /* Limit max. item size. */
if (len > 0)
{
len++;
strlcpy(buf, value, len);
*p = buf;
}
else
{
len = 0;
}
}
return len;
}

View file

@ -0,0 +1,69 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <inttypes.h>
#include "metadata.h"
#ifdef ROCKBOX_BIG_ENDIAN
#define IS_BIG_ENDIAN 1
#else
#define IS_BIG_ENDIAN 0
#endif
#define TAG_NAME_LENGTH 32
#define TAG_VALUE_LENGTH 128
#define FOURCC(a,b,c,d) (((a)<<24) | ((b) << 16) | ((c) << 8) | (d))
enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS };
bool read_ape_tags(int fd, struct mp3entry* id3);
long read_vorbis_tags(int fd, struct mp3entry *id3,
long tag_remaining);
bool skip_id3v2(int fd, struct mp3entry *id3);
long read_string(int fd, char* buf, long buf_size, int eos, long size);
int read_uint8(int fd, uint8_t* buf);
#ifdef ROCKBOX_BIG_ENDIAN
#define read_uint16be(fd,buf) read((fd), (buf), 2)
#define read_uint32be(fd,buf) read((fd), (buf), 4)
#define read_uint64be(fd,buf) read((fd), (buf), 8)
int read_uint16le(int fd, uint16_t* buf);
int read_uint32le(int fd, uint32_t* buf);
int read_uint64le(int fd, uint64_t* buf);
#else
int read_uint16be(int fd, uint16_t* buf);
int read_uint32be(int fd, uint32_t* buf);
int read_uint64be(int fd, uint64_t* buf);
#define read_uint16le(fd,buf) read((fd), (buf), 2)
#define read_uint32le(fd,buf) read((fd), (buf), 4)
#define read_uint64le(fd,buf) read((fd), (buf), 8)
#endif
uint64_t get_uint64_le(void* buf);
uint32_t get_long_le(void* buf);
uint16_t get_short_le(void* buf);
uint32_t get_long_be(void* buf);
uint16_t get_short_be(void* buf);
int32_t get_slong(void* buf);
uint32_t get_itunes_int32(char* value, int count);
long parse_tag(const char* name, char* value, struct mp3entry* id3,
char* buf, long buf_remaining, enum tagtype type);

View file

@ -0,0 +1,59 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#if CONFIG_CODEC == SWCODEC
char* id3_get_num_genre(unsigned int genre_num);
#endif
int getid3v2len(int fd);
bool setid3v1title(int fd, struct mp3entry *entry);
void setid3v2title(int fd, struct mp3entry *entry);
bool get_mp3_metadata(int fd, struct mp3entry* id3);
#if CONFIG_CODEC == SWCODEC
bool get_adx_metadata(int fd, struct mp3entry* id3);
bool get_aiff_metadata(int fd, struct mp3entry* id3);
bool get_flac_metadata(int fd, struct mp3entry* id3);
bool get_mp4_metadata(int fd, struct mp3entry* id3);
bool get_monkeys_metadata(int fd, struct mp3entry* id3);
bool get_musepack_metadata(int fd, struct mp3entry *id3);
bool get_sid_metadata(int fd, struct mp3entry* id3);
bool get_mod_metadata(int fd, struct mp3entry* id3);
bool get_spc_metadata(int fd, struct mp3entry* id3);
bool get_ogg_metadata(int fd, struct mp3entry* id3);
bool get_wave_metadata(int fd, struct mp3entry* id3);
bool get_wavpack_metadata(int fd, struct mp3entry* id3);
bool get_a52_metadata(int fd, struct mp3entry* id3);
bool get_asf_metadata(int fd, struct mp3entry* id3);
bool get_asap_metadata(int fd, struct mp3entry* id3);
bool get_rm_metadata(int fd, struct mp3entry* id3);
bool get_nsf_metadata(int fd, struct mp3entry* id3);
bool get_oma_metadata(int fd, struct mp3entry* id3);
bool get_smaf_metadata(int fd, struct mp3entry* id3);
bool get_au_metadata(int fd, struct mp3entry* id3);
bool get_vox_metadata(int fd, struct mp3entry* id3);
bool get_wave64_metadata(int fd, struct mp3entry* id3);
bool get_tta_metadata(int fd, struct mp3entry* id3);
bool get_ay_metadata(int fd, struct mp3entry* id3);
bool get_gbs_metadata(int fd, struct mp3entry* id3);
bool get_hes_metadata(int fd, struct mp3entry* id3);
bool get_sgc_metadata(int fd, struct mp3entry* id3);
bool get_vgm_metadata(int fd, struct mp3entry* id3);
bool get_kss_metadata(int fd, struct mp3entry* id3);
#endif /* CONFIG_CODEC == SWCODEC */

103
lib/rbcodec/metadata/mod.c Normal file
View file

@ -0,0 +1,103 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include <string-extra.h>
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#define MODULEHEADERSIZE 0x438
bool get_mod_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char *buf = id3->id3v2buf;
unsigned char id[4];
bool is_mod_file = false;
/* Seek to file begin */
if (lseek(fd, 0, SEEK_SET) < 0)
return false;
/* Use id3v2buf as buffer for the track name */
if (read(fd, buf, sizeof(id3->id3v2buf)) < (ssize_t)sizeof(id3->id3v2buf))
return false;
/* Seek to MOD ID position */
if (lseek(fd, MODULEHEADERSIZE, SEEK_SET) < 0)
return false;
/* Read MOD ID */
if (read(fd, id, sizeof(id)) < (ssize_t)sizeof(id))
return false;
/* Mod type checking based on MikMod */
/* Protracker and variants */
if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {
is_mod_file = true;
}
/* Star Tracker */
if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&
(isdigit(id[3]))) {
char numchn = id[3] - '0';
if (numchn == 4 || numchn == 8)
is_mod_file = true;
}
/* Oktalyzer (Amiga) */
if (!memcmp(id, "OKTA", 4)) {
is_mod_file = true;
}
/* Oktalyser (Atari) */
if (!memcmp(id, "CD81", 4)) {
is_mod_file = true;
}
/* Fasttracker */
if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {
is_mod_file = true;
}
/* Fasttracker or Taketracker */
if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))
&& (isdigit(id[0])) && (isdigit(id[1]))) {
is_mod_file = true;
}
/* Don't try to play if we can't find a known mod type
* (there are mod files which have nothing to do with music) */
if (!is_mod_file)
return false;
id3->title = id3->id3v2buf; /* Point title to previous read ID3 buffer. */
id3->bitrate = filesize(fd)/1024; /* size in kb */
id3->frequency = 44100;
id3->length = 120*1000;
id3->vbr = false;
id3->filesize = filesize(fd);
return true;
}

View file

@ -0,0 +1,97 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
bool get_monkeys_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
unsigned char* header;
bool rc = false;
uint32_t descriptorlength;
uint32_t totalsamples;
uint32_t blocksperframe, finalframeblocks, totalframes;
int fileversion;
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, 4) < 4)
{
return rc;
}
if (memcmp(buf, "MAC ", 4) != 0)
{
return rc;
}
read(fd, buf + 4, MAX_PATH - 4);
fileversion = get_short_le(buf+4);
if (fileversion < 3970)
{
/* Not supported */
return false;
}
if (fileversion >= 3980)
{
descriptorlength = get_long_le(buf+8);
header = buf + descriptorlength;
blocksperframe = get_long_le(header+4);
finalframeblocks = get_long_le(header+8);
totalframes = get_long_le(header+12);
id3->frequency = get_long_le(header+20);
}
else
{
/* v3.95 and later files all have a fixed framesize */
blocksperframe = 73728 * 4;
finalframeblocks = get_long_le(buf+28);
totalframes = get_long_le(buf+24);
id3->frequency = get_long_le(buf+12);
}
id3->vbr = true; /* All APE files are VBR */
id3->filesize = filesize(fd);
totalsamples = finalframeblocks;
if (totalframes > 1)
totalsamples += blocksperframe * (totalframes-1);
id3->length = ((int64_t) totalsamples * 1000) / id3->frequency;
id3->bitrate = (id3->filesize * 8) / id3->length;
read_ape_tags(fd, id3);
return true;
}

193
lib/rbcodec/metadata/mp3.c Normal file
View file

@ -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<EFBFBD>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;
}

View file

@ -0,0 +1,849 @@
/***************************************************************************
* __________ __ ___.
* 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 "system.h"
//#define DEBUG_VERBOSE
#ifdef DEBUG_VERBOSE
#define VDEBUGF DEBUGF
#else
#define VDEBUGF(...) do { } while(0)
#endif
#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)
/* Maximum number of bytes needed by Xing/Info/VBRI parser. */
#define VBR_HEADER_MAX_SIZE (180)
/* 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 (b0 & 0xFF) << (3*8) |
(b1 & 0xFF) << (2*8) |
(b2 & 0xFF) << (1*8) |
(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;
/* Rockbox: not used
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;
/* Rockbox: not used
info->mode_extension = (header & MODE_EXT_MASK) >> 4;
info->emphasis = header & EMPHASIS_MASK;
*/
VDEBUGF( "Header: %08lx, Ver %d, lay %d, bitr %d, freq %ld, "
"chmode %d, bytes: %d time: %d/%d\n",
header, info->version, info->layer+1, info->bitrate,
info->frequency, info->channel_mode,
info->frame_size, info->ft_num, info->ft_den);
return true;
}
static bool headers_have_same_type(unsigned long header1,
unsigned long header2)
{
/* Compare MPEG version, layer and sampling frequency. If header1 is zero
* it is assumed both frame headers are of same type. */
unsigned int mask = SYNC_MASK | VERSION_MASK | LAYER_MASK | SAMPLERATE_MASK;
header1 &= mask;
header2 &= mask;
return header1 ? (header1 == header2) : true;
}
/* Helper function to read 4-byte in big endian format. */
static void read_uint32be_mp3data(int fd, unsigned long *data)
{
#ifdef ROCKBOX_BIG_ENDIAN
(void)read(fd, (char*)data, 4);
#else
(void)read(fd, (char*)data, 4);
*data = betoh32(*data);
#endif
}
static unsigned long __find_next_frame(int fd, long *offset, long max_offset,
unsigned long reference_header,
int(*getfunc)(int fd, unsigned char *c),
bool single_header)
{
unsigned long header=0;
unsigned char tmp;
long pos = 0;
/* We will search until we find two consecutive MPEG frame headers with
* the same MPEG version, layer and sampling frequency. The first header
* of this pair is assumed to be the first valid MPEG frame header of the
* whole stream. */
do {
/* Read 1 new byte. */
header <<= 8;
if (!getfunc(fd, &tmp))
return 0;
header |= tmp;
pos++;
/* Abort if max_offset is reached. Stop parsing. */
if (max_offset > 0 && pos > max_offset)
return 0;
if (is_mp3frameheader(header)) {
if (single_header) {
/* We search for one _single_ valid header that has the same
* type as the reference_header (if reference_header != 0).
* In this case we are finished. */
if (headers_have_same_type(reference_header, header))
break;
} else {
/* The current header is valid. Now gather the frame size,
* seek to this byte position and check if there is another
* valid MPEG frame header of the same type. */
struct mp3info info;
/* Gather frame size from given header and seek to next
* frame header. */
mp3headerinfo(&info, header);
lseek(fd, info.frame_size-4, SEEK_CUR);
/* Read possible next frame header and seek back to last frame
* headers byte position. */
reference_header = 0;
read_uint32be_mp3data(fd, &reference_header);
//
lseek(fd, -info.frame_size, SEEK_CUR);
/* If the current header is of the same type as the previous
* header we are finished. */
if (headers_have_same_type(header, reference_header))
break;
}
}
} while (true);
*offset = pos - 4;
if(*offset)
VDEBUGF("Warning: skipping %ld bytes of garbage\n", *offset);
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 reference_header)
{
return __find_next_frame(fd, offset, max_offset, reference_header,
fileread, true);
}
#ifndef __PCTOOL__
static int fnf_read_index;
static int fnf_buf_len;
static unsigned char *fnf_buf;
static int buf_getbyte(int fd, unsigned char *c)
{
if(fnf_read_index < fnf_buf_len)
{
*c = fnf_buf[fnf_read_index++];
return 1;
}
else
{
fnf_buf_len = read(fd, fnf_buf, fnf_buf_len);
if(fnf_buf_len < 0)
return -1;
fnf_read_index = 0;
if(fnf_buf_len > 0)
{
*c = fnf_buf[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, fnf_buf, fnf_buf_len);
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(unsigned char* buf, size_t buflen)
{
fnf_buf = buf;
fnf_buf_len = buflen;
fnf_read_index = 0;
}
static unsigned long buf_find_next_frame(int fd, long *offset, long max_offset)
{
return __find_next_frame(fd, offset, max_offset, 0, buf_getbyte, true);
}
static size_t mem_buflen;
static unsigned char* mem_buf;
static size_t mem_pos;
static int mem_cnt;
static int mem_maxlen;
static int mem_getbyte(int dummy, unsigned char *c)
{
(void)dummy;
*c = mem_buf[mem_pos++];
if(mem_pos >= mem_buflen)
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 reference_header,
unsigned char* buf, size_t buflen)
{
mem_buf = buf;
mem_buflen = buflen;
mem_pos = startpos;
mem_cnt = 0;
mem_maxlen = max_offset;
return __find_next_frame(0, offset, max_offset, reference_header,
mem_getbyte, true);
}
#endif
/* Extract information from a 'Xing' or 'Info' header. */
static void get_xing_info(struct mp3info *info, unsigned char *buf)
{
int i = 8;
/* Is it a VBR file? */
info->is_vbr = !memcmp(buf, "Xing", 4);
if (buf[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */
{
info->frame_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[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 (buf[7] & VBR_BYTES_FLAG) /* Is byte count there? */
{
info->byte_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[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);
}
if (buf[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */
{
info->has_toc = true;
memcpy( info->toc, buf+i, 100 );
i += 100;
}
if (buf[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 = ((int)buf[i ] << 4) | (buf[i+1] >> 4);
info->enc_padding = ((int)(buf[i+1]&0xF) << 8) | buf[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
}
/* Extract information from a 'VBRI' header. */
static void get_vbri_info(struct mp3info *info, unsigned char *buf)
{
/* We don't parse the TOC, since we don't yet know how to (FIXME) */
/*
int i, num_offsets, offset = 0;
*/
info->is_vbr = true; /* Yes, it is a FhG VBR file */
info->has_toc = false; /* We don't parse the TOC (yet) */
info->byte_count = bytes2int(buf[10], buf[11], buf[12], buf[13]);
info->frame_count = bytes2int(buf[14], buf[15], buf[16], buf[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);
VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n",
info->bitrate, info->frame_size, info->frame_size);
VDEBUGF("Frame count: %lx\n", info->frame_count);
VDEBUGF("Byte count: %lx\n", info->byte_count);
/* We don't parse the TOC, since we don't yet know how to (FIXME) */
/*
num_offsets = bytes2int(0, 0, buf[18], buf[19]);
VDEBUGF("Offsets: %d\n", num_offsets);
VDEBUGF("Frames/entry: %ld\n", bytes2int(0, 0, buf[24], buf[25]));
for(i = 0; i < num_offsets; i++)
{
offset += bytes2int(0, 0, buf[26+i*2], buf[27+i*2]);;
VDEBUGF("%03d: %lx\n", i, offset - bytecount,);
}
*/
}
/* Seek to next mpeg header and extract relevant information. */
static int get_next_header_info(int fd, long *bytecount, struct mp3info *info,
bool single_header)
{
long tmp;
unsigned long header = 0;
header = __find_next_frame(fd, &tmp, 0x20000, 0, fileread, single_header);
if(header == 0)
return -1;
if(!mp3headerinfo(info, header))
return -2;
/* Next frame header is tmp bytes away. */
*bytecount += tmp;
return 0;
}
int get_mp3file_info(int fd, struct mp3info *info)
{
unsigned char frame[VBR_HEADER_MAX_SIZE], *vbrheader;
long bytecount = 0;
int result, buf_size;
/* Initialize info and frame */
memset(info, 0, sizeof(struct mp3info));
memset(frame, 0, sizeof(frame));
#if CONFIG_CODEC==SWCODEC
/* These two are needed for proper LAME gapless MP3 playback */
info->enc_delay = -1;
info->enc_padding = -1;
#endif
/* Get the very first single MPEG frame. */
result = get_next_header_info(fd, &bytecount, info, true);
if(result)
return result;
/* Read the amount of frame data to the buffer that is required for the
* vbr tag parsing. Skip the rest. */
buf_size = MIN(info->frame_size-4, (int)sizeof(frame));
if(read(fd, frame, buf_size) < 0)
return -3;
lseek(fd, info->frame_size - 4 - buf_size, SEEK_CUR);
/* Calculate position of a possible 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))
{
VDEBUGF("-- XING header --\n");
/* We want to skip the Xing frame when playing the stream */
bytecount += info->frame_size;
/* Now get the next frame to read the real info about the mp3 stream */
result = get_next_header_info(fd, &bytecount, info, false);
if(result)
return result;
get_xing_info(info, vbrheader);
}
else if (!memcmp(vbrheader, "VBRI", 4))
{
VDEBUGF("-- VBRI header --\n");
/* We want to skip the VBRI frame when playing the stream */
bytecount += info->frame_size;
/* Now get the next frame to read the real info about the mp3 stream */
result = get_next_header_info(fd, &bytecount, info, false);
if(result)
return result;
get_vbri_info(info, vbrheader);
}
else
{
VDEBUGF("-- No VBR header --\n");
/* There was no VBR header found. So, we seek back to beginning and
* search for the first MPEG frame header of the mp3 stream. */
lseek(fd, -info->frame_size, SEEK_CUR);
result = get_next_header_info(fd, &bytecount, info, false);
if(result)
return result;
}
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 char* buf, size_t buflen)
{
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(buf, buflen);
/* Find out the total number of frames */
num_frames = 0;
cnt = 0;
while((header = buf_find_next_frame(fd, &bytes, 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;
}
}
}
VDEBUGF("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,
unsigned char *tempbuf, size_t tempbuflen )
{
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(tempbuf, tempbuflen);
/* 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, 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;
}
VDEBUGF("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

View file

@ -0,0 +1,89 @@
/***************************************************************************
* __________ __ ___.
* 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
#include <string.h> /* size_t */
struct mp3info {
/* Standard MP3 frame header fields */
int version;
int layer;
int bitrate;
long frequency;
int padding;
int channel_mode;
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 */
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 */
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 reference_header);
unsigned long mem_find_next_frame(int startpos,
long *offset,
long max_offset,
unsigned long reference_header,
unsigned char* buf, size_t buflen);
int get_mp3file_info(int fd,
struct mp3info *info);
int count_mp3_frames(int fd, int startpos, int filesize,
void (*progressfunc)(int),
unsigned char* buf, size_t buflen);
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,
unsigned char *tempbuf, size_t tempbuflen );
extern unsigned long bytes2int(unsigned long b0,
unsigned long b1,
unsigned long b2,
unsigned long b3);
#endif

842
lib/rbcodec/metadata/mp4.c Normal file
View file

@ -0,0 +1,842 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Magnus Holmgren
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "errno.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
#include "debug.h"
#include "replaygain.h"
#ifdef DEBUGF
#undef DEBUGF
#define DEBUGF(...)
#endif
#define MP4_3gp6 FOURCC('3', 'g', 'p', '6')
#define MP4_aART FOURCC('a', 'A', 'R', 'T')
#define MP4_alac FOURCC('a', 'l', 'a', 'c')
#define MP4_calb FOURCC(0xa9, 'a', 'l', 'b')
#define MP4_cART FOURCC(0xa9, 'A', 'R', 'T')
#define MP4_cgrp FOURCC(0xa9, 'g', 'r', 'p')
#define MP4_cgen FOURCC(0xa9, 'g', 'e', 'n')
#define MP4_chpl FOURCC('c', 'h', 'p', 'l')
#define MP4_cnam FOURCC(0xa9, 'n', 'a', 'm')
#define MP4_cwrt FOURCC(0xa9, 'w', 'r', 't')
#define MP4_ccmt FOURCC(0xa9, 'c', 'm', 't')
#define MP4_cday FOURCC(0xa9, 'd', 'a', 'y')
#define MP4_covr FOURCC('c', 'o', 'v', 'r')
#define MP4_disk FOURCC('d', 'i', 's', 'k')
#define MP4_esds FOURCC('e', 's', 'd', 's')
#define MP4_ftyp FOURCC('f', 't', 'y', 'p')
#define MP4_gnre FOURCC('g', 'n', 'r', 'e')
#define MP4_hdlr FOURCC('h', 'd', 'l', 'r')
#define MP4_ilst FOURCC('i', 'l', 's', 't')
#define MP4_isom FOURCC('i', 's', 'o', 'm')
#define MP4_M4A FOURCC('M', '4', 'A', ' ')
#define MP4_m4a FOURCC('m', '4', 'a', ' ') /*technically its "M4A "*/
#define MP4_M4B FOURCC('M', '4', 'B', ' ') /*but files exist with lower case*/
#define MP4_mdat FOURCC('m', 'd', 'a', 't')
#define MP4_mdia FOURCC('m', 'd', 'i', 'a')
#define MP4_mdir FOURCC('m', 'd', 'i', 'r')
#define MP4_meta FOURCC('m', 'e', 't', 'a')
#define MP4_minf FOURCC('m', 'i', 'n', 'f')
#define MP4_moov FOURCC('m', 'o', 'o', 'v')
#define MP4_mp4a FOURCC('m', 'p', '4', 'a')
#define MP4_mp42 FOURCC('m', 'p', '4', '2')
#define MP4_qt FOURCC('q', 't', ' ', ' ')
#define MP4_soun FOURCC('s', 'o', 'u', 'n')
#define MP4_stbl FOURCC('s', 't', 'b', 'l')
#define MP4_stsd FOURCC('s', 't', 's', 'd')
#define MP4_stts FOURCC('s', 't', 't', 's')
#define MP4_trak FOURCC('t', 'r', 'a', 'k')
#define MP4_trkn FOURCC('t', 'r', 'k', 'n')
#define MP4_udta FOURCC('u', 'd', 't', 'a')
#define MP4_extra FOURCC('-', '-', '-', '-')
/* Read the tag data from an MP4 file, storing up to buffer_size bytes in
* buffer.
*/
static unsigned long read_mp4_tag(int fd, unsigned int size_left, char* buffer,
unsigned int buffer_left)
{
unsigned int bytes_read = 0;
if (buffer_left == 0)
{
lseek(fd, size_left, SEEK_CUR); /* Skip everything */
}
else
{
/* Skip the data tag header - maybe we should parse it properly? */
lseek(fd, 16, SEEK_CUR);
size_left -= 16;
if (size_left > buffer_left)
{
read(fd, buffer, buffer_left);
lseek(fd, size_left - buffer_left, SEEK_CUR);
bytes_read = buffer_left;
}
else
{
read(fd, buffer, size_left);
bytes_read = size_left;
}
}
return bytes_read;
}
/* Read a string tag from an MP4 file */
static unsigned int read_mp4_tag_string(int fd, int size_left, char** buffer,
unsigned int* buffer_left, char** dest)
{
unsigned int bytes_read = read_mp4_tag(fd, size_left, *buffer,
*buffer_left > 0 ? *buffer_left - 1 : 0);
unsigned int length = 0;
if (bytes_read)
{
/* Do not overwrite already available metadata. Especially when reading
* tags with e.g. multiple genres / artists. This way only the first
* of multiple entries is used, all following are dropped. */
if (*dest == NULL)
{
(*buffer)[bytes_read] = 0; /* zero-terminate for correct strlen().*/
length = strlen(*buffer) + 1;
length = MIN(length, ID3V2_MAX_ITEM_SIZE); /* Limit item size. */
*dest = *buffer;
(*buffer)[length-1] = 0; /* zero-terminate buffer. */
*buffer_left -= length;
*buffer += length;
}
}
else
{
*dest = NULL;
}
return length;
}
static unsigned int read_mp4_atom(int fd, uint32_t* size,
uint32_t* type, uint32_t size_left)
{
read_uint32be(fd, size);
read_uint32be(fd, type);
if (*size == 1)
{
/* FAT32 doesn't support files this big, so something seems to
* be wrong. (64-bit sizes should only be used when required.)
*/
errno = EFBIG;
*type = 0;
return 0;
}
if (*size > 0)
{
if (*size > size_left)
{
size_left = 0;
}
else
{
size_left -= *size;
}
*size -= 8;
}
else
{
*size = size_left;
size_left = 0;
}
return size_left;
}
static unsigned int read_mp4_length(int fd, uint32_t* size)
{
unsigned int length = 0;
int bytes = 0;
unsigned char c;
do
{
read(fd, &c, 1);
bytes++;
(*size)--;
length = (length << 7) | (c & 0x7F);
}
while ((c & 0x80) && (bytes < 4) && (*size > 0));
return length;
}
static bool read_mp4_esds(int fd, struct mp3entry* id3, uint32_t* size)
{
unsigned char buf[8];
bool sbr = false;
lseek(fd, 4, SEEK_CUR); /* Version and flags. */
read(fd, buf, 1); /* Verify ES_DescrTag. */
*size -= 5;
if (*buf == 3)
{
/* read length */
if (read_mp4_length(fd, size) < 20)
{
return sbr;
}
lseek(fd, 3, SEEK_CUR);
*size -= 3;
}
else
{
lseek(fd, 2, SEEK_CUR);
*size -= 2;
}
read(fd, buf, 1); /* Verify DecoderConfigDescrTab. */
*size -= 1;
if (*buf != 4)
{
return sbr;
}
if (read_mp4_length(fd, size) < 13)
{
return sbr;
}
lseek(fd, 13, SEEK_CUR); /* Skip audio type, bit rates, etc. */
read(fd, buf, 1);
*size -= 14;
if (*buf != 5) /* Verify DecSpecificInfoTag. */
{
return sbr;
}
{
static const int sample_rates[] =
{
96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000
};
unsigned long bits;
unsigned int length;
unsigned int index;
unsigned int type;
/* Read the (leading part of the) decoder config. */
length = read_mp4_length(fd, size);
length = MIN(length, *size);
length = MIN(length, sizeof(buf));
memset(buf, 0, sizeof(buf));
read(fd, buf, length);
*size -= length;
/* Maybe time to write a simple read_bits function... */
/* Decoder config format:
* Object type - 5 bits
* Frequency index - 4 bits
* Channel configuration - 4 bits
*/
bits = get_long_be(buf);
type = bits >> 27; /* Object type - 5 bits */
index = (bits >> 23) & 0xf; /* Frequency index - 4 bits */
if (index < (sizeof(sample_rates) / sizeof(*sample_rates)))
{
id3->frequency = sample_rates[index];
}
if (type == 5)
{
unsigned int old_index = index;
sbr = true;
index = (bits >> 15) & 0xf; /* Frequency index - 4 bits */
if (index == 15)
{
/* 17 bits read so far... */
bits = get_long_be(&buf[2]);
id3->frequency = (bits >> 7) & 0x00ffffff;
}
else if (index < (sizeof(sample_rates) / sizeof(*sample_rates)))
{
id3->frequency = sample_rates[index];
}
if (old_index == index)
{
/* Downsampled SBR */
id3->frequency *= 2;
}
}
/* Skip 13 bits from above, plus 3 bits, then read 11 bits */
else if ((length >= 4) && (((bits >> 5) & 0x7ff) == 0x2b7))
{
/* We found an extensionAudioObjectType */
type = bits & 0x1f; /* Object type - 5 bits*/
bits = get_long_be(&buf[4]);
if (type == 5)
{
sbr = bits >> 31;
if (sbr)
{
unsigned int old_index = index;
/* 1 bit read so far */
index = (bits >> 27) & 0xf; /* Frequency index - 4 bits */
if (index == 15)
{
/* 5 bits read so far */
id3->frequency = (bits >> 3) & 0x00ffffff;
}
else if (index < (sizeof(sample_rates) / sizeof(*sample_rates)))
{
id3->frequency = sample_rates[index];
}
if (old_index == index)
{
/* Downsampled SBR */
id3->frequency *= 2;
}
}
}
}
if (!sbr && (id3->frequency <= 24000) && (length <= 2))
{
/* Double the frequency for low-frequency files without a "long"
* DecSpecificConfig header. The file may or may not contain SBR,
* but here we guess it does if the header is short. This can
* fail on some files, but it's the best we can do, short of
* decoding (parts of) the file.
*/
id3->frequency *= 2;
sbr = true;
}
}
return sbr;
}
static bool read_mp4_tags(int fd, struct mp3entry* id3,
uint32_t size_left)
{
uint32_t size;
uint32_t type;
unsigned int buffer_left = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
char* buffer = id3->id3v2buf;
bool cwrt = false;
do
{
size_left = read_mp4_atom(fd, &size, &type, size_left);
/* DEBUGF("Tag atom: '%c%c%c%c' (%d bytes left)\n", type >> 24 & 0xff,
type >> 16 & 0xff, type >> 8 & 0xff, type & 0xff, size); */
switch (type)
{
case MP4_cnam:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->title);
break;
case MP4_cART:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->artist);
break;
case MP4_aART:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->albumartist);
break;
case MP4_cgrp:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->grouping);
break;
case MP4_calb:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->album);
break;
case MP4_cwrt:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->composer);
cwrt = false;
break;
case MP4_ccmt:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->comment);
break;
case MP4_cday:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->year_string);
/* Try to parse it as a year, for the benefit of the database.
*/
if(id3->year_string)
{
id3->year = atoi(id3->year_string);
if (id3->year < 1900)
{
id3->year = 0;
}
}
else
id3->year = 0;
break;
case MP4_gnre:
{
unsigned short genre;
read_mp4_tag(fd, size, (char*) &genre, sizeof(genre));
id3->genre_string = id3_get_num_genre(betoh16(genre) - 1);
}
break;
case MP4_cgen:
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->genre_string);
break;
case MP4_disk:
{
unsigned short n[2];
read_mp4_tag(fd, size, (char*) &n, sizeof(n));
id3->discnum = betoh16(n[1]);
}
break;
case MP4_trkn:
{
unsigned short n[2];
read_mp4_tag(fd, size, (char*) &n, sizeof(n));
id3->tracknum = betoh16(n[1]);
}
break;
#ifdef HAVE_ALBUMART
case MP4_covr:
{
int pos = lseek(fd, 0, SEEK_CUR) + 16;
read_mp4_tag(fd, size, buffer, 8);
id3->albumart.type = AA_TYPE_UNKNOWN;
if (memcmp(buffer, "\xff\xd8\xff\xe0", 4) == 0)
{
id3->albumart.type = AA_TYPE_JPG;
}
else if (memcmp(buffer, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0)
{
id3->albumart.type = AA_TYPE_PNG;
}
if (id3->albumart.type != AA_TYPE_UNKNOWN)
{
id3->albumart.pos = pos;
id3->albumart.size = size - 16;
id3->has_embedded_albumart = true;
}
}
break;
#endif
case MP4_extra:
{
char tag_name[TAG_NAME_LENGTH];
uint32_t sub_size;
/* "mean" atom */
read_uint32be(fd, &sub_size);
size -= sub_size;
lseek(fd, sub_size - 4, SEEK_CUR);
/* "name" atom */
read_uint32be(fd, &sub_size);
size -= sub_size;
lseek(fd, 8, SEEK_CUR);
sub_size -= 12;
if (sub_size > sizeof(tag_name) - 1)
{
read(fd, tag_name, sizeof(tag_name) - 1);
lseek(fd, sub_size - (sizeof(tag_name) - 1), SEEK_CUR);
tag_name[sizeof(tag_name) - 1] = 0;
}
else
{
read(fd, tag_name, sub_size);
tag_name[sub_size] = 0;
}
if ((strcasecmp(tag_name, "composer") == 0) && !cwrt)
{
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->composer);
}
else if (strcasecmp(tag_name, "iTunSMPB") == 0)
{
char value[TAG_VALUE_LENGTH];
char* value_p = value;
char* any;
unsigned int length = sizeof(value);
read_mp4_tag_string(fd, size, &value_p, &length, &any);
id3->lead_trim = get_itunes_int32(value, 1);
id3->tail_trim = get_itunes_int32(value, 2);
DEBUGF("AAC: lead_trim %d, tail_trim %d\n",
id3->lead_trim, id3->tail_trim);
}
else if (strcasecmp(tag_name, "musicbrainz track id") == 0)
{
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->mb_track_id);
}
else if ((strcasecmp(tag_name, "album artist") == 0))
{
read_mp4_tag_string(fd, size, &buffer, &buffer_left,
&id3->albumartist);
}
else
{
char* any = NULL;
unsigned int length = read_mp4_tag_string(fd, size,
&buffer, &buffer_left, &any);
if (length > 0)
{
/* Re-use the read buffer as the dest buffer... */
buffer -= length;
buffer_left += length;
parse_replaygain(tag_name, buffer, id3);
}
}
}
break;
default:
lseek(fd, size, SEEK_CUR);
break;
}
}
while ((size_left > 0) && (errno == 0));
return true;
}
static bool read_mp4_container(int fd, struct mp3entry* id3,
uint32_t size_left)
{
uint32_t size = 0;
uint32_t type = 0;
uint32_t handler = 0;
bool rc = true;
bool done = false;
do
{
size_left = read_mp4_atom(fd, &size, &type, size_left);
/* DEBUGF("Atom: '%c%c%c%c' (0x%08lx, %lu bytes left)\n",
(int) ((type >> 24) & 0xff), (int) ((type >> 16) & 0xff),
(int) ((type >> 8) & 0xff), (int) (type & 0xff),
type, size); */
switch (type)
{
case MP4_ftyp:
{
uint32_t id;
read_uint32be(fd, &id);
size -= 4;
if ((id != MP4_M4A) && (id != MP4_M4B) && (id != MP4_mp42)
&& (id != MP4_qt) && (id != MP4_3gp6) && (id != MP4_m4a)
&& (id != MP4_isom))
{
DEBUGF("Unknown MP4 file type: '%c%c%c%c'\n",
(int)(id >> 24 & 0xff), (int)(id >> 16 & 0xff),
(int)(id >> 8 & 0xff), (int)(id & 0xff));
return false;
}
}
break;
case MP4_meta:
lseek(fd, 4, SEEK_CUR); /* Skip version */
size -= 4;
/* Fall through */
case MP4_moov:
case MP4_udta:
case MP4_mdia:
case MP4_stbl:
case MP4_trak:
rc = read_mp4_container(fd, id3, size);
size = 0;
break;
case MP4_ilst:
/* We need at least a size of 8 to read the next atom. */
if (handler == MP4_mdir && size>8)
{
rc = read_mp4_tags(fd, id3, size);
size = 0;
}
break;
case MP4_minf:
if (handler == MP4_soun)
{
rc = read_mp4_container(fd, id3, size);
size = 0;
}
break;
case MP4_stsd:
lseek(fd, 8, SEEK_CUR);
size -= 8;
rc = read_mp4_container(fd, id3, size);
size = 0;
break;
case MP4_hdlr:
lseek(fd, 8, SEEK_CUR);
read_uint32be(fd, &handler);
size -= 12;
/* DEBUGF(" Handler '%c%c%c%c'\n", handler >> 24 & 0xff,
handler >> 16 & 0xff, handler >> 8 & 0xff,handler & 0xff); */
break;
case MP4_stts:
{
uint32_t entries;
unsigned int i;
/* Reset to false. */
id3->needs_upsampling_correction = false;
lseek(fd, 4, SEEK_CUR);
read_uint32be(fd, &entries);
id3->samples = 0;
for (i = 0; i < entries; i++)
{
uint32_t n;
uint32_t l;
read_uint32be(fd, &n);
read_uint32be(fd, &l);
/* Some AAC file use HE profile. In this case the number
* of output samples is doubled to a maximum of 2048
* samples per frame. This means that files which already
* report a frame size of 2048 in their header will not
* need any further special handling. */
if (id3->codectype==AFMT_MP4_AAC_HE && l<=1024)
{
id3->samples += n * l * 2;
id3->needs_upsampling_correction = true;
}
else
{
id3->samples += n * l;
}
}
size = 0;
}
break;
case MP4_mp4a:
{
uint32_t subsize;
uint32_t subtype;
/* Move to the next expected mp4 atom. */
lseek(fd, 28, SEEK_CUR);
read_mp4_atom(fd, &subsize, &subtype, size);
size -= 36;
if (subtype == MP4_esds)
{
/* Read esds metadata and return if AAC-HE/SBR is used. */
if (read_mp4_esds(fd, id3, &size))
id3->codectype = AFMT_MP4_AAC_HE;
else
id3->codectype = AFMT_MP4_AAC;
}
}
break;
case MP4_alac:
{
uint32_t frequency;
uint32_t subsize;
uint32_t subtype;
/* Move to the next expected mp4 atom. */
lseek(fd, 28, SEEK_CUR);
read_mp4_atom(fd, &subsize, &subtype, size);
size -= 36;
#if 0
/* We might need to parse for the alac metadata atom. */
while (!((subsize==28) && (subtype==MP4_alac)) && (size>0))
{
lseek(fd, -7, SEEK_CUR);
read_mp4_atom(fd, &subsize, &subtype, size);
size -= 1;
errno = 0; /* will most likely be set while parsing */
}
#endif
if (subtype == MP4_alac)
{
lseek(fd, 24, SEEK_CUR);
read_uint32be(fd, &frequency);
size -= 28;
id3->frequency = frequency;
id3->codectype = AFMT_MP4_ALAC;
}
}
break;
case MP4_mdat:
/* Some AAC files appear to contain additional empty mdat chunks.
Ignore them. */
if(size == 0)
break;
id3->filesize = size;
if(id3->samples > 0) {
/* We've already seen the moov chunk. */
done = true;
}
break;
case MP4_chpl:
{
/* ADDME: add support for real chapters. Right now it's only
* used for Nero's gapless hack */
uint8_t chapters;
uint64_t timestamp;
lseek(fd, 8, SEEK_CUR);
read_uint8(fd, &chapters);
size -= 9;
/* the first chapter will be used as the lead_trim */
if (chapters > 0) {
read_uint64be(fd, &timestamp);
id3->lead_trim = (timestamp * id3->frequency) / 10000000;
size -= 8;
}
}
break;
default:
break;
}
/* Skip final seek. */
if (!done)
{
lseek(fd, size, SEEK_CUR);
}
} while (rc && (size_left > 0) && (errno == 0) && !done);
return rc;
}
bool get_mp4_metadata(int fd, struct mp3entry* id3)
{
id3->codectype = AFMT_UNKNOWN;
id3->filesize = 0;
errno = 0;
if (read_mp4_container(fd, id3, filesize(fd)) && (errno == 0)
&& (id3->samples > 0) && (id3->frequency > 0)
&& (id3->filesize > 0))
{
if (id3->codectype == AFMT_UNKNOWN)
{
logf("Not an ALAC or AAC file");
return false;
}
id3->length = ((int64_t) id3->samples * 1000) / id3->frequency;
id3->vbr = true; /* ALAC is native VBR, AAC very unlikely is CBR. */
if (id3->length <= 0)
{
logf("mp4 length invalid!");
return false;
}
id3->bitrate = ((int64_t) id3->filesize * 8) / id3->length;
DEBUGF("MP4 bitrate %d, frequency %ld Hz, length %ld ms\n",
id3->bitrate, id3->frequency, id3->length);
}
else
{
logf("MP4 metadata error");
DEBUGF("MP4 metadata error. errno %d, samples %ld, frequency %ld, "
"filesize %ld\n", errno, id3->samples, id3->frequency,
id3->filesize);
return false;
}
return true;
}

220
lib/rbcodec/metadata/mpc.c Normal file
View file

@ -0,0 +1,220 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Thom Johansen
* Copyright (C) 2010 Andree Buschmann
*
* 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.
*
****************************************************************************/
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
#include "replaygain.h"
#include "fixedpoint.h"
/* Needed for replay gain and clipping prevention of SV8 files. */
#define SV8_TO_SV7_CONVERT_GAIN (6482) /* 64.82 * 100, MPC_OLD_GAIN_REF */
#define SV8_TO_SV7_CONVERT_PEAK (23119) /* 256 * 20 * log10(32768) */
static int set_replaygain_sv7(struct mp3entry* id3,
bool album,
long value,
long used)
{
long gain = (int16_t) ((value >> 16) & 0xffff);
long peak = (uint16_t) (value & 0xffff);
/* We use a peak value of 0 to indicate a given gain type isn't used. */
if (peak != 0) {
/* Save the ReplayGain data to id3-structure for further processing. */
parse_replaygain_int(album, gain * 512 / 100, peak << 9, id3);
}
return used;
}
static int set_replaygain_sv8(struct mp3entry* id3,
bool album,
long gain,
long peak,
long used)
{
gain = (long)(SV8_TO_SV7_CONVERT_GAIN - ((gain*100)/256));
/* Transform SV8's logarithmic peak representation to the desired linear
* representation: linear = pow(10, peak/256/20).
*
* FP_BITS = 24 bits = desired fp representation for dsp routines
* FRAC_BITS = 12 bits = resolution used for fp_bits
* fp_factor(peak*(1<<FRAC_BITS)/256, FRAC_BITS) << (FP_BITS-FRAC_BITS)
**/
peak = (fp_factor((peak-SV8_TO_SV7_CONVERT_PEAK)*16, 12) << 12);
/* We use a peak value of 0 to indicate a given gain type isn't used. */
if (peak != 0) {
/* Save the ReplayGain data to id3-structure for further processing. */
parse_replaygain_int(album, gain * 512 / 100, peak, id3);
}
return used;
}
static int sv8_get_size(uint8_t *buffer, int index, uint64_t *p_size)
{
unsigned char tmp;
uint64_t size = 0;
do {
tmp = buffer[index++];
size = (size << 7) | (tmp & 0x7F);
} while((tmp & 0x80));
*p_size = size;
return index;
}
bool get_musepack_metadata(int fd, struct mp3entry *id3)
{
static const int32_t sfreqs[4] = { 44100, 48000, 37800, 32000 };
uint32_t header[8];
uint64_t samples = 0;
int i;
if (!skip_id3v2(fd, id3))
return false;
if (read(fd, header, 4*8) != 4*8) return false;
/* Musepack files are little endian, might need swapping */
for (i = 1; i < 8; i++)
header[i] = letoh32(header[i]);
if (!memcmp(header, "MP+", 3)) { /* Compare to sig "MP+" */
unsigned int streamversion;
header[0] = letoh32(header[0]);
streamversion = (header[0] >> 24) & 15;
if (streamversion == 7) {
unsigned int gapless = (header[5] >> 31) & 0x0001;
unsigned int last_frame_samples = (header[5] >> 20) & 0x07ff;
unsigned int bufused = 0;
id3->frequency = sfreqs[(header[2] >> 16) & 0x0003];
samples = (uint64_t)header[1]*1152; /* 1152 is mpc frame size */
if (gapless)
samples -= 1152 - last_frame_samples;
else
samples -= 481; /* Musepack subband synth filter delay */
bufused = set_replaygain_sv7(id3, false, header[3], bufused);
bufused = set_replaygain_sv7(id3, true , header[4], bufused);
id3->codectype = AFMT_MPC_SV7;
} else {
return false; /* only SV7 is allowed within a "MP+" signature */
}
} else if (!memcmp(header, "MPCK", 4)) { /* Compare to sig "MPCK" */
uint8_t sv8_header[32];
/* 4 bytes 'MPCK' */
lseek(fd, 4, SEEK_SET);
if (read(fd, sv8_header, 2) != 2) return false; /* read frame ID */
if (!memcmp(sv8_header, "SH", 2)) { /* Stream Header ID */
int32_t k = 0;
uint32_t streamversion;
uint64_t size = 0; /* tag size */
uint64_t dummy = 0; /* used to dummy read data from header */
/* 4 bytes 'MPCK' + 2 'SH' */
lseek(fd, 6, SEEK_SET);
if (read(fd, sv8_header, 32) != 32) return false;
/* Read the size of 'SH'-tag */
k = sv8_get_size(sv8_header, k, &size);
/* Skip crc32 */
k += 4;
/* Read stream version */
streamversion = sv8_header[k++];
if (streamversion != 8) return false; /* Only SV8 is allowed. */
/* Number of samples */
k = sv8_get_size(sv8_header, k, &samples);
/* Number of leading zero-samples */
k = sv8_get_size(sv8_header, k, &dummy);
/* Sampling frequency */
id3->frequency = sfreqs[(sv8_header[k++] >> 5) & 0x0003];
/* Number of channels */
id3->channels = (sv8_header[k++] >> 4) + 1;
/* Skip to next tag: k = size -2 */
k = size - 2;
if (!memcmp(sv8_header+k, "RG", 2)) { /* Replay Gain ID */
long peak, gain;
int bufused = 0;
k += 2; /* 2 bytes 'RG' */
/* sv8_get_size must be called to skip the right amount of
* bits within the header data. */
k = sv8_get_size(sv8_header, k, &size);
/* Read and set replay gain */
if (sv8_header[k++] == 1) {
/* Title's peak and gain */
gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
bufused += set_replaygain_sv8(id3, false, gain, peak, bufused);
/* Album's peak and gain */
gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
bufused += set_replaygain_sv8(id3, true , gain, peak, bufused);
}
}
id3->codectype = AFMT_MPC_SV8;
} else {
/* No sv8 stream header found */
return false;
}
} else {
return false; /* SV4-6 is not supported anymore */
}
id3->vbr = true;
/* Estimate bitrate, we should probably subtract the various header sizes
here for super-accurate results */
id3->length = ((int64_t) samples * 1000) / id3->frequency;
if (id3->length <= 0)
{
logf("mpc length invalid!");
return false;
}
id3->filesize = filesize(fd);
id3->bitrate = id3->filesize * 8 / id3->length;
read_ape_tags(fd, id3);
return true;
}

278
lib/rbcodec/metadata/nsf.c Normal file
View file

@ -0,0 +1,278 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#include "string-extra.h"
/* NOTE: This file was modified to work properly with the new nsf codec based
on Game_Music_Emu */
struct NESM_HEADER
{
uint32_t nHeader;
uint8_t nHeaderExtra;
uint8_t nVersion;
uint8_t nTrackCount;
uint8_t nInitialTrack;
uint16_t nLoadAddress;
uint16_t nInitAddress;
uint16_t nPlayAddress;
uint8_t szGameTitle[32];
uint8_t szArtist[32];
uint8_t szCopyright[32];
uint16_t nSpeedNTSC;
uint8_t nBankSwitch[8];
uint16_t nSpeedPAL;
uint8_t nNTSC_PAL;
uint8_t nExtraChip;
uint8_t nExpansion[4];
} __attribute__((packed));
struct NSFE_INFOCHUNK
{
uint16_t nLoadAddress;
uint16_t nInitAddress;
uint16_t nPlayAddress;
uint8_t nIsPal;
uint8_t nExt;
uint8_t nTrackCount;
uint8_t nStartingTrack;
} __attribute__((packed));
#define CHAR4_CONST(a, b, c, d) FOURCC(a, b, c, d)
#define CHUNK_INFO 0x0001
#define CHUNK_DATA 0x0002
#define CHUNK_NEND 0x0004
#define CHUNK_plst 0x0008
#define CHUNK_time 0x0010
#define CHUNK_fade 0x0020
#define CHUNK_tlbl 0x0040
#define CHUNK_auth 0x0080
#define CHUNK_BANK 0x0100
static bool parse_nsfe(int fd, struct mp3entry *id3)
{
unsigned int chunks_found = 0;
long track_count = 0;
long playlist_count = 0;
struct NSFE_INFOCHUNK info;
memset(&info, 0, sizeof(struct NSFE_INFOCHUNK));
/* default values */
info.nTrackCount = 1;
id3->length = 150 * 1000;
/* begin reading chunks */
while (!(chunks_found & CHUNK_NEND))
{
uint32_t chunk_size, chunk_type;
if (read_uint32le(fd, &chunk_size) != (int)sizeof(uint32_t))
return false;
if (read_uint32be(fd, &chunk_type) != (int)sizeof(uint32_t))
return false;
switch (chunk_type)
{
/* first three types are mandatory (but don't worry about NEND
anyway) */
case CHAR4_CONST('I', 'N', 'F', 'O'):
{
if (chunks_found & CHUNK_INFO)
return false; /* only one info chunk permitted */
chunks_found |= CHUNK_INFO;
/* minimum size */
if (chunk_size < 8)
return false;
ssize_t size = MIN(sizeof(struct NSFE_INFOCHUNK), chunk_size);
if (read(fd, &info, size) != size)
return false;
if (size >= 9)
track_count = info.nTrackCount;
chunk_size -= size;
break;
}
case CHAR4_CONST('D', 'A', 'T', 'A'):
{
if (!(chunks_found & CHUNK_INFO))
return false;
if (chunks_found & CHUNK_DATA)
return false; /* only one may exist */
if (chunk_size < 1)
return false;
chunks_found |= CHUNK_DATA;
break;
}
case CHAR4_CONST('N', 'E', 'N', 'D'):
{
/* just end parsing regardless of whether or not this really is the
last chunk/data (but it _should_ be) */
chunks_found |= CHUNK_NEND;
continue;
}
/* remaining types are optional */
case CHAR4_CONST('a', 'u', 't', 'h'):
{
if (chunks_found & CHUNK_auth)
return false; /* only one may exist */
chunks_found |= CHUNK_auth;
/* szGameTitle, szArtist, szCopyright */
char ** const ar[] = { &id3->title, &id3->artist, &id3->album };
char *p = id3->id3v2buf;
long buf_rem = sizeof (id3->id3v2buf);
unsigned int i;
for (i = 0; i < ARRAYLEN(ar) && chunk_size && buf_rem; i++)
{
long len = read_string(fd, p, buf_rem, '\0', chunk_size);
if (len < 0)
return false;
*ar[i] = p;
p += len;
buf_rem -= len;
if (chunk_size >= (uint32_t)len)
chunk_size -= len;
else
chunk_size = 0;
}
break;
}
case CHAR4_CONST('p', 'l', 's', 't'):
{
if (chunks_found & CHUNK_plst)
return false; /* only one may exist */
chunks_found |= CHUNK_plst;
/* each byte is the index of one track */
playlist_count = chunk_size;
break;
}
case CHAR4_CONST('t', 'i', 'm', 'e'):
case CHAR4_CONST('f', 'a', 'd', 'e'):
case CHAR4_CONST('t', 'l', 'b', 'l'): /* we unfortunately can't use these anyway */
{
/* don't care how many of these there are even though there should
be only one */
if (!(chunks_found & CHUNK_INFO))
return false;
case CHAR4_CONST('B', 'A', 'N', 'K'):
break;
}
default: /* unknown chunk */
{
/* check the first byte */
chunk_type = (uint8_t)chunk_type;
/* chunk is vital... don't continue */
if(chunk_type >= 'A' && chunk_type <= 'Z')
return false;
/* otherwise, just skip it */
break;
}
} /* end switch */
lseek(fd, chunk_size, SEEK_CUR);
} /* end while */
if (track_count | playlist_count)
id3->length = MAX(track_count, playlist_count)*1000;
/* Single subtrack files will be treated differently
by gme's nsf codec */
if (id3->length <= 1000) id3->length = 150 * 1000;
/*
* if we exited the while loop without a 'return', we must have hit an NEND
* chunk if this is the case, the file was layed out as it was expected.
* now.. make sure we found both an info chunk, AND a data chunk... since
* these are minimum requirements for a valid NSFE file
*/
return (chunks_found & (CHUNK_INFO | CHUNK_DATA)) ==
(CHUNK_INFO | CHUNK_DATA);
}
static bool parse_nesm(int fd, struct mp3entry *id3)
{
struct NESM_HEADER hdr;
char *p = id3->id3v2buf;
lseek(fd, 0, SEEK_SET);
if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
return false;
/* Length */
id3->length = (hdr.nTrackCount > 1 ? hdr.nTrackCount : 150) * 1000;
/* Title */
id3->title = p;
p += strlcpy(p, hdr.szGameTitle, 32) + 1;
/* Artist */
id3->artist = p;
p += strlcpy(p, hdr.szArtist, 32) + 1;
/* Copyright (per codec) */
id3->album = p;
strlcpy(p, hdr.szCopyright, 32);
return true;
}
bool get_nsf_metadata(int fd, struct mp3entry* id3)
{
uint32_t nsf_type;
if (lseek(fd, 0, SEEK_SET) < 0 ||
read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type))
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
/* we only render 16 bits, 44.1KHz, Mono */
id3->bitrate = 706;
id3->frequency = 44100;
if (nsf_type == CHAR4_CONST('N', 'S', 'F', 'E'))
return parse_nsfe(fd, id3);
else if (nsf_type == CHAR4_CONST('N', 'E', 'S', 'M'))
return parse_nesm(fd, id3);
/* not a valid format*/
return false;
}

215
lib/rbcodec/metadata/ogg.c Normal file
View file

@ -0,0 +1,215 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
/* A simple parser to read vital metadata from an Ogg Vorbis file.
* Can also handle parsing Ogg Speex files for metadata. Returns
* false if metadata needed by the codec couldn't be read.
*/
bool get_ogg_metadata(int fd, struct mp3entry* id3)
{
/* An Ogg File is split into pages, each starting with the string
* "OggS". Each page has a timestamp (in PCM samples) referred to as
* the "granule position".
*
* An Ogg Vorbis has the following structure:
* 1) Identification header (containing samplerate, numchannels, etc)
* 2) Comment header - containing the Vorbis Comments
* 3) Setup header - containing codec setup information
* 4) Many audio packets...
*
* An Ogg Speex has the following structure:
* 1) Identification header (containing samplerate, numchannels, etc)
* Described in this page: (http://www.speex.org/manual2/node7.html)
* 2) Comment header - containing the Vorbis Comments
* 3) Many audio packets.
*/
/* Use the path name of the id3 structure as a temporary buffer. */
unsigned char* buf = (unsigned char *)id3->path;
long comment_size;
long remaining = 0;
long last_serial = 0;
long serial, r;
int segments, header_size;
int i;
bool eof = false;
/* 92 bytes is enough for both Vorbis and Speex headers */
if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 92) < 92))
{
return false;
}
/* All Ogg streams start with OggS */
if (memcmp(buf, "OggS", 4) != 0)
{
return false;
}
/* Check for format magic and then get metadata */
if (memcmp(&buf[29], "vorbis", 6) == 0)
{
id3->codectype = AFMT_OGG_VORBIS;
id3->frequency = get_long_le(&buf[40]);
id3->vbr = true;
/* Comments are in second Ogg page (byte 58 onwards for Vorbis) */
if (lseek(fd, 58, SEEK_SET) < 0)
{
return false;
}
}
else if (memcmp(&buf[28], "Speex ", 8) == 0)
{
id3->codectype = AFMT_SPEEX;
id3->frequency = get_slong(&buf[64]);
id3->vbr = get_long_le(&buf[88]);
header_size = get_long_le(&buf[60]);
/* Comments are in second Ogg page (byte 108 onwards for Speex) */
if (lseek(fd, 28 + header_size, SEEK_SET) < 0)
{
return false;
}
}
else
{
/* Unsupported format, try to print the marker, catches Ogg/FLAC at least */
DEBUGF("Usupported format in Ogg stream: %16s\n", &buf[28]);
return false;
}
id3->filesize = filesize(fd);
/* We need to ensure the serial number from this page is the same as the
* one from the last page (since we only support a single bitstream).
*/
serial = get_long_le(&buf[14]);
comment_size = read_vorbis_tags(fd, id3, remaining);
/* We now need to search for the last page in the file - identified by
* by ('O','g','g','S',0) and retrieve totalsamples.
*/
/* A page is always < 64 kB */
if (lseek(fd, -(MIN(64 * 1024, id3->filesize)), SEEK_END) < 0)
{
return false;
}
remaining = 0;
while (!eof)
{
r = read(fd, &buf[remaining], MAX_PATH - remaining);
if (r <= 0)
{
eof = true;
}
else
{
remaining += r;
}
/* Inefficient (but simple) search */
i = 0;
while (i < (remaining - 3))
{
if ((buf[i] == 'O') && (memcmp(&buf[i], "OggS", 4) == 0))
{
if (i < (remaining - 17))
{
/* Note that this only reads the low 32 bits of a
* 64 bit value.
*/
id3->samples = get_long_le(&buf[i + 6]);
last_serial = get_long_le(&buf[i + 14]);
/* If this page is very small the beginning of the next
* header could be in buffer. Jump near end of this header
* and continue */
i += 27;
}
else
{
break;
}
}
else
{
i++;
}
}
if (i < remaining)
{
/* Move the remaining bytes to start of buffer.
* Reuse var 'segments' as it is no longer needed */
segments = 0;
while (i < remaining)
{
buf[segments++] = buf[i++];
}
remaining = segments;
}
else
{
/* Discard the rest of the buffer */
remaining = 0;
}
}
/* This file has mutiple vorbis bitstreams (or is corrupt). */
/* FIXME we should display an error here. */
if (serial != last_serial)
{
logf("serialno mismatch");
logf("%ld", serial);
logf("%ld", last_serial);
return false;
}
id3->length = ((int64_t) id3->samples * 1000) / id3->frequency;
if (id3->length <= 0)
{
logf("ogg length invalid!");
return false;
}
id3->bitrate = (((int64_t) id3->filesize - comment_size) * 8) / id3->length;
return true;
}

189
lib/rbcodec/metadata/oma.c Normal file
View file

@ -0,0 +1,189 @@
/*
* Sony OpenMG (OMA) demuxer
*
* Copyright (c) 2008 Maxim Poliakovski
* 2008 Benjamin Larsson
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file oma.c
* This is a demuxer for Sony OpenMG Music files
*
* Known file extensions: ".oma", "aa3"
* The format of such files consists of three parts:
* - "ea3" header carrying overall info and metadata.
* - "EA3" header is a Sony-specific header containing information about
* the OpenMG file: codec type (usually ATRAC, can also be MP3 or WMA),
* codec specific info (packet size, sample rate, channels and so on)
* and DRM related info (file encryption, content id).
* - Sound data organized in packets follow the EA3 header
* (can be encrypted using the Sony DRM!).
*
* LIMITATIONS: This version supports only plain (unencrypted) OMA files.
* If any DRM-protected (encrypted) file is encountered you will get the
* corresponding error message. Try to remove the encryption using any
* Sony software (for example SonicStage).
* CODEC SUPPORT: Only ATRAC3 codec is currently supported!
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "metadata.h"
#include "metadata_parsers.h"
#define EA3_HEADER_SIZE 96
#if 0
#define DEBUGF printf
#else
#define DEBUGF(...)
#endif
/* Various helper macros taken from ffmpeg for reading *
* and writing buffers with a specified endianess. */
# define AV_RB16(x) \
((((const uint8_t*)(x))[0] << 8) | \
((const uint8_t*)(x))[1])
# define AV_RB24(x) \
((((const uint8_t*)(x))[0] << 16) | \
(((const uint8_t*)(x))[1] << 8) | \
((const uint8_t*)(x))[2])
# define AV_RB32(x) \
((((const uint8_t*)(x))[0] << 24) | \
(((const uint8_t*)(x))[1] << 16) | \
(((const uint8_t*)(x))[2] << 8) | \
((const uint8_t*)(x))[3])
# define AV_WL32(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
} while(0)
# define AV_WL16(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
} while(0)
/* Different codecs that could be present in a Sony OMA *
* container file. */
enum {
OMA_CODECID_ATRAC3 = 0,
OMA_CODECID_ATRAC3P = 1,
OMA_CODECID_MP3 = 3,
OMA_CODECID_LPCM = 4,
OMA_CODECID_WMA = 5,
};
/* FIXME: This functions currently read different file *
* parameters required for decoding. It still *
* does not read the metadata - which should be *
* present in the ea3 (first) header. The *
* metadata in ea3 is stored as a variation of *
* the ID3v2 metadata format. */
static int oma_read_header(int fd, struct mp3entry* id3)
{
static const uint16_t srate_tab[6] = {320,441,480,882,960,0};
int ret, ea3_taglen, EA3_pos, jsflag;
uint32_t codec_params;
int16_t eid;
uint8_t buf[EA3_HEADER_SIZE];
ret = read(fd, buf, 10);
if (ret != 10)
return -1;
ea3_taglen = ((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f);
EA3_pos = ea3_taglen + 10;
if (buf[5] & 0x10)
EA3_pos += 10;
lseek(fd, EA3_pos, SEEK_SET);
ret = read(fd, buf, EA3_HEADER_SIZE);
if (ret != EA3_HEADER_SIZE)
return -1;
if (memcmp(buf, ((const uint8_t[]){'E', 'A', '3'}),3) || buf[4] != 0 || buf[5] != EA3_HEADER_SIZE) {
DEBUGF("Couldn't find the EA3 header !\n");
return -1;
}
eid = AV_RB16(&buf[6]);
if (eid != -1 && eid != -128) {
DEBUGF("Encrypted file! Eid: %d\n", eid);
return -1;
}
codec_params = AV_RB24(&buf[33]);
switch (buf[32]) {
case OMA_CODECID_ATRAC3:
id3->frequency = srate_tab[(codec_params >> 13) & 7]*100;
if (id3->frequency != 44100) {
DEBUGF("Unsupported sample rate, send sample file to developers: %d\n", id3->frequency);
return -1;
}
id3->bytesperframe = (codec_params & 0x3FF) * 8;
id3->codectype = AFMT_OMA_ATRAC3;
jsflag = (codec_params >> 17) & 1; /* get stereo coding mode, 1 for joint-stereo */
id3->bitrate = id3->frequency * id3->bytesperframe * 8 / (1024 * 1000);
/* fake the atrac3 extradata (wav format, makes stream copy to wav work) */
/* ATRAC3 expects and extra-data size of 14 bytes for wav format, and *
* looks for that in the id3v2buf. */
id3->extradata_size = 14;
AV_WL16(&id3->id3v2buf[0], 1); // always 1
AV_WL32(&id3->id3v2buf[2], id3->frequency); // samples rate
AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[10], 1); // always 1
AV_WL16(&id3->id3v2buf[12], 0); // always 0
id3->channels = 2;
DEBUGF("sample_rate = %d\n", id3->frequency);
DEBUGF("frame_size = %d\n", id3->bytesperframe);
DEBUGF("stereo_coding_mode = %d\n", jsflag);
break;
default:
DEBUGF("Unsupported codec %d!\n",buf[32]);
return -1;
break;
}
/* Store the the offset of the first audio frame, to be able to seek to it *
* directly in atrac3_oma.codec. */
id3->first_frame_offset = EA3_pos + EA3_HEADER_SIZE;
return 0;
}
bool get_oma_metadata(int fd, struct mp3entry* id3)
{
if(oma_read_header(fd, id3) < 0)
return false;
/* Currently, there's no means of knowing the duration *
* directly from the the file so we calculate it. */
id3->filesize = filesize(fd);
id3->length = ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate;
return true;
}

View file

@ -0,0 +1,222 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Magnus Holmgren
*
* 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.
*
****************************************************************************/
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "strlcpy.h"
#include "strcasecmp.h"
#include "system.h"
#include "metadata.h"
#include "debug.h"
#include "replaygain.h"
#include "fixedpoint.h"
#define FP_BITS (12)
#define FP_ONE (1 << FP_BITS)
#define FP_MIN (-48 * FP_ONE)
#define FP_MAX ( 17 * FP_ONE)
void replaygain_itoa(char* buffer, int length, long int_gain)
{
/* int_gain uses Q19.12 format. */
int one = abs(int_gain) >> FP_BITS;
int cent = ((abs(int_gain) & 0x0fff) * 100 + (FP_ONE/2)) >> FP_BITS;
snprintf(buffer, length, "%s%d.%02d dB", (int_gain<0) ? "-":"", one, cent);
}
static long fp_atof(const char* s, int precision)
{
long int_part = 0;
long int_one = BIT_N(precision);
long frac_part = 0;
long frac_count = 0;
long frac_max = ((precision * 4) + 12) / 13;
long frac_max_int = 1;
long sign = 1;
bool point = false;
while ((*s != '\0') && isspace(*s))
{
s++;
}
if (*s == '-')
{
sign = -1;
s++;
}
else if (*s == '+')
{
s++;
}
while (*s != '\0')
{
if (*s == '.')
{
if (point)
{
break;
}
point = true;
}
else if (isdigit(*s))
{
if (point)
{
if (frac_count < frac_max)
{
frac_part = frac_part * 10 + (*s - '0');
frac_count++;
frac_max_int *= 10;
}
}
else
{
int_part = int_part * 10 + (*s - '0');
}
}
else
{
break;
}
s++;
}
while (frac_count < frac_max)
{
frac_part *= 10;
frac_count++;
frac_max_int *= 10;
}
return sign * ((int_part * int_one)
+ (((int64_t) frac_part * int_one) / frac_max_int));
}
static long convert_gain(long gain)
{
/* Don't allow unreasonably low or high gain changes.
* Our math code can't handle it properly anyway. :) */
gain = MAX(gain, FP_MIN);
gain = MIN(gain, FP_MAX);
return fp_factor(gain, FP_BITS) << (24 - FP_BITS);
}
/* Get the sample scale factor in Q19.12 format from a gain value. Returns 0
* for no gain.
*
* str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
*/
static long get_replaygain(const char* str)
{
return fp_atof(str, FP_BITS);
}
/* Get the peak volume in Q7.24 format.
*
* str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
*/
static long get_replaypeak(const char* str)
{
return fp_atof(str, 24);
}
/* Get a sample scale factor in Q7.24 format from a gain value.
*
* int_gain Gain in dB, multiplied by 100.
*/
long get_replaygain_int(long int_gain)
{
return convert_gain(int_gain * FP_ONE / 100);
}
/* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
* valid tag is found, update mp3entry struct accordingly. Existing values
* are not overwritten.
*
* key Name of the tag.
* value Value of the tag.
* entry mp3entry struct to update.
*/
void parse_replaygain(const char* key, const char* value,
struct mp3entry* entry)
{
if (((strcasecmp(key, "replaygain_track_gain") == 0) ||
(strcasecmp(key, "rg_radio") == 0)) &&
!entry->track_gain)
{
entry->track_level = get_replaygain(value);
entry->track_gain = convert_gain(entry->track_level);
}
else if (((strcasecmp(key, "replaygain_album_gain") == 0) ||
(strcasecmp(key, "rg_audiophile") == 0)) &&
!entry->album_gain)
{
entry->album_level = get_replaygain(value);
entry->album_gain = convert_gain(entry->album_level);
}
else if (((strcasecmp(key, "replaygain_track_peak") == 0) ||
(strcasecmp(key, "rg_peak") == 0)) &&
!entry->track_peak)
{
entry->track_peak = get_replaypeak(value);
}
else if ((strcasecmp(key, "replaygain_album_peak") == 0) &&
!entry->album_peak)
{
entry->album_peak = get_replaypeak(value);
}
}
/* Set ReplayGain values from integers. Existing values are not overwritten.
*
* album If true, set album values, otherwise set track values.
* gain Gain value in dB, multiplied by 512. 0 for no gain.
* peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
* peak volume.
* entry mp3entry struct to update.
*/
void parse_replaygain_int(bool album, long gain, long peak,
struct mp3entry* entry)
{
gain = gain * FP_ONE / 512;
if (album)
{
entry->album_level = gain;
entry->album_gain = convert_gain(gain);
entry->album_peak = peak;
}
else
{
entry->track_level = gain;
entry->track_gain = convert_gain(gain);
entry->track_peak = peak;
}
}

View file

@ -0,0 +1,34 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Magnus Holmgren
*
* 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 _REPLAYGAIN_H
#define _REPLAYGAIN_H
#include "metadata.h"
long get_replaygain_int(long int_gain);
void parse_replaygain(const char* key, const char* value,
struct mp3entry* entry);
void parse_replaygain_int(bool album, long gain, long peak,
struct mp3entry* entry);
void replaygain_itoa(char* buffer, int length, long int_gain);
#endif

464
lib/rbcodec/metadata/rm.c Normal file
View file

@ -0,0 +1,464 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 Mohamed Tarek
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include <codecs/librm/rm.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
/* Uncomment the following line for debugging */
//#define DEBUG_RM
#ifndef DEBUG_RM
#undef DEBUGF
#define DEBUGF(...)
#endif
#define ID3V1_OFFSET -128
#define METADATA_FOOTER_OFFSET -140
static inline void print_cook_extradata(RMContext *rmctx) {
DEBUGF(" cook_version = 0x%08lx\n", rm_get_uint32be(rmctx->codec_extradata));
DEBUGF(" samples_per_frame_per_channel = %d\n", rm_get_uint16be(&rmctx->codec_extradata[4]));
DEBUGF(" number_of_subbands_in_freq_domain = %d\n", rm_get_uint16be(&rmctx->codec_extradata[6]));
if(rmctx->extradata_size == 16) {
DEBUGF(" joint_stereo_subband_start = %d\n",rm_get_uint16be(&rmctx->codec_extradata[12]));
DEBUGF(" joint_stereo_vlc_bits = %d\n", rm_get_uint16be(&rmctx->codec_extradata[14]));
}
}
struct real_object_t
{
uint32_t fourcc;
uint32_t size;
uint16_t version;
};
static int real_read_object_header(int fd, struct real_object_t* obj)
{
int n;
if ((n = read_uint32be(fd, &obj->fourcc)) <= 0)
return n;
if ((n = read_uint32be(fd, &obj->size)) <= 0)
return n;
if ((n = read_uint16be(fd, &obj->version)) <= 0)
return n;
return 1;
}
#if (defined(SIMULATOR) && defined(DEBUG_RM))
static char* fourcc2str(uint32_t f)
{
static char res[5];
res[0] = (f & 0xff000000) >> 24;
res[1] = (f & 0xff0000) >> 16;
res[2] = (f & 0xff00) >> 8;
res[3] = (f & 0xff);
res[4] = 0;
return res;
}
#endif
static inline int real_read_audio_stream_info(int fd, RMContext *rmctx)
{
int skipped = 0;
uint32_t version;
struct real_object_t obj;
#ifdef SIMULATOR
uint32_t header_size;
uint16_t flavor;
uint32_t coded_framesize;
uint8_t interleaver_id_length;
uint8_t fourcc_length;
#endif
uint32_t interleaver_id;
uint32_t fourcc = 0;
memset(&obj,0,sizeof(obj));
read_uint32be(fd, &version);
skipped += 4;
DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff));
if (((version >> 16) & 0xff) == 3) {
/* Very old version */
} else {
#ifdef SIMULATOR
real_read_object_header(fd, &obj);
read_uint32be(fd, &header_size);
/* obj.size will be filled with an unknown value, replaced with header_size */
DEBUGF(" Object: %s, size: %ld bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version);
read_uint16be(fd, &flavor);
read_uint32be(fd, &coded_framesize);
#else
lseek(fd, 20, SEEK_CUR);
#endif
lseek(fd, 12, SEEK_CUR); /* unknown */
read_uint16be(fd, &rmctx->sub_packet_h);
read_uint16be(fd, &rmctx->block_align);
read_uint16be(fd, &rmctx->sub_packet_size);
lseek(fd, 2, SEEK_CUR); /* unknown */
skipped += 40;
if (((version >> 16) & 0xff) == 5)
{
lseek(fd, 6, SEEK_CUR); /* unknown */
skipped += 6;
}
read_uint16be(fd, &rmctx->sample_rate);
lseek(fd, 4, SEEK_CUR); /* unknown */
read_uint16be(fd, &rmctx->nb_channels);
skipped += 8;
if (((version >> 16) & 0xff) == 4)
{
#ifdef SIMULATOR
read_uint8(fd, &interleaver_id_length);
read_uint32be(fd, &interleaver_id);
read_uint8(fd, &fourcc_length);
#else
lseek(fd, 6, SEEK_CUR);
#endif
read_uint32be(fd, &fourcc);
skipped += 10;
}
if (((version >> 16) & 0xff) == 5)
{
read_uint32be(fd, &interleaver_id);
read_uint32be(fd, &fourcc);
skipped += 8;
}
lseek(fd, 3, SEEK_CUR); /* unknown */
skipped += 3;
if (((version >> 16) & 0xff) == 5)
{
lseek(fd, 1, SEEK_CUR); /* unknown */
skipped += 1;
}
switch(fourcc) {
case FOURCC('c','o','o','k'):
rmctx->codec_type = CODEC_COOK;
read_uint32be(fd, &rmctx->extradata_size);
skipped += 4;
read(fd, rmctx->codec_extradata, rmctx->extradata_size);
skipped += rmctx->extradata_size;
break;
case FOURCC('r','a','a','c'):
case FOURCC('r','a','c','p'):
rmctx->codec_type = CODEC_AAC;
read_uint32be(fd, &rmctx->extradata_size);
skipped += 4;
read(fd, rmctx->codec_extradata, rmctx->extradata_size);
skipped += rmctx->extradata_size;
break;
case FOURCC('d','n','e','t'):
rmctx->codec_type = CODEC_AC3;
break;
case FOURCC('a','t','r','c'):
rmctx->codec_type = CODEC_ATRAC;
read_uint32be(fd, &rmctx->extradata_size);
skipped += 4;
read(fd, rmctx->codec_extradata, rmctx->extradata_size);
skipped += rmctx->extradata_size;
break;
default: /* Not a supported codec */
return -1;
}
DEBUGF(" flavor = %d\n",flavor);
DEBUGF(" coded_frame_size = %ld\n",coded_framesize);
DEBUGF(" sub_packet_h = %d\n",rmctx->sub_packet_h);
DEBUGF(" frame_size = %d\n",rmctx->block_align);
DEBUGF(" sub_packet_size = %d\n",rmctx->sub_packet_size);
DEBUGF(" sample_rate= %d\n",rmctx->sample_rate);
DEBUGF(" channels= %d\n",rmctx->nb_channels);
DEBUGF(" fourcc = %s\n",fourcc2str(fourcc));
DEBUGF(" codec_extra_data_length = %ld\n",rmctx->extradata_size);
DEBUGF(" codec_extradata :\n");
if(rmctx->codec_type == CODEC_COOK) {
DEBUGF(" cook_extradata :\n");
print_cook_extradata(rmctx);
}
}
return skipped;
}
static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3)
{
struct real_object_t obj;
int res;
int skipped;
off_t curpos __attribute__((unused));
uint8_t len; /* Holds a string_length, which is then passed to read_string() */
#ifdef SIMULATOR
uint32_t avg_bitrate = 0;
uint32_t max_packet_size;
uint32_t avg_packet_size;
uint32_t packet_count;
uint32_t duration;
uint32_t preroll;
uint32_t index_offset;
uint16_t stream_id;
uint32_t start_time;
uint32_t codec_data_size;
#endif
uint32_t v;
uint32_t max_bitrate;
uint16_t num_streams;
uint32_t next_data_off;
uint8_t header_end;
memset(&obj,0,sizeof(obj));
curpos = lseek(fd, 0, SEEK_SET);
res = real_read_object_header(fd, &obj);
if (obj.fourcc == FOURCC('.','r','a',0xfd))
{
/* Very old .ra format - not yet supported */
return -1;
}
else if (obj.fourcc != FOURCC('.','R','M','F'))
{
return -1;
}
lseek(fd, 8, SEEK_CUR); /* unknown */
DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos);
res = real_read_object_header(fd, &obj);
header_end = 0;
while(res)
{
DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos);
skipped = 10;
if(obj.fourcc == FOURCC('I','N','D','X'))
break;
switch (obj.fourcc)
{
case FOURCC('P','R','O','P'): /* File properties */
read_uint32be(fd, &max_bitrate);
read_uint32be(fd, &rmctx->bit_rate); /*avg bitrate*/
#ifdef SIMULATOR
read_uint32be(fd, &max_packet_size);
read_uint32be(fd, &avg_packet_size);
read_uint32be(fd, &packet_count);
#else
lseek(fd, 3*sizeof(uint32_t), SEEK_CUR);
#endif
read_uint32be(fd, &rmctx->duration);
#ifdef SIMULATOR
read_uint32be(fd, &preroll);
read_uint32be(fd, &index_offset);
#else
lseek(fd, 2*sizeof(uint32_t), SEEK_CUR);
#endif
read_uint32be(fd, &rmctx->data_offset);
read_uint16be(fd, &num_streams);
read_uint16be(fd, &rmctx->flags);
skipped += 40;
DEBUGF(" max_bitrate = %ld\n",max_bitrate);
DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_rate);
DEBUGF(" max_packet_size = %ld\n",max_packet_size);
DEBUGF(" avg_packet_size = %ld\n",avg_packet_size);
DEBUGF(" packet_count = %ld\n",packet_count);
DEBUGF(" duration = %ld\n",rmctx->duration);
DEBUGF(" preroll = %ld\n",preroll);
DEBUGF(" index_offset = %ld\n",index_offset);
DEBUGF(" data_offset = %ld\n",rmctx->data_offset);
DEBUGF(" num_streams = %d\n",num_streams);
DEBUGF(" flags=0x%04x\n",rmctx->flags);
break;
case FOURCC('C','O','N','T'):
/* Four strings - Title, Author, Copyright, Comment */
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[0], sizeof(id3->id3v1buf[0]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[1], sizeof(id3->id3v1buf[1]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[2], sizeof(id3->id3v1buf[2]), '\0', len);
read_uint8(fd,&len);
skipped += (int)read_string(fd, id3->id3v1buf[3], sizeof(id3->id3v1buf[3]), '\0', len);
skipped += 4;
DEBUGF(" title=\"%s\"\n",id3->id3v1buf[0]);
DEBUGF(" author=\"%s\"\n",id3->id3v1buf[1]);
DEBUGF(" copyright=\"%s\"\n",id3->id3v1buf[2]);
DEBUGF(" comment=\"%s\"\n",id3->id3v1buf[3]);
break;
case FOURCC('M','D','P','R'): /* Media properties */
#ifdef SIMULATOR
read_uint16be(fd,&stream_id);
read_uint32be(fd,&max_bitrate);
read_uint32be(fd,&avg_bitrate);
read_uint32be(fd,&max_packet_size);
read_uint32be(fd,&avg_packet_size);
read_uint32be(fd,&start_time);
read_uint32be(fd,&preroll);
read_uint32be(fd,&duration);
#else
lseek(fd, 30, SEEK_CUR);
#endif
skipped += 30;
read_uint8(fd,&len);
skipped += 1;
lseek(fd, len, SEEK_CUR); /* desc */
skipped += len;
read_uint8(fd,&len);
skipped += 1;
#ifdef SIMULATOR
lseek(fd, len, SEEK_CUR); /* mimetype */
read_uint32be(fd,&codec_data_size);
#else
lseek(fd, len + 4, SEEK_CUR);
#endif
skipped += len + 4;
read_uint32be(fd,&v);
skipped += 4;
DEBUGF(" stream_id = 0x%04x\n",stream_id);
DEBUGF(" max_bitrate = %ld\n",max_bitrate);
DEBUGF(" avg_bitrate = %ld\n",avg_bitrate);
DEBUGF(" max_packet_size = %ld\n",max_packet_size);
DEBUGF(" avg_packet_size = %ld\n",avg_packet_size);
DEBUGF(" start_time = %ld\n",start_time);
DEBUGF(" preroll = %ld\n",preroll);
DEBUGF(" duration = %ld\n",duration);
DEBUGF(" codec_data_size = %ld\n",codec_data_size);
DEBUGF(" v=\"%s\"\n", fourcc2str(v));
if (v == FOURCC('.','r','a',0xfd))
{
int temp;
temp= real_read_audio_stream_info(fd, rmctx);
if(temp < 0)
return -1;
else
skipped += temp;
}
else if (v == FOURCC('L','S','D',':'))
{
DEBUGF("Real audio lossless is not supported.");
return -1;
}
else
{
/* We shall not abort with -1 here. *.rm file often seem
* to have a second media properties header that contains
* other metadata. */
DEBUGF("Unknown header signature :\"%s\"\n", fourcc2str(v));
}
break;
case FOURCC('D','A','T','A'):
read_uint32be(fd,&rmctx->nb_packets);
skipped += 4;
read_uint32be(fd,&next_data_off);
skipped += 4;
/***
* nb_packets correction :
* in some samples, number of packets may not exactly form
* an integer number of scrambling units. This is corrected
* by constructing a partially filled unit out of the few
* remaining samples at the end of decoding.
***/
if(rmctx->nb_packets % rmctx->sub_packet_h)
rmctx->nb_packets += rmctx->sub_packet_h - (rmctx->nb_packets % rmctx->sub_packet_h);
DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets);
DEBUGF(" next DATA offset = %ld\n",next_data_off);
header_end = 1;
break;
}
if(header_end) break;
curpos = lseek(fd, obj.size - skipped, SEEK_CUR);
res = real_read_object_header(fd, &obj);
}
return 0;
}
bool get_rm_metadata(int fd, struct mp3entry* id3)
{
RMContext *rmctx = (RMContext*) (( (intptr_t)id3->id3v2buf + 3 ) &~ 3);
memset(rmctx,0,sizeof(RMContext));
if(rm_parse_header(fd, rmctx, id3) < 0)
return false;
if (!setid3v1title(fd, id3)) {
/* file has no id3v1 tags, use the tags from CONT chunk */
id3->title = id3->id3v1buf[0];
id3->artist = id3->id3v1buf[1];
id3->comment= id3->id3v1buf[3];
}
switch(rmctx->codec_type)
{
case CODEC_COOK:
/* Already set, do nothing */
break;
case CODEC_AAC:
id3->codectype = AFMT_RM_AAC;
break;
case CODEC_AC3:
id3->codectype = AFMT_RM_AC3;
break;
case CODEC_ATRAC:
id3->codectype = AFMT_RM_ATRAC3;
break;
}
id3->channels = rmctx->nb_channels;
id3->extradata_size = rmctx->extradata_size;
id3->bitrate = rmctx->bit_rate / 1000;
id3->frequency = rmctx->sample_rate;
id3->length = rmctx->duration;
id3->filesize = filesize(fd);
return true;
}

View file

@ -0,0 +1,67 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
static bool parse_sgc_header(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, 0xA0) < 0xA0)
return false;
/* calculate track length with number of tracks */
id3->length = buf[37] * 1000;
/* If meta info was found in the m3u skip next step */
if (id3->title && id3->title[0]) return true;
char *p = id3->id3v2buf;
/* Some metadata entries have 32 bytes length */
/* Game */
memcpy(p, &buf[64], 32); *(p + 33) = '\0';
id3->title = p;
p += strlen(p)+1;
/* Artist */
memcpy(p, &buf[96], 32); *(p + 33) = '\0';
id3->artist = p;
p += strlen(p)+1;
/* Copyright */
memcpy(p, &buf[128], 32); *(p + 33) = '\0';
id3->album = p;
p += strlen(p)+1;
return true;
}
bool get_sgc_metadata(int fd, struct mp3entry* id3)
{
uint32_t sgc_type;
if ((lseek(fd, 0, SEEK_SET) < 0) ||
read_uint32be(fd, &sgc_type) != (int)sizeof(sgc_type))
return false;
id3->vbr = false;
id3->filesize = filesize(fd);
/* we only render 16 bits, 44.1KHz, Stereo */
id3->bitrate = 706;
id3->frequency = 44100;
/* Make sure this is an SGC file */
if (sgc_type != FOURCC('S','G','C',0x1A))
return false;
return parse_sgc_header(fd, id3);
}

View file

@ -0,0 +1,89 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
/* PSID metadata info is available here:
http://www.unusedino.de/ec64/technical/formats/sidplay.html */
bool get_sid_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
char *p;
if ((lseek(fd, 0, SEEK_SET) < 0)
|| (read(fd, buf, 0x80) < 0x80))
{
return false;
}
if ((memcmp(buf, "PSID", 4) != 0))
{
return false;
}
p = id3->id3v2buf;
/* Copy Title (assumed max 0x1f letters + 1 zero byte) */
id3->title = p;
buf[0x16+0x1f] = 0;
p = iso_decode(&buf[0x16], p, 0, strlen(&buf[0x16])+1);
/* Copy Artist (assumed max 0x1f letters + 1 zero byte) */
id3->artist = p;
buf[0x36+0x1f] = 0;
p = iso_decode(&buf[0x36], p, 0, strlen(&buf[0x36])+1);
/* Copy Year (assumed max 4 letters + 1 zero byte) */
buf[0x56+0x4] = 0;
id3->year = atoi(&buf[0x56]);
/* Copy Album (assumed max 0x1f-0x05 letters + 1 zero byte) */
id3->album = p;
buf[0x56+0x1f] = 0;
iso_decode(&buf[0x5b], p, 0, strlen(&buf[0x5b])+1);
id3->bitrate = 706;
id3->frequency = 44100;
/* New idea as posted by Marco Alanen (ravon):
* Set the songlength in seconds to the number of subsongs
* so every second represents a subsong.
* Users can then skip the current subsong by seeking
*
* Note: the number of songs is a 16bit value at 0xE, so this code only
* uses the lower 8 bits of the counter.
*/
id3->length = (buf[0xf]-1)*1000;
id3->vbr = false;
id3->filesize = filesize(fd);
return true;
}

470
lib/rbcodec/metadata/smaf.c Normal file
View file

@ -0,0 +1,470 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Yoshihisa Uchida
*
* 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.
*
****************************************************************************/
#include <inttypes.h>
#include <stdio.h>
#include "string-extra.h"
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#include "logf.h"
static const int basebits[4] = { 4, 8, 12, 16 };
static const int frequency[5] = { 4000, 8000, 11025, 22050, 44100 };
static const int support_codepages[5] = {
#ifdef HAVE_LCD_BITMAP
SJIS, ISO_8859_1, -1, GB_2312, BIG_5,
#else
-1, ISO_8859_1, -1, -1, -1,
#endif
};
/* extra codepage */
#define UCS2 (NUM_CODEPAGES + 1)
/* support id3 tag */
#define TAG_TITLE (('S'<<8)|'T')
#define TAG_ARTIST (('A'<<8)|'N')
#define TAG_COMPOSER (('S'<<8)|'W')
/* convert functions */
#define CONVERT_SMAF_CHANNELS(c) (((c) >> 7) + 1)
static inline int convert_smaf_audio_basebit(unsigned int basebit)
{
if (basebit > 3)
return 0;
return basebits[basebit];
}
static inline int convert_smaf_audio_frequency(unsigned int freq)
{
if (freq > 4)
return 0;
return frequency[freq];
}
static int convert_smaf_codetype(unsigned int codetype)
{
if (codetype < 5)
return support_codepages[codetype];
else if (codetype == 0x20 || codetype == 0x24) /* In Rockbox, UCS2 and UTF-16 are same. */
return UCS2;
else if (codetype == 0x23)
return UTF_8;
else if (codetype == 0xff)
return ISO_8859_1;
return -1;
}
static void set_length(struct mp3entry *id3, unsigned int ch, unsigned int basebit,
unsigned int numbytes)
{
int bitspersample = convert_smaf_audio_basebit(basebit);
if (bitspersample != 0 && id3->frequency != 0)
{
/* Calculate track length [ms] and bitrate [kbit/s] */
id3->length = (uint64_t)numbytes * 8000LL
/ (bitspersample * CONVERT_SMAF_CHANNELS(ch) * id3->frequency);
id3->bitrate = bitspersample * id3->frequency / 1000;
}
/* output contents/wave data/id3 info (for debug) */
DEBUGF("contents info ----\n");
DEBUGF(" TITLE: %s\n", (id3->title)? id3->title : "(NULL)");
DEBUGF(" ARTIST: %s\n", (id3->artist)? id3->artist : "(NULL)");
DEBUGF(" COMPOSER: %s\n", (id3->composer)? id3->composer : "(NULL)");
DEBUGF("wave data info ----\n");
DEBUGF(" channels: %u\n", CONVERT_SMAF_CHANNELS(ch));
DEBUGF(" bitspersample: %d\n", bitspersample);
DEBUGF(" numbytes; %u\n", numbytes);
DEBUGF("id3 info ----\n");
DEBUGF(" frquency: %u\n", (unsigned int)id3->frequency);
DEBUGF(" bitrate: %d\n", id3->bitrate);
DEBUGF(" length: %u\n", (unsigned int)id3->length);
}
/* contents parse functions */
/* Note:
* 1) When the codepage is UTF-8 or UCS2, contents data do not start BOM.
* 2) The byte order of contents data is big endian.
*/
static void decode2utf8(const unsigned char *src, unsigned char **dst,
int srcsize, int *dstsize, int codepage)
{
unsigned char tmpbuf[srcsize * 3 + 1];
unsigned char *p;
int utf8size;
if (codepage < NUM_CODEPAGES)
p = iso_decode(src, tmpbuf, codepage, srcsize);
else /* codepage == UCS2 */
p = utf16BEdecode(src, tmpbuf, srcsize);
*p = '\0';
strlcpy(*dst, tmpbuf, *dstsize);
utf8size = (p - tmpbuf) + 1;
if (utf8size > *dstsize)
{
DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n",
utf8size, *dstsize);
utf8size = *dstsize;
}
*dst += utf8size;
*dstsize -= utf8size;
}
static int read_audio_track_contets(int fd, int codepage, unsigned char **dst,
int *dstsize)
{
/* value length <= 256 bytes */
unsigned char buf[256];
unsigned char *p = buf;
unsigned char *q = buf;
int datasize;
read(fd, buf, 256);
while (p - buf < 256 && *p != ',')
{
/* skip yen mark */
if (codepage != UCS2)
{
if (*p == '\\')
p++;
}
else if (*p == '\0' && *(p+1) == '\\')
p += 2;
if (*p > 0x7f)
{
if (codepage == UTF_8)
{
while ((*p & MASK) != COMP)
*q++ = *p++;
}
#ifdef HAVE_LCD_BITMAP
else if (codepage == SJIS)
{
if (*p <= 0xa0 || *p >= 0xe0)
*q++ = *p++;
}
#endif
}
*q++ = *p++;
if (codepage == UCS2)
*q++ = *p++;
}
datasize = p - buf + 1;
lseek(fd, datasize - 256, SEEK_CUR);
if (dst != NULL)
decode2utf8(buf, dst, q - buf, dstsize, codepage);
return datasize;
}
static void read_score_track_contets(int fd, int codepage, int datasize,
unsigned char **dst, int *dstsize)
{
unsigned char buf[datasize];
read(fd, buf, datasize);
decode2utf8(buf, dst, datasize, dstsize, codepage);
}
/* traverse chunk functions */
static unsigned int search_chunk(int fd, const unsigned char *name, int nlen)
{
unsigned char buf[8];
unsigned int chunksize;
while (read(fd, buf, 8) > 0)
{
chunksize = get_long_be(buf + 4);
if (memcmp(buf, name, nlen) == 0)
return chunksize;
lseek(fd, chunksize, SEEK_CUR);
}
DEBUGF("metadata error: missing '%s' chunk\n", name);
return 0;
}
static bool parse_smaf_audio_track(int fd, struct mp3entry *id3, unsigned int datasize)
{
/* temporary buffer */
unsigned char *tmp = (unsigned char*)id3->path;
/* contents stored buffer */
unsigned char *buf = id3->id3v2buf;
int bufsize = sizeof(id3->id3v2buf);
unsigned int chunksize = datasize;
int valsize;
int codepage;
/* parse contents info */
read(fd, tmp, 5);
codepage = convert_smaf_codetype(tmp[2]);
if (codepage < 0)
{
DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[2]);
return false;
}
datasize -= 5;
while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
&& (datasize > 0 && bufsize > 0))
{
if (read(fd, tmp, 3) <= 0)
return false;
if (tmp[2] != ':')
{
DEBUGF("metadata error: illegal tag: %c%c%c\n", tmp[0], tmp[1], tmp[2]);
return false;
}
switch ((tmp[0]<<8)|tmp[1])
{
case TAG_TITLE:
id3->title = buf;
valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
break;
case TAG_ARTIST:
id3->artist = buf;
valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
break;
case TAG_COMPOSER:
id3->composer = buf;
valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
break;
default:
valsize = read_audio_track_contets(fd, codepage, NULL, &bufsize);
break;
}
datasize -= (valsize + 3);
}
/* search PCM Audio Track Chunk */
lseek(fd, 16 + chunksize, SEEK_SET);
chunksize = search_chunk(fd, "ATR", 3);
if (chunksize == 0)
{
DEBUGF("metadata error: missing PCM Audio Track Chunk\n");
return false;
}
/*
* get format
* tmp
* +0: Format Type
* +1: Sequence Type
* +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency
* +3: bit 4-7: base bit
* +4: TimeBase_D
* +5: TimeBase_G
*
* Note: If PCM Audio Track does not include Sequence Data Chunk,
* tmp+6 is the start position of Wave Data Chunk.
*/
read(fd, tmp, 6);
/* search Wave Data Chunk */
chunksize = search_chunk(fd, "Awa", 3);
if (chunksize == 0)
{
DEBUGF("metadata error: missing Wave Data Chunk\n");
return false;
}
/* set track length and bitrate */
id3->frequency = convert_smaf_audio_frequency(tmp[2] & 0x0f);
set_length(id3, tmp[2], tmp[3] >> 4, chunksize);
return true;
}
static bool parse_smaf_score_track(int fd, struct mp3entry *id3)
{
/* temporary buffer */
unsigned char *tmp = (unsigned char*)id3->path;
unsigned char *p = tmp;
/* contents stored buffer */
unsigned char *buf = id3->id3v2buf;
int bufsize = sizeof(id3->id3v2buf);
unsigned int chunksize;
unsigned int datasize;
int valsize;
int codepage;
/* parse Optional Data Chunk */
read(fd, tmp, 21);
if (memcmp(tmp + 5, "OPDA", 4) != 0)
{
DEBUGF("metadata error: missing Optional Data Chunk\n");
return false;
}
/* Optional Data Chunk size */
chunksize = get_long_be(tmp + 9);
/* parse Data Chunk */
if (memcmp(tmp + 13, "Dch", 3) != 0)
{
DEBUGF("metadata error: missing Data Chunk\n");
return false;
}
codepage = convert_smaf_codetype(tmp[16]);
if (codepage < 0)
{
DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[16]);
return false;
}
/* Data Chunk size */
datasize = get_long_be(tmp + 17);
while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
&& (datasize > 0 && bufsize > 0))
{
if (read(fd, tmp, 4) <= 0)
return false;
valsize = (tmp[2] << 8) | tmp[3];
datasize -= (valsize + 4);
switch ((tmp[0]<<8)|tmp[1])
{
case TAG_TITLE:
id3->title = buf;
read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
break;
case TAG_ARTIST:
id3->artist = buf;
read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
break;
case TAG_COMPOSER:
id3->composer = buf;
read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
break;
default:
lseek(fd, valsize, SEEK_CUR);
break;
}
}
/* search Score Track Chunk */
lseek(fd, 29 + chunksize, SEEK_SET);
if (search_chunk(fd, "MTR", 3) == 0)
{
DEBUGF("metadata error: missing Score Track Chunk\n");
return false;
}
/*
* search next chunk
* usually, next chunk ('M***') found within 40 bytes.
*/
chunksize = 40;
read(fd, tmp, chunksize);
tmp[chunksize] = 'M'; /* stopper */
while (*p != 'M')
p++;
chunksize -= (p - tmp);
if (chunksize == 0)
{
DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk");
return false;
}
/* search Score Track Stream PCM Data Chunk */
lseek(fd, -chunksize, SEEK_CUR);
if (search_chunk(fd, "Mtsp", 4) == 0)
{
DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk\n");
return false;
}
/*
* parse Score Track Stream Wave Data Chunk
* tmp
* +4-7: chunk size (WaveType(3bytes) + wave data count)
* +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit
* +9: frequency (MSB)
* +10: frequency (LSB)
*/
read(fd, tmp, 11);
if (memcmp(tmp, "Mwa", 3) != 0)
{
DEBUGF("metadata error: missing Score Track Stream Wave Data Chunk\n");
return false;
}
/* set track length and bitrate */
id3->frequency = (tmp[9] << 8) | tmp[10];
set_length(id3, tmp[8], tmp[8] & 0x0f, get_long_be(tmp + 4) - 3);
return true;
}
bool get_smaf_metadata(int fd, struct mp3entry* id3)
{
/* temporary buffer */
unsigned char *tmp = (unsigned char *)id3->path;
unsigned int chunksize;
id3->title = NULL;
id3->artist = NULL;
id3->composer = NULL;
id3->vbr = false; /* All SMAF files are CBR */
id3->filesize = filesize(fd);
/* check File Chunk and Contents Info Chunk */
lseek(fd, 0, SEEK_SET);
read(fd, tmp, 16);
if ((memcmp(tmp, "MMMD", 4) != 0) || (memcmp(tmp + 8, "CNTI", 4) != 0))
{
DEBUGF("metadata error: does not smaf format\n");
return false;
}
chunksize = get_long_be(tmp + 12);
if (chunksize > 5)
return parse_smaf_audio_track(fd, id3, chunksize);
return parse_smaf_score_track(fd, id3);
}

130
lib/rbcodec/metadata/spc.c Normal file
View file

@ -0,0 +1,130 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "debug.h"
#include "rbunicode.h"
bool get_spc_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char * buf = (unsigned char *)id3->path;
char * p;
unsigned long length;
unsigned long fade;
bool isbinary = true;
int i;
/* try to get the ID666 tag */
if ((lseek(fd, 0x2e, SEEK_SET) < 0)
|| (read(fd, buf, 0xD2) < 0xD2))
{
DEBUGF("lseek or read failed\n");
return false;
}
p = id3->id3v2buf;
id3->title = p;
buf[31] = 0;
p = iso_decode(buf, p, 0, 32);
buf += 32;
id3->album = p;
buf[31] = 0;
p = iso_decode(buf, p, 0, 32);
buf += 48;
id3->comment = p;
buf[31] = 0;
p = iso_decode(buf, p, 0, 32);
buf += 32;
/* Date check */
if(buf[2] == '/' && buf[5] == '/')
isbinary = false;
/* Reserved bytes check */
if(buf[0xD2 - 0x2E - 112] >= '0' &&
buf[0xD2 - 0x2E - 112] <= '9' &&
buf[0xD3 - 0x2E - 112] == 0x00)
isbinary = false;
/* is length & fade only digits? */
for (i=0;i<8 && (
(buf[0xA9 - 0x2E - 112+i]>='0'&&buf[0xA9 - 0x2E - 112+i]<='9') ||
buf[0xA9 - 0x2E - 112+i]=='\0');
i++);
if (i==8) isbinary = false;
if(isbinary) {
id3->year = buf[0] | (buf[1]<<8);
buf += 11;
length = (buf[0] | (buf[1]<<8) | (buf[2]<<16)) * 1000;
buf += 3;
fade = (buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24));
buf += 4;
} else {
char tbuf[6];
buf += 6;
buf[4] = 0;
id3->year = atoi(buf);
buf += 5;
memcpy(tbuf, buf, 3);
tbuf[3] = 0;
length = atoi(tbuf) * 1000;
buf += 3;
memcpy(tbuf, buf, 5);
tbuf[5] = 0;
fade = atoi(tbuf);
buf += 5;
}
id3->artist = p;
buf[31] = 0;
iso_decode(buf, p, 0, 32);
if (length==0) {
length=3*60*1000; /* 3 minutes */
fade=5*1000; /* 5 seconds */
}
id3->length = length+fade;
id3->filesize = filesize(fd);
id3->genre_string = id3_get_num_genre(36);
return true;
}

123
lib/rbcodec/metadata/tta.c Normal file
View file

@ -0,0 +1,123 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Yoshihisa Uchida
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
#define TTA1_SIGN 0x31415454
#define TTA_HEADER_ID 0
#define TTA_HEADER_AUDIO_FORMAT (TTA_HEADER_ID + sizeof(unsigned int))
#define TTA_HEADER_NUM_CHANNELS (TTA_HEADER_AUDIO_FORMAT + sizeof(unsigned short))
#define TTA_HEADER_BITS_PER_SAMPLE (TTA_HEADER_NUM_CHANNELS + sizeof(unsigned short))
#define TTA_HEADER_SAMPLE_RATE (TTA_HEADER_BITS_PER_SAMPLE + sizeof(unsigned short))
#define TTA_HEADER_DATA_LENGTH (TTA_HEADER_SAMPLE_RATE + sizeof(unsigned int))
#define TTA_HEADER_CRC32 (TTA_HEADER_DATA_LENGTH + sizeof(unsigned int))
#define TTA_HEADER_SIZE (TTA_HEADER_CRC32 + sizeof(unsigned int))
#define TTA_HEADER_GETTER_ID(x) get_long_le(x)
#define TTA_HEADER_GETTER_AUDIO_FORMAT(x) get_short_le(x)
#define TTA_HEADER_GETTER_NUM_CHANNELS(x) get_short_le(x)
#define TTA_HEADER_GETTER_BITS_PER_SAMPLE(x) get_short_le(x)
#define TTA_HEADER_GETTER_SAMPLE_RATE(x) get_long_le(x)
#define TTA_HEADER_GETTER_DATA_LENGTH(x) get_long_le(x)
#define TTA_HEADER_GETTER_CRC32(x) get_long_le(x)
#define GET_HEADER(x, tag) TTA_HEADER_GETTER_ ## tag((x) + TTA_HEADER_ ## tag)
static void read_id3_tags(int fd, struct mp3entry* id3)
{
id3->title = NULL;
id3->filesize = filesize(fd);
id3->id3v2len = getid3v2len(fd);
id3->tracknum = 0;
id3->discnum = 0;
id3->vbr = false; /* All TTA files are CBR */
/* first get id3v2 tags. if no id3v2 tags ware found, get id3v1 tags */
if (id3->id3v2len)
{
setid3v2title(fd, id3);
id3->first_frame_offset = id3->id3v2len;
return;
}
setid3v1title(fd, id3);
}
bool get_tta_metadata(int fd, struct mp3entry* id3)
{
unsigned char ttahdr[TTA_HEADER_SIZE];
unsigned int datasize;
unsigned int origsize;
int bps;
lseek(fd, 0, SEEK_SET);
/* read id3 tags */
read_id3_tags(fd, id3);
lseek(fd, id3->id3v2len, SEEK_SET);
/* read TTA header */
if (read(fd, ttahdr, TTA_HEADER_SIZE) < 0)
return false;
/* check for TTA3 signature */
if ((GET_HEADER(ttahdr, ID)) != TTA1_SIGN)
return false;
/* skip check CRC */
id3->channels = (GET_HEADER(ttahdr, NUM_CHANNELS));
id3->frequency = (GET_HEADER(ttahdr, SAMPLE_RATE));
id3->length = ((GET_HEADER(ttahdr, DATA_LENGTH)) / id3->frequency) * 1000LL;
bps = (GET_HEADER(ttahdr, BITS_PER_SAMPLE));
datasize = id3->filesize - id3->first_frame_offset;
origsize = (GET_HEADER(ttahdr, DATA_LENGTH)) * ((bps + 7) / 8) * id3->channels;
id3->bitrate = (int) ((uint64_t) datasize * id3->frequency * id3->channels * bps
/ (origsize * 1000LL));
/* output header info (for debug) */
DEBUGF("TTA header info ----\n");
DEBUGF("id: %x\n", (unsigned int)(GET_HEADER(ttahdr, ID)));
DEBUGF("channels: %d\n", id3->channels);
DEBUGF("frequency: %ld\n", id3->frequency);
DEBUGF("length: %ld\n", id3->length);
DEBUGF("bitrate: %d\n", id3->bitrate);
DEBUGF("bits per sample: %d\n", bps);
DEBUGF("compressed size: %d\n", datasize);
DEBUGF("original size: %d\n", origsize);
DEBUGF("id3----\n");
DEBUGF("artist: %s\n", id3->artist);
DEBUGF("title: %s\n", id3->title);
DEBUGF("genre: %s\n", id3->genre_string);
return true;
}

195
lib/rbcodec/metadata/vgm.c Normal file
View file

@ -0,0 +1,195 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
typedef unsigned char byte;
enum { header_size = 0x40 };
enum { max_field = 64 };
struct header_t
{
char tag [4];
byte data_size [4];
byte version [4];
byte psg_rate [4];
byte ym2413_rate [4];
byte gd3_offset [4];
byte track_duration [4];
byte loop_offset [4];
byte loop_duration [4];
byte frame_rate [4];
byte noise_feedback [2];
byte noise_width;
byte unused1;
byte ym2612_rate [4];
byte ym2151_rate [4];
byte data_offset [4];
byte unused2 [8];
};
static byte const* skip_gd3_str( byte const* in, byte const* end )
{
while ( end - in >= 2 )
{
in += 2;
if ( !(in [-2] | in [-1]) )
break;
}
return in;
}
static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
{
byte const* mid = skip_gd3_str( in, end );
int len = (mid - in) / 2 - 1;
if ( field && len > 0 )
{
len = len < (int) max_field ? len : (int) max_field;
field [len] = 0;
/* Conver to utf8 */
utf16LEdecode( in, field, len );
/* Copy string back to id3v2buf */
strcpy( (char*) in, field );
}
return mid;
}
static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
{
return skip_gd3_str( get_gd3_str( in, end, field ), end );
}
static void parse_gd3( byte const* in, byte const* end, struct mp3entry* id3 )
{
char* p = id3->path;
id3->title = (char *) in;
in = get_gd3_pair( in, end, p ); /* Song */
id3->album = (char *) in;
in = get_gd3_pair( in, end, p ); /* Game */
in = get_gd3_pair( in, end, NULL ); /* System */
id3->artist = (char *) in;
in = get_gd3_pair( in, end, p ); /* Author */
#if MEMORYSIZE > 2
in = get_gd3_str ( in, end, NULL ); /* Copyright */
in = get_gd3_pair( in, end, NULL ); /* Dumper */
id3->comment = (char *) in;
in = get_gd3_str ( in, end, p ); /* Comment */
#endif
}
int const gd3_header_size = 12;
static long check_gd3_header( byte* h, long remain )
{
if ( remain < gd3_header_size ) return 0;
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
if ( get_long_le( h + 4 ) >= 0x200 ) return 0;
long gd3_size = get_long_le( h + 8 );
if ( gd3_size > remain - gd3_header_size )
gd3_size = remain - gd3_header_size;
return gd3_size;
}
static void get_vgm_length( struct header_t* h, struct mp3entry* id3 )
{
long length = get_long_le( h->track_duration ) * 10 / 441;
if ( length > 0 )
{
long loop_length = 0, intro_length = 0;
long loop = get_long_le( h->loop_duration );
if ( loop > 0 && get_long_le( h->loop_offset ) )
{
loop_length = loop * 10 / 441;
intro_length = length - loop_length;
}
else
{
intro_length = length; /* make it clear that track is no longer than length */
loop_length = 0;
}
id3->length = intro_length + 2 * loop_length; /* intro + 2 loops */
return;
}
id3->length = 150 * 1000; /* 2.5 minutes */
}
bool get_vgm_metadata(int fd, struct mp3entry* id3)
{
/* Use the id3v2 part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->id3v2buf;
int read_bytes;
memset(buf, 0, ID3V2_BUF_SIZE);
if ((lseek(fd, 0, SEEK_SET) < 0)
|| ((read_bytes = read(fd, buf, header_size)) < header_size))
{
return false;
}
id3->vbr = false;
id3->filesize = filesize(fd);
id3->bitrate = 706;
id3->frequency = 44100;
/* If file is gzipped, will get metadata later */
if (memcmp(buf, "Vgm ", 4))
{
/* We must set a default song length here because
the codec can't do it anymore */
id3->length = 150 * 1000; /* 2.5 minutes */
return true;
}
/* Get song length from header */
struct header_t* header = (struct header_t*) buf;
get_vgm_length( header, id3 );
long gd3_offset = get_long_le( header->gd3_offset ) - 0x2C;
/* No gd3 tag found */
if ( gd3_offset < 0 )
return true;
/* Seek to gd3 offset and read as
many bytes posible */
gd3_offset = id3->filesize - (header_size + gd3_offset);
if ((lseek(fd, -gd3_offset, SEEK_END) < 0)
|| ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) <= 0))
return true;
byte* gd3 = buf;
long gd3_size = check_gd3_header( gd3, read_bytes );
/* GD3 tag is zero */
if ( gd3_size == 0 )
return true;
/* Finally, parse gd3 tag */
if ( gd3 )
parse_gd3( gd3 + gd3_header_size, gd3 + read_bytes, id3 );
return true;
}

View file

@ -0,0 +1,381 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "structec.h"
/* Define LOGF_ENABLE to enable logf output in this file */
/*#define LOGF_ENABLE*/
#include "logf.h"
struct file
{
int fd;
bool packet_ended;
long packet_remaining;
};
/* Read an Ogg page header. file->packet_remaining is set to the size of the
* first packet on the page; file->packet_ended is set to true if the packet
* ended on the current page. Returns true if the page header was
* successfully read.
*/
static bool file_read_page_header(struct file* file)
{
unsigned char buffer[64];
ssize_t table_left;
/* Size of page header without segment table */
if (read(file->fd, buffer, 27) != 27)
{
return false;
}
if (memcmp("OggS", buffer, 4))
{
return false;
}
/* Skip pattern (4), version (1), flags (1), granule position (8),
* serial (4), pageno (4), checksum (4)
*/
table_left = buffer[26];
file->packet_remaining = 0;
/* Read segment table for the first packet */
do
{
ssize_t count = MIN(sizeof(buffer), (size_t) table_left);
int i;
if (read(file->fd, buffer, count) < count)
{
return false;
}
table_left -= count;
for (i = 0; i < count; i++)
{
file->packet_remaining += buffer[i];
if (buffer[i] < 255)
{
file->packet_ended = true;
/* Skip remainder of the table */
if (lseek(file->fd, table_left, SEEK_CUR) < 0)
{
return false;
}
table_left = 0;
break;
}
}
}
while (table_left > 0);
return true;
}
/* Read (up to) buffer_size of data from the file. If buffer is NULL, just
* skip ahead buffer_size bytes (like lseek). Returns number of bytes read,
* 0 if there is no more data to read (in the packet or the file), < 0 if a
* read error occurred.
*/
static ssize_t file_read(struct file* file, void* buffer, size_t buffer_size)
{
ssize_t done = 0;
ssize_t count = -1;
do
{
if (file->packet_remaining <= 0)
{
if (file->packet_ended)
{
break;
}
if (!file_read_page_header(file))
{
count = -1;
break;
}
}
count = MIN(buffer_size, (size_t) file->packet_remaining);
if (buffer)
{
count = read(file->fd, buffer, count);
}
else
{
if (lseek(file->fd, count, SEEK_CUR) < 0)
{
count = -1;
}
}
if (count <= 0)
{
break;
}
if (buffer)
{
buffer += count;
}
buffer_size -= count;
done += count;
file->packet_remaining -= count;
}
while (buffer_size > 0);
return (count < 0 ? count : done);
}
/* Read an int32 from file. Returns false if a read error occurred.
*/
static bool file_read_int32(struct file* file, int32_t* value)
{
char buf[sizeof(int32_t)];
if (file_read(file, buf, sizeof(buf)) < (ssize_t) sizeof(buf))
{
return false;
}
*value = get_long_le(buf);
return true;
}
/* Read a string from the file. Read up to buffer_size bytes, or, if eos
* != -1, until the eos character is found (eos is not stored in buf,
* unless it is nil). Writes up to buffer_size chars to buf, always
* terminating with a nil. Returns number of chars read or < 0 if a read
* error occurred.
*
* Unfortunately this is a slightly modified copy of read_string() in
* metadata_common.c...
*/
static long file_read_string(struct file* file, char* buffer,
long buffer_size, int eos, long size)
{
long read_bytes = 0;
while (size > 0)
{
char c;
if (file_read(file, &c, 1) != 1)
{
read_bytes = -1;
break;
}
read_bytes++;
size--;
if ((eos != -1) && (eos == (unsigned char) c))
{
break;
}
if (buffer_size > 1)
{
*buffer++ = c;
buffer_size--;
}
else if (eos == -1)
{
/* No point in reading any more, skip remaining data */
if (file_read(file, NULL, size) < 0)
{
read_bytes = -1;
}
else
{
read_bytes += size;
}
break;
}
}
*buffer = 0;
return read_bytes;
}
/* Init struct file for reading from fd. type is the AFMT_* codec type of
* the file, and determines if Ogg pages are to be read. remaining is the
* max amount to read if codec type is FLAC; it is ignored otherwise.
* Returns true if the file was successfully initialized.
*/
static bool file_init(struct file* file, int fd, int type, int remaining)
{
memset(file, 0, sizeof(*file));
file->fd = fd;
if (type == AFMT_OGG_VORBIS || type == AFMT_SPEEX)
{
if (!file_read_page_header(file))
{
return false;
}
}
if (type == AFMT_OGG_VORBIS)
{
char buffer[7];
/* Read packet header (type and id string) */
if (file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer))
{
return false;
}
/* The first byte of a packet is the packet type; comment packets
* are type 3.
*/
if (buffer[0] != 3)
{
return false;
}
}
else if (type == AFMT_FLAC)
{
file->packet_remaining = remaining;
file->packet_ended = true;
}
return true;
}
/* Read the items in a Vorbis comment packet. For Ogg files, the file must
* be located on a page start, for other files, the beginning of the comment
* data (i.e., the vendor string length). Returns total size of the
* comments, or 0 if there was a read error.
*/
long read_vorbis_tags(int fd, struct mp3entry *id3,
long tag_remaining)
{
struct file file;
char *buf = id3->id3v2buf;
int32_t comment_count;
int32_t len;
long comment_size = 0;
int buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
int i;
if (!file_init(&file, fd, id3->codectype, tag_remaining))
{
return 0;
}
/* Skip vendor string */
if (!file_read_int32(&file, &len) || (file_read(&file, NULL, len) < 0))
{
return 0;
}
if (!file_read_int32(&file, &comment_count))
{
return 0;
}
comment_size += 4 + len + 4;
for (i = 0; i < comment_count && file.packet_remaining > 0; i++)
{
char name[TAG_NAME_LENGTH];
int32_t read_len;
if (!file_read_int32(&file, &len))
{
return 0;
}
comment_size += 4 + len;
read_len = file_read_string(&file, name, sizeof(name), '=', len);
if (read_len < 0)
{
return 0;
}
len -= read_len;
read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len);
if (read_len < 0)
{
return 0;
}
logf("Vorbis comment %d: %s=%s", i, name, id3->path);
/* Is it an embedded cuesheet? */
if (!strcasecmp(name, "CUESHEET"))
{
id3->has_embedded_cuesheet = true;
id3->embedded_cuesheet.pos = lseek(file.fd, 0, SEEK_CUR) - read_len;
id3->embedded_cuesheet.size = len;
id3->embedded_cuesheet.encoding = CHAR_ENC_UTF_8;
}
else
{
len = parse_tag(name, id3->path, id3, buf, buf_remaining,
TAGTYPE_VORBIS);
}
buf += len;
buf_remaining -= len;
}
/* Skip to the end of the block (needed by FLAC) */
if (file.packet_remaining)
{
if (file_read(&file, NULL, file.packet_remaining) < 0)
{
return 0;
}
}
return comment_size;
}

View file

@ -0,0 +1,49 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Yoshihisa Uchida
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
bool get_vox_metadata(int fd, struct mp3entry* id3)
{
/*
* vox is headerless format
*
* frequency: 8000 Hz
* channels: mono
* bitspersample: 4
*/
id3->frequency = 8000;
id3->bitrate = 8000 * 4 / 1000;
id3->vbr = false; /* All VOX files are CBR */
id3->filesize = filesize(fd);
id3->length = id3->filesize >> 2;
return true;
}

432
lib/rbcodec/metadata/wave.c Normal file
View file

@ -0,0 +1,432 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
* Copyright (C) 2010 Yoshihisa Uchida
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "rbunicode.h"
#include "logf.h"
#ifdef DEBUGF
#undef DEBUGF
#define DEBUGF(...)
#endif
/* Wave(RIFF)/Wave64 format */
# define AV_WL32(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
} while(0)
# define AV_WL16(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
} while(0)
enum {
RIFF_CHUNK = 0,
WAVE_CHUNK,
FMT_CHUNK,
FACT_CHUNK,
DATA_CHUNK,
LIST_CHUNK,
};
/* Wave chunk names */
#define WAVE_CHUNKNAME_LENGTH 4
#define WAVE_CHUNKSIZE_LENGTH 4
static const unsigned char * const wave_chunklist
= "RIFF"
"WAVE"
"fmt "
"fact"
"data"
"LIST";
/* Wave64 GUIDs */
#define WAVE64_CHUNKNAME_LENGTH 16
#define WAVE64_CHUNKSIZE_LENGTH 8
static const unsigned char * const wave64_chunklist
= "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00"
"wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a"
"fmt \xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a"
"fact\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a"
"data\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a"
"\xbc\x94\x5f\x92\x5a\x52\xd2\x11\x86\xdc\x00\xc0\x4f\x8e\xdb\x8a";
/* list/info chunk */
struct info_chunk {
const unsigned char* tag;
size_t offset;
};
/* info chunk names are common wave/wave64 */
static const struct info_chunk info_chunks[] = {
{ "INAM", offsetof(struct mp3entry, title), }, /* title */
{ "IART", offsetof(struct mp3entry, artist), }, /* artist */
{ "ISBJ", offsetof(struct mp3entry, albumartist), }, /* albumartist */
{ "IPRD", offsetof(struct mp3entry, album), }, /* album */
{ "IWRI", offsetof(struct mp3entry, composer), }, /* composer */
{ "ICMT", offsetof(struct mp3entry, comment), }, /* comment */
{ "ISRF", offsetof(struct mp3entry, grouping), }, /* grouping */
{ "IGNR", offsetof(struct mp3entry, genre_string), }, /* genre */
{ "ICRD", offsetof(struct mp3entry, year_string), }, /* date */
{ "IPRT", offsetof(struct mp3entry, track_string), }, /* track/trackcount */
{ "IFRM", offsetof(struct mp3entry, disc_string), }, /* disc/disccount */
};
#define INFO_CHUNK_COUNT ((int)ARRAYLEN(info_chunks))
/* support formats */
enum
{
WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */
WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */
WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */
WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */
WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */
WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */
WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */
WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */
WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */
IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */
IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */
WAVE_FORMAT_ATRAC3 = 0x0270, /* Atrac3 stream */
WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */
WAVE_FORMAT_EXTENSIBLE = 0xFFFE,
};
struct wave_fmt {
unsigned int formattag;
unsigned int channels;
unsigned int blockalign;
unsigned int bitspersample;
unsigned int samplesperblock;
uint32_t totalsamples;
uint64_t numbytes;
};
static unsigned char *convert_utf8(const unsigned char *src, unsigned char *dst,
int size, bool is_64)
{
if (is_64)
{
/* Note: wave64: metadata codepage is UTF-16 only */
return utf16LEdecode(src, dst, size);
}
return iso_decode(src, dst, -1, size);
}
static void set_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3)
{
switch (fmt->formattag)
{
case WAVE_FORMAT_PCM:
case WAVE_FORMAT_IEEE_FLOAT:
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
case IBM_FORMAT_ALAW:
case IBM_FORMAT_MULAW:
fmt->blockalign = fmt->bitspersample * fmt->channels >> 3;
fmt->samplesperblock = 1;
break;
case WAVE_FORMAT_YAMAHA_ADPCM:
if (id3->channels != 0)
{
fmt->samplesperblock =
(fmt->blockalign == ((id3->frequency / 60) + 4) * fmt->channels)?
id3->frequency / 30 : (fmt->blockalign << 1) / fmt->channels;
}
break;
case WAVE_FORMAT_DIALOGIC_OKI_ADPCM:
fmt->blockalign = 1;
fmt->samplesperblock = 2;
break;
case WAVE_FORMAT_SWF_ADPCM:
if (fmt->bitspersample != 0 && id3->channels != 0)
{
fmt->samplesperblock
= (((fmt->blockalign << 3) - 2) / fmt->channels - 22)
/ fmt->bitspersample + 1;
}
break;
default:
break;
}
if (fmt->blockalign != 0)
fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock;
}
static void parse_riff_format(unsigned char* buf, int fmtsize, struct wave_fmt *fmt,
struct mp3entry* id3)
{
/* wFormatTag */
fmt->formattag = buf[0] | (buf[1] << 8);
/* wChannels */
fmt->channels = buf[2] | (buf[3] << 8);
/* dwSamplesPerSec */
id3->frequency = get_long_le(&buf[4]);
/* dwAvgBytesPerSec */
id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000;
/* wBlockAlign */
fmt->blockalign = buf[12] | (buf[13] << 8);
/* wBitsPerSample */
fmt->bitspersample = buf[14] | (buf[15] << 8);
if (fmt->formattag != WAVE_FORMAT_EXTENSIBLE)
{
if (fmtsize > 19)
{
/* wSamplesPerBlock */
fmt->samplesperblock = buf[18] | (buf[19] << 8);
}
}
else if (fmtsize > 25)
{
/* wValidBitsPerSample */
fmt->bitspersample = buf[18] | (buf[19] << 8);
/* SubFormat */
fmt->formattag = buf[24] | (buf[25] << 8);
}
/* Check for ATRAC3 stream */
if (fmt->formattag == WAVE_FORMAT_ATRAC3)
{
int jsflag = 0;
if(id3->bitrate == 66 || id3->bitrate == 94)
jsflag = 1;
id3->extradata_size = 14;
id3->channels = 2;
id3->codectype = AFMT_OMA_ATRAC3;
id3->bytesperframe = fmt->blockalign;
/* Store the extradata for the codec */
AV_WL16(&id3->id3v2buf[0], 1); // always 1
AV_WL32(&id3->id3v2buf[2], id3->frequency);// samples rate
AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[10], 1); // always 1
AV_WL16(&id3->id3v2buf[12], 0); // always 0
}
}
static void parse_list_chunk(int fd, struct mp3entry* id3, int chunksize, bool is_64)
{
unsigned char tmpbuf[ID3V2_BUF_SIZE];
unsigned char *bp = tmpbuf;
unsigned char *endp;
unsigned char *data_pos;
unsigned char *tag_pos = id3->id3v2buf;
int datasize;
int infosize;
int remain;
int i;
if (is_64)
lseek(fd, 4, SEEK_CUR);
else if (read(fd, bp, 4) < 4 || memcmp(bp, "INFO", 4))
return;
/* decrease skip bytes */
chunksize -= 4;
infosize = read(fd, bp, (ID3V2_BUF_SIZE > chunksize)? chunksize : ID3V2_BUF_SIZE);
if (infosize <= 8)
return;
endp = bp + infosize;
while (bp < endp)
{
datasize = get_long_le(bp + 4);
data_pos = bp + 8;
remain = ID3V2_BUF_SIZE - (tag_pos - (unsigned char*)id3->id3v2buf);
if (remain < 1)
break;
for (i = 0; i < INFO_CHUNK_COUNT; i++)
{
if (memcmp(bp, info_chunks[i].tag, 4) == 0)
{
*((char **)(((char*)id3) + info_chunks[i].offset)) = tag_pos;
tag_pos = convert_utf8(data_pos, tag_pos,
(datasize + 1 >= remain )? remain - 1 : datasize,
is_64);
*tag_pos++ = 0;
break;
}
}
bp = data_pos + datasize + (datasize & 1);
};
}
static bool read_header(int fd, struct mp3entry* id3, const unsigned char *chunknames,
bool is_64)
{
/* Use the temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
struct wave_fmt fmt;
const unsigned int namelen = (is_64)? WAVE64_CHUNKNAME_LENGTH : WAVE_CHUNKNAME_LENGTH;
const unsigned int sizelen = (is_64)? WAVE64_CHUNKSIZE_LENGTH : WAVE_CHUNKSIZE_LENGTH;
const unsigned int len = namelen + sizelen;
uint64_t chunksize;
uint64_t offset = len + namelen;
int read_data;
memset(&fmt, 0, sizeof(struct wave_fmt));
id3->vbr = false; /* All Wave/Wave64 files are CBR */
id3->filesize = filesize(fd);
/* get RIFF chunk header */
lseek(fd, 0, SEEK_SET);
read(fd, buf, offset);
if ((memcmp(buf, chunknames + RIFF_CHUNK * namelen, namelen) != 0) ||
(memcmp(buf + len, chunknames + WAVE_CHUNK * namelen, namelen) != 0))
{
DEBUGF("metadata error: missing riff header.\n");
return false;
}
/* iterate over WAVE chunks until 'data' chunk */
while (read(fd, buf, len) > 0)
{
offset += len;
/* get chunk size (when the header is wave64, chunksize includes GUID and data length) */
chunksize = (is_64) ? get_uint64_le(buf + namelen) - len :
get_long_le(buf + namelen);
read_data = 0;
if (memcmp(buf, chunknames + FMT_CHUNK * namelen, namelen) == 0)
{
DEBUGF("find 'fmt ' chunk\n");
if (chunksize < 16)
{
DEBUGF("metadata error: 'fmt ' chunk is too small: %d\n", (int)chunksize);
return false;
}
/* get and parse format */
read_data = (chunksize > 25)? 26 : chunksize;
read(fd, buf, read_data);
parse_riff_format(buf, read_data, &fmt, id3);
}
else if (memcmp(buf, chunknames + FACT_CHUNK * namelen, namelen) == 0)
{
DEBUGF("find 'fact' chunk\n");
/* dwSampleLength */
if (chunksize >= sizelen)
{
/* get totalsamples */
read_data = sizelen;
read(fd, buf, read_data);
fmt.totalsamples = (is_64)? get_uint64_le(buf) : get_long_le(buf);
}
}
else if (memcmp(buf, chunknames + DATA_CHUNK * namelen, namelen) == 0)
{
DEBUGF("find 'data' chunk\n");
fmt.numbytes = chunksize;
if (fmt.formattag == WAVE_FORMAT_ATRAC3)
id3->first_frame_offset = offset;
}
else if (memcmp(buf, chunknames + LIST_CHUNK * namelen, namelen) == 0)
{
DEBUGF("find 'LIST' chunk\n");
parse_list_chunk(fd, id3, chunksize, is_64);
lseek(fd, offset, SEEK_SET);
}
/* padded to next chunk */
chunksize += ((is_64)? ((1 + ~chunksize) & 0x07) : (chunksize & 1));
offset += chunksize;
if (offset >= id3->filesize)
break;
lseek(fd, chunksize - read_data, SEEK_CUR);
}
if (fmt.numbytes == 0)
{
DEBUGF("metadata error: read error or missing 'data' chunk.\n");
return false;
}
if (fmt.totalsamples == 0)
set_totalsamples(&fmt, id3);
if (id3->frequency == 0 || id3->bitrate == 0)
{
DEBUGF("metadata error: frequency or bitrate is 0\n");
return false;
}
/* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
id3->length = (fmt.formattag != WAVE_FORMAT_ATRAC3)?
(uint64_t)fmt.totalsamples * 1000 / id3->frequency :
((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate;
/* output header/id3 info (for debug) */
DEBUGF("%s header info ----\n", (is_64)? "wave64" : "wave");
DEBUGF(" format: %04x\n", (int)fmt.formattag);
DEBUGF(" channels: %u\n", fmt.channels);
DEBUGF(" blockalign: %u\n", fmt.blockalign);
DEBUGF(" bitspersample: %u\n", fmt.bitspersample);
DEBUGF(" samplesperblock: %u\n", fmt.samplesperblock);
DEBUGF(" totalsamples: %u\n", (unsigned int)fmt.totalsamples);
DEBUGF(" numbytes: %u\n", (unsigned int)fmt.numbytes);
DEBUGF("id3 info ----\n");
DEBUGF(" frequency: %u\n", (unsigned int)id3->frequency);
DEBUGF(" bitrate: %d\n", id3->bitrate);
DEBUGF(" length: %u\n", (unsigned int)id3->length);
return true;
}
bool get_wave_metadata(int fd, struct mp3entry* id3)
{
return read_header(fd, id3, wave_chunklist, false);
}
bool get_wave64_metadata(int fd, struct mp3entry* id3)
{
return read_header(fd, id3, wave64_chunklist, true);
}

View file

@ -0,0 +1,160 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 David Bryant
*
* 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.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
#define ID_UNIQUE 0x3f
#define ID_LARGE 0x80
#define ID_SAMPLE_RATE 0x27
#define MONO_FLAG 4
#define HYBRID_FLAG 8
static const long wavpack_sample_rates [] =
{
6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000, 192000
};
/* A simple parser to read basic information from a WavPack file. This
* now works with self-extrating WavPack files and also will scan the
* metadata for non-standard sampling rates. This no longer fails on
* WavPack files containing floating-point audio data because these are
* now converted to standard Rockbox format in the decoder, and also
* handles the case where up to 15 non-audio blocks might occur at the
* beginning of the file.
*/
bool get_wavpack_metadata(int fd, struct mp3entry* id3)
{
/* Use the trackname part of the id3 structure as a temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
uint32_t totalsamples = (uint32_t) -1;
int i;
for (i = 0; i < 256; ++i) {
/* at every 256 bytes into file, try to read a WavPack header */
if ((lseek(fd, i * 256, SEEK_SET) < 0) || (read(fd, buf, 32) < 32))
return false;
/* if valid WavPack 4 header version, break */
if (memcmp (buf, "wvpk", 4) == 0 && buf [9] == 4 &&
(buf [8] >= 2 && buf [8] <= 0x10))
break;
}
if (i == 256) {
logf ("Not a WavPack file");
return false;
}
id3->vbr = true; /* All WavPack files are VBR */
id3->filesize = filesize (fd);
/* check up to 16 headers before we give up finding one with audio */
for (i = 0; i < 16; ++i) {
uint32_t meta_bytes = get_long_le(&buf [4]) - 24;
uint32_t trial_totalsamples = get_long_le(&buf[12]);
uint32_t blockindex = get_long_le(&buf[16]);
uint32_t blocksamples = get_long_le(&buf[20]);
uint32_t flags = get_long_le(&buf[24]);
if (totalsamples == (uint32_t) -1 && blockindex == 0)
totalsamples = trial_totalsamples;
if (blocksamples) {
int srindx = ((buf [26] >> 7) & 1) + ((buf [27] << 1) & 14);
if (srindx == 15) {
uint32_t meta_size;
id3->frequency = 44100;
while (meta_bytes >= 6) {
if (read(fd, buf, 2) < 2)
break;
if (buf [0] & ID_LARGE) {
if (read(fd, buf + 2, 2) < 2)
break;
meta_size = (buf [1] << 1) + (buf [2] << 9) + (buf [3] << 17);
meta_bytes -= meta_size + 4;
}
else {
meta_size = buf [1] << 1;
meta_bytes -= meta_size + 2;
if ((buf [0] & ID_UNIQUE) == ID_SAMPLE_RATE) {
if (meta_size == 4 && read(fd, buf + 2, 4) == 4)
id3->frequency = buf [2] + (buf [3] << 8) + (buf [4] << 16);
break;
}
}
if (meta_size > 0 && lseek(fd, meta_size, SEEK_CUR) < 0)
break;
}
}
else
id3->frequency = wavpack_sample_rates[srindx];
/* if the total number of samples is still unknown, make a guess on the high side (for now) */
if (totalsamples == (uint32_t) -1) {
totalsamples = id3->filesize * 3;
if (!(flags & HYBRID_FLAG))
totalsamples /= 2;
if (!(flags & MONO_FLAG))
totalsamples /= 2;
}
id3->length = ((int64_t) totalsamples * 1000) / id3->frequency;
id3->bitrate = id3->filesize / (id3->length / 8);
read_ape_tags(fd, id3);
return true;
}
else { /* block did not contain audio, so seek to the end and see if there's another */
if ((meta_bytes > 0 && lseek(fd, meta_bytes, SEEK_CUR) < 0) ||
read(fd, buf, 32) < 32 || memcmp (buf, "wvpk", 4) != 0)
break;
}
}
return false;
}