1
0
Fork 0
forked from len0rd/rockbox

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

View file

@ -26,7 +26,6 @@ menus/audiohw_eq_menu.c
menus/eq_menu.c
buffering.c
voice_thread.c
replaygain.c
#else /* !SWCODEC */
mpeg.c
#endif
@ -42,7 +41,6 @@ menus/sound_menu.c
menus/time_menu.c
#endif
misc.c
mp3data.c
onplay.c
playlist.c
playlist_catalog.c
@ -168,29 +166,13 @@ pcmbuf.c
codec_thread.c
playback.c
codecs.c
dsp.c
compressor.c
#ifndef HAVE_HARDWARE_BEEP
beep.c
#endif
#ifdef HAVE_PITCHSCREEN
tdspeed.c
#endif
#ifdef HAVE_RECORDING
enc_config.c
recorder/pcm_record.c
#endif
eq.c
#if defined(CPU_COLDFIRE)
dsp_cf.S
eq_cf.S
#elif defined(CPU_ARM)
dsp_arm.S
#if ARM_ARCH >= 6
dsp_arm_v6.S
#endif
eq_arm.S
#endif
#endif
#ifdef USB_ENABLE_HID
usb_keymaps.c
@ -198,42 +180,6 @@ usb_keymaps.c
#ifndef USB_NONE
gui/usb_screen.c
#endif
metadata.c
metadata/id3tags.c
metadata/mp3.c
#if CONFIG_CODEC == SWCODEC
metadata/metadata_common.c
metadata/aiff.c
metadata/ape.c
metadata/asf.c
metadata/adx.c
metadata/flac.c
metadata/monkeys.c
metadata/mp4.c
metadata/mpc.c
metadata/ogg.c
metadata/sid.c
metadata/mod.c
metadata/spc.c
metadata/vorbis.c
metadata/wave.c
metadata/wavpack.c
metadata/a52.c
metadata/asap.c
metadata/rm.c
metadata/nsf.c
metadata/oma.c
metadata/smaf.c
metadata/au.c
metadata/vox.c
metadata/tta.c
metadata/ay.c
metadata/gbs.c
metadata/hes.c
metadata/sgc.c
metadata/vgm.c
metadata/kss.c
#endif
#ifdef HAVE_TAGCACHE
tagcache.c
#endif

View file

@ -1,363 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 Jeffrey Goode
*
* 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 "config.h"
#include "fixedpoint.h"
#include "fracmul.h"
#include "settings.h"
#include "dsp.h"
#include "compressor.h"
/* Define LOGF_ENABLE to enable logf output in this file */
/*#define LOGF_ENABLE*/
#include "logf.h"
static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */
static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */
static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */
static int32_t release_gain IBSS_ATTR; /* S7.24 format */
#define UNITY (1L << 24) /* unity gain in S7.24 format */
/** COMPRESSOR UPDATE
* Called via the menu system to configure the compressor process */
bool compressor_update(void)
{
static int curr_set[5];
int new_set[5] = {
global_settings.compressor_threshold,
global_settings.compressor_makeup_gain,
global_settings.compressor_ratio,
global_settings.compressor_knee,
global_settings.compressor_release_time};
/* make menu values useful */
int threshold = new_set[0];
bool auto_gain = (new_set[1] == 1);
const int comp_ratios[] = {2, 4, 6, 10, 0};
int ratio = comp_ratios[new_set[2]];
bool soft_knee = (new_set[3] == 1);
int release = new_set[4] * NATIVE_FREQUENCY / 1000;
bool changed = false;
bool active = (threshold < 0);
for (int i = 0; i < 5; i++)
{
if (curr_set[i] != new_set[i])
{
changed = true;
curr_set[i] = new_set[i];
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
switch (i)
{
case 0:
logf(" Compressor Threshold: %d dB\tEnabled: %s",
threshold, active ? "Yes" : "No");
break;
case 1:
logf(" Compressor Makeup Gain: %s",
auto_gain ? "Auto" : "Off");
break;
case 2:
if (ratio)
{ logf(" Compressor Ratio: %d:1", ratio); }
else
{ logf(" Compressor Ratio: Limit"); }
break;
case 3:
logf(" Compressor Knee: %s", soft_knee?"Soft":"Hard");
break;
case 4:
logf(" Compressor Release: %d", release);
break;
}
#endif
}
}
if (changed && active)
{
/* configure variables for compressor operation */
static const int32_t db[] = {
/* positive db equivalents in S15.16 format */
0x000000, 0x241FA4, 0x1E1A5E, 0x1A94C8,
0x181518, 0x1624EA, 0x148F82, 0x1338BD,
0x120FD2, 0x1109EB, 0x101FA4, 0x0F4BB6,
0x0E8A3C, 0x0DD840, 0x0D3377, 0x0C9A0E,
0x0C0A8C, 0x0B83BE, 0x0B04A5, 0x0A8C6C,
0x0A1A5E, 0x09ADE1, 0x094670, 0x08E398,
0x0884F6, 0x082A30, 0x07D2FA, 0x077F0F,
0x072E31, 0x06E02A, 0x0694C8, 0x064BDF,
0x060546, 0x05C0DA, 0x057E78, 0x053E03,
0x04FF5F, 0x04C273, 0x048726, 0x044D64,
0x041518, 0x03DE30, 0x03A89B, 0x037448,
0x03412A, 0x030F32, 0x02DE52, 0x02AE80,
0x027FB0, 0x0251D6, 0x0224EA, 0x01F8E2,
0x01CDB4, 0x01A359, 0x0179C9, 0x0150FC,
0x0128EB, 0x010190, 0x00DAE4, 0x00B4E1,
0x008F82, 0x006AC1, 0x004699, 0x002305};
struct curve_point
{
int32_t db; /* S15.16 format */
int32_t offset; /* S15.16 format */
} db_curve[5];
/** Set up the shape of the compression curve first as decibel
values */
/* db_curve[0] = bottom of knee
[1] = threshold
[2] = top of knee
[3] = 0 db input
[4] = ~+12db input (2 bits clipping overhead) */
db_curve[1].db = threshold << 16;
if (soft_knee)
{
/* bottom of knee is 3dB below the threshold for soft knee*/
db_curve[0].db = db_curve[1].db - (3 << 16);
/* top of knee is 3dB above the threshold for soft knee */
db_curve[2].db = db_curve[1].db + (3 << 16);
if (ratio)
/* offset = -3db * (ratio - 1) / ratio */
db_curve[2].offset = (int32_t)((long long)(-3 << 16)
* (ratio - 1) / ratio);
else
/* offset = -3db for hard limit */
db_curve[2].offset = (-3 << 16);
}
else
{
/* bottom of knee is at the threshold for hard knee */
db_curve[0].db = threshold << 16;
/* top of knee is at the threshold for hard knee */
db_curve[2].db = threshold << 16;
db_curve[2].offset = 0;
}
/* Calculate 0db and ~+12db offsets */
db_curve[4].db = 0xC0A8C; /* db of 2 bits clipping */
if (ratio)
{
/* offset = threshold * (ratio - 1) / ratio */
db_curve[3].offset = (int32_t)((long long)(threshold << 16)
* (ratio - 1) / ratio);
db_curve[4].offset = (int32_t)((long long)-db_curve[4].db
* (ratio - 1) / ratio) + db_curve[3].offset;
}
else
{
/* offset = threshold for hard limit */
db_curve[3].offset = (threshold << 16);
db_curve[4].offset = -db_curve[4].db + db_curve[3].offset;
}
/** Now set up the comp_curve table with compression offsets in the
form of gain factors in S7.24 format */
/* comp_curve[0] is 0 (-infinity db) input */
comp_curve[0] = UNITY;
/* comp_curve[1 to 63] are intermediate compression values
corresponding to the 6 MSB of the input values of a non-clipped
signal */
for (int i = 1; i < 64; i++)
{
/* db constants are stored as positive numbers;
make them negative here */
int32_t this_db = -db[i];
/* no compression below the knee */
if (this_db <= db_curve[0].db)
comp_curve[i] = UNITY;
/* if soft knee and below top of knee,
interpolate along soft knee slope */
else if (soft_knee && (this_db <= db_curve[2].db))
comp_curve[i] = fp_factor(fp_mul(
((this_db - db_curve[0].db) / 6),
db_curve[2].offset, 16), 16) << 8;
/* interpolate along ratio slope above the knee */
else
comp_curve[i] = fp_factor(fp_mul(
fp_div((db_curve[1].db - this_db), db_curve[1].db, 16),
db_curve[3].offset, 16), 16) << 8;
}
/* comp_curve[64] is the compression level of a maximum level,
non-clipped signal */
comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8;
/* comp_curve[65] is the compression level of a maximum level,
clipped signal */
comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8;
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
logf("\n *** Compression Offsets ***");
/* some settings for display only, not used in calculations */
db_curve[0].offset = 0;
db_curve[1].offset = 0;
db_curve[3].db = 0;
for (int i = 0; i <= 4; i++)
{
logf("Curve[%d]: db: % 6.2f\toffset: % 6.2f", i,
(float)db_curve[i].db / (1 << 16),
(float)db_curve[i].offset / (1 << 16));
}
logf("\nGain factors:");
for (int i = 1; i <= 65; i++)
{
debugf("%02d: %.6f ", i, (float)comp_curve[i] / UNITY);
if (i % 4 == 0) debugf("\n");
}
debugf("\n");
#endif
/* if using auto peak, then makeup gain is max offset -
.1dB headroom */
comp_makeup_gain = auto_gain ?
fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY;
logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY);
/* calculate per-sample gain change a rate of 10db over release time
*/
comp_rel_slope = 0xAF0BB2 / release;
logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY);
release_gain = UNITY;
}
return active;
}
/** GET COMPRESSION GAIN
* Returns the required gain factor in S7.24 format in order to compress the
* sample in accordance with the compression curve. Always 1 or less.
*/
static inline int32_t get_compression_gain(struct dsp_data *data,
int32_t sample)
{
const int frac_bits_offset = data->frac_bits - 15;
/* sample must be positive */
if (sample < 0)
sample = -(sample + 1);
/* shift sample into 15 frac bit range */
if (frac_bits_offset > 0)
sample >>= frac_bits_offset;
if (frac_bits_offset < 0)
sample <<= -frac_bits_offset;
/* normal case: sample isn't clipped */
if (sample < (1 << 15))
{
/* index is 6 MSB, rem is 9 LSB */
int index = sample >> 9;
int32_t rem = (sample & 0x1FF) << 22;
/* interpolate from the compression curve:
higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */
return comp_curve[index] - (FRACMUL(rem,
(comp_curve[index] - comp_curve[index + 1])));
}
/* sample is somewhat clipped, up to 2 bits of overhead */
if (sample < (1 << 17))
{
/* straight interpolation:
higher gain - ((clipped portion of sample * 4/3
/ (1 << 31)) * (higher gain - lower gain)) */
return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16,
(comp_curve[64] - comp_curve[65])));
}
/* sample is too clipped, return invalid value */
return -1;
}
/** COMPRESSOR PROCESS
* Changes the gain of the samples according to the compressor curve
*/
void compressor_process(int count, struct dsp_data *data, int32_t *buf[])
{
const int num_chan = data->num_channels;
int32_t *in_buf[2] = {buf[0], buf[1]};
while (count-- > 0)
{
int ch;
/* use lowest (most compressed) gain factor of the output buffer
sample pair for both samples (mono is also handled correctly here)
*/
int32_t sample_gain = UNITY;
for (ch = 0; ch < num_chan; ch++)
{
int32_t this_gain = get_compression_gain(data, *in_buf[ch]);
if (this_gain < sample_gain)
sample_gain = this_gain;
}
/* perform release slope; skip if no compression and no release slope
*/
if ((sample_gain != UNITY) || (release_gain != UNITY))
{
/* if larger offset than previous slope, start new release slope
*/
if ((sample_gain <= release_gain) && (sample_gain > 0))
{
release_gain = sample_gain;
}
else
/* keep sloping towards unity gain (and ignore invalid value) */
{
release_gain += comp_rel_slope;
if (release_gain > UNITY)
{
release_gain = UNITY;
}
}
}
/* total gain factor is the product of release gain and makeup gain,
but avoid computation if possible */
int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain :
(comp_makeup_gain == UNITY) ? release_gain :
FRACMUL_SHL(release_gain, comp_makeup_gain, 7));
/* Implement the compressor: apply total gain factor (if any) to the
output buffer sample pair/mono sample */
if (total_gain != UNITY)
{
for (ch = 0; ch < num_chan; ch++)
{
*in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7);
}
}
in_buf[0]++;
in_buf[1]++;
}
}
void compressor_reset(void)
{
release_gain = UNITY;
}

