mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-11 22:35:59 +01:00
commit
000f0a7295
@ -203,9 +203,16 @@ void flush_ea_mt(VGMSTREAM *vgmstream);
|
||||
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample);
|
||||
void free_ea_mt(ea_mt_codec_data *data, int channels);
|
||||
|
||||
/* relic_decoder */
|
||||
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate);
|
||||
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_relic(relic_codec_data* data);
|
||||
void seek_relic(relic_codec_data* data, int32_t num_sample);
|
||||
void free_relic(relic_codec_data* data);
|
||||
|
||||
/* hca_decoder */
|
||||
hca_codec_data *init_hca(STREAMFILE *streamFile);
|
||||
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do);
|
||||
void decode_hca(hca_codec_data * data, sample_t * outbuf, int32_t samples_to_do);
|
||||
void reset_hca(hca_codec_data * data);
|
||||
void loop_hca(hca_codec_data * data, int32_t num_sample);
|
||||
void free_hca(hca_codec_data * data);
|
||||
|
500
src/coding/relic_decoder.c
Normal file
500
src/coding/relic_decoder.c
Normal file
@ -0,0 +1,500 @@
|
||||
#include "coding.h"
|
||||
|
||||
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.
|
||||
*
|
||||
* Decompiled from Relic's dec.exe with some info from Homeworld source code .h/lib
|
||||
* files (released around 2003 through Relic Dev Network), accurate with minor +-1
|
||||
* samples due to double<>float ops or maybe original compiler (Intel's) diffs.
|
||||
*
|
||||
* TODO: clean API, improve validations (can segfault on bad data) and naming
|
||||
*/
|
||||
|
||||
/* mixfft.c */
|
||||
extern void fft(int n, float *xRe, float *xIm, float *yRe, float *yIm);
|
||||
|
||||
static relic_codec_data* init_codec(int channels, int bitrate, int codec_rate);
|
||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data);
|
||||
static void copy_samples(relic_codec_data* data, sample_t* outbuf, int32_t samples_to_get);
|
||||
static void reset_codec(relic_codec_data* data);
|
||||
|
||||
#define RELIC_MAX_CHANNELS 2
|
||||
#define RELIC_MAX_SCALES 6
|
||||
#define RELIC_BASE_SCALE 10.0f
|
||||
#define RELIC_FREQUENCY_MASKING_FACTOR 1.0f
|
||||
#define RELIC_CRITICAL_BAND_COUNT 27
|
||||
#define RELIC_PI 3.14159265358979323846f
|
||||
#define RELIC_SIZE_LOW 128
|
||||
#define RELIC_SIZE_MID 256
|
||||
#define RELIC_SIZE_HIGH 512
|
||||
#define RELIC_MAX_SIZE RELIC_SIZE_HIGH
|
||||
#define RELIC_MAX_FREQ (RELIC_MAX_SIZE / 2)
|
||||
#define RELIC_MAX_FFT (RELIC_MAX_SIZE / 4)
|
||||
#define RELIC_BITRATE_22 256
|
||||
#define RELIC_BITRATE_44 512
|
||||
#define RELIC_BITRATE_88 1024
|
||||
#define RELIC_BITRATE_176 2048
|
||||
#define RELIC_MAX_FRAME_SIZE ((RELIC_BITRATE_176 / 8) + 0x04) /* extra 0x04 for the bitreader */
|
||||
|
||||
|
||||
struct relic_codec_data {
|
||||
/* decoder info */
|
||||
int channels;
|
||||
int frame_size;
|
||||
int wave_size;
|
||||
int freq_size;
|
||||
int dct_mode;
|
||||
int samples_mode;
|
||||
/* decoder init state */
|
||||
float scales[RELIC_MAX_SCALES]; /* quantization scales */
|
||||
float dct[RELIC_MAX_SIZE];
|
||||
float window[RELIC_MAX_SIZE];
|
||||
/* decoder frame state */
|
||||
uint8_t exponents[RELIC_MAX_CHANNELS][RELIC_MAX_FREQ]; /* quantization/scale indexes */
|
||||
float freq1[RELIC_MAX_FREQ]; /* dequantized spectrum */
|
||||
float freq2[RELIC_MAX_FREQ];
|
||||
float wave_cur[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* current frame samples */
|
||||
float wave_prv[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* previous frame samples */
|
||||
|
||||
/* sample state */
|
||||
int32_t samples_discard;
|
||||
int32_t samples_consumed;
|
||||
int32_t samples_filled;
|
||||
};
|
||||
|
||||
/* ************************************* */
|
||||
|
||||
|
||||
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate) {
|
||||
return init_codec(channels, bitrate, codec_rate);
|
||||
}
|
||||
|
||||
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
|
||||
if (data->samples_consumed < data->samples_filled) {
|
||||
/* consume samples */
|
||||
int samples_to_get = (data->samples_filled - data->samples_consumed);
|
||||
|
||||
if (data->samples_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > data->samples_discard)
|
||||
samples_to_get = data->samples_discard;
|
||||
data->samples_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and copy */
|
||||
if (samples_to_get > samples_to_do)
|
||||
samples_to_get = samples_to_do;
|
||||
|
||||
copy_samples(data, outbuf, samples_to_get);
|
||||
|
||||
samples_to_do -= samples_to_get;
|
||||
outbuf += samples_to_get * data->channels;
|
||||
}
|
||||
|
||||
/* mark consumed samples */
|
||||
data->samples_consumed += samples_to_get;
|
||||
}
|
||||
else {
|
||||
int ok = decode_frame_next(stream, data);
|
||||
if (!ok) goto decode_fail;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
/* on error just put some 0 samples */
|
||||
VGM_LOG("RELIC: decode fail, missing %i samples\n", samples_to_do);
|
||||
memset(outbuf, 0, samples_to_do * data->channels * sizeof(sample));
|
||||
}
|
||||
|
||||
void reset_relic(relic_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
reset_codec(data);
|
||||
data->samples_filled = 0;
|
||||
data->samples_consumed = 0;
|
||||
data->samples_discard = 0;
|
||||
}
|
||||
|
||||
void seek_relic(relic_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_relic(data);
|
||||
data->samples_discard = num_sample;
|
||||
}
|
||||
|
||||
void free_relic(relic_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ***************************************** */
|
||||
|
||||
static const int16_t critical_band_data[RELIC_CRITICAL_BAND_COUNT] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
9, 11, 13, 15, 17, 20, 23, 27,
|
||||
31, 37, 43, 51, 62, 74, 89, 110,
|
||||
139, 180, 256
|
||||
};
|
||||
|
||||
static void init_dct(float *dct, int dct_size) {
|
||||
int i;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size);
|
||||
dct[i] = sin(temp);
|
||||
dct[dct_quarter + i] = cos(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int apply_idct(const float *freq, float *wave, const float *dct, int dct_size) {
|
||||
int i;
|
||||
float factor;
|
||||
float out_re[RELIC_MAX_FFT];
|
||||
float out_im[RELIC_MAX_FFT];
|
||||
float in_re[RELIC_MAX_FFT];
|
||||
float in_im[RELIC_MAX_FFT];
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
int dct_3quarter = 3 * (dct_size >> 2);
|
||||
|
||||
/* prerotation? */
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float coef1 = freq[2 * i] * 0.5f;
|
||||
float coef2 = freq[dct_half - 1 - 2 * i] * 0.5f;
|
||||
in_re[i] = coef1 * dct[dct_quarter + i] + coef2 * dct[i];
|
||||
in_im[i] = -coef1 * dct[i] + coef2 * dct[dct_quarter + i];
|
||||
}
|
||||
|
||||
/* main FFT */
|
||||
fft(dct_quarter, in_re, in_im, out_re, out_im);
|
||||
|
||||
/* postrotation, window and reorder? */
|
||||
factor = 8.0 / sqrt(dct_size);
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float out_re_i = out_re[i];
|
||||
out_re[i] = (out_re[i] * dct[dct_quarter + i] + out_im[i] * dct[i]) * factor;
|
||||
out_im[i] = (-out_re_i * dct[i] + out_im[i] * dct[dct_quarter + i]) * factor;
|
||||
wave_tmp[i * 2] = out_re[i];
|
||||
wave_tmp[i * 2 + dct_half] = out_im[i];
|
||||
}
|
||||
for (i = 1; i < dct_size; i += 2) {
|
||||
wave_tmp[i] = -wave_tmp[dct_size - 1 - i];
|
||||
}
|
||||
|
||||
/* wave mix thing? */
|
||||
for (i = 0; i < dct_3quarter; i++) {
|
||||
wave[i] = wave_tmp[dct_quarter + i];
|
||||
}
|
||||
for (i = dct_3quarter; i < dct_size; i++) {
|
||||
wave[i] = -wave_tmp[i - dct_3quarter];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void decode_frame(const float *freq1, const float *freq2, float *wave_cur, float *wave_prv, const float *dct, const float *window, int dct_size) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
|
||||
/* copy for first half(?) */
|
||||
memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
/* transform frequency domain to time domain with DCT/FFT */
|
||||
apply_idct(freq1, wave_tmp, dct, dct_size);
|
||||
apply_idct(freq2, wave_prv, dct, dct_size);
|
||||
|
||||
/* overlap and apply window function to filter this block's beginning */
|
||||
for (i = 0; i < dct_half; i++) {
|
||||
wave_cur[dct_half + i] = wave_tmp[i] * window[i] + wave_cur[dct_half + i] * window[dct_half + i];
|
||||
wave_prv[i] = wave_prv[i] * window[i] + wave_tmp[dct_half + i] * window[dct_half + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void init_window(float *window, int dct_size) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dct_size; i++) {
|
||||
window[i] = sin((float)i * (RELIC_PI / dct_size));
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_frame_base(const float *freq1, const float *freq2, float *wave_cur, float *wave_prv, const float *dct, const float *window, int dct_mode, int samples_mode) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
|
||||
/* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */
|
||||
|
||||
if (samples_mode == RELIC_SIZE_LOW) {
|
||||
{
|
||||
/* 128 DCT to 128 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_MID) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 256 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 256 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 256 DCT to 256 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_HIGH) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 512 samples (repeat sample x4) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 512 - 1; i += 4) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 2] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 3] = wave_tmp[i >> 2];
|
||||
}
|
||||
}
|
||||
else if (dct_mode == RELIC_SIZE_MID) {
|
||||
/* 256 DCT to 512 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
for (i = 0; i < 512 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 512 DCT to 512 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* reads 32b max, packed in LSB order per byte (like Vorbis), ex.
|
||||
* with 0x45 6A=01000101 01101010 could read 4b=0101, 6b=100100, 3b=010 ...
|
||||
* assumes buf has enough extra bits to read 32b (size +0x04) */
|
||||
static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t *buf) {
|
||||
uint32_t shift, mask, pos, val;
|
||||
|
||||
shift = offset - 8 * (offset / 8);
|
||||
mask = (1 << bits) - 1;
|
||||
pos = offset / 8;
|
||||
val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24);
|
||||
return (val >> shift) & mask;
|
||||
}
|
||||
static int read_sbits(uint8_t bits, uint32_t offset, uint8_t *buf) {
|
||||
uint32_t val = read_ubits(bits, offset, buf);
|
||||
if (val >> (bits - 1) == 1) { /* upper bit = sign */
|
||||
uint32_t mask = (1 << (bits - 1)) - 1;
|
||||
return -(val & mask);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void init_dequantization(float* scales) {
|
||||
int i;
|
||||
|
||||
scales[0] = RELIC_BASE_SCALE;
|
||||
for (i = 1; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = scales[i - 1] * scales[0];
|
||||
}
|
||||
for (i = 0; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = RELIC_FREQUENCY_MASKING_FACTOR / (double) ((1 << (i + 1)) - 1) * scales[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void unpack_frame(uint8_t *buf, int buf_size, float *freq1, float *freq2, const float* scales, uint8_t *exponents, int freq_size) {
|
||||
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
|
||||
int qv;
|
||||
uint8_t ev;
|
||||
uint8_t move, pos;
|
||||
uint32_t bit_offset;
|
||||
int i, j;
|
||||
int freq_half = freq_size >> 1;
|
||||
|
||||
|
||||
memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
memset(freq2, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
|
||||
flags = read_ubits(2u, 0u, buf);
|
||||
cb_bits = read_ubits(3u, 2u, buf);
|
||||
ev_bits = read_ubits(2u, 5u, buf);
|
||||
ei_bits = read_ubits(4u, 7u, buf);
|
||||
bit_offset = 11;
|
||||
|
||||
/* reset exponents indexes */
|
||||
if ((flags & 1) == 1) {
|
||||
memset(exponents, 0, RELIC_MAX_FREQ);
|
||||
}
|
||||
|
||||
/* read packed exponents indexes for all bands */
|
||||
if (cb_bits > 0 && ev_bits > 0) {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
|
||||
if (bit_offset >= 8*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(cb_bits, bit_offset, buf);
|
||||
bit_offset += cb_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
ev = read_ubits(ev_bits, bit_offset, buf);
|
||||
bit_offset += ev_bits;
|
||||
for (j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++)
|
||||
exponents[j] = ev;
|
||||
}
|
||||
}
|
||||
|
||||
/* read quantized values */
|
||||
if (freq_half > 0 && ei_bits > 0) {
|
||||
|
||||
/* read first part */
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset >= 8*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
qv_bits = exponents[pos];
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6)
|
||||
freq1[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
|
||||
/* read second part, or clone it */
|
||||
if ((flags & 2) == 2) {
|
||||
memcpy(freq2, freq1, RELIC_MAX_FREQ * sizeof(float));
|
||||
}
|
||||
else {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset >= 8*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
qv_bits = exponents[pos];
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6)
|
||||
freq2[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
|
||||
int ch;
|
||||
int bytes;
|
||||
uint8_t buf[RELIC_MAX_FRAME_SIZE];
|
||||
|
||||
for (ch = 0; ch < data->channels; ch++) {
|
||||
/* clean extra bytes for bitreader */
|
||||
memset(buf + data->frame_size, 0, RELIC_MAX_FRAME_SIZE - data->frame_size);
|
||||
|
||||
bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
if (bytes != data->frame_size) goto fail;
|
||||
stream->offset += data->frame_size;
|
||||
|
||||
unpack_frame(buf, sizeof(buf), data->freq1, data->freq2, data->scales, data->exponents[ch], data->freq_size);
|
||||
|
||||
decode_frame_base(data->freq1, data->freq2, data->wave_cur[ch], data->wave_prv[ch], data->dct, data->window, data->dct_mode, data->samples_mode);
|
||||
}
|
||||
|
||||
data->samples_consumed = 0;
|
||||
data->samples_filled = data->wave_size;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_samples(relic_codec_data* data, sample_t* outbuf, int32_t samples) {
|
||||
int s, ch;
|
||||
int ichs = data->channels;
|
||||
int skip = data->samples_consumed;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
double d64_sample = data->wave_cur[ch][skip + s];
|
||||
int pcm_sample = clamp16(d64_sample);
|
||||
|
||||
/* f32 in PCM 32767.0 .. -32768.0 format, original code
|
||||
* does some custom double-to-int rint() though */
|
||||
//FQ_BNUM ((float)(1<<26)*(1<<26)*1.5)
|
||||
//rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64))
|
||||
|
||||
outbuf[s*ichs + ch] = pcm_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static relic_codec_data* init_codec(int channels, int bitrate, int codec_rate) {
|
||||
relic_codec_data *data = NULL;
|
||||
|
||||
if (channels > RELIC_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
data = calloc(1, sizeof(relic_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->channels = channels;
|
||||
|
||||
/* dequantized freq1+2 size (separate from DCT) */
|
||||
if (codec_rate < 22050) /* probably 11025 only */
|
||||
data->freq_size = RELIC_SIZE_LOW;
|
||||
if (codec_rate == 22050)
|
||||
data->freq_size = RELIC_SIZE_MID;
|
||||
if (codec_rate > 22050) /* probably 44100 only */
|
||||
data->freq_size = RELIC_SIZE_HIGH;
|
||||
|
||||
/* default for streams (only a few mode combos are valid, see decode) */
|
||||
data->wave_size = RELIC_SIZE_HIGH;
|
||||
data->dct_mode = RELIC_SIZE_HIGH;
|
||||
data->samples_mode = RELIC_SIZE_HIGH;
|
||||
|
||||
init_dct(data->dct, RELIC_SIZE_HIGH);
|
||||
init_window(data->window, RELIC_SIZE_HIGH);
|
||||
init_dequantization(data->scales);
|
||||
memset(data->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
switch(bitrate) {
|
||||
case RELIC_BITRATE_22:
|
||||
case RELIC_BITRATE_44:
|
||||
case RELIC_BITRATE_88:
|
||||
case RELIC_BITRATE_176:
|
||||
data->frame_size = (bitrate / 8); /* 0x100 and 0x80 are common */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_relic(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void reset_codec(relic_codec_data* data) {
|
||||
memset(data->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
}
|
618
src/coding/relic_decoder_mixfft.c
Normal file
618
src/coding/relic_decoder_mixfft.c
Normal file
@ -0,0 +1,618 @@
|
||||
/* Original Relic code uses mixfft.c v1 by Jens Jorgen Nielsen, though was
|
||||
* modified to use floats instead of doubles. This is a 100% decompilation
|
||||
* that somehow resulted in the exact same code (no compiler optims set?),
|
||||
* so restores comments back but removes/cleans globals (could be simplified). */
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/************************************************************************
|
||||
fft(int n, double xRe[], double xIm[], double yRe[], double yIm[])
|
||||
------------------------------------------------------------------------
|
||||
NOTE : This is copyrighted material, Not public domain. See below.
|
||||
------------------------------------------------------------------------
|
||||
Input/output:
|
||||
int n transformation length.
|
||||
double xRe[] real part of input sequence.
|
||||
double xIm[] imaginary part of input sequence.
|
||||
double yRe[] real part of output sequence.
|
||||
double yIm[] imaginary part of output sequence.
|
||||
------------------------------------------------------------------------
|
||||
Function:
|
||||
The procedure performs a fast discrete Fourier transform (FFT) of
|
||||
a complex sequence, x, of an arbitrary length, n. The output, y,
|
||||
is also a complex sequence of length n.
|
||||
|
||||
y[k] = sum(x[m]*exp(-i*2*pi*k*m/n), m=0..(n-1)), k=0,...,(n-1)
|
||||
|
||||
The largest prime factor of n must be less than or equal to the
|
||||
constant maxPrimeFactor defined below.
|
||||
------------------------------------------------------------------------
|
||||
Author:
|
||||
Jens Joergen Nielsen For non-commercial use only.
|
||||
Bakkehusene 54 A $100 fee must be paid if used
|
||||
DK-2970 Hoersholm commercially. Please contact.
|
||||
DENMARK
|
||||
|
||||
E-mail : jjn@get2net.dk All rights reserved. October 2000.
|
||||
Homepage : http://home.get2net.dk/jjn
|
||||
------------------------------------------------------------------------
|
||||
Implementation notes:
|
||||
The general idea is to factor the length of the DFT, n, into
|
||||
factors that are efficiently handled by the routines.
|
||||
|
||||
A number of short DFT's are implemented with a minimum of
|
||||
arithmetical operations and using (almost) straight line code
|
||||
resulting in very fast execution when the factors of n belong
|
||||
to this set. Especially radix-10 is optimized.
|
||||
|
||||
Prime factors, that are not in the set of short DFT's are handled
|
||||
with direct evaluation of the DFP expression.
|
||||
|
||||
Please report any problems to the author.
|
||||
Suggestions and improvements are welcomed.
|
||||
------------------------------------------------------------------------
|
||||
Benchmarks:
|
||||
The Microsoft Visual C++ compiler was used with the following
|
||||
compile options:
|
||||
/nologo /Gs /G2 /W4 /AH /Ox /D "NDEBUG" /D "_DOS" /FR
|
||||
and the FFTBENCH test executed on a 50MHz 486DX :
|
||||
|
||||
Length Time [s] Accuracy [dB]
|
||||
|
||||
128 0.0054 -314.8
|
||||
256 0.0116 -309.8
|
||||
512 0.0251 -290.8
|
||||
1024 0.0567 -313.6
|
||||
2048 0.1203 -306.4
|
||||
4096 0.2600 -291.8
|
||||
8192 0.5800 -305.1
|
||||
100 0.0040 -278.5
|
||||
200 0.0099 -280.3
|
||||
500 0.0256 -278.5
|
||||
1000 0.0540 -278.5
|
||||
2000 0.1294 -280.6
|
||||
5000 0.3300 -278.4
|
||||
10000 0.7133 -278.5
|
||||
------------------------------------------------------------------------
|
||||
The following procedures are used :
|
||||
factorize : factor the transformation length.
|
||||
transTableSetup : setup table with sofar-, actual-, and remainRadix.
|
||||
permute : permutation allows in-place calculations.
|
||||
twiddleTransf : twiddle multiplications and DFT's for one stage.
|
||||
initTrig : initialise sine/cosine table.
|
||||
fft_4 : length 4 DFT, a la Nussbaumer.
|
||||
fft_5 : length 5 DFT, a la Nussbaumer.
|
||||
fft_10 : length 10 DFT using prime factor FFT.
|
||||
fft_odd : length n DFT, n odd.
|
||||
*************************************************************************/
|
||||
|
||||
#define maxPrimeFactor 37
|
||||
#define maxPrimeFactorDiv2 ((maxPrimeFactor+1)/2)
|
||||
#define maxFactorCount 20
|
||||
|
||||
static const float c3_1 = -1.5f; /* c3_1 = cos(2*pi/3)-1; */
|
||||
static const float c3_2 = 0.866025388240814208984375f; /* c3_2 = sin(2*pi/3); */
|
||||
|
||||
// static const float u5 = 1.256637096405029296875f; /* u5 = 2*pi/5; */
|
||||
static const float c5_1 = -1.25f; /* c5_1 = (cos(u5)+cos(2*u5))/2-1;*/
|
||||
static const float c5_2 = 0.559017002582550048828125f; /* c5_2 = (cos(u5)-cos(2*u5))/2; */
|
||||
static const float c5_3 = -0.951056540012359619140625f; /* c5_3 = -sin(u5); */
|
||||
static const float c5_4 = -1.538841724395751953125f; /* c5_4 = -(sin(u5)+sin(2*u5)); */
|
||||
static const float c5_5 = 0.3632712662220001220703125f; /* c5_5 = (sin(u5)-sin(2*u5)); */
|
||||
static const float c8 = 0.707106769084930419921875f; /* c8 = 1/sqrt(2); */
|
||||
|
||||
static const float pi = 3.1415927410125732421875f;
|
||||
|
||||
#if 0 /* extra */
|
||||
static int groupOffset,dataOffset,adr; //,blockOffset
|
||||
static int groupNo,dataNo,blockNo,twNo;
|
||||
static float omega, tw_re,tw_im;
|
||||
static float twiddleRe[maxPrimeFactor], twiddleIm[maxPrimeFactor],
|
||||
trigRe[maxPrimeFactor], trigIm[maxPrimeFactor],
|
||||
zRe[maxPrimeFactor], zIm[maxPrimeFactor];
|
||||
static float vRe[maxPrimeFactorDiv2], vIm[maxPrimeFactorDiv2];
|
||||
static float wRe[maxPrimeFactorDiv2], wIm[maxPrimeFactorDiv2];
|
||||
#endif
|
||||
|
||||
|
||||
static void factorize(int n, int *nFact, int *fact)
|
||||
{
|
||||
int i,j,k;
|
||||
int nRadix;
|
||||
int radices[7];
|
||||
int factors[maxFactorCount];
|
||||
|
||||
nRadix = 6;
|
||||
radices[1]= 2;
|
||||
radices[2]= 3;
|
||||
radices[3]= 4;
|
||||
radices[4]= 5;
|
||||
radices[5]= 8;
|
||||
radices[6]= 10;
|
||||
|
||||
radices[0]= 1; /* extra (assumed) */
|
||||
factors[0]= 0; /* extra (assumed) */
|
||||
fact[0]= 0; /* extra (assumed) */
|
||||
fact[1]= 0; /* extra (assumed) */
|
||||
|
||||
if (n==1)
|
||||
{
|
||||
j=1;
|
||||
factors[1]=1;
|
||||
}
|
||||
else j=0;
|
||||
i=nRadix;
|
||||
while ((n>1) && (i>0))
|
||||
{
|
||||
if ((n % radices[i]) == 0)
|
||||
{
|
||||
n=n / radices[i];
|
||||
j=j+1;
|
||||
factors[j]=radices[i];
|
||||
}
|
||||
else i=i-1;
|
||||
}
|
||||
if (factors[j] == 2) /*substitute factors 2*8 with 4*4 */
|
||||
{
|
||||
i = j-1;
|
||||
while ((i>0) && (factors[i] != 8)) i--;
|
||||
if (i>0)
|
||||
{
|
||||
factors[j] = 4;
|
||||
factors[i] = 4;
|
||||
}
|
||||
}
|
||||
if (n>1)
|
||||
{
|
||||
for (k=2; k<sqrt(n)+1; k++)
|
||||
while ((n % k) == 0)
|
||||
{
|
||||
n=n / k;
|
||||
j=j+1;
|
||||
factors[j]=k;
|
||||
}
|
||||
if (n>1)
|
||||
{
|
||||
j=j+1;
|
||||
factors[j]=n;
|
||||
}
|
||||
}
|
||||
for (i=1; i<=j; i++)
|
||||
{
|
||||
fact[i] = factors[j-i+1];
|
||||
}
|
||||
*nFact=j;
|
||||
} /* factorize */
|
||||
|
||||
/****************************************************************************
|
||||
After N is factored the parameters that control the stages are generated.
|
||||
For each stage we have:
|
||||
sofar : the product of the radices so far.
|
||||
actual : the radix handled in this stage.
|
||||
remain : the product of the remaining radices.
|
||||
****************************************************************************/
|
||||
|
||||
static void transTableSetup(int *sofar, int *actual, int *remain,
|
||||
int *nFact,
|
||||
int *nPoints)
|
||||
{
|
||||
int i;
|
||||
|
||||
factorize(*nPoints, nFact, actual);
|
||||
if (actual[*nFact] > maxPrimeFactor)
|
||||
{
|
||||
#if 0 /* extra */
|
||||
printf("\nPrime factor of FFT length too large : %6d", actual[*nFact]);
|
||||
exit(1);
|
||||
#endif
|
||||
actual[*nFact] = maxPrimeFactor - 1; /* extra */
|
||||
}
|
||||
remain[0]=*nPoints;
|
||||
sofar[1]=1;
|
||||
remain[1]=*nPoints / actual[1];
|
||||
for (i=2; i<=*nFact; i++)
|
||||
{
|
||||
sofar[i]=sofar[i-1]*actual[i-1];
|
||||
remain[i]=remain[i-1] / actual[i];
|
||||
}
|
||||
} /* transTableSetup */
|
||||
|
||||
/****************************************************************************
|
||||
The sequence y is the permuted input sequence x so that the following
|
||||
transformations can be performed in-place, and the final result is the
|
||||
normal order.
|
||||
****************************************************************************/
|
||||
|
||||
static void permute(int nPoint, int nFact,
|
||||
int *fact, int *remain,
|
||||
float *xRe, float *xIm,
|
||||
float *yRe, float *yIm)
|
||||
|
||||
{
|
||||
int i,j,k;
|
||||
int count[maxFactorCount];
|
||||
|
||||
for (i=1; i<=nFact; i++) count[i]=0;
|
||||
k=0;
|
||||
for (i=0; i<=nPoint-2; i++)
|
||||
{
|
||||
yRe[i] = xRe[k];
|
||||
yIm[i] = xIm[k];
|
||||
j=1;
|
||||
k=k+remain[j];
|
||||
count[1] = count[1]+1;
|
||||
while (count[j] >= fact[j])
|
||||
{
|
||||
count[j]=0;
|
||||
k=k-remain[j-1]+remain[j+1];
|
||||
j=j+1;
|
||||
count[j]=count[j]+1;
|
||||
}
|
||||
}
|
||||
yRe[nPoint-1]=xRe[nPoint-1];
|
||||
yIm[nPoint-1]=xIm[nPoint-1];
|
||||
} /* permute */
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Twiddle factor multiplications and transformations are performed on a
|
||||
group of data. The number of multiplications with 1 are reduced by skipping
|
||||
the twiddle multiplication of the first stage and of the first group of the
|
||||
following stages.
|
||||
***************************************************************************/
|
||||
|
||||
static void initTrig(int radix, float *trigRe, float*trigIm)
|
||||
{
|
||||
int i;
|
||||
float w,xre,xim;
|
||||
|
||||
w=2*pi/radix;
|
||||
trigRe[0]=1; trigIm[0]=0;
|
||||
xre=cos(w);
|
||||
xim=-sin(w);
|
||||
trigRe[1]=xre; trigIm[1]=xim;
|
||||
for (i=2; i<radix; i++)
|
||||
{
|
||||
trigRe[i]=xre*trigRe[i-1] - xim*trigIm[i-1];
|
||||
trigIm[i]=xim*trigRe[i-1] + xre*trigIm[i-1];
|
||||
}
|
||||
} /* initTrig */
|
||||
|
||||
static void fft_4(float *aRe, float *aIm)
|
||||
{
|
||||
float t1_re,t1_im, t2_re,t2_im;
|
||||
float m2_re,m2_im, m3_re,m3_im;
|
||||
|
||||
t1_re=aRe[0] + aRe[2]; t1_im=aIm[0] + aIm[2];
|
||||
t2_re=aRe[1] + aRe[3]; t2_im=aIm[1] + aIm[3];
|
||||
|
||||
m2_re=aRe[0] - aRe[2]; m2_im=aIm[0] - aIm[2];
|
||||
m3_re=aIm[1] - aIm[3]; m3_im=aRe[3] - aRe[1];
|
||||
|
||||
aRe[0]=t1_re + t2_re; aIm[0]=t1_im + t2_im;
|
||||
aRe[2]=t1_re - t2_re; aIm[2]=t1_im - t2_im;
|
||||
aRe[1]=m2_re + m3_re; aIm[1]=m2_im + m3_im;
|
||||
aRe[3]=m2_re - m3_re; aIm[3]=m2_im - m3_im;
|
||||
} /* fft_4 */
|
||||
|
||||
|
||||
static void fft_5(float *aRe, float *aIm)
|
||||
{
|
||||
float t1_re,t1_im, t2_re,t2_im, t3_re,t3_im;
|
||||
float t4_re,t4_im, t5_re,t5_im;
|
||||
float m2_re,m2_im, m3_re,m3_im, m4_re,m4_im;
|
||||
float m1_re,m1_im, m5_re,m5_im;
|
||||
float s1_re,s1_im, s2_re,s2_im, s3_re,s3_im;
|
||||
float s4_re,s4_im, s5_re,s5_im;
|
||||
|
||||
t1_re=aRe[1] + aRe[4]; t1_im=aIm[1] + aIm[4];
|
||||
t2_re=aRe[2] + aRe[3]; t2_im=aIm[2] + aIm[3];
|
||||
t3_re=aRe[1] - aRe[4]; t3_im=aIm[1] - aIm[4];
|
||||
t4_re=aRe[3] - aRe[2]; t4_im=aIm[3] - aIm[2];
|
||||
t5_re=t1_re + t2_re; t5_im=t1_im + t2_im;
|
||||
aRe[0]=aRe[0] + t5_re; aIm[0]=aIm[0] + t5_im;
|
||||
m1_re=c5_1*t5_re; m1_im=c5_1*t5_im;
|
||||
m2_re=c5_2*(t1_re - t2_re); m2_im=c5_2*(t1_im - t2_im);
|
||||
|
||||
m3_re=-c5_3*(t3_im + t4_im); m3_im=c5_3*(t3_re + t4_re);
|
||||
m4_re=-c5_4*t4_im; m4_im=c5_4*t4_re;
|
||||
m5_re=-c5_5*t3_im; m5_im=c5_5*t3_re;
|
||||
|
||||
s3_re=m3_re - m4_re; s3_im=m3_im - m4_im;
|
||||
s5_re=m3_re + m5_re; s5_im=m3_im + m5_im;
|
||||
s1_re=aRe[0] + m1_re; s1_im=aIm[0] + m1_im;
|
||||
s2_re=s1_re + m2_re; s2_im=s1_im + m2_im;
|
||||
s4_re=s1_re - m2_re; s4_im=s1_im - m2_im;
|
||||
|
||||
aRe[1]=s2_re + s3_re; aIm[1]=s2_im + s3_im;
|
||||
aRe[2]=s4_re + s5_re; aIm[2]=s4_im + s5_im;
|
||||
aRe[3]=s4_re - s5_re; aIm[3]=s4_im - s5_im;
|
||||
aRe[4]=s2_re - s3_re; aIm[4]=s2_im - s3_im;
|
||||
} /* fft_5 */
|
||||
|
||||
static void fft_8(float *zRe, float *zIm)
|
||||
{
|
||||
float aRe[4], aIm[4], bRe[4], bIm[4], gem;
|
||||
|
||||
aRe[0] = zRe[0]; bRe[0] = zRe[1];
|
||||
aRe[1] = zRe[2]; bRe[1] = zRe[3];
|
||||
aRe[2] = zRe[4]; bRe[2] = zRe[5];
|
||||
aRe[3] = zRe[6]; bRe[3] = zRe[7];
|
||||
|
||||
aIm[0] = zIm[0]; bIm[0] = zIm[1];
|
||||
aIm[1] = zIm[2]; bIm[1] = zIm[3];
|
||||
aIm[2] = zIm[4]; bIm[2] = zIm[5];
|
||||
aIm[3] = zIm[6]; bIm[3] = zIm[7];
|
||||
|
||||
fft_4(aRe, aIm); fft_4(bRe, bIm);
|
||||
|
||||
gem = c8*(bRe[1] + bIm[1]);
|
||||
bIm[1] = c8*(bIm[1] - bRe[1]);
|
||||
bRe[1] = gem;
|
||||
gem = bIm[2];
|
||||
bIm[2] =-bRe[2];
|
||||
bRe[2] = gem;
|
||||
gem = c8*(bIm[3] - bRe[3]);
|
||||
bIm[3] =-c8*(bRe[3] + bIm[3]);
|
||||
bRe[3] = gem;
|
||||
|
||||
zRe[0] = aRe[0] + bRe[0]; zRe[4] = aRe[0] - bRe[0];
|
||||
zRe[1] = aRe[1] + bRe[1]; zRe[5] = aRe[1] - bRe[1];
|
||||
zRe[2] = aRe[2] + bRe[2]; zRe[6] = aRe[2] - bRe[2];
|
||||
zRe[3] = aRe[3] + bRe[3]; zRe[7] = aRe[3] - bRe[3];
|
||||
|
||||
zIm[0] = aIm[0] + bIm[0]; zIm[4] = aIm[0] - bIm[0];
|
||||
zIm[1] = aIm[1] + bIm[1]; zIm[5] = aIm[1] - bIm[1];
|
||||
zIm[2] = aIm[2] + bIm[2]; zIm[6] = aIm[2] - bIm[2];
|
||||
zIm[3] = aIm[3] + bIm[3]; zIm[7] = aIm[3] - bIm[3];
|
||||
} /* fft_8 */
|
||||
|
||||
static void fft_10(float *zRe, float *zIm)
|
||||
{
|
||||
float aRe[5], aIm[5], bRe[5], bIm[5];
|
||||
|
||||
aRe[0] = zRe[0]; bRe[0] = zRe[5];
|
||||
aRe[1] = zRe[2]; bRe[1] = zRe[7];
|
||||
aRe[2] = zRe[4]; bRe[2] = zRe[9];
|
||||
aRe[3] = zRe[6]; bRe[3] = zRe[1];
|
||||
aRe[4] = zRe[8]; bRe[4] = zRe[3];
|
||||
|
||||
aIm[0] = zIm[0]; bIm[0] = zIm[5];
|
||||
aIm[1] = zIm[2]; bIm[1] = zIm[7];
|
||||
aIm[2] = zIm[4]; bIm[2] = zIm[9];
|
||||
aIm[3] = zIm[6]; bIm[3] = zIm[1];
|
||||
aIm[4] = zIm[8]; bIm[4] = zIm[3];
|
||||
|
||||
fft_5(aRe, aIm); fft_5(bRe, bIm);
|
||||
|
||||
zRe[0] = aRe[0] + bRe[0]; zRe[5] = aRe[0] - bRe[0];
|
||||
zRe[6] = aRe[1] + bRe[1]; zRe[1] = aRe[1] - bRe[1];
|
||||
zRe[2] = aRe[2] + bRe[2]; zRe[7] = aRe[2] - bRe[2];
|
||||
zRe[8] = aRe[3] + bRe[3]; zRe[3] = aRe[3] - bRe[3];
|
||||
zRe[4] = aRe[4] + bRe[4]; zRe[9] = aRe[4] - bRe[4];
|
||||
|
||||
zIm[0] = aIm[0] + bIm[0]; zIm[5] = aIm[0] - bIm[0];
|
||||
zIm[6] = aIm[1] + bIm[1]; zIm[1] = aIm[1] - bIm[1];
|
||||
zIm[2] = aIm[2] + bIm[2]; zIm[7] = aIm[2] - bIm[2];
|
||||
zIm[8] = aIm[3] + bIm[3]; zIm[3] = aIm[3] - bIm[3];
|
||||
zIm[4] = aIm[4] + bIm[4]; zIm[9] = aIm[4] - bIm[4];
|
||||
} /* fft_10 */
|
||||
|
||||
static void fft_odd(int radix, float *trigRe, float *trigIm, float *zRe, float* zIm)
|
||||
{
|
||||
float rere, reim, imre, imim;
|
||||
int i,j,k,n,max;
|
||||
float vRe[maxPrimeFactorDiv2] = {0}, vIm[maxPrimeFactorDiv2] = {0}; /* extra */
|
||||
float wRe[maxPrimeFactorDiv2] = {0}, wIm[maxPrimeFactorDiv2] = {0}; /* extra */
|
||||
|
||||
n = radix;
|
||||
max = (n + 1)/2;
|
||||
for (j=1; j < max; j++)
|
||||
{
|
||||
vRe[j] = zRe[j] + zRe[n-j];
|
||||
vIm[j] = zIm[j] - zIm[n-j];
|
||||
wRe[j] = zRe[j] - zRe[n-j];
|
||||
wIm[j] = zIm[j] + zIm[n-j];
|
||||
}
|
||||
|
||||
for (j=1; j < max; j++)
|
||||
{
|
||||
zRe[j]=zRe[0];
|
||||
zIm[j]=zIm[0];
|
||||
zRe[n-j]=zRe[0];
|
||||
zIm[n-j]=zIm[0];
|
||||
k=j;
|
||||
for (i=1; i < max; i++)
|
||||
{
|
||||
rere = trigRe[k] * vRe[i];
|
||||
imim = trigIm[k] * vIm[i];
|
||||
reim = trigRe[k] * wIm[i];
|
||||
imre = trigIm[k] * wRe[i];
|
||||
|
||||
zRe[n-j] += rere + imim;
|
||||
zIm[n-j] += reim - imre;
|
||||
zRe[j] += rere - imim;
|
||||
zIm[j] += reim + imre;
|
||||
|
||||
k = k + j;
|
||||
if (k >= n) k = k - n;
|
||||
}
|
||||
}
|
||||
for (j=1; j < max; j++)
|
||||
{
|
||||
zRe[0]=zRe[0] + vRe[j];
|
||||
zIm[0]=zIm[0] + wIm[j];
|
||||
}
|
||||
} /* fft_odd */
|
||||
|
||||
|
||||
static void twiddleTransf(int sofarRadix, int radix, int remainRadix,
|
||||
float *yRe, float *yIm)
|
||||
|
||||
{ /* twiddleTransf */
|
||||
float cosw, sinw, gem;
|
||||
float t1_re,t1_im, t2_re,t2_im, t3_re,t3_im;
|
||||
float t4_re,t4_im, t5_re,t5_im;
|
||||
float m1_re,m1_im, m2_re,m2_im, m3_re,m3_im;
|
||||
float m4_re,m4_im, m5_re,m5_im;
|
||||
float s1_re,s1_im, s2_re,s2_im, s3_re,s3_im;
|
||||
float s4_re,s4_im, s5_re,s5_im;
|
||||
int groupOffset,dataOffset,adr; //,blockOffset /* extra */
|
||||
int groupNo,dataNo,blockNo,twNo; /* extra */
|
||||
float omega, tw_re,tw_im; /* extra */
|
||||
float twiddleRe[maxPrimeFactor] = {0}, twiddleIm[maxPrimeFactor] = {0}, /* extra */
|
||||
trigRe[maxPrimeFactor] = {0}, trigIm[maxPrimeFactor] = {0}, /* extra */
|
||||
zRe[maxPrimeFactor] = {0}, zIm[maxPrimeFactor] = {0}; /* extra */
|
||||
|
||||
|
||||
initTrig(radix, trigRe, trigIm);
|
||||
omega = 2*pi/(double)(sofarRadix*radix);
|
||||
cosw = cos(omega);
|
||||
sinw = -sin(omega);
|
||||
tw_re = 1.0;
|
||||
tw_im = 0;
|
||||
dataOffset=0;
|
||||
groupOffset=dataOffset;
|
||||
adr=groupOffset;
|
||||
for (dataNo=0; dataNo<sofarRadix; dataNo++)
|
||||
{
|
||||
if (sofarRadix>1)
|
||||
{
|
||||
twiddleRe[0] = 1.0;
|
||||
twiddleIm[0] = 0.0;
|
||||
twiddleRe[1] = tw_re;
|
||||
twiddleIm[1] = tw_im;
|
||||
for (twNo=2; twNo<radix; twNo++)
|
||||
{
|
||||
twiddleRe[twNo]=tw_re*twiddleRe[twNo-1]
|
||||
- tw_im*twiddleIm[twNo-1];
|
||||
twiddleIm[twNo]=tw_im*twiddleRe[twNo-1]
|
||||
+ tw_re*twiddleIm[twNo-1];
|
||||
}
|
||||
gem = cosw*tw_re - sinw*tw_im;
|
||||
tw_im = sinw*tw_re + cosw*tw_im;
|
||||
tw_re = gem;
|
||||
}
|
||||
for (groupNo=0; groupNo<remainRadix; groupNo++)
|
||||
{
|
||||
if ((sofarRadix>1) && (dataNo > 0))
|
||||
{
|
||||
zRe[0]=yRe[adr];
|
||||
zIm[0]=yIm[adr];
|
||||
blockNo=1;
|
||||
do {
|
||||
adr = adr + sofarRadix;
|
||||
zRe[blockNo]= twiddleRe[blockNo] * yRe[adr]
|
||||
- twiddleIm[blockNo] * yIm[adr];
|
||||
zIm[blockNo]= twiddleRe[blockNo] * yIm[adr]
|
||||
+ twiddleIm[blockNo] * yRe[adr];
|
||||
|
||||
blockNo++;
|
||||
} while (blockNo < radix);
|
||||
}
|
||||
else {
|
||||
for (blockNo=0; blockNo<radix; blockNo++)
|
||||
{
|
||||
zRe[blockNo]=yRe[adr];
|
||||
zIm[blockNo]=yIm[adr];
|
||||
adr=adr+sofarRadix;
|
||||
}
|
||||
}
|
||||
switch(radix) {
|
||||
case 2 : gem=zRe[0] + zRe[1];
|
||||
zRe[1]=zRe[0] - zRe[1]; zRe[0]=gem;
|
||||
gem=zIm[0] + zIm[1];
|
||||
zIm[1]=zIm[0] - zIm[1]; zIm[0]=gem;
|
||||
break;
|
||||
case 3 : t1_re=zRe[1] + zRe[2]; t1_im=zIm[1] + zIm[2];
|
||||
zRe[0]=zRe[0] + t1_re; zIm[0]=zIm[0] + t1_im;
|
||||
m1_re=c3_1*t1_re; m1_im=c3_1*t1_im;
|
||||
m2_re=c3_2*(zIm[1] - zIm[2]);
|
||||
m2_im=c3_2*(zRe[2] - zRe[1]);
|
||||
s1_re=zRe[0] + m1_re; s1_im=zIm[0] + m1_im;
|
||||
zRe[1]=s1_re + m2_re; zIm[1]=s1_im + m2_im;
|
||||
zRe[2]=s1_re - m2_re; zIm[2]=s1_im - m2_im;
|
||||
break;
|
||||
case 4 : t1_re=zRe[0] + zRe[2]; t1_im=zIm[0] + zIm[2];
|
||||
t2_re=zRe[1] + zRe[3]; t2_im=zIm[1] + zIm[3];
|
||||
|
||||
m2_re=zRe[0] - zRe[2]; m2_im=zIm[0] - zIm[2];
|
||||
m3_re=zIm[1] - zIm[3]; m3_im=zRe[3] - zRe[1];
|
||||
|
||||
zRe[0]=t1_re + t2_re; zIm[0]=t1_im + t2_im;
|
||||
zRe[2]=t1_re - t2_re; zIm[2]=t1_im - t2_im;
|
||||
zRe[1]=m2_re + m3_re; zIm[1]=m2_im + m3_im;
|
||||
zRe[3]=m2_re - m3_re; zIm[3]=m2_im - m3_im;
|
||||
break;
|
||||
case 5 : t1_re=zRe[1] + zRe[4]; t1_im=zIm[1] + zIm[4];
|
||||
t2_re=zRe[2] + zRe[3]; t2_im=zIm[2] + zIm[3];
|
||||
t3_re=zRe[1] - zRe[4]; t3_im=zIm[1] - zIm[4];
|
||||
t4_re=zRe[3] - zRe[2]; t4_im=zIm[3] - zIm[2];
|
||||
t5_re=t1_re + t2_re; t5_im=t1_im + t2_im;
|
||||
zRe[0]=zRe[0] + t5_re; zIm[0]=zIm[0] + t5_im;
|
||||
m1_re=c5_1*t5_re; m1_im=c5_1*t5_im;
|
||||
m2_re=c5_2*(t1_re - t2_re);
|
||||
m2_im=c5_2*(t1_im - t2_im);
|
||||
|
||||
m3_re=-c5_3*(t3_im + t4_im);
|
||||
m3_im=c5_3*(t3_re + t4_re);
|
||||
m4_re=-c5_4*t4_im; m4_im=c5_4*t4_re;
|
||||
m5_re=-c5_5*t3_im; m5_im=c5_5*t3_re;
|
||||
|
||||
s3_re=m3_re - m4_re; s3_im=m3_im - m4_im;
|
||||
s5_re=m3_re + m5_re; s5_im=m3_im + m5_im;
|
||||
s1_re=zRe[0] + m1_re; s1_im=zIm[0] + m1_im;
|
||||
s2_re=s1_re + m2_re; s2_im=s1_im + m2_im;
|
||||
s4_re=s1_re - m2_re; s4_im=s1_im - m2_im;
|
||||
|
||||
zRe[1]=s2_re + s3_re; zIm[1]=s2_im + s3_im;
|
||||
zRe[2]=s4_re + s5_re; zIm[2]=s4_im + s5_im;
|
||||
zRe[3]=s4_re - s5_re; zIm[3]=s4_im - s5_im;
|
||||
zRe[4]=s2_re - s3_re; zIm[4]=s2_im - s3_im;
|
||||
break;
|
||||
case 8 : fft_8(zRe, zIm); break;
|
||||
case 10 : fft_10(zRe, zIm); break;
|
||||
default : fft_odd(radix, trigRe, trigIm, zRe, zIm); break;
|
||||
}
|
||||
adr=groupOffset;
|
||||
for (blockNo=0; blockNo<radix; blockNo++)
|
||||
{
|
||||
yRe[adr]=zRe[blockNo]; yIm[adr]=zIm[blockNo];
|
||||
adr=adr+sofarRadix;
|
||||
}
|
||||
groupOffset=groupOffset+sofarRadix*radix;
|
||||
adr=groupOffset;
|
||||
}
|
||||
dataOffset=dataOffset+1;
|
||||
groupOffset=dataOffset;
|
||||
adr=groupOffset;
|
||||
}
|
||||
} /* twiddleTransf */
|
||||
|
||||
/*static*/ void fft(int n, float *xRe, float *xIm,
|
||||
float *yRe, float *yIm)
|
||||
{
|
||||
int sofarRadix[maxFactorCount],
|
||||
actualRadix[maxFactorCount],
|
||||
remainRadix[maxFactorCount];
|
||||
int nFactor;
|
||||
int count;
|
||||
|
||||
#if 0
|
||||
pi = 4*atan(1);
|
||||
#endif
|
||||
|
||||
transTableSetup(sofarRadix, actualRadix, remainRadix, &nFactor, &n);
|
||||
permute(n, nFactor, actualRadix, remainRadix, xRe, xIm, yRe, yIm);
|
||||
|
||||
for (count=1; count<=nFactor; count++)
|
||||
twiddleTransf(sofarRadix[count], actualRadix[count], remainRadix[count],
|
||||
yRe, yIm);
|
||||
} /* fft */
|
||||
|
||||
|
@ -164,6 +164,7 @@ static const char* extension_list[] = {
|
||||
"ezw",
|
||||
|
||||
"fag",
|
||||
"fda",
|
||||
"ffw",
|
||||
"filp",
|
||||
//"flac", //common
|
||||
@ -745,6 +746,7 @@ static const coding_info coding_info_list[] = {
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
{coding_RELIC, "Relic Codec"},
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
@ -1252,6 +1254,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_ISB, "Creative ISACT header"},
|
||||
{meta_XSSB, "Artoon XSSB header"},
|
||||
{meta_XMA_UE3, "Unreal Engine XMA header"},
|
||||
{meta_FDA, "Relic FDA header"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -66,14 +66,27 @@ void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
header_size = 0x1c;
|
||||
channel_size = (block_size - header_size) / vgmstream->channels;
|
||||
break;
|
||||
|
||||
case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */
|
||||
header_size = 0x14; //todo the first block is 0x18
|
||||
if (read_32bit(block_offset+0x10, streamFile) == 0x53444154) { /* "SDAT" */
|
||||
header_size = 0x14;
|
||||
channel_size = (block_size - header_size) / vgmstream->channels;
|
||||
}
|
||||
else {
|
||||
header_size = 0;
|
||||
channel_size = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x46494C4C: /* "FILL" (FILLs do that up to 0x6000, but at 0x5FFC don't actually have size) */
|
||||
case 0x46494C4C: /* "FILL" (FILLs do that up to a block size, but near end don't actually have size) */
|
||||
if ((block_offset + 0x04) % 0x6000 == 0)
|
||||
block_size = 0x04;
|
||||
else if ((block_offset + 0x04) % 0x10000 == 0)
|
||||
block_size = 0x04;
|
||||
else if (block_size > 0x100000) { /* other unknown block sizes */
|
||||
VGM_LOG("EA SWVR: bad block size at 0x%lx\n", block_offset);
|
||||
block_size = 0x04;
|
||||
}
|
||||
header_size = 0x08;
|
||||
break;
|
||||
|
||||
|
@ -1422,6 +1422,10 @@
|
||||
RelativePath=".\meta\fag.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fda.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ffdl.c"
|
||||
>
|
||||
@ -2073,6 +2077,14 @@
|
||||
<File
|
||||
RelativePath=".\coding\ptadpcm_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\relic_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\relic_decoder_mixfft.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\sassc_decoder.c"
|
||||
|
@ -443,6 +443,7 @@
|
||||
<ClCompile Include="meta\mta2.c" />
|
||||
<ClCompile Include="meta\xa.c" />
|
||||
<ClCompile Include="meta\fag.c" />
|
||||
<ClCompile Include="meta\fda.c" />
|
||||
<ClCompile Include="meta\ffdl.c" />
|
||||
<ClCompile Include="meta\seb.c" />
|
||||
<ClCompile Include="meta\ea_swvr.c" />
|
||||
@ -570,6 +571,8 @@
|
||||
<ClCompile Include="coding\psv_decoder.c" />
|
||||
<ClCompile Include="coding\psx_decoder.c" />
|
||||
<ClCompile Include="coding\ptadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\relic_decoder.c" />
|
||||
<ClCompile Include="coding\relic_decoder_mixfft.c" />
|
||||
<ClCompile Include="coding\sassc_decoder.c" />
|
||||
<ClCompile Include="coding\sdx2_decoder.c" />
|
||||
<ClCompile Include="coding\ubi_adpcm_decoder.c" />
|
||||
|
@ -877,6 +877,9 @@
|
||||
<ClCompile Include="meta\fag.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\fda.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ffdl.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1237,6 +1240,12 @@
|
||||
<ClCompile Include="coding\ptadpcm_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\relic_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\relic_decoder_mixfft.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\sassc_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
104
src/meta/aifc.c
104
src/meta/aifc.c
@ -1,15 +1,17 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* for reading integers inexplicably packed into 80-bit ('double extended') floats */
|
||||
static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
|
||||
uint8_t buf[10];
|
||||
uint8_t buf[0x0a];
|
||||
int32_t exponent;
|
||||
int32_t mantissa;
|
||||
int i;
|
||||
|
||||
if (read_streamfile(buf,offset,10,streamFile) != 10) return 0;
|
||||
if (read_streamfile(buf,offset,0x0a,streamFile) != 0x0a)
|
||||
return 0;
|
||||
|
||||
exponent = ((buf[0]<<8)|(buf[1]))&0x7fff;
|
||||
exponent -= 16383;
|
||||
@ -26,13 +28,13 @@ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
|
||||
return mantissa*((buf[0]&0x80)?-1:1);
|
||||
}
|
||||
|
||||
static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, int marker_id) {
|
||||
static uint32_t find_marker(STREAMFILE *streamFile, off_t mark_offset, int marker_id) {
|
||||
uint16_t marker_count;
|
||||
int i;
|
||||
off_t marker_offset;
|
||||
|
||||
marker_count = read_16bitBE(MarkerChunkOffset+8,streamFile);
|
||||
marker_offset = MarkerChunkOffset+10;
|
||||
marker_count = read_16bitBE(mark_offset+8,streamFile);
|
||||
marker_offset = mark_offset+10;
|
||||
for (i=0;i<marker_count;i++) {
|
||||
int name_length;
|
||||
|
||||
@ -61,8 +63,8 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
int32_t loop_start = 0, loop_end = 0;
|
||||
|
||||
int is_aiff_ext = 0, is_aifc_ext = 0, is_aiff = 0, is_aifc = 0;
|
||||
int FormatVersionChunkFound = 0, CommonChunkFound = 0, SoundDataChunkFound = 0, MarkerChunkFound = 0, InstrumentChunkFound = 0;
|
||||
off_t MarkerChunkOffset = -1, InstrumentChunkOffset = -1;
|
||||
int fver_found = 0, comm_found = 0, data_found = 0, mark_found = 0, inst_found = 0;
|
||||
off_t mark_offset = -1, inst_offset = -1;
|
||||
|
||||
|
||||
/* checks */
|
||||
@ -74,12 +76,13 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
* .acm: Crusader - No Remorse (SAT)
|
||||
* .adp: Sonic Jam (SAT)
|
||||
* .ai: Dragon Force (SAT)
|
||||
* (extensionless: Doom (3DO) */
|
||||
* (extensionless: Doom (3DO)
|
||||
* .fda: Homeworld 2 (PC) */
|
||||
if (check_extensions(streamFile, "aif,laif,")) {
|
||||
is_aifc_ext = 1;
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else if (check_extensions(streamFile, "aifc,laifc,aifcl,afc,cbd2,bgm")) {
|
||||
else if (check_extensions(streamFile, "aifc,laifc,aifcl,afc,cbd2,bgm,fda")) {
|
||||
is_aifc_ext = 1;
|
||||
}
|
||||
else if (check_extensions(streamFile, "aiff,laiff,acm,adp,ai,aiffl")) {
|
||||
@ -123,9 +126,9 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
|
||||
switch(chunk_type) {
|
||||
case 0x46564552: /* "FVER" (version info) */
|
||||
if (FormatVersionChunkFound) goto fail;
|
||||
if (fver_found) goto fail;
|
||||
if (is_aiff) goto fail; /* plain AIFF shouldn't have */
|
||||
FormatVersionChunkFound = 1;
|
||||
fver_found = 1;
|
||||
|
||||
/* specific size */
|
||||
if (chunk_size != 4) goto fail;
|
||||
@ -135,8 +138,8 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
break;
|
||||
|
||||
case 0x434F4D4D: /* "COMM" (main header) */
|
||||
if (CommonChunkFound) goto fail;
|
||||
CommonChunkFound = 1;
|
||||
if (comm_found) goto fail;
|
||||
comm_found = 1;
|
||||
|
||||
channel_count = read_16bitBE(current_chunk+8,streamFile);
|
||||
if (channel_count <= 0) goto fail;
|
||||
@ -146,24 +149,45 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
sample_rate = read80bitSANE(current_chunk+0x10,streamFile);
|
||||
|
||||
if (is_aifc) {
|
||||
switch (read_32bitBE(current_chunk+0x1a,streamFile)) {
|
||||
uint32_t codec = read_32bitBE(current_chunk+0x1a,streamFile);
|
||||
switch (codec) {
|
||||
case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */
|
||||
coding_type = coding_SDX2;
|
||||
interleave = 0x01;
|
||||
break;
|
||||
|
||||
case 0x43424432: /* "CBD2" [M2 (arcade 3DO) games: IMSA Racing (M2), etc] */
|
||||
coding_type = coding_CBD2;
|
||||
interleave = 0x01;
|
||||
break;
|
||||
|
||||
case 0x41445034: /* "ADP4" */
|
||||
coding_type = coding_DVI_IMA_int;
|
||||
if (channel_count != 1) break; /* don't know how stereo DVI is laid out */
|
||||
break;
|
||||
|
||||
case 0x696D6134: /* "ima4" [Alida (PC), Lunar SSS (iOS)] */
|
||||
coding_type = coding_APPLE_IMA4;
|
||||
interleave = 0x22;
|
||||
sample_count = sample_count * ((interleave-0x2)*2);
|
||||
break;
|
||||
|
||||
case 0x434F4D50: { /* "COMP" (generic compression) */
|
||||
uint8_t comp_name[255] = {0};
|
||||
uint8_t comp_size = read_8bit(current_chunk + 0x1e, streamFile);
|
||||
if (comp_size >= sizeof(comp_name) - 1) goto fail;
|
||||
|
||||
read_streamfile(comp_name, current_chunk + 0x1f, comp_size, streamFile);
|
||||
if (memcmp(comp_name, "Relic Codec v1.6", comp_size) == 0) { /* Homeworld 2 (PC) */
|
||||
coding_type = coding_RELIC;
|
||||
sample_count = sample_count * 512;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VGM_LOG("AIFC: unknown codec\n");
|
||||
goto fail;
|
||||
@ -192,25 +216,25 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
|
||||
case 0x53534E44: /* "SSND" (main data) */
|
||||
case 0x4150434D: /* "APCM" (main data for XA) */
|
||||
if (SoundDataChunkFound) goto fail;
|
||||
SoundDataChunkFound = 1;
|
||||
if (data_found) goto fail;
|
||||
data_found = 1;
|
||||
|
||||
start_offset = current_chunk + 0x10 + read_32bitBE(current_chunk+0x08,streamFile);
|
||||
/* when "APCM" XA frame size is at 0x0c, fixed to 0x914 */
|
||||
break;
|
||||
|
||||
case 0x4D41524B: /* "MARK" (loops) */
|
||||
if (MarkerChunkFound) goto fail;
|
||||
MarkerChunkFound = 1;
|
||||
if (mark_found) goto fail;
|
||||
mark_found = 1;
|
||||
|
||||
MarkerChunkOffset = current_chunk;
|
||||
mark_offset = current_chunk;
|
||||
break;
|
||||
|
||||
case 0x494E5354: /* "INST" (loops) */
|
||||
if (InstrumentChunkFound) goto fail;
|
||||
InstrumentChunkFound = 1;
|
||||
if (inst_found) goto fail;
|
||||
inst_found = 1;
|
||||
|
||||
InstrumentChunkOffset = current_chunk;
|
||||
inst_offset = current_chunk;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -223,28 +247,28 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
if (is_aifc) {
|
||||
if (!FormatVersionChunkFound || !CommonChunkFound || !SoundDataChunkFound)
|
||||
if (!fver_found || !comm_found || !data_found)
|
||||
goto fail;
|
||||
} else if (is_aiff) {
|
||||
if (!CommonChunkFound || !SoundDataChunkFound)
|
||||
if (!comm_found || !data_found)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* read loop points */
|
||||
if (InstrumentChunkFound && MarkerChunkFound) {
|
||||
if (inst_found && mark_found) {
|
||||
int start_marker;
|
||||
int end_marker;
|
||||
/* use the sustain loop */
|
||||
/* if playMode=ForwardLooping */
|
||||
if (read_16bitBE(InstrumentChunkOffset+16,streamFile) == 1) {
|
||||
start_marker = read_16bitBE(InstrumentChunkOffset+18,streamFile);
|
||||
end_marker = read_16bitBE(InstrumentChunkOffset+20,streamFile);
|
||||
if (read_16bitBE(inst_offset+16,streamFile) == 1) {
|
||||
start_marker = read_16bitBE(inst_offset+18,streamFile);
|
||||
end_marker = read_16bitBE(inst_offset+20,streamFile);
|
||||
/* check for sustain markers != 0 (invalid marker no) */
|
||||
if (start_marker && end_marker) {
|
||||
/* find start marker */
|
||||
loop_start = find_marker(streamFile,MarkerChunkOffset,start_marker);
|
||||
loop_end = find_marker(streamFile,MarkerChunkOffset,end_marker);
|
||||
loop_start = find_marker(streamFile,mark_offset,start_marker);
|
||||
loop_end = find_marker(streamFile,mark_offset,end_marker);
|
||||
|
||||
/* find_marker is type uint32_t as the spec says that's the type
|
||||
* of the position value, but it returns a -1 on error, and the
|
||||
@ -257,6 +281,8 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
loop_flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Relic has "beg loop" "end loop" comments but no actual looping? */
|
||||
}
|
||||
|
||||
|
||||
@ -270,14 +296,28 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->coding_type = coding_type;
|
||||
if (coding_type == coding_XA) {
|
||||
switch(coding_type) {
|
||||
case coding_XA:
|
||||
vgmstream->layout_type = layout_blocked_xa_aiff;
|
||||
/* AIFF XA can use sample rates other than 37800/18900 */
|
||||
/* some Crusader: No Remorse tracks have XA headers with incorrect 0xFF, rip bug/encoder feature? */
|
||||
break;
|
||||
|
||||
case coding_RELIC: {
|
||||
int bitrate = read_16bitBE(start_offset, streamFile);
|
||||
start_offset += 0x02;
|
||||
|
||||
vgmstream->codec_data = init_relic(channel_count, bitrate, sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->sample_rate = 44100; /* fixed output */
|
||||
break;
|
||||
}
|
||||
else {
|
||||
default:
|
||||
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_aifc)
|
||||
|
@ -89,7 +89,6 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
if (!utf_query_s8(utf_sdl, i, "fmt", &fmt))
|
||||
goto fail;
|
||||
VGM_LOG("fmt=%i\n", fmt);
|
||||
/* also nch/sfreq/nsmpl info */
|
||||
|
||||
found = 1;
|
||||
@ -98,7 +97,7 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) {
|
||||
|
||||
if (!found) goto fail;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
VGM_LOG("5: %x, %x\n", sdl_offset, offset);
|
||||
|
||||
subfile_offset = /*sdl_offset +*/ offset;
|
||||
subfile_size = size;
|
||||
|
||||
@ -107,14 +106,12 @@ VGM_LOG("5: %x, %x\n", sdl_offset, offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
|
||||
//;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
|
||||
|
||||
|
||||
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "aax");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
VGM_LOG("7: %i\n", fmt);
|
||||
|
||||
switch(fmt) {
|
||||
case 0: /* AAX */
|
||||
case 6: /* HCA */
|
||||
@ -132,8 +129,6 @@ VGM_LOG("5: %x, %x\n", sdl_offset, offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
VGM_LOG("8\n");
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
strncpy(vgmstream->stream_name, stream_name, STREAM_NAME_SIZE-1);
|
||||
|
||||
|
@ -39,7 +39,7 @@ static VGMSTREAM *parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16
|
||||
VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset);
|
||||
|
||||
|
||||
/* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
|
||||
/* .SNR+SNS - from EA latest games (~2005-2010), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"snr"))
|
||||
@ -51,7 +51,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .SPS - from EA latest games (~2014), v1 header */
|
||||
/* .SPS - from EA latest games (~2010~present), v1 header */
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) {
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"sps"))
|
||||
@ -1556,6 +1556,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
||||
uint8_t buf[0x100];
|
||||
int bytes, block_size, block_count;
|
||||
size_t stream_size;
|
||||
int is_xma1;
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset);
|
||||
if (!temp_sf) goto fail;
|
||||
@ -1564,6 +1565,13 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
||||
block_size = 0x10000; /* unused */
|
||||
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
|
||||
|
||||
/* EA adopted XMA2 when it appeared around 2006, but detection isn't so easy
|
||||
* (SNS with XMA2 do exist). Decoder should work when playing XMA1 as XMA2, but
|
||||
* the other way around can cause issues, so it's safer to just use XMA2. */
|
||||
is_xma1 = 0; //eaac->version == EAAC_VERSION_V0; /* approximate */
|
||||
if (is_xma1)
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0);
|
||||
else
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, block_count, block_size);
|
||||
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size);
|
||||
if (!data->layers[i]->codec_data) goto fail;
|
||||
|
@ -102,9 +102,14 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
|
||||
sample_rate = 14008;
|
||||
break;
|
||||
case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */
|
||||
coding = coding_PCM8_U_int;
|
||||
if (read_32bit(start_offset+0x10, streamFile) == 0x53484F43) { /* SHDR */
|
||||
coding = coding_PCM8_U_int; //todo there are other codecs
|
||||
channel_count = 1;
|
||||
sample_rate = 14008;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("EA SWVR: unknown block id\n");
|
||||
|
96
src/meta/fda.c
Normal file
96
src/meta/fda.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* FDA - from Relic Entertainment games [Warhammer 4000: Dawn of War (PC)] */
|
||||
VGMSTREAM * init_vgmstream_fda(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, bitrate, sample_rate, num_samples;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "fda"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf) != 0x52656C69 || /* "Reli" */
|
||||
read_u32be(0x04, sf) != 0x63204368 || /* "c Ch" */
|
||||
read_u32be(0x08, sf) != 0x756E6B79 || /* "unky" */
|
||||
read_u32be(0x0c, sf) != 0x0D0A1A00) /* "\r\n\1a\00"*/
|
||||
goto fail;
|
||||
|
||||
/* version? (later .fda change this) */
|
||||
if (read_u32le(0x10, sf) != 1 ||
|
||||
read_u32le(0x14, sf) != 1)
|
||||
goto fail;
|
||||
|
||||
/* read chunks (abridged) */
|
||||
{
|
||||
off_t offset = 0x18;
|
||||
size_t chunk_size, name_size, data_size;
|
||||
|
||||
/* Relic Chunkys have: type, subtype, version, data size, name size, then
|
||||
* name (optional) and data. But format has fixed chunks so we'll simplify a bit. */
|
||||
|
||||
/* DATA-FBIF (file info) */
|
||||
chunk_size = read_u32le(offset + 0x0c, sf);
|
||||
name_size = read_u32le(offset + 0x10, sf);
|
||||
offset += 0x14 + name_size + chunk_size;
|
||||
|
||||
/* FOLD-FDA (folder of chunks) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x46444120) /* "FDA " */
|
||||
goto fail;
|
||||
offset += 0x14;
|
||||
|
||||
/* DATA-INFO (header) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x494E464F) /* "INFO" */
|
||||
goto fail;
|
||||
chunk_size = read_u32le(offset + 0x0c, sf);
|
||||
name_size = read_u32le(offset + 0x10, sf);
|
||||
offset += 0x14 + name_size;
|
||||
|
||||
channel_count = read_s32le(offset + 0x00, sf);
|
||||
/* 0x04: bps */
|
||||
bitrate = read_s32le(offset + 0x08, sf);
|
||||
sample_rate = read_s32le(offset + 0x0c, sf);
|
||||
/* 0x10: loop start? (always 0) */
|
||||
/* 0x14: loop end? (always -1) */
|
||||
/* 0x18: loop offset? (always 0) */
|
||||
loop_flag = 0; /* never set? */
|
||||
offset += chunk_size;
|
||||
|
||||
/* DATA-DATA (data) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x44415441) /* "DATA" */
|
||||
goto fail;
|
||||
chunk_size = read_u32le(offset + 0x0c, sf);
|
||||
name_size = read_u32le(offset + 0x10, sf);
|
||||
offset += 0x14 + name_size;
|
||||
|
||||
data_size = read_s32le(offset + 0x00, sf);
|
||||
|
||||
start_offset = offset + 0x04;
|
||||
num_samples = data_size / channel_count / (bitrate / 8) * 512;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_FDA;
|
||||
vgmstream->sample_rate = 44100; /* fixed output */
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
vgmstream->codec_data = init_relic(channel_count, bitrate, sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_RELIC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -877,4 +877,6 @@ VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_csb(STREAMFILE *sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_fda(STREAMFILE *sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -203,7 +203,11 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
||||
wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */
|
||||
|
||||
internal_size = read_32bit(wavres_offset + 0x00, sf);
|
||||
if (internal_size > sizeof(hx->internal_name)+1) goto fail;
|
||||
/* Xbox has some kind of big size and "flags" has a value of 2, instead of 3/4 like other platforms */
|
||||
if (strcmp(class_name, "CXBoxWavResData") == 0 && internal_size > 0x100)
|
||||
return 1;
|
||||
if (internal_size > sizeof(hx->internal_name)+1)
|
||||
goto fail;
|
||||
|
||||
/* usually 0 in consoles */
|
||||
if (internal_size != 0) {
|
||||
@ -249,7 +253,8 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
||||
|
||||
if (strcmp(hx->class_name, "CPCWaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CPS2WaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) {
|
||||
strcmp(hx->class_name, "CGCWaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CXBoxWaveFileIdObj") == 0) {
|
||||
uint32_t flag_type = read_32bit(offset + 0x00, sf);
|
||||
|
||||
if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */
|
||||
@ -319,7 +324,10 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
||||
case 0x02: hx->codec = UBI; break;
|
||||
case 0x03: hx->codec = PSX; break;
|
||||
case 0x04: hx->codec = DSP; break;
|
||||
default: goto fail;
|
||||
case 0x05: hx->codec = XIMA; break;
|
||||
default:
|
||||
VGM_LOG("UBI HX: unknown codec %i\n", hx->codec_id);
|
||||
goto fail;
|
||||
}
|
||||
hx->channels = read_16bit(riff_offset + 0x16, sf);
|
||||
hx->sample_rate = read_32bit(riff_offset + 0x18, sf);
|
||||
@ -529,6 +537,7 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
|
||||
else if (strcmp(class_name, "CPCWaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CPS2WaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CGCWaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CXBoxWaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CXBoxStreamHWWaveFileIdObj") == 0 ||
|
||||
strcmp(class_name, "CPS3StaticAC3WaveFileIdObj") == 0 ||
|
||||
|
@ -486,6 +486,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_xssb,
|
||||
init_vgmstream_xma_ue3,
|
||||
init_vgmstream_csb,
|
||||
init_vgmstream_fda,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
@ -656,6 +657,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
reset_relic(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_CRI_HCA) {
|
||||
reset_hca(vgmstream->codec_data);
|
||||
}
|
||||
@ -823,6 +828,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
free_relic(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_CRI_HCA) {
|
||||
free_hca(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
@ -1273,6 +1283,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return 0; /* varies per mode */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_RELIC:
|
||||
return 0; /* 512 */
|
||||
case coding_CRI_HCA:
|
||||
return 0; /* 1024 - delay/padding (which can be bigger than 1024) */
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
@ -1757,6 +1769,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
samples_to_do,vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
case coding_RELIC:
|
||||
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do);
|
||||
break;
|
||||
case coding_CRI_HCA:
|
||||
decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do);
|
||||
@ -2191,6 +2207,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
|
||||
/* prepare certain codecs' internal state for looping */
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
seek_relic(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_CRI_HCA) {
|
||||
loop_hca(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
@ -183,6 +183,7 @@ typedef enum {
|
||||
|
||||
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
|
||||
|
||||
coding_RELIC, /* Relic Codec (DCT-based) */
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
@ -718,6 +719,7 @@ typedef enum {
|
||||
meta_ISB,
|
||||
meta_XSSB,
|
||||
meta_XMA_UE3,
|
||||
meta_FDA,
|
||||
|
||||
} meta_t;
|
||||
|
||||
@ -1147,6 +1149,7 @@ typedef struct {
|
||||
NWAData *nwa;
|
||||
} nwa_codec_data;
|
||||
|
||||
typedef struct relic_codec_data relic_codec_data;
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE *streamfile;
|
||||
|
Loading…
Reference in New Issue
Block a user