View file

@ -1,29 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 Jeffrey Goode
*
* 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 COMPRESSOR_H
#define COMPRESSOR_H
void compressor_process(int count, struct dsp_data *data, int32_t *buf[]);
bool compressor_update(void);
void compressor_reset(void);
#endif /* COMPRESSOR_H */

1573
apps/dsp.c

File diff suppressed because it is too large Load diff

View file

@ -1,125 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Miika Pekkarinen
*
* 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 _DSP_H
#define _DSP_H
#include <stdlib.h>
#include <stdbool.h>
#define NATIVE_FREQUENCY 44100
enum
{
STEREO_INTERLEAVED = 0,
STEREO_NONINTERLEAVED,
STEREO_MONO,
STEREO_NUM_MODES,
};
enum
{
CODEC_IDX_AUDIO = 0,
CODEC_IDX_VOICE,
};
enum
{
DSP_MYDSP = 1,
DSP_SET_FREQUENCY,
DSP_SWITCH_FREQUENCY,
DSP_SET_SAMPLE_DEPTH,
DSP_SET_STEREO_MODE,
DSP_RESET,
DSP_FLUSH,
DSP_SET_TRACK_GAIN,
DSP_SET_ALBUM_GAIN,
DSP_SET_TRACK_PEAK,
DSP_SET_ALBUM_PEAK,
DSP_CROSSFEED
};
/****************************************************************************
* NOTE: Any assembly routines that use these structures must be updated
* if current data members are moved or changed.
*/
struct resample_data
{
uint32_t delta; /* 00h */
uint32_t phase; /* 04h */
int32_t last_sample[2]; /* 08h */
/* 10h */
};
/* This is for passing needed data to external dsp routines. If another
* dsp parameter needs to be passed, add to the end of the structure
* and remove from dsp_config.
* If another function type becomes assembly/external and requires dsp
* config info, add a pointer paramter of type "struct dsp_data *".
* If removing something from other than the end, reserve the spot or
* else update every implementation for every target.
* Be sure to add the offset of the new member for easy viewing as well. :)
* It is the first member of dsp_config and all members can be accessesed
* through the main aggregate but this is intended to make a safe haven
* for these items whereas the c part can be rearranged at will. dsp_data
* could even moved within dsp_config without disurbing the order.
*/
struct dsp_data
{
int output_scale; /* 00h */
int num_channels; /* 04h */
struct resample_data resample_data; /* 08h */
int32_t clip_min; /* 18h */
int32_t clip_max; /* 1ch */
int32_t gain; /* 20h - Note that this is in S8.23 format. */
int frac_bits; /* 24h */
/* 28h */
};
struct dsp_config;
int dsp_process(struct dsp_config *dsp, char *dest,
const char *src[], int count);
int dsp_input_count(struct dsp_config *dsp, int count);
int dsp_output_count(struct dsp_config *dsp, int count);
intptr_t dsp_configure(struct dsp_config *dsp, int setting,
intptr_t value);
int get_replaygain_mode(bool have_track_gain, bool have_album_gain);
void dsp_set_replaygain(void);
void dsp_set_crossfeed(bool enable);
void dsp_set_crossfeed_direct_gain(int gain);
void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain,
long cutoff);
void dsp_set_eq(bool enable);
void dsp_set_eq_precut(int precut);
void dsp_set_eq_coefs(int band);
void dsp_dither_enable(bool enable);
void dsp_timestretch_enable(bool enable);
bool dsp_timestretch_available(void);
void sound_set_pitch(int32_t r);
int32_t sound_get_pitch(void);
void dsp_set_timestretch(int32_t percent);
int32_t dsp_get_timestretch(void);
int dsp_callback(int msg, intptr_t param);
void dsp_set_compressor(void);
#endif

View file

@ -1,561 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
*
* 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 "config.h"
/****************************************************************************
* void channels_process_sound_chan_mono(int count, int32_t *buf[])
*/
#include "config.h"
.section .icode, "ax", %progbits
.align 2
.global channels_process_sound_chan_mono
.type channels_process_sound_chan_mono, %function
channels_process_sound_chan_mono:
@ input: r0 = count, r1 = buf
stmfd sp!, { r4, lr } @
@
ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
subs r0, r0, #1 @ odd: end at 0; even: end at -1
beq .mono_singlesample @ Zero? Only one sample!
@
.monoloop: @
ldmia r1, { r3, r4 } @ r3, r4 = Li0, Li1
ldmia r2, { r12, r14 } @ r12, r14 = Ri0, Ri1
mov r3, r3, asr #1 @ Mo0 = Li0 / 2 + Ri0 / 2
mov r4, r4, asr #1 @ Mo1 = Li1 / 2 + Ri1 / 2
add r12, r3, r12, asr #1 @
add r14, r4, r14, asr #1 @
subs r0, r0, #2 @
stmia r1!, { r12, r14 } @ store Mo0, Mo1
stmia r2!, { r12, r14 } @ store Mo0, Mo1
bgt .monoloop @
@
ldmpc cond=lt, regs=r4 @ if count was even, we're done
@
.mono_singlesample: @
ldr r3, [r1] @ r3 = Ls
ldr r12, [r2] @ r12 = Rs
mov r3, r3, asr #1 @ Mo = Ls / 2 + Rs / 2
add r12, r3, r12, asr #1 @
str r12, [r1] @ store Mo
str r12, [r2] @ store Mo
@
ldmpc regs=r4 @
.size channels_process_sound_chan_mono, \
.-channels_process_sound_chan_mono
/****************************************************************************
* void channels_process_sound_chan_custom(int count, int32_t *buf[])
*/
.section .icode, "ax", %progbits
.align 2
.global channels_process_sound_chan_custom
.type channels_process_sound_chan_custom, %function
channels_process_sound_chan_custom:
stmfd sp!, { r4-r10, lr }
ldr r3, =dsp_sw_gain
ldr r4, =dsp_sw_cross
ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
ldr r3, [r3] @ r3 = dsp_sw_gain
ldr r4, [r4] @ r4 = dsp_sw_cross
subs r0, r0, #1
beq .custom_single_sample @ Zero? Only one sample!
.custom_loop:
ldmia r1, { r5, r6 } @ r5 = Li0, r6 = Li1
ldmia r2, { r7, r8 } @ r7 = Ri0, r8 = Ri1
subs r0, r0, #2
smull r9, r10, r5, r3 @ Lc0 = Li0*gain
smull r12, r14, r7, r3 @ Rc0 = Ri0*gain
smlal r9, r10, r7, r4 @ Lc0 += Ri0*cross
smlal r12, r14, r5, r4 @ Rc0 += Li0*cross
mov r9, r9, lsr #31 @ Convert to s0.31
mov r12, r12, lsr #31
orr r5, r9, r10, asl #1
orr r7, r12, r14, asl #1
smull r9, r10, r6, r3 @ Lc1 = Li1*gain
smull r12, r14, r8, r3 @ Rc1 = Ri1*gain
smlal r9, r10, r8, r4 @ Lc1 += Ri1*cross
smlal r12, r14, r6, r4 @ Rc1 += Li1*cross
mov r9, r9, lsr #31 @ Convert to s0.31
mov r12, r12, lsr #31
orr r6, r9, r10, asl #1
orr r8, r12, r14, asl #1
stmia r1!, { r5, r6 } @ Store Lc0, Lc1
stmia r2!, { r7, r8 } @ Store Rc0, Rc1
bgt .custom_loop
ldmpc cond=lt, regs=r4-r10 @ < 0? even count
.custom_single_sample:
ldr r5, [r1] @ handle odd sample
ldr r7, [r2]
smull r9, r10, r5, r3 @ Lc0 = Li0*gain
smull r12, r14, r7, r3 @ Rc0 = Ri0*gain
smlal r9, r10, r7, r4 @ Lc0 += Ri0*cross
smlal r12, r14, r5, r4 @ Rc0 += Li0*cross
mov r9, r9, lsr #31 @ Convert to s0.31
mov r12, r12, lsr #31
orr r5, r9, r10, asl #1
orr r7, r12, r14, asl #1
str r5, [r1] @ Store Lc0
str r7, [r2] @ Store Rc0
ldmpc regs=r4-r10
.size channels_process_sound_chan_custom, \
.-channels_process_sound_chan_custom
/****************************************************************************
* void channels_process_sound_chan_karaoke(int count, int32_t *buf[])
*/
.section .icode, "ax", %progbits
.align 2
.global channels_process_sound_chan_karaoke
.type channels_process_sound_chan_karaoke, %function
channels_process_sound_chan_karaoke:
@ input: r0 = count, r1 = buf
stmfd sp!, { r4, lr } @
@
ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
subs r0, r0, #1 @ odd: end at 0; even: end at -1
beq .karaoke_singlesample @ Zero? Only one sample!
@
.karaokeloop: @
ldmia r1, { r3, r4 } @ r3, r4 = Li0, Li1
ldmia r2, { r12, r14 } @ r12, r14 = Ri0, Ri1
mov r3, r3, asr #1 @ Lo0 = Li0 / 2 - Ri0 / 2
mov r4, r4, asr #1 @ Lo1 = Li1 / 2 - Ri1 / 2
sub r3, r3, r12, asr #1 @
sub r4, r4, r14, asr #1 @
rsb r12, r3, #0 @ Ro0 = -Lk0 = Rs0 / 2 - Ls0 / 2
rsb r14, r4, #0 @ Ro1 = -Lk1 = Ri1 / 2 - Li1 / 2
subs r0, r0, #2 @
stmia r1!, { r3, r4 } @ store Lo0, Lo1
stmia r2!, { r12, r14 } @ store Ro0, Ro1
bgt .karaokeloop @
@
ldmpc cond=lt, regs=r4 @ if count was even, we're done
@
.karaoke_singlesample: @
ldr r3, [r1] @ r3 = Li
ldr r12, [r2] @ r12 = Ri
mov r3, r3, asr #1 @ Lk = Li / 2 - Ri /2
sub r3, r3, r12, asr #1 @
rsb r12, r3, #0 @ Rk = -Lo = Ri / 2 - Li / 2
str r3, [r1] @ store Lo
str r12, [r2] @ store Ro
@
ldmpc regs=r4 @
.size channels_process_sound_chan_karaoke, \
.-channels_process_sound_chan_karaoke
#if ARM_ARCH < 6
/****************************************************************************
* void sample_output_mono(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*/
.section .icode, "ax", %progbits
.align 2
.global sample_output_mono
.type sample_output_mono, %function
sample_output_mono:
@ input: r0 = count, r1 = data, r2 = src, r3 = dst
stmfd sp!, { r4-r6, lr }
ldr r1, [r1] @ lr = data->output_scale
ldr r2, [r2] @ r2 = src[0]
mov r4, #1
mov r4, r4, lsl r1 @ r4 = 1 << (scale-1)
mov r4, r4, lsr #1
mvn r14, #0x8000 @ r14 = 0xffff7fff, needed for
@ clipping and masking
subs r0, r0, #1 @
beq .som_singlesample @ Zero? Only one sample!
.somloop:
ldmia r2!, { r5, r6 }
add r5, r5, r4 @ r6 = (r6 + 1<<(scale-1)) >> scale
mov r5, r5, asr r1
mov r12, r5, asr #15
teq r12, r12, asr #31
eorne r5, r14, r5, asr #31 @ Clip (-32768...+32767)
add r6, r6, r4
mov r6, r6, asr r1 @ r7 = (r7 + 1<<(scale-1)) >> scale
mov r12, r6, asr #15
teq r12, r12, asr #31
eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767)
and r5, r5, r14, lsr #16
and r6, r6, r14, lsr #16
orr r5, r5, r5, lsl #16 @ pack first 2 halfwords into 1 word
orr r6, r6, r6, lsl #16 @ pack last 2 halfwords into 1 word
stmia r3!, { r5, r6 }
subs r0, r0, #2
bgt .somloop
ldmpc cond=lt, regs=r4-r6 @ even 'count'? return
.som_singlesample:
ldr r5, [r2] @ do odd sample
add r5, r5, r4
mov r5, r5, asr r1
mov r12, r5, asr #15
teq r12, r12, asr #31
eorne r5, r14, r5, asr #31
and r5, r5, r14, lsr #16 @ pack 2 halfwords into 1 word
orr r5, r5, r5, lsl #16
str r5, [r3]
ldmpc regs=r4-r6
.size sample_output_mono, .-sample_output_mono
/****************************************************************************
* void sample_output_stereo(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*/
.section .icode, "ax", %progbits
.align 2
.global sample_output_stereo
.type sample_output_stereo, %function
sample_output_stereo:
@ input: r0 = count, r1 = data, r2 = src, r3 = dst
stmfd sp!, { r4-r9, lr }
ldr r1, [r1] @ r1 = data->output_scale
ldmia r2, { r2, r5 } @ r2 = src[0], r5 = src[1]
mov r4, #1
mov r4, r4, lsl r1 @ r4 = 1 << (scale-1)
mov r4, r4, lsr #1 @
mvn r14, #0x8000 @ r14 = 0xffff7fff, needed for
@ clipping and masking
subs r0, r0, #1 @
beq .sos_singlesample @ Zero? Only one sample!
.sosloop:
ldmia r2!, { r6, r7 } @ 2 left
ldmia r5!, { r8, r9 } @ 2 right
add r6, r6, r4 @ r6 = (r6 + 1<<(scale-1)) >> scale
mov r6, r6, asr r1
mov r12, r6, asr #15
teq r12, r12, asr #31
eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767)
add r7, r7, r4
mov r7, r7, asr r1 @ r7 = (r7 + 1<<(scale-1)) >> scale
mov r12, r7, asr #15
teq r12, r12, asr #31
eorne r7, r14, r7, asr #31 @ Clip (-32768...+32767)
add r8, r8, r4 @ r8 = (r8 + 1<<(scale-1)) >> scale
mov r8, r8, asr r1
mov r12, r8, asr #15
teq r12, r12, asr #31
eorne r8, r14, r8, asr #31 @ Clip (-32768...+32767)
add r9, r9, r4 @ r9 = (r9 + 1<<(scale-1)) >> scale
mov r9, r9, asr r1
mov r12, r9, asr #15
teq r12, r12, asr #31
eorne r9, r14, r9, asr #31 @ Clip (-32768...+32767)
and r6, r6, r14, lsr #16 @ pack first 2 halfwords into 1 word
orr r8, r6, r8, asl #16
and r7, r7, r14, lsr #16 @ pack last 2 halfwords into 1 word
orr r9, r7, r9, asl #16
stmia r3!, { r8, r9 }
subs r0, r0, #2
bgt .sosloop
ldmpc cond=lt, regs=r4-r9 @ even 'count'? return
.sos_singlesample:
ldr r6, [r2] @ left odd sample
ldr r8, [r5] @ right odd sample
add r6, r6, r4 @ r6 = (r7 + 1<<(scale-1)) >> scale
mov r6, r6, asr r1
mov r12, r6, asr #15
teq r12, r12, asr #31
eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767)
add r8, r8, r4 @ r8 = (r8 + 1<<(scale-1)) >> scale
mov r8, r8, asr r1
mov r12, r8, asr #15
teq r12, r12, asr #31
eorne r8, r14, r8, asr #31 @ Clip (-32768...+32767)
and r6, r6, r14, lsr #16 @ pack 2 halfwords into 1 word
orr r8, r6, r8, asl #16
str r8, [r3]
ldmpc regs=r4-r9
.size sample_output_stereo, .-sample_output_stereo
#endif /* ARM_ARCH < 6 */
/****************************************************************************
* void apply_crossfeed(int count, int32_t* src[])
*/
.section .text
.global apply_crossfeed
apply_crossfeed:
@ unfortunately, we ended up in a bit of a register squeeze here, and need
@ to keep the count on the stack :/
stmdb sp!, { r4-r11, lr } @ stack modified regs
ldmia r1, { r2-r3 } @ r2 = src[0], r3 = src[1]
ldr r1, =crossfeed_data
ldmia r1!, { r4-r11 } @ load direct gain and filter data
mov r12, r0 @ better to ldm delay + count later
add r0, r1, #13*4*2 @ calculate end of delay
stmdb sp!, { r0, r12 } @ stack end of delay adr and count
ldr r0, [r1, #13*4*2] @ fetch current delay line address
/* Register usage in loop:
* r0 = &delay[index][0], r1 = accumulator high, r2 = src[0], r3 = src[1],
* r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs),
* r8-r11 = filter history, r12 = temp, r14 = accumulator low
*/
.cfloop:
smull r14, r1, r6, r8 @ acc = b1*dr[n - 1]
smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1]
ldr r8, [r0, #4] @ r8 = dr[n]
smlal r14, r1, r5, r8 @ acc += b0*dr[n]
mov r9, r1, lsl #1 @ fix format for filter history
ldr r12, [r2] @ load left input
smlal r14, r1, r4, r12 @ acc += gain*x_l[n]
mov r1, r1, lsl #1 @ fix format
str r1, [r2], #4 @ save result
smull r14, r1, r6, r10 @ acc = b1*dl[n - 1]
smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1]
ldr r10, [r0] @ r10 = dl[n]
str r12, [r0], #4 @ save left input to delay line
smlal r14, r1, r5, r10 @ acc += b0*dl[n]
mov r11, r1, lsl #1 @ fix format for filter history
ldr r12, [r3] @ load right input
smlal r14, r1, r4, r12 @ acc += gain*x_r[n]
str r12, [r0], #4 @ save right input to delay line
mov r1, r1, lsl #1 @ fix format
ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack
str r1, [r3], #4 @ save result
cmp r0, r12 @ need to wrap to start of delay?
subeq r0, r0, #13*4*2 @ wrap back delay line ptr to start
subs r14, r14, #1 @ are we finished?
strne r14, [sp, #4] @ nope, save count back to stack
bne .cfloop
@ save data back to struct
ldr r12, =crossfeed_data + 4*4
stmia r12, { r8-r11 } @ save filter history
str r0, [r12, #30*4] @ save delay line index
add sp, sp, #8 @ remove temp variables from stack
ldmpc regs=r4-r11
.size apply_crossfeed, .-apply_crossfeed
/****************************************************************************
* int dsp_downsample(int count, struct dsp_data *data,
* in32_t *src[], int32_t *dst[])
*/
.section .text
.global dsp_downsample
dsp_downsample:
stmdb sp!, { r4-r11, lr } @ stack modified regs
ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta
sub r5, r5, #1 @ pre-decrement num_channels for use
add r4, r1, #12 @ r4 = &resample_data.phase
mov r12, #0xff
orr r12, r12, #0xff00 @ r12 = 0xffff
.dschannel_loop:
ldr r1, [r4] @ r1 = resample_data.phase
ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1]
ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1]
add r9, r4, #4 @ r9 = &last_sample[0]
ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1]
sub r11, r0, #1
ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ...
str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample
movs r9, r1, lsr #16 @ r9 = pos = phase >> 16
ldreq r11, [r7] @ if pos = 0, load src[0] and jump into loop
beq .dsuse_last_start
cmp r9, r0 @ if pos >= count, we're already done
bge .dsloop_skip
@ Register usage in loop:
@ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel,
@ r6 = delta, r7 = s, r8 = d, r9 = pos, r10 = s[pos - 1], r11 = s[pos]
.dsloop:
add r9, r7, r9, lsl #2 @ r9 = &s[pos]
ldmda r9, { r10, r11 } @ r10 = s[pos - 1], r11 = s[pos]
.dsuse_last_start:
sub r11, r11, r10 @ r11 = diff = s[pos] - s[pos - 1]
@ keep frac in lower bits to take advantage of multiplier early termination
and r9, r1, r12 @ frac = phase & 0xffff
smull r9, r14, r11, r9
add r1, r1, r6 @ phase += delta
add r10, r10, r9, lsr #16 @ r10 = out = s[pos - 1] + frac*diff
add r10, r10, r14, lsl #16
str r10, [r8], #4 @ *d++ = out
mov r9, r1, lsr #16 @ pos = phase >> 16
cmp r9, r0 @ pos < count?
blt .dsloop @ yup, do more samples
.dsloop_skip:
subs r5, r5, #1
bpl .dschannel_loop @ if (--ch) >= 0, do another channel
sub r1, r1, r0, lsl #16 @ wrap phase back to start
str r1, [r4] @ store back
ldr r1, [r3] @ r1 = &dst[0]
sub r8, r8, r1 @ dst - &dst[0]
mov r0, r8, lsr #2 @ convert bytes->samples
ldmpc regs=r4-r11 @ ... and we're out
.size dsp_downsample, .-dsp_downsample
/****************************************************************************
* int dsp_upsample(int count, struct dsp_data *dsp,
* in32_t *src[], int32_t *dst[])
*/
.section .text
.global dsp_upsample
dsp_upsample:
stmfd sp!, { r4-r11, lr } @ stack modified regs
ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta
sub r5, r5, #1 @ pre-decrement num_channels for use
add r4, r1, #12 @ r4 = &resample_data.phase
mov r6, r6, lsl #16 @ we'll use carry to detect pos increments
stmfd sp!, { r0, r4 } @ stack count and &resample_data.phase
.uschannel_loop:
ldr r12, [r4] @ r12 = resample_data.phase
ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1]
ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1]
add r9, r4, #4 @ r9 = &last_sample[0]
mov r1, r12, lsl #16 @ we'll use carry to detect pos increments
sub r11, r0, #1
ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ...
ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1]
str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample
movs r14, r12, lsr #16 @ pos = resample_data.phase >> 16
beq .usstart_0 @ pos = 0
cmp r14, r0 @ if pos >= count, we're already done
bge .usloop_skip
add r7, r7, r14, lsl #2 @ r7 = &s[pos]
ldr r10, [r7, #-4] @ r11 = s[pos - 1]
b .usstart_0
@ Register usage in loop:
@ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel,
@ r6 = delta, r7 = s, r8 = d, r9 = diff, r10 = s[pos - 1], r11 = s[pos]
.usloop_1:
mov r10, r11 @ r10 = previous sample
.usstart_0:
ldr r11, [r7], #4 @ r11 = next sample
mov r4, r1, lsr #16 @ r4 = frac = phase >> 16
sub r9, r11, r10 @ r9 = diff = s[pos] - s[pos - 1]
.usloop_0:
smull r12, r14, r4, r9
adds r1, r1, r6 @ phase += delta << 16
mov r4, r1, lsr #16 @ r4 = frac = phase >> 16
add r14, r10, r14, lsl #16
add r14, r14, r12, lsr #16 @ r14 = out = s[pos - 1] + frac*diff
str r14, [r8], #4 @ *d++ = out
bcc .usloop_0 @ if carry is set, pos is incremented
subs r0, r0, #1 @ if count > 0, do another sample
bgt .usloop_1
.usloop_skip:
subs r5, r5, #1
ldmfd sp, { r0, r4 } @ reload count and &resample_data.phase
bpl .uschannel_loop @ if (--ch) >= 0, do another channel
mov r1, r1, lsr #16 @ wrap phase back to start of next frame
ldr r2, [r3] @ r1 = &dst[0]
str r1, [r4] @ store phase
sub r8, r8, r2 @ dst - &dst[0]
mov r0, r8, lsr #2 @ convert bytes->samples
add sp, sp, #8 @ adjust stack for temp variables
ldmpc regs=r4-r11 @ ... and we're out
.size dsp_upsample, .-dsp_upsample
/****************************************************************************
* void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
*/
.section .icode, "ax", %progbits
.align 2
.global dsp_apply_gain
.type dsp_apply_gain, %function
dsp_apply_gain:
@ input: r0 = count, r1 = data, r2 = buf[]
stmfd sp!, { r4-r8, lr }
ldr r3, [r1, #4] @ r3 = data->num_channels
ldr r4, [r1, #32] @ r5 = data->gain
.dag_outerloop:
ldr r1, [r2], #4 @ r1 = buf[0] and increment index of buf[]
subs r12, r0, #1 @ r12 = r0 = count - 1
beq .dag_singlesample @ Zero? Only one sample!
.dag_innerloop:
ldmia r1, { r5, r6 } @ load r5, r6 from r1
smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
smull r14, r5, r6, r4 @ r14 = FRACMUL_SHL(r6, r4, 8)
subs r12, r12, #2
mov r7, r7, lsr #23
mov r14, r14, lsr #23
orr r7, r7, r8, asl #9
orr r14, r14, r5, asl #9
stmia r1!, { r7, r14 } @ save r7, r14 to [r1] and increment r1
bgt .dag_innerloop @ end of inner loop
blt .dag_evencount @ < 0? even count
.dag_singlesample:
ldr r5, [r1] @ handle odd sample
smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
mov r7, r7, lsr #23
orr r7, r7, r8, asl #9
str r7, [r1]
.dag_evencount:
subs r3, r3, #1
bgt .dag_outerloop @ end of outer loop
ldmpc regs=r4-r8
.size dsp_apply_gain, .-dsp_apply_gain

View file

@ -1,127 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Michael Sevakis
*
* 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.
*
****************************************************************************/
/****************************************************************************
* void sample_output_mono(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*/
.section .text, "ax", %progbits
.align 2
.global sample_output_mono
.type sample_output_mono, %function
sample_output_mono:
@ input: r0 = count, r1 = data, r2 = src, r3 = dst
stmfd sp!, { r4, lr } @
@
ldr r1, [r1] @ r1 = data->output_scale
ldr r2, [r2] @ r2 = src[0]
@
mov r4, #1 @ r4 = 1 << (scale - 1)
mov r4, r4, lsl r1 @
subs r0, r0, #1 @ odd: end at 0; even: end at -1
mov r4, r4, lsr #1 @
beq 2f @ Zero? Only one sample!
@
1: @
ldmia r2!, { r12, r14 } @ load Mi0, Mi1
qadd r12, r12, r4 @ round, scale, saturate and
qadd r14, r14, r4 @ pack Mi0 to So0, Mi1 to So1
mov r12, r12, asr r1 @
mov r14, r14, asr r1 @
ssat r12, #16, r12 @
ssat r14, #16, r14 @
pkhbt r12, r12, r12, asl #16 @
pkhbt r14, r14, r14, asl #16 @
subs r0, r0, #2 @
stmia r3!, { r12, r14 } @ store So0, So1
bgt 1b @
@
ldmltfd sp!, { r4, pc } @ if count was even, we're done
@
2: @
ldr r12, [r2] @ round, scale, saturate
qadd r12, r12, r4 @ and pack Mi to So
mov r12, r12, asr r1 @
ssat r12, #16, r12 @
pkhbt r12, r12, r12, asl #16 @
str r12, [r3] @ store So
@
ldmfd sp!, { r4, pc } @
.size sample_output_mono, .-sample_output_mono
/****************************************************************************
* void sample_output_stereo(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*/
.section .text, "ax", %progbits
.align 2
.global sample_output_stereo
.type sample_output_stereo, %function
sample_output_stereo:
@ input: r0 = count, r1 = data, r2 = src, r3 = dst
stmfd sp!, { r4-r7, lr } @
@
ldr r1, [r1] @ r1 = data->output_scale
ldmia r2, { r2, r4 } @ r2 = src[0], r4 = src[1]
@
mov r5, #1 @ r5 = 1 << (scale - 1)
mov r5, r5, lsl r1 @
subs r0, r0, #1 @ odd: end at 0; even: end at -1
mov r5, r5, lsr #1 @
beq 2f @ Zero? Only one sample!
@
1: @
ldmia r2!, { r6, r7 } @ r6, r7 = Li0, Li1
ldmia r4!, { r12, r14 } @ r12, r14 = Ri0, Ri1
qadd r6, r6, r5 @ round, scale, saturate and pack
qadd r7, r7, r5 @ Li0+Ri0 to So0, Li1+Ri1 to So1
qadd r12, r12, r5 @
qadd r14, r14, r5 @
mov r6, r6, asr r1 @
mov r7, r7, asr r1 @
mov r12, r12, asr r1 @
mov r14, r14, asr r1 @
ssat r6, #16, r6 @
ssat r12, #16, r12 @
ssat r7, #16, r7 @
ssat r14, #16, r14 @
pkhbt r6, r6, r12, asl #16 @
pkhbt r7, r7, r14, asl #16 @
subs r0, r0, #2 @
stmia r3!, { r6, r7 } @ store So0, So1
bgt 1b @
@
ldmltfd sp!, { r4-r7, pc } @ if count was even, we're done
@
2: @
ldr r6, [r2] @ r6 = Li
ldr r12, [r4] @ r12 = Ri
qadd r6, r6, r5 @ round, scale, saturate
qadd r12, r12, r5 @ and pack Li+Ri to So
mov r6, r6, asr r1 @
mov r12, r12, asr r1 @
ssat r6, #16, r6 @
ssat r12, #16, r12 @
pkhbt r6, r6, r12, asl #16 @
str r6, [r3] @ store So
@
ldmfd sp!, { r4-r7, pc } @
.size sample_output_stereo, .-sample_output_stereo

View file

@ -1,86 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Thom Johansen
*
* 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 <config.h>
#ifndef _DSP_ASM_H
#define _DSP_ASM_H
/* Set the appropriate #defines based on CPU or whatever matters */
#if defined(CPU_ARM)
#define DSP_HAVE_ASM_APPLY_GAIN
#define DSP_HAVE_ASM_RESAMPLING
#define DSP_HAVE_ASM_CROSSFEED
#define DSP_HAVE_ASM_SOUND_CHAN_MONO
#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
#elif defined (CPU_COLDFIRE)
#define DSP_HAVE_ASM_APPLY_GAIN
#define DSP_HAVE_ASM_RESAMPLING
#define DSP_HAVE_ASM_CROSSFEED
#define DSP_HAVE_ASM_SOUND_CHAN_MONO
#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
#endif /* CPU_COLDFIRE */
/* Declare prototypes based upon what's #defined above */
#ifdef DSP_HAVE_ASM_CROSSFEED
void apply_crossfeed(int count, int32_t *buf[]);
#endif
#ifdef DSP_HAVE_ASM_APPLY_GAIN
void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]);
#endif /* DSP_HAVE_ASM_APPLY_GAIN* */
#ifdef DSP_HAVE_ASM_RESAMPLING
int dsp_upsample(int count, struct dsp_data *data,
const int32_t *src[], int32_t *dst[]);
int dsp_downsample(int count, struct dsp_data *data,
const int32_t *src[], int32_t *dst[]);
#endif /* DSP_HAVE_ASM_RESAMPLING */
#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO
void channels_process_sound_chan_mono(int count, int32_t *buf[]);
#endif
#ifdef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
void channels_process_sound_chan_custom(int count, int32_t *buf[]);
#endif
#ifdef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
void channels_process_sound_chan_karaoke(int count, int32_t *buf[]);
#endif
#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
void sample_output_stereo(int count, struct dsp_data *data,
const int32_t *src[], int16_t *dst);
#endif
#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
void sample_output_mono(int count, struct dsp_data *data,
const int32_t *src[], int16_t *dst);
#endif
#endif /* _DSP_ASM_H */

View file

@ -1,611 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Thom Johansen
* Portions Copyright (C) 2007 Michael Sevakis
*
* 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.
*
****************************************************************************/
/****************************************************************************
* void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
*/
.section .text
.align 2
.global dsp_apply_gain
dsp_apply_gain:
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
movem.l 28(%sp), %a0-%a1 | %a0 = data,
| %a1 = buf
move.l 4(%a0), %d1 | %d1 = data->num_channels
move.l 32(%a0), %a0 | %a0 = data->gain (in s8.23)
10: | channel loop |
move.l 24(%sp), %d0 | %d0 = count
move.l -4(%a1, %d1.l*4), %a2 | %a2 = s = buf[ch-1]
move.l %a2, %a3 | %a3 = d = s
move.l (%a2)+, %d2 | %d2 = *s++,
mac.l %a0, %d2, (%a2)+, %d2, %acc0 | %acc0 = S(n)*gain, load S(n+1)
subq.l #1, %d0 | --count > 0 ? : effectively n++
ble.b 30f | loop done | no? finish up
20: | loop |
move.l %accext01, %d4 | fetch S(n-1)[7:0]
movclr.l %acc0, %d3 | fetch S(n-1)[40:8] in %d5[31:0]
asl.l #8, %d3 | *s++ = (S(n-1)[40:8] << 8) | S(n-1)[7:0]
mac.l %a0, %d2, (%a2)+, %d2, %acc0 | %acc0 = S(n)*gain, load S(n+1)
move.b %d4, %d3 |
move.l %d3, (%a3)+ |
subq.l #1, %d0 | --count > 0 ? : effectively n++
bgt.b 20b | loop | yes? do more samples
30: | loop done |
move.l %accext01, %d4 | fetch S(n-1)[7:0]
movclr.l %acc0, %d3 | fetch S(n-1)[40:8] in %d5[31:0]
asl.l #8, %d3 | *s = (S(n-1)[40:8] << 8) | S(n-1)[7:0]
move.b %d4, %d3 |
move.l %d3, (%a3) |
subq.l #1, %d1 | next channel
bgt.b 10b | channel loop |
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup stack
rts |
.size dsp_apply_gain,.-dsp_apply_gain
/****************************************************************************
* void apply_crossfeed(int count, int32_t *buf[])
*/
.section .text
.align 2
.global apply_crossfeed
apply_crossfeed:
lea.l -44(%sp), %sp |
movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs
movem.l 48(%sp), %d7/%a4 | %d7 = count, %a4 = src
movem.l (%a4), %a4-%a5 | %a4 = src[0], %a5 = src[1]
lea.l crossfeed_data, %a1 | %a1 = &crossfeed_data
move.l (%a1)+, %d6 | %d6 = direct gain
movem.l 12(%a1), %d0-%d3 | fetch filter history samples
move.l 132(%a1), %a0 | fetch delay line address
movem.l (%a1), %a1-%a3 | load filter coefs
lea.l crossfeed_data+136, %a6 | %a6 = delay line wrap limit
bra.b 20f | loop start | go to loop start point
/* Register usage in loop:
* %a0 = delay_p, %a1..%a3 = b0, b1, a1 (filter coefs),
* %a4 = buf[0], %a5 = buf[1],
* %a6 = delay line pointer wrap limit,
* %d0..%d3 = history
* %d4..%d5 = temp.
* %d6 = direct gain,
* %d7 = count
*/
10: | loop |
movclr.l %acc0, %d4 | write outputs
move.l %d4, (%a4)+ | .
movclr.l %acc1, %d5 | .
move.l %d5, (%a5)+ | .
20: | loop start |
mac.l %a2, %d0, (%a0)+, %d0, %acc0 | %acc0 = b1*dl[n - 1], %d0 = dl[n]
mac.l %a1, %d0 , %acc0 | %acc0 += b0*dl[n]
mac.l %a3, %d1, (%a5), %d5, %acc0 | %acc0 += a1*y_r[n - 1], load R
mac.l %a2, %d2, (%a0)+, %d2, %acc1 | %acc1 = b1*dr[n - 1], %d2 = dr[n]
mac.l %a1, %d2 , %acc1 | %acc1 += b0*dr[n]
mac.l %a3, %d3, (%a4), %d4, %acc1 | %acc1 += a1*y_l[n - 1], load L
movem.l %d4-%d5, -8(%a0) | save left & right inputs to delay line
move.l %acc0, %d3 | get filtered delayed left sample (y_l[n])
move.l %acc1, %d1 | get filtered delayed right sample (y_r[n])
mac.l %d6, %d4, %acc0 | %acc0 += gain*x_l[n]
mac.l %d6, %d5, %acc1 | %acc1 += gain*x_r[n]
cmp.l %a6, %a0 | wrap %a0 if passed end
bhs.b 30f | wrap buffer |
.word 0x51fb | tpf.l | trap the buffer wrap
30: | wrap buffer | ...fwd taken branches more costly
lea.l -104(%a0), %a0 | wrap it up
subq.l #1, %d7 | --count > 0 ?
bgt.b 10b | loop | yes? do more
movclr.l %acc0, %d4 | write last outputs
move.l %d4, (%a4) | .
movclr.l %acc1, %d5 | .
move.l %d5, (%a5) | .
lea.l crossfeed_data+16, %a1 | save data back to struct
movem.l %d0-%d3, (%a1) | ...history
move.l %a0, 120(%a1) | ...delay_p
movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs
lea.l 44(%sp), %sp |
rts |
.size apply_crossfeed,.-apply_crossfeed
/****************************************************************************
* int dsp_downsample(int count, struct dsp_data *data,
* in32_t *src[], int32_t *dst[])
*/
.section .text
.align 2
.global dsp_downsample
dsp_downsample:
lea.l -40(%sp), %sp | save non-clobberables
movem.l %d2-%d7/%a2-%a5, (%sp) |
movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count
| %a0 = data
| %a1 = src
| %a2 = dst
movem.l 4(%a0), %d3-%d4 | %d3 = ch = data->num_channels
| %d4 = delta = data->resample_data.delta
moveq.l #16, %d7 | %d7 = shift
10: | channel loop |
move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase
move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1]
move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1]
lea.l 12(%a0, %d3.l*4), %a5 | %a5 = &data->resample_data.ast_sample[ch-1]
move.l (%a5), %d0 | %d0 = last = data->resample_data.last_sample[ch-1]
move.l -4(%a3, %d2.l*4), (%a5) | data->resample_data.last_sample[ch-1] = s[count-1]
move.l %d5, %d6 | %d6 = pos = phase >> 16
lsr.l %d7, %d6 |
cmp.l %d2, %d6 | past end of samples?
bge.b 40f | skip resample loop| yes? skip loop
tst.l %d6 | need last sample of prev. frame?
bne.b 20f | resample loop | no? start main loop
move.l (%a3, %d6.l*4), %d1 | %d1 = s[pos]
bra.b 30f | resample start last | start with last (last in %d0)
20: | resample loop |
lea.l -4(%a3, %d6.l*4), %a5 | load s[pos-1] and s[pos]
movem.l (%a5), %d0-%d1 |
30: | resample start last |
sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
move.l %d0, %acc0 | %acc0 = previous sample
move.l %d5, %d0 | frac = (phase << 16) >> 1
lsl.l %d7, %d0 |
lsr.l #1, %d0 |
mac.l %d0, %d1, %acc0 | %acc0 += frac * diff
add.l %d4, %d5 | phase += delta
move.l %d5, %d6 | pos = phase >> 16
lsr.l %d7, %d6 |
movclr.l %acc0, %d0 |
move.l %d0, (%a4)+ | *d++ = %d0
cmp.l %d2, %d6 | pos < count?
blt.b 20b | resample loop | yes? continue resampling
40: | skip resample loop |
subq.l #1, %d3 | ch > 0?
bgt.b 10b | channel loop | yes? process next channel
lsl.l %d7, %d2 | wrap phase to start of next frame
sub.l %d2, %d5 | data->resample_data.phase =
move.l %d5, 12(%a0) | ... phase - (count << 16)
move.l %a4, %d0 | return d - d[0]
sub.l (%a2), %d0 |
asr.l #2, %d0 | convert bytes->samples
movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables
lea.l 40(%sp), %sp | cleanup stack
rts | buh-bye
.size dsp_downsample,.-dsp_downsample
/****************************************************************************
* int dsp_upsample(int count, struct dsp_data *dsp,
* const int32_t *src[], int32_t *dst[])
*/
.section .text
.align 2
.global dsp_upsample
dsp_upsample:
lea.l -40(%sp), %sp | save non-clobberables
movem.l %d2-%d7/%a2-%a5, (%sp) |
movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count
| %a0 = data
| %a1 = src
| %a2 = dst
movem.l 4(%a0), %d3-%d4 | %d3 = ch = channels
| %d4 = delta = data->resample_data.delta
swap %d4 | swap delta to high word to use...
| ...carries to increment position
10: | channel loop |
move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase
move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1]
lea.l 12(%a0, %d3.l*4), %a4 | %a4 = &data->resample_data.last_sample[ch-1]
lea.l -4(%a3, %d2.l*4), %a5 | %a5 = src_end = &src[count-1]
move.l (%a4), %d0 | %d0 = last = data->resample_data.last_sample[ch-1]
move.l (%a5), (%a4) | data->resample_data.last_sample[ch-1] = s[count-1]
move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1]
move.l (%a3)+, %d1 | fetch first sample - might throw this...
| ...away later but we'll be preincremented
move.l %d1, %d6 | save sample value
sub.l %d0, %d1 | %d1 = diff = s[0] - last
swap %d5 | swap phase to high word to use
| carries to increment position
move.l %d5, %d7 | %d7 = pos = phase >> 16
clr.w %d5 |
eor.l %d5, %d7 | pos == 0?
beq.b 40f | loop start | yes? start loop
cmp.l %d2, %d7 | past end of samples?
bge.b 50f | skip resample loop| yes? go to next channel and collect info
lea.l (%a3, %d7.l*4), %a3 | %a3 = s = &s[pos+1]
movem.l -8(%a3), %d0-%d1 | %d0 = s[pos-1], %d1 = s[pos]
move.l %d1, %d6 | save sample value
sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
bra.b 40f | loop start |
20: | next sample loop |
move.l %d6, %d0 | move previous sample to %d0
move.l (%a3)+, %d1 | fetch next sample
move.l %d1, %d6 | save sample value
sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
30: | same sample loop |
movclr.l %acc0, %d7 | %d7 = result
move.l %d7, (%a4)+ | *d++ = %d7
40: | loop start |
lsr.l #1, %d5 | make phase into frac
move.l %d0, %acc0 | %acc0 = s[pos-1]
mac.l %d1, %d5, %acc0 | %acc0 = diff * frac
lsl.l #1, %d5 | restore frac to phase
add.l %d4, %d5 | phase += delta
bcc.b 30b | same sample loop | load next values?
cmp.l %a5, %a3 | src <= src_end?
bls.b 20b | next sample loop | yes? continue resampling
movclr.l %acc0, %d7 | %d7 = result
move.l %d7, (%a4)+ | *d++ = %d7
50: | skip resample loop |
subq.l #1, %d3 | ch > 0?
bgt.b 10b | channel loop | yes? process next channel
swap %d5 | wrap phase to start of next frame
move.l %d5, 12(%a0) | ...and save in data->resample_data.phase
move.l %a4, %d0 | return d - d[0]
sub.l (%a2), %d0 |
movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables
asr.l #2, %d0 | convert bytes->samples
lea.l 40(%sp), %sp | cleanup stack
rts | buh-bye
.size dsp_upsample,.-dsp_upsample
/****************************************************************************
* void channels_process_sound_chan_mono(int count, int32_t *buf[])
*
* Mix left and right channels 50/50 into a center channel.
*/
.section .text
.align 2
.global channels_process_sound_chan_mono
channels_process_sound_chan_mono:
movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
movem.l (%a0), %a0-%a1 | get channel pointers
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
move.l #0x40000000, %d3 | %d3 = 0.5
move.l (%a0)+, %d1 | prime the input registers
move.l (%a1)+, %d2 |
mac.l %d1, %d3, (%a0)+, %d1, %acc0 |
mac.l %d2, %d3, (%a1)+, %d2, %acc0 |
subq.l #1, %d0 |
ble.s 20f | loop done |
10: | loop |
movclr.l %acc0, %d4 | L = R = l/2 + r/2
mac.l %d1, %d3, (%a0)+, %d1, %acc0 |
mac.l %d2, %d3, (%a1)+, %d2, %acc0 |
move.l %d4, (%a2)+ | output to original buffer
move.l %d4, (%a3)+ |
subq.l #1, %d0 |
bgt.s 10b | loop |
20: | loop done |
movclr.l %acc0, %d4 | output last sample
move.l %d4, (%a2) |
move.l %d4, (%a3) |
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup
rts |
.size channels_process_sound_chan_mono, \
.-channels_process_sound_chan_mono
/****************************************************************************
* void channels_process_sound_chan_custom(int count, int32_t *buf[])
*
* Apply stereo width (narrowing/expanding) effect.
*/
.section .text
.align 2
.global channels_process_sound_chan_custom
channels_process_sound_chan_custom:
movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
lea.l -28(%sp), %sp | save registers
movem.l %d2-%d6/%a2-%a3, (%sp) |
movem.l (%a0), %a0-%a1 | get channel pointers
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
move.l dsp_sw_gain, %d3 | load straight (mid) gain
move.l dsp_sw_cross, %d4 | load cross (side) gain
move.l (%a0)+, %d1 | prime the input registers
move.l (%a1)+, %d2 |
mac.l %d1, %d3 , %acc0 | L = l*gain + r*cross
mac.l %d1, %d4, (%a0)+, %d1, %acc1 | R = r*gain + l*cross
mac.l %d2, %d4 , %acc0 |
mac.l %d2, %d3, (%a1)+, %d2, %acc1 |
subq.l #1, %d0 |
ble.b 20f | loop done |
10: | loop |
movclr.l %acc0, %d5 |
movclr.l %acc1, %d6 |
mac.l %d1, %d3 , %acc0 | L = l*gain + r*cross
mac.l %d1, %d4, (%a0)+, %d1, %acc1 | R = r*gain + l*cross
mac.l %d2, %d4 , %acc0 |
mac.l %d2, %d3, (%a1)+, %d2, %acc1 |
move.l %d5, (%a2)+ |
move.l %d6, (%a3)+ |
subq.l #1, %d0 |
bgt.s 10b | loop |
20: | loop done |
movclr.l %acc0, %d5 | output last sample
movclr.l %acc1, %d6 |
move.l %d5, (%a2) |
move.l %d6, (%a3) |
movem.l (%sp), %d2-%d6/%a2-%a3 | restore registers
lea.l 28(%sp), %sp | cleanup
rts |
.size channels_process_sound_chan_custom, \
.-channels_process_sound_chan_custom
/****************************************************************************
* void channels_process_sound_chan_karaoke(int count, int32_t *buf[])
*
* Separate channels into side channels.
*/
.section .text
.align 2
.global channels_process_sound_chan_karaoke
channels_process_sound_chan_karaoke:
movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
movem.l (%a0), %a0-%a1 | get channel src pointers
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
move.l #0x40000000, %d3 | %d3 = 0.5
move.l (%a0)+, %d1 | prime the input registers
move.l (%a1)+, %d2 |
mac.l %d1, %d3, (%a0)+, %d1, %acc0 | L = l/2 - r/2
msac.l %d2, %d3, (%a1)+, %d2, %acc0 |
subq.l #1, %d0 |
ble.b 20f | loop done |
10: | loop |
movclr.l %acc0, %d4 |
mac.l %d1, %d3, (%a0)+, %d1, %acc0 | L = l/2 - r/2
msac.l %d2, %d3, (%a1)+, %d2, %acc0 |
move.l %d4, (%a2)+ |
neg.l %d4 | R = -L = -(l/2 - r/2) = r/2 - l/2
move.l %d4, (%a3)+ |
subq.l #1, %d0 |
bgt.s 10b | loop |
20: | loop done |
movclr.l %acc0, %d4 | output last sample
move.l %d4, (%a2) |
neg.l %d4 | R = -L = -(l/2 - r/2) = r/2 - l/2
move.l %d4, (%a3) |
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup
rts |
.size channels_process_sound_chan_karaoke, \
.-channels_process_sound_chan_karaoke
/****************************************************************************
* void sample_output_stereo(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*
* Framework based on the ubiquitous Rockbox line transfer logic for
* Coldfire CPUs.
*
* Does emac clamping and scaling (which proved faster than the usual
* checks and branches - even single test clamping) and writes using
* line burst transfers. Also better than writing a single L-R pair per
* loop but a good deal more code.
*
* Attemping bursting during reads is rather futile since the source and
* destination alignments rarely agree and too much complication will
* slow us up. The parallel loads seem to do a bit better at least until
* a pcm buffer can always give line aligned chunk and then aligning the
* dest can then imply the source is aligned if the source buffers are.
* For now longword alignment is assumed of both the source and dest.
*
*/
.section .text
.align 2
.global sample_output_stereo
sample_output_stereo:
lea.l -48(%sp), %sp | save registers
move.l %macsr, %d1 | do it now as at many lines will
movem.l %d1-%d7/%a2-%a6, (%sp) | be the far more common condition
move.l #0x80, %macsr | put emac unit in signed int mode
movem.l 52(%sp), %a0-%a2/%a4 |
lea.l (%a4, %a0.l*4), %a0 | %a0 = end address
move.l (%a1), %d1 | %a1 = multiplier: (1 << (16 - scale))
sub.l #16, %d1 |
neg.l %d1 |
moveq.l #1, %d0 |
asl.l %d1, %d0 |
move.l %d0, %a1 |
move.l #0x8000, %a6 | %a6 = rounding term
movem.l (%a2), %a2-%a3 | get L/R channel pointers
moveq.l #28, %d0 | %d0 = second line bound
add.l %a4, %d0 |
and.l #0xfffffff0, %d0 |
cmp.l %a0, %d0 | at least a full line?
bhi.w 40f | long loop 1 start | no? do as trailing longwords
sub.l #16, %d0 | %d1 = first line bound
cmp.l %a4, %d0 | any leading longwords?
bls.b 20f | line loop start | no? start line loop
10: | long loop 0 |
move.l (%a2)+, %d1 | read longword from L and R
move.l %a6, %acc0 |
move.l %acc0, %acc1 |
mac.l %d1, %a1, (%a3)+, %d2, %acc0 | shift L to high word
mac.l %d2, %a1, %acc1 | shift R to high word
movclr.l %acc0, %d1 | get possibly saturated results
movclr.l %acc1, %d2 |
swap %d2 | move R to low word
move.w %d2, %d1 | interleave MS 16 bits of each
move.l %d1, (%a4)+ | ...and write both
cmp.l %a4, %d0 |
bhi.b 10b | long loop 0 |
20: | line loop start |
lea.l -12(%a0), %a5 | %a5 = at or just before last line bound
30: | line loop |
move.l (%a3)+, %d4 | get next 4 R samples and scale
move.l %a6, %acc0 |
move.l %acc0, %acc1 |
move.l %acc1, %acc2 |
move.l %acc2, %acc3 |
mac.l %d4, %a1, (%a3)+, %d5, %acc0 | with saturation
mac.l %d5, %a1, (%a3)+, %d6, %acc1 |
mac.l %d6, %a1, (%a3)+, %d7, %acc2 |
mac.l %d7, %a1, (%a2)+, %d0, %acc3 |
lea.l 16(%a4), %a4 | increment dest here, mitigate stalls
movclr.l %acc0, %d4 | obtain R results
movclr.l %acc1, %d5 |
movclr.l %acc2, %d6 |
movclr.l %acc3, %d7 |
move.l %a6, %acc0 |
move.l %acc0, %acc1 |
move.l %acc1, %acc2 |
move.l %acc2, %acc3 |
mac.l %d0, %a1, (%a2)+, %d1, %acc0 | get next 4 L samples and scale
mac.l %d1, %a1, (%a2)+, %d2, %acc1 | with saturation
mac.l %d2, %a1, (%a2)+, %d3, %acc2 |
mac.l %d3, %a1 , %acc3 |
swap %d4 | a) interleave most significant...
swap %d5 |
swap %d6 |
swap %d7 |
movclr.l %acc0, %d0 | obtain L results
movclr.l %acc1, %d1 |
movclr.l %acc2, %d2 |
movclr.l %acc3, %d3 |
move.w %d4, %d0 | a) ... 16 bits of L and R
move.w %d5, %d1 |
move.w %d6, %d2 |
move.w %d7, %d3 |
movem.l %d0-%d3, -16(%a4) | write four stereo samples
cmp.l %a4, %a5 |
bhi.b 30b | line loop |
40: | long loop 1 start |
cmp.l %a4, %a0 | any longwords left?
bls.b 60f | output end | no? stop
50: | long loop 1 |
move.l (%a2)+, %d1 | handle trailing longwords
move.l %a6, %acc0 |
move.l %acc0, %acc1 |
mac.l %d1, %a1, (%a3)+, %d2, %acc0 | the same way as leading ones
mac.l %d2, %a1, %acc1 |
movclr.l %acc0, %d1 |
movclr.l %acc1, %d2 |
swap %d2 |
move.w %d2, %d1 |
move.l %d1, (%a4)+ |
cmp.l %a4, %a0 |
bhi.b 50b | long loop 1
60: | output end |
movem.l (%sp), %d1-%d7/%a2-%a6 | restore registers
move.l %d1, %macsr |
lea.l 48(%sp), %sp | cleanup
rts |
.size sample_output_stereo, .-sample_output_stereo
/****************************************************************************
* void sample_output_mono(int count, struct dsp_data *data,
* const int32_t *src[], int16_t *dst)
*
* Same treatment as sample_output_stereo but for one channel.
*/
.section .text
.align 2
.global sample_output_mono
sample_output_mono:
lea.l -32(%sp), %sp | save registers
move.l %macsr, %d1 | do it now as at many lines will
movem.l %d1-%d5/%a2-%a4, (%sp) | be the far more common condition
move.l #0x80, %macsr | put emac unit in signed int mode
movem.l 36(%sp), %a0-%a3 |
lea.l (%a3, %a0.l*4), %a0 | %a0 = end address
move.l (%a1), %d1 | %d5 = multiplier: (1 << (16 - scale))
sub.l #16, %d1 |
neg.l %d1 |
moveq.l #1, %d5 |
asl.l %d1, %d5 |
move.l #0x8000, %a4 | %a4 = rounding term
movem.l (%a2), %a2 | get source channel pointer
moveq.l #28, %d0 | %d0 = second line bound
add.l %a3, %d0 |
and.l #0xfffffff0, %d0 |
cmp.l %a0, %d0 | at least a full line?
bhi.w 40f | long loop 1 start | no? do as trailing longwords
sub.l #16, %d0 | %d1 = first line bound
cmp.l %a3, %d0 | any leading longwords?
bls.b 20f | line loop start | no? start line loop
10: | long loop 0 |
move.l (%a2)+, %d1 | read longword from L and R
move.l %a4, %acc0 |
mac.l %d1, %d5, %acc0 | shift L to high word
movclr.l %acc0, %d1 | get possibly saturated results
move.l %d1, %d2 |
swap %d2 | move R to low word
move.w %d2, %d1 | duplicate single channel into
move.l %d1, (%a3)+ | L and R
cmp.l %a3, %d0 |
bhi.b 10b | long loop 0 |
20: | line loop start |
lea.l -12(%a0), %a1 | %a1 = at or just before last line bound
30: | line loop |
move.l (%a2)+, %d0 | get next 4 L samples and scale
move.l %a4, %acc0 |
move.l %acc0, %acc1 |
move.l %acc1, %acc2 |
move.l %acc2, %acc3 |
mac.l %d0, %d5, (%a2)+, %d1, %acc0 | with saturation
mac.l %d1, %d5, (%a2)+, %d2, %acc1 |
mac.l %d2, %d5, (%a2)+, %d3, %acc2 |
mac.l %d3, %d5 , %acc3 |
lea.l 16(%a3), %a3 | increment dest here, mitigate stalls
movclr.l %acc0, %d0 | obtain results
movclr.l %acc1, %d1 |
movclr.l %acc2, %d2 |
movclr.l %acc3, %d3 |
move.l %d0, %d4 | duplicate single channel
swap %d4 | into L and R
move.w %d4, %d0 |
move.l %d1, %d4 |
swap %d4 |
move.w %d4, %d1 |
move.l %d2, %d4 |
swap %d4 |
move.w %d4, %d2 |
move.l %d3, %d4 |
swap %d4 |
move.w %d4, %d3 |
movem.l %d0-%d3, -16(%a3) | write four stereo samples
cmp.l %a3, %a1 |
bhi.b 30b | line loop |
40: | long loop 1 start |
cmp.l %a3, %a0 | any longwords left?
bls.b 60f | output end | no? stop
50: | loop loop 1 |
move.l (%a2)+, %d1 | handle trailing longwords
move.l %a4, %acc0 |
mac.l %d1, %d5, %acc0 | the same way as leading ones
movclr.l %acc0, %d1 |
move.l %d1, %d2 |
swap %d2 |
move.w %d2, %d1 |
move.l %d1, (%a3)+ |
cmp.l %a3, %a0 |
bhi.b 50b | long loop 1 |
60: | output end |
movem.l (%sp), %d1-%d5/%a2-%a4 | restore registers
move.l %d1, %macsr |
lea.l 32(%sp), %sp | cleanup
rts |
.size sample_output_mono, .-sample_output_mono

268
apps/eq.c
View file

@ -1,268 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
*
* 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 "config.h"
#include "fixedpoint.h"
#include "fracmul.h"
#include "eq.h"
#include "replaygain.h"
/**
* Calculate first order shelving filter. Filter is not directly usable by the
* eq_filter() function.
* @param cutoff shelf midpoint frequency. See eq_pk_coefs for format.
* @param A decibel value multiplied by ten, describing gain/attenuation of
* shelf. Max value is 24 dB.
* @param low true for low-shelf filter, false for high-shelf filter.
* @param c pointer to coefficient storage. Coefficients are s4.27 format.
*/
void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
{
long sin, cos;
int32_t b0, b1, a0, a1; /* s3.28 */
const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */
sin = fp_sincos(cutoff/2, &cos);
if (low) {
const int32_t sin_div_g = fp_div(sin, g, 25);
const int32_t sin_g = FRACMUL(sin, g);
cos >>= 3;
b0 = sin_g + cos; /* 0.25 .. 4.10 */
b1 = sin_g - cos; /* -1 .. 3.98 */
a0 = sin_div_g + cos; /* 0.25 .. 4.10 */
a1 = sin_div_g - cos; /* -1 .. 3.98 */
} else {
const int32_t cos_div_g = fp_div(cos, g, 25);
const int32_t cos_g = FRACMUL(cos, g);
sin >>= 3;
b0 = sin + cos_g; /* 0.25 .. 4.10 */
b1 = sin - cos_g; /* -3.98 .. 1 */
a0 = sin + cos_div_g; /* 0.25 .. 4.10 */
a1 = sin - cos_div_g; /* -3.98 .. 1 */
}
const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */
*c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */
*c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */
*c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
}
#ifdef HAVE_SW_TONE_CONTROLS
/**
* Calculate second order section filter consisting of one low-shelf and one
* high-shelf section.
* @param cutoff_low low-shelf midpoint frequency. See eq_pk_coefs for format.
* @param cutoff_high high-shelf midpoint frequency.
* @param A_low decibel value multiplied by ten, describing gain/attenuation of
* low-shelf part. Max value is 24 dB.
* @param A_high decibel value multiplied by ten, describing gain/attenuation of
* high-shelf part. Max value is 24 dB.
* @param A decibel value multiplied by ten, describing additional overall gain.
* @param c pointer to coefficient storage. Coefficients are s4.27 format.
*/
void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
long A_low, long A_high, long A, int32_t *c)
{
const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
int32_t c_ls[3], c_hs[3];
filter_shelf_coefs(cutoff_low, A_low, true, c_ls);
filter_shelf_coefs(cutoff_high, A_high, false, c_hs);
c_ls[0] = FRACMUL(g, c_ls[0]);
c_ls[1] = FRACMUL(g, c_ls[1]);
/* now we cascade the two first order filters to one second order filter
* which can be used by eq_filter(). these resulting coefficients have a
* really wide numerical range, so we use a fixed point format which will
* work for the selected cutoff frequencies (in dsp.c) only.
*/
const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1];
const int32_t a0 = c_ls[2], a1 = c_hs[2];
*c++ = FRACMUL_SHL(b0, b2, 4);
*c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4);
*c++ = FRACMUL_SHL(b1, b3, 4);
*c++ = a0 + a1;
*c++ = -FRACMUL_SHL(a0, a1, 4);
}
#endif
/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
* Slightly faster calculation can be done by deriving forms which use tan()
* instead of cos() and sin(), but the latter are far easier to use when doing
* fixed point math, and performance is not a big point in the calculation part.
* All the 'a' filter coefficients are negated so we can use only additions
* in the filtering equation.
*/
/**
* Calculate second order section peaking filter coefficients.
* @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and
* 0x80000000 represents the Nyquist frequency (samplerate/2).
* @param Q Q factor value multiplied by ten. Lower bound is artificially set
* at 0.5.
* @param db decibel value multiplied by ten, describing gain/attenuation at
* peak freq. Max value is 24 dB.
* @param c pointer to coefficient storage. Coefficients are s3.28 format.
*/
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
long cs;
const long one = 1 << 28; /* s3.28 */
const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */
const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
int32_t a0, a1, a2; /* these are all s3.28 format */
int32_t b0, b1, b2;
const long alphadivA = fp_div(alpha, A, 27);
const long alphaA = FRACMUL(alpha, A);
/* possible numerical ranges are in comments by each coef */
b0 = one + alphaA; /* [1 .. 5] */
b1 = a1 = -2*(cs >> 3); /* [-2 .. 2] */
b2 = one - alphaA; /* [-3 .. 1] */
a0 = one + alphadivA; /* [1 .. 5] */
a2 = one - alphadivA; /* [-3 .. 1] */
/* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */
const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */
*c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */
*c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */
*c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */
*c++ = FRACMUL(-a1, rcp_a0); /* [-2 .. 2] */
*c++ = FRACMUL(-a2, rcp_a0); /* [-0.6 .. 1] */
}
/**
* Calculate coefficients for lowshelf filter. Parameters are as for
* eq_pk_coefs, but the coefficient format is s5.26 fixed point.
*/
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
long cs;
const long one = 1 << 25; /* s6.25 */
const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
const long ap1 = (A >> 4) + one;
const long am1 = (A >> 4) - one;
const long ap1_cs = FRACMUL(ap1, cs);
const long am1_cs = FRACMUL(am1, cs);
const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
int32_t a0, a1, a2; /* these are all s6.25 format */
int32_t b0, b1, b2;
/* [0.1 .. 40] */
b0 = FRACMUL_SHL(A, ap1 - am1_cs + twosqrtalpha, 2);
/* [-16 .. 63.4] */
b1 = FRACMUL_SHL(A, am1 - ap1_cs, 3);
/* [0 .. 31.7] */
b2 = FRACMUL_SHL(A, ap1 - am1_cs - twosqrtalpha, 2);
/* [0.5 .. 10] */
a0 = ap1 + am1_cs + twosqrtalpha;
/* [-16 .. 4] */
a1 = -2*(am1 + ap1_cs);
/* [0 .. 8] */
a2 = ap1 + am1_cs - twosqrtalpha;
/* [0.1 .. 1.99] */
const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
*c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */
*c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */
*c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */
*c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
*c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
}
/**
* Calculate coefficients for highshelf filter. Parameters are as for
* eq_pk_coefs, but the coefficient format is s5.26 fixed point.
*/
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
long cs;
const long one = 1 << 25; /* s6.25 */
const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
const long ap1 = (A >> 4) + one;
const long am1 = (A >> 4) - one;
const long ap1_cs = FRACMUL(ap1, cs);
const long am1_cs = FRACMUL(am1, cs);
const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
int32_t a0, a1, a2; /* these are all s6.25 format */
int32_t b0, b1, b2;
/* [0.1 .. 40] */
b0 = FRACMUL_SHL(A, ap1 + am1_cs + twosqrtalpha, 2);
/* [-63.5 .. 16] */
b1 = -FRACMUL_SHL(A, am1 + ap1_cs, 3);
/* [0 .. 32] */
b2 = FRACMUL_SHL(A, ap1 + am1_cs - twosqrtalpha, 2);
/* [0.5 .. 10] */
a0 = ap1 - am1_cs + twosqrtalpha;
/* [-4 .. 16] */
a1 = 2*(am1 - ap1_cs);
/* [0 .. 8] */
a2 = ap1 - am1_cs - twosqrtalpha;
/* [0.1 .. 1.99] */
const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
*c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */
*c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */
*c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */
*c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
*c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
}
/* We realise the filters as a second order direct form 1 structure. Direct
* form 1 was chosen because of better numerical properties for fixed point
* implementations.
*/
#if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM))
void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
unsigned channels, unsigned shift)
{
unsigned c, i;
long long acc;
/* Direct form 1 filtering code.
y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
where y[] is output and x[] is input.
*/
for (c = 0; c < channels; c++) {
for (i = 0; i < num; i++) {
acc = (long long) x[c][i] * f->coefs[0];
acc += (long long) f->history[c][0] * f->coefs[1];
acc += (long long) f->history[c][1] * f->coefs[2];
acc += (long long) f->history[c][2] * f->coefs[3];
acc += (long long) f->history[c][3] * f->coefs[4];
f->history[c][1] = f->history[c][0];
f->history[c][0] = x[c][i];
f->history[c][3] = f->history[c][2];
x[c][i] = (acc << shift) >> 32;
f->history[c][2] = x[c][i];
}
}
}
#endif

View file

@ -1,50 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
*
* 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 _EQ_H
#define _EQ_H
#include <inttypes.h>
#include <stdbool.h>
/* These depend on the fixed point formats used by the different filter types
and need to be changed when they change.
*/
#define FILTER_BISHELF_SHIFT 5
#define EQ_PEAK_SHIFT 4
#define EQ_SHELF_SHIFT 6
struct eqfilter {
int32_t coefs[5]; /* Order is b0, b1, b2, a1, a2 */
int32_t history[2][4];
};
void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c);
void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
long A_low, long A_high, long A, int32_t *c);
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
unsigned channels, unsigned shift);
#endif

View file

@ -1,89 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
*
* 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 "config.h"
/* uncomment this to make filtering calculate lower bits after shifting.
* without this, "shift" of the lower bits will be lost here.
*/
/* #define HIGH_PRECISION */
/*
* void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
* unsigned channels, unsigned shift)
*/
#if CONFIG_CPU == PP5002
.section .icode,"ax",%progbits
#else
.text
#endif
.global eq_filter
eq_filter:
ldr r12, [sp] @ get shift parameter
stmdb sp!, { r0-r11, lr } @ save all params and clobbered regs
ldmia r1!, { r4-r8 } @ load coefs
mov r10, r1 @ loop prelude expects filter struct addr in r10
.filterloop:
ldr r9, [sp] @ get pointer to this channels data
add r0, r9, #4
str r0, [sp] @ save back pointer to next channels data
ldr r9, [r9] @ r9 = x[]
ldr r14, [sp, #8] @ r14 = numsamples
ldmia r10, { r0-r3 } @ load history, r10 should be filter struct addr
str r10, [sp, #4] @ save it for loop end
/* r0-r3 = history, r4-r8 = coefs, r9 = x[], r10..r11 = accumulator,
* r12 = shift amount, r14 = number of samples.
*/
.loop:
/* Direct form 1 filtering code.
* y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
* where y[] is output and x[] is input. This is performed out of order to
* reuse registers, we're pretty short on regs.
*/
smull r10, r11, r6, r1 @ acc = b2*x[i - 2]
mov r1, r0 @ fix input history
smlal r10, r11, r5, r0 @ acc += b1*x[i - 1]
ldr r0, [r9] @ load input and fix history in same operation
smlal r10, r11, r7, r2 @ acc += a1*y[i - 1]
smlal r10, r11, r8, r3 @ acc += a2*y[i - 2]
smlal r10, r11, r4, r0 @ acc += b0*x[i] /* avoid stall on arm9*/
mov r3, r2 @ fix output history
mov r2, r11, asl r12 @ get upper part of result and shift left
#ifdef HIGH_PRECISION
rsb r11, r12, #32 @ get shift amount for lower part
orr r2, r2, r10, lsr r11 @ then mix in correctly shifted lower part
#endif
str r2, [r9], #4 @ save result
subs r14, r14, #1 @ are we done with this channel?
bne .loop
ldr r10, [sp, #4] @ load filter struct pointer
stmia r10!, { r0-r3 } @ save back history
ldr r11, [sp, #12] @ load number of channels
subs r11, r11, #1 @ all channels processed?
strne r11, [sp, #12]
bne .filterloop
add sp, sp, #16 @ compensate for temp storage
ldmpc regs=r4-r11

View file

@ -1,91 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
*
* 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.
*
****************************************************************************/
/* uncomment this to make filtering calculate lower bits after shifting.
* without this, "shift" - 1 of the lower bits will be lost here.
*/
/* #define HIGH_PRECISION */
/*
* void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
* unsigned channels, unsigned shift)
*/
.text
.global eq_filter
eq_filter:
lea.l (-11*4, %sp), %sp
movem.l %d2-%d7/%a2-%a6, (%sp) | save clobbered regs
move.l (11*4+8, %sp), %a5 | fetch filter structure address
move.l (11*4+20, %sp), %d7 | load shift count
subq.l #1, %d7 | EMAC gives us one free shift
#ifdef HIGH_PRECISION
moveq.l #8, %d6
sub.l %d7, %d6 | shift for lower part of accumulator
#endif
movem.l (%a5), %a0-%a4 | load coefs
lea.l (5*4, %a5), %a5 | point to filter history
.filterloop:
move.l (11*4+4, %sp), %a6 | load input channel pointer
addq.l #4, (11*4+4, %sp) | point x to next channel
move.l (%a6), %a6
move.l (11*4+12, %sp), %d5 | number of samples
movem.l (%a5), %d0-%d3 | load filter history
/* d0-d3 = history, d4 = temp, d5 = sample count, d6 = lower shift amount,
* d7 = upper shift amount, a0-a4 = coefs, a5 = history pointer, a6 = x[]
*/
.loop:
/* Direct form 1 filtering code. We assume DSP has put EMAC in frac mode.
* y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
* where y[] is output and x[] is input. This is performed out of order
* to do parallel load of input value.
*/
mac.l %a2, %d1, %acc0 | acc = b2*x[i - 2]
move.l %d0, %d1 | fix input history
mac.l %a1, %d0, (%a6), %d0, %acc0 | acc += b1*x[i - 1], x[i] -> d0
mac.l %a0, %d0, %acc0 | acc += b0*x[i]
mac.l %a3, %d2, %acc0 | acc += a1*y[i - 1]
mac.l %a4, %d3, %acc0 | acc += a2*y[i - 2]
move.l %d2, %d3 | fix output history
#ifdef HIGH_PRECISION
move.l %accext01, %d2 | fetch lower part of accumulator
move.b %d2, %d4 | clear upper three bytes
lsr.l %d6, %d4 | shift lower bits
#endif
movclr.l %acc0, %d2 | fetch upper part of result
asl.l %d7, %d2 | restore fixed point format
#ifdef HIGH_PRECISION
or.l %d2, %d4 | combine lower and upper parts
#endif
move.l %d2, (%a6)+ | save result
subq.l #1, %d5 | are we done with this channel?
jne .loop
movem.l %d0-%d3, (%a5) | save history back to struct
lea.l (4*4, %a5), %a5 | point to next channel's history
subq.l #1, (11*4+16, %sp) | have we processed both channels?
jne .filterloop
movem.l (%sp), %d2-%d7/%a2-%a6
lea.l (11*4, %sp), %sp
rts

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 45
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 45
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 10
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 15
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 30
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 20

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 50
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 50
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 35
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 15
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 5
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: -5

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 50
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 50
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 40
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: -20
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 10
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 20

View file

@ -1,17 +0,0 @@
eq enabled: off
eq precut: 0
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 0
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 0
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 0
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 0
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 0

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 45
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 30
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 10
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 45
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 25
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 10

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 55
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 45
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 5
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 25
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 15
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 55

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 65
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 65
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 25
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: -10
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 15
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 35

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 60
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 40
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 15
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: -25
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 5
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 60

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 20
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: -25
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 5
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 20
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: -15
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 15

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 50
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: -10
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 5
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 50
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 15
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: -10

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 45
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 35
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 45
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 5
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 25
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 30

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 45
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: 25
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 10
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 0
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 20
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 45

View file

@ -1,17 +0,0 @@
eq enabled: on
eq precut: 45
eq band 0 cutoff: 60
eq band 0 q: 7
eq band 0 gain: -45
eq band 1 cutoff: 200
eq band 1 q: 10
eq band 1 gain: 5
eq band 2 cutoff: 800
eq band 2 q: 10
eq band 2 gain: 45
eq band 3 cutoff: 4000
eq band 3 q: 10
eq band 3 gain: 20
eq band 4 cutoff: 12000
eq band 4 q: 7
eq band 4 gain: 0

View file

@ -4,7 +4,7 @@
#include <stdint.h>
#include "gcc_extensions.h"
/** FRACTIONAL MULTIPLICATION - TAKEN FROM apps/dsp.h
/** FRACTIONAL MULTIPLICATION
* Multiply two fixed point numbers with 31 fractional bits:
* FRACMUL(x, y)
*

View file

@ -1,641 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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/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

@ -1,353 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,103 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,124 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,108 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,182 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,254 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,591 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,105 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,148 +0,0 @@
#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);
}

View file

@ -1,127 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,65 +0,0 @@
#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

@ -1,39 +0,0 @@
#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

@ -1,53 +0,0 @@
#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

@ -1,374 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,69 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,59 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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 */

View file

@ -1,103 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,97 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,193 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Daniel Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* 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

@ -1,842 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,220 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,278 +0,0 @@
#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;
}

View file

@ -1,215 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,189 +0,0 @@
/*
* 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

@ -1,464 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,67 +0,0 @@
#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

@ -1,89 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,470 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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);
}

View file

@ -1,130 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,123 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,195 +0,0 @@
#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

@ -1,381 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,49 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,432 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,160 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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;
}

View file

@ -1,849 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Daniel Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* 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

@ -1,89 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

View file

@ -1113,7 +1113,6 @@ static void load_lrc_file(void)
/*******************************
* read lyrics from id3
*******************************/
/* taken from apps/metadata/mp3.c */
static unsigned long unsync(unsigned long b0, unsigned long b1,
unsigned long b2, unsigned long b3)
{

View file

@ -1,222 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

@ -1,34 +0,0 @@
/***************************************************************************
* __________ __ ___.
* 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

View file

@ -1,450 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
* Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca>
*
* 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 <stddef.h>
#include <stdio.h>
#include <string.h>
#include "sound.h"
#include "core_alloc.h"
#include "system.h"
#include "tdspeed.h"
#include "settings.h"
#define assert(cond)
#define MIN_RATE 8000
#define MAX_RATE 48000 /* double buffer for double rate */
#define MINFREQ 100
#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
static int32_t** dsp_src;
static int handles[4];
static int32_t *overlap_buffer[2] = { NULL, NULL };
static int32_t *outbuf[2] = { NULL, NULL };
static int move_callback(int handle, void* current, void* new)
{
/* TODO */
(void)handle;
if (dsp_src)
{
int ch = (current == outbuf[0]) ? 0 : 1;
dsp_src[ch] = outbuf[ch] = new;
}
return BUFLIB_CB_OK;
}
static struct buflib_callbacks ops = {
.move_callback = move_callback,
.shrink_callback = NULL,
};
static int ovl_move_callback(int handle, void* current, void* new)
{
/* TODO */
(void)handle;
if (dsp_src)
{
int ch = (current == overlap_buffer[0]) ? 0 : 1;
overlap_buffer[ch] = new;
}
return BUFLIB_CB_OK;
}
static struct buflib_callbacks ovl_ops = {
.move_callback = ovl_move_callback,
.shrink_callback = NULL,
};
static struct tdspeed_state_s
{
bool stereo;
int32_t shift_max; /* maximum displacement on a frame */
int32_t src_step; /* source window pace */
int32_t dst_step; /* destination window pace */
int32_t dst_order; /* power of two for dst_step */
int32_t ovl_shift; /* overlap buffer frame shift */
int32_t ovl_size; /* overlap buffer used size */
int32_t ovl_space; /* overlap buffer size */
int32_t *ovl_buff[2]; /* overlap buffer */
} tdspeed_state;
void tdspeed_init(void)
{
if (!global_settings.timestretch_enabled)
return;
/* Allocate buffers */
if (overlap_buffer[0] == NULL)
{
handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
overlap_buffer[0] = core_get_data(handles[0]);
}
if (overlap_buffer[1] == NULL)
{
handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
overlap_buffer[1] = core_get_data(handles[1]);
}
if (outbuf[0] == NULL)
{
handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
outbuf[0] = core_get_data(handles[2]);
}
if (outbuf[1] == NULL)
{
handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
outbuf[1] = core_get_data(handles[3]);
}
}
void tdspeed_finish(void)
{
for(unsigned i = 0; i < ARRAYLEN(handles); i++)
{
if (handles[i] > 0)
{
core_free(handles[i]);
handles[i] = 0;
}
}
overlap_buffer[0] = overlap_buffer[1] = NULL;
outbuf[0] = outbuf[1] = NULL;
}
bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
{
struct tdspeed_state_s *st = &tdspeed_state;
int src_frame_sz;
/* Check buffers were allocated ok */
if (overlap_buffer[0] == NULL || overlap_buffer[1] == NULL)
return false;
if (outbuf[0] == NULL || outbuf[1] == NULL)
return false;
/* Check parameters */
if (factor == PITCH_SPEED_100)
return false;
if (samplerate < MIN_RATE || samplerate > MAX_RATE)
return false;
if (factor < STRETCH_MIN || factor > STRETCH_MAX)
return false;
st->stereo = stereo;
st->dst_step = samplerate / MINFREQ;
if (factor > PITCH_SPEED_100)
st->dst_step = st->dst_step * PITCH_SPEED_100 / factor;
st->dst_order = 1;
while (st->dst_step >>= 1)
st->dst_order++;
st->dst_step = (1 << st->dst_order);
st->src_step = st->dst_step * factor / PITCH_SPEED_100;
st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step;
src_frame_sz = st->shift_max + st->dst_step;
if (st->dst_step > st->src_step)
src_frame_sz += st->dst_step - st->src_step;
st->ovl_space = ((src_frame_sz - 2) / st->src_step) * st->src_step
+ src_frame_sz;
if (st->src_step > st->dst_step)
st->ovl_space += 2*st->src_step - st->dst_step;
if (st->ovl_space > FIXED_BUFSIZE)
st->ovl_space = FIXED_BUFSIZE;
st->ovl_size = 0;
st->ovl_shift = 0;
st->ovl_buff[0] = overlap_buffer[0];
if (stereo)
st->ovl_buff[1] = overlap_buffer[1];
else
st->ovl_buff[1] = st->ovl_buff[0];
return true;
}
static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
int data_len, int last, int out_size)
/* data_len in samples */
{
struct tdspeed_state_s *st = &tdspeed_state;
int32_t *dest[2];
int32_t next_frame, prev_frame, src_frame_sz;
bool stereo = buf_in[0] != buf_in[1];
assert(stereo == st->stereo);
src_frame_sz = st->shift_max + st->dst_step;
if (st->dst_step > st->src_step)
src_frame_sz += st->dst_step - st->src_step;
/* deal with overlap data first, if any */
if (st->ovl_size)
{
int32_t have, copy, steps;
have = st->ovl_size;
if (st->ovl_shift > 0)
have -= st->ovl_shift;
/* append just enough data to have all of the overlap buffer consumed */
steps = (have - 1) / st->src_step;
copy = steps * st->src_step + src_frame_sz - have;
if (copy < src_frame_sz - st->dst_step)
copy += st->src_step; /* one more step to allow for pregap data */
if (copy > data_len)
copy = data_len;
assert(st->ovl_size + copy <= FIXED_BUFSIZE);
memcpy(st->ovl_buff[0] + st->ovl_size, buf_in[0],
copy * sizeof(int32_t));
if (stereo)
memcpy(st->ovl_buff[1] + st->ovl_size, buf_in[1],
copy * sizeof(int32_t));
if (!last && have + copy < src_frame_sz)
{
/* still not enough to process at least one frame */
st->ovl_size += copy;
return 0;
}
/* recursively call ourselves to process the overlap buffer */
have = st->ovl_size;
st->ovl_size = 0;
if (copy == data_len)
{
assert(have + copy <= FIXED_BUFSIZE);
return tdspeed_apply(buf_out, st->ovl_buff, have+copy, last,
out_size);
}
assert(have + copy <= FIXED_BUFSIZE);
int i = tdspeed_apply(buf_out, st->ovl_buff, have+copy, -1, out_size);
dest[0] = buf_out[0] + i;
dest[1] = buf_out[1] + i;
/* readjust pointers to account for data already consumed */
next_frame = copy - src_frame_sz + st->src_step;
prev_frame = next_frame - st->ovl_shift;
}
else
{
dest[0] = buf_out[0];
dest[1] = buf_out[1];
next_frame = prev_frame = 0;
if (st->ovl_shift > 0)
next_frame += st->ovl_shift;
else
prev_frame += -st->ovl_shift;
}
st->ovl_shift = 0;
/* process all complete frames */
while (data_len - next_frame >= src_frame_sz)
{
/* find frame overlap by autocorelation */
int const INC1 = 8;
int const INC2 = 32;
int64_t min_delta = INT64_MAX; /* most positive */
int shift = 0;
/* Power of 2 of a 28bit number requires 56bits, can accumulate
256times in a 64bit variable. */
assert(st->dst_step / INC2 <= 256);
assert(next_frame + st->shift_max - 1 + st->dst_step - 1 < data_len);
assert(prev_frame + st->dst_step - 1 < data_len);
for (int i = 0; i < st->shift_max; i += INC1)
{
int64_t delta = 0;
int32_t *curr = buf_in[0] + next_frame + i;
int32_t *prev = buf_in[0] + prev_frame;
for (int j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
{
int32_t diff = *curr - *prev;
delta += abs(diff);
if (delta >= min_delta)
goto skip;
}
if (stereo)
{
curr = buf_in[1] + next_frame + i;
prev = buf_in[1] + prev_frame;
for (int j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
{
int32_t diff = *curr - *prev;
delta += abs(diff);
if (delta >= min_delta)
goto skip;
}
}
min_delta = delta;
shift = i;
skip:;
}
/* overlap fading-out previous frame with fading-in current frame */
int32_t *curr = buf_in[0] + next_frame + shift;
int32_t *prev = buf_in[0] + prev_frame;
int32_t *d = dest[0];
assert(next_frame + shift + st->dst_step - 1 < data_len);
assert(prev_frame + st->dst_step - 1 < data_len);
assert(dest[0] - buf_out[0] + st->dst_step - 1 < out_size);
for (int i = 0, j = st->dst_step; j; i++, j--)
{
*d++ = (*curr++ * (int64_t)i +
*prev++ * (int64_t)j) >> st->dst_order;
}
dest[0] = d;
if (stereo)
{
curr = buf_in[1] + next_frame + shift;
prev = buf_in[1] + prev_frame;
d = dest[1];
for (int i = 0, j = st->dst_step; j; i++, j--)
{
assert(d < buf_out[1] + out_size);
*d++ = (*curr++ * (int64_t)i +
*prev++ * (int64_t)j) >> st->dst_order;
}
dest[1] = d;
}
/* adjust pointers for next frame */
prev_frame = next_frame + shift + st->dst_step;
next_frame += st->src_step;
/* here next_frame - prev_frame = src_step - dst_step - shift */
assert(next_frame - prev_frame == st->src_step - st->dst_step - shift);
}
/* now deal with remaining partial frames */
if (last == -1)
{
/* special overlap buffer processing: remember frame shift only */
st->ovl_shift = next_frame - prev_frame;
}
else if (last != 0)
{
/* last call: purge all remaining data to output buffer */
int i = data_len - prev_frame;
assert(dest[0] + i <= buf_out[0] + out_size);
memcpy(dest[0], buf_in[0] + prev_frame, i * sizeof(int32_t));
dest[0] += i;
if (stereo)
{
assert(dest[1] + i <= buf_out[1] + out_size);
memcpy(dest[1], buf_in[1] + prev_frame, i * sizeof(int32_t));
dest[1] += i;
}
}
else
{
/* preserve remaining data + needed overlap data for next call */
st->ovl_shift = next_frame - prev_frame;
int i = (st->ovl_shift < 0) ? next_frame : prev_frame;
st->ovl_size = data_len - i;
assert(st->ovl_size <= FIXED_BUFSIZE);
memcpy(st->ovl_buff[0], buf_in[0] + i, st->ovl_size * sizeof(int32_t));
if (stereo)
memcpy(st->ovl_buff[1], buf_in[1] + i, st->ovl_size * sizeof(int32_t));
}
return dest[0] - buf_out[0];
}
long tdspeed_est_output_size()
{
return TDSPEED_OUTBUFSIZE;
}
long tdspeed_est_input_size(long size)
{
struct tdspeed_state_s *st = &tdspeed_state;
size = (size - st->ovl_size) * st->src_step / st->dst_step;
if (size < 0)
size = 0;
return size;
}
int tdspeed_doit(int32_t *src[], int count)
{
dsp_src = src;
count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
src, count, 0, TDSPEED_OUTBUFSIZE);
src[0] = outbuf[0];
src[1] = outbuf[1];
return count;
}

View file

@ -1,49 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
* Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca>
*
* 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 _TDSPEED_H
#define _TDSPEED_H
#include "dsp.h"
#define TDSPEED_OUTBUFSIZE 4096
/* some #define functions to get the pitch, stretch and speed values based on */
/* two known values. Remember that params are alphabetical. */
#define GET_SPEED(pitch, stretch) \
((pitch * stretch + PITCH_SPEED_100 / 2L) / PITCH_SPEED_100)
#define GET_PITCH(speed, stretch) \
((speed * PITCH_SPEED_100 + stretch / 2L) / stretch)
#define GET_STRETCH(pitch, speed) \
((speed * PITCH_SPEED_100 + pitch / 2L) / pitch)
void tdspeed_init(void);
void tdspeed_finish(void);
bool tdspeed_config(int samplerate, bool stereo, int32_t factor);
long tdspeed_est_output_size(void);
long tdspeed_est_input_size(long size);
int tdspeed_doit(int32_t *src[], int count);
#define STRETCH_MAX (250L * PITCH_SPEED_PRECISION) /* 250% */
#define STRETCH_MIN (35L * PITCH_SPEED_PRECISION) /* 35% */
#endif