Updated VGMStream to r1050-2552-g2b1de051
parent
a1ec4dba40
commit
0f93b0c7bc
|
@ -1,155 +1,84 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
void decode_adx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int32_t frame_samples = (frame_bytes - 2) * 2;
|
||||
|
||||
int framesin = first_sample/frame_samples;
|
||||
|
||||
int32_t scale = read_16bitBE(stream->offset+framesin*frame_bytes,stream->streamfile) + 1;
|
||||
void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_size, coding_t coding_type) {
|
||||
uint8_t frame[0x12] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int scale, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int coef1 = stream->adpcm_coef[0];
|
||||
int coef2 = stream->adpcm_coef[1];
|
||||
|
||||
first_sample = first_sample%frame_samples;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(stream->offset+framesin*frame_bytes +2+i/2,stream->streamfile);
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 32 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
outbuf[sample_count] = clamp16(
|
||||
(i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale +
|
||||
(coef1 * hist1 >> 12) + (coef2 * hist2 >> 12)
|
||||
);
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
scale = get_16bitBE(frame+0x00);
|
||||
switch(coding_type) {
|
||||
case coding_CRI_ADX:
|
||||
scale = scale + 1;
|
||||
coef1 = stream->adpcm_coef[0];
|
||||
coef2 = stream->adpcm_coef[1];
|
||||
break;
|
||||
case coding_CRI_ADX_exp:
|
||||
scale = 1 << (12 - scale);
|
||||
coef1 = stream->adpcm_coef[0];
|
||||
coef2 = stream->adpcm_coef[1];
|
||||
break;
|
||||
case coding_CRI_ADX_fixed:
|
||||
scale = (scale & 0x1fff) + 1;
|
||||
coef1 = stream->adpcm_coef[(frame[0] >> 5)*2 + 0];
|
||||
coef2 = stream->adpcm_coef[(frame[0] >> 5)*2 + 1];
|
||||
break;
|
||||
case coding_CRI_ADX_enc_8:
|
||||
case coding_CRI_ADX_enc_9:
|
||||
scale = ((scale ^ stream->adx_xor) & 0x1fff) + 1;
|
||||
coef1 = stream->adpcm_coef[0];
|
||||
coef2 = stream->adpcm_coef[1];
|
||||
break;
|
||||
default:
|
||||
scale = scale + 1;
|
||||
coef1 = stream->adpcm_coef[0];
|
||||
coef2 = stream->adpcm_coef[1];
|
||||
break;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
void decode_adx_exp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int32_t frame_samples = (frame_bytes - 2) * 2;
|
||||
sample = i&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles):
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = sample * scale + (coef1 * hist1 >> 12) + (coef2 * hist2 >> 12);
|
||||
sample = clamp16(sample);
|
||||
|
||||
int framesin = first_sample/frame_samples;
|
||||
|
||||
int32_t scale = read_16bitBE(stream->offset+framesin*frame_bytes,stream->streamfile);
|
||||
int32_t hist1, hist2;
|
||||
int coef1, coef2;
|
||||
scale = 1 << (12 - scale);
|
||||
hist1 = stream->adpcm_history1_32;
|
||||
hist2 = stream->adpcm_history2_32;
|
||||
coef1 = stream->adpcm_coef[0];
|
||||
coef2 = stream->adpcm_coef[1];
|
||||
|
||||
first_sample = first_sample%frame_samples;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(stream->offset+framesin*frame_bytes +2+i/2,stream->streamfile);
|
||||
|
||||
outbuf[sample_count] = clamp16(
|
||||
(i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale +
|
||||
(coef1 * hist1 >> 12) + (coef2 * hist2 >> 12)
|
||||
);
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
void decode_adx_fixed(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int32_t frame_samples = (frame_bytes - 2) * 2;
|
||||
|
||||
int framesin = first_sample/frame_samples;
|
||||
|
||||
int32_t scale = (read_16bitBE(stream->offset + framesin*frame_bytes, stream->streamfile) & 0x1FFF) + 1;
|
||||
int32_t predictor = read_8bit(stream->offset + framesin*frame_bytes, stream->streamfile) >> 5;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int coef1 = stream->adpcm_coef[predictor * 2];
|
||||
int coef2 = stream->adpcm_coef[predictor * 2 + 1];
|
||||
|
||||
first_sample = first_sample%frame_samples;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(stream->offset+framesin*frame_bytes +2+i/2,stream->streamfile);
|
||||
|
||||
outbuf[sample_count] = clamp16(
|
||||
(i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale +
|
||||
(coef1 * hist1 >> 12) + (coef2 * hist2 >> 12)
|
||||
);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
void adx_next_key(VGMSTREAMCHANNEL * stream)
|
||||
{
|
||||
stream->adx_xor = ( stream->adx_xor * stream->adx_mult + stream->adx_add ) & 0x7fff;
|
||||
}
|
||||
|
||||
void decode_adx_enc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int32_t frame_samples = (frame_bytes - 2) * 2;
|
||||
|
||||
int framesin = first_sample/frame_samples;
|
||||
|
||||
int32_t scale = ((read_16bitBE(stream->offset+framesin*frame_bytes,stream->streamfile) ^ stream->adx_xor)&0x1fff) + 1;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int coef1 = stream->adpcm_coef[0];
|
||||
int coef2 = stream->adpcm_coef[1];
|
||||
|
||||
first_sample = first_sample%frame_samples;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(stream->offset+framesin*frame_bytes +2+i/2,stream->streamfile);
|
||||
|
||||
outbuf[sample_count] = clamp16(
|
||||
(i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale +
|
||||
(coef1 * hist1 >> 12) + (coef2 * hist2 >> 12)
|
||||
);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
|
||||
if (!(i % 32)) {
|
||||
for (i=0;i<stream->adx_channels;i++)
|
||||
{
|
||||
if ((coding_type == coding_CRI_ADX_enc_8 || coding_type == coding_CRI_ADX_enc_9) && !(i % 32)) {
|
||||
for (i =0; i < stream->adx_channels; i++) {
|
||||
adx_next_key(stream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void adx_next_key(VGMSTREAMCHANNEL * stream) {
|
||||
stream->adx_xor = (stream->adx_xor * stream->adx_mult + stream->adx_add) & 0x7fff;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
|
|||
data->data_buffer_size = data->info.superframeSize;
|
||||
/* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */
|
||||
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size + 0x10);
|
||||
/* while ATRAC9 uses float internally, Sony's API only return PCM16 */
|
||||
data->sample_buffer = calloc(sizeof(sample_t), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe);
|
||||
|
||||
data->samples_to_discard = cfg->encoder_delay;
|
||||
|
|
|
@ -4,10 +4,7 @@
|
|||
#include "../vgmstream.h"
|
||||
|
||||
/* adx_decoder */
|
||||
void decode_adx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes);
|
||||
void decode_adx_exp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes);
|
||||
void decode_adx_fixed(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes);
|
||||
void decode_adx_enc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes);
|
||||
void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes, coding_t coding_type);
|
||||
void adx_next_key(VGMSTREAMCHANNEL * stream);
|
||||
|
||||
/* g721_decoder */
|
||||
|
@ -92,10 +89,10 @@ size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels);
|
|||
int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max);
|
||||
|
||||
/* psv_decoder */
|
||||
void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* xa_decoder */
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked);
|
||||
|
||||
/* ea_xa_decoder */
|
||||
|
@ -308,6 +305,8 @@ void free_ffmpeg(ffmpeg_codec_data *data);
|
|||
void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data);
|
||||
void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap);
|
||||
const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data);
|
||||
void ffmpeg_set_force_seek(ffmpeg_codec_data * data);
|
||||
|
||||
|
||||
/* ffmpeg_decoder_utils.c (helper-things) */
|
||||
|
|
|
@ -1159,26 +1159,26 @@ int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) {
|
|||
/* CUSTOM STREAMFILES */
|
||||
/* ******************************************** */
|
||||
|
||||
STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* extension) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
STREAMFILE* setup_subfile_streamfile(STREAMFILE *sf, off_t subfile_offset, size_t subfile_size, const char* extension) {
|
||||
STREAMFILE *temp_sf = NULL, *new_sf = NULL;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
new_sf = open_clamp_streamfile(temp_sf, subfile_offset, subfile_size);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
if (extension) {
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
new_sf = open_fakename_streamfile(temp_sf, NULL, extension);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
}
|
||||
|
||||
return temp_streamFile;
|
||||
return temp_sf;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
#if 0
|
||||
/* known game code/platforms use float buffer and coefs, but some approximations around use this int math:
|
||||
* ...
|
||||
* coef1 = table[index + 0]
|
||||
* coef2 = table[index + 4]
|
||||
* sample = clamp16(((signed_nibble << (20 - shift)) + hist1 * coef1 + hist2 * coef2 + 128) >> 8); */
|
||||
static const int EA_XA_TABLE[20] = {
|
||||
0, 240, 460, 392,
|
||||
0, 0, -208, -220,
|
||||
|
@ -8,33 +14,58 @@ static const int EA_XA_TABLE[20] = {
|
|||
7, 8, 10, 11,
|
||||
0, -1, -3, -4
|
||||
};
|
||||
#endif
|
||||
|
||||
/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
|
||||
/* standard CD-XA's K0/K1 filter pairs */
|
||||
static const float xa_coefs[16][2] = {
|
||||
{ 0.0, 0.0 },
|
||||
{ 0.9375, 0.0 },
|
||||
{ 1.796875, -0.8125 },
|
||||
{ 1.53125, -0.859375 },
|
||||
/* only 4 pairs exist, assume 0s for bad indexes */
|
||||
};
|
||||
|
||||
/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. Reverse engineered from various .exes/.so
|
||||
*
|
||||
* Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
|
||||
* Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes.
|
||||
* Original code reads all headers first then processes all nibbles (for CPU cache/parallelism/SIMD optimizations).
|
||||
* To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */
|
||||
void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int group, row, i;
|
||||
int samples_done = 0, sample_count = 0;
|
||||
void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x4c] = {0};
|
||||
off_t frame_offset;
|
||||
int group, row, i, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
|
||||
|
||||
/* internal interleave */
|
||||
int block_samples = 128;
|
||||
first_sample = first_sample % block_samples;
|
||||
bytes_per_frame = 0x4c;
|
||||
samples_per_frame = 128;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + bytes_per_frame * channel;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
//todo: original code uses float sample buffer:
|
||||
//- header pcm-hist to float-hist: hist * (1/32768)
|
||||
//- nibble to signed to float: (int32_t)(pnibble << 28) * SHIFT_MUL_LUT[shift_index]
|
||||
// look-up table just simplifies ((nibble << 12 << 12) >> 12 + shift) * (1/32768)
|
||||
// though maybe introduces rounding errors?
|
||||
//- coefs apply normally, though hists are already floats
|
||||
//- final float sample isn't clamped
|
||||
|
||||
|
||||
/* process groups */
|
||||
/* parse group headers */
|
||||
for (group = 0; group < 4; group++) {
|
||||
int coef1, coef2;
|
||||
float coef1, coef2;
|
||||
int16_t hist1, hist2;
|
||||
uint8_t shift;
|
||||
uint32_t group_header = (uint32_t)read_32bitLE(stream->offset + channel*0x4c + group*0x4, stream->streamfile); /* always LE */
|
||||
uint32_t group_header = (uint32_t)get_32bitLE(frame + group*0x4); /* always LE */
|
||||
|
||||
coef1 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 0];
|
||||
coef2 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 4];
|
||||
hist2 = (int16_t)(group_header & 0xFFF0);
|
||||
coef1 = xa_coefs[group_header & 0x0F][0];
|
||||
coef2 = xa_coefs[group_header & 0x0F][1];
|
||||
hist2 = (int16_t)((group_header >> 0) & 0xFFF0);
|
||||
hist1 = (int16_t)((group_header >> 16) & 0xFFF0);
|
||||
shift = 20 - ((group_header >> 16) & 0x0F);
|
||||
shift = (group_header >> 16) & 0x0F;
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
|
@ -51,12 +82,14 @@ void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
|
|||
/* process nibbles per group */
|
||||
for (row = 0; row < 15; row++) {
|
||||
for (i = 0; i < 1*2; i++) {
|
||||
uint8_t sample_byte = (uint8_t)read_8bit(stream->offset + channel*0x4c + 4*4 + row*0x04 + group + i/2, stream->streamfile);
|
||||
uint8_t nibbles = frame[4*4 + row*0x04 + group + i/2];
|
||||
int sample;
|
||||
|
||||
sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */
|
||||
sample = sample << shift;
|
||||
sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8;
|
||||
sample = i&1 ? /* high nibble first */
|
||||
(nibbles >> 0) & 0x0f :
|
||||
(nibbles >> 4) & 0x0f;
|
||||
sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */
|
||||
sample = sample + hist1 * coef1 + hist2 * coef2;
|
||||
sample = clamp16(sample);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
|
@ -73,37 +106,43 @@ void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
|
|||
|
||||
|
||||
/* internal interleave (interleaved channels, but manually advances to co-exist with ea blocks) */
|
||||
if (first_sample + samples_done == block_samples) {
|
||||
stream->offset += 0x4c * channelspacing;
|
||||
if (first_sample + samples_done == samples_per_frame) {
|
||||
stream->offset += bytes_per_frame * channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */
|
||||
void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x13] = {0};
|
||||
off_t frame_offset;
|
||||
int i;
|
||||
int block_samples, frames_in, samples_done = 0, sample_count = 0;
|
||||
int i, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
block_samples = 32;
|
||||
frames_in = first_sample / block_samples;
|
||||
first_sample = first_sample % block_samples;
|
||||
bytes_per_frame = 0x02 + 0x02 + 0x0f;
|
||||
samples_per_frame = 1 + 1 + 0x0f*2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + (0x0f+0x02+0x02)*frames_in;
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
/* process frames */
|
||||
//todo see above
|
||||
|
||||
/* process frame */
|
||||
{
|
||||
int coef1, coef2;
|
||||
float coef1, coef2;
|
||||
int16_t hist1, hist2;
|
||||
uint8_t shift;
|
||||
uint32_t frame_header = (uint32_t)read_32bitLE(frame_offset, stream->streamfile); /* always LE */
|
||||
uint32_t frame_header = (uint32_t)get_32bitLE(frame); /* always LE */
|
||||
|
||||
coef1 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 0];
|
||||
coef2 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 4];
|
||||
hist2 = (int16_t)(frame_header & 0xFFF0);
|
||||
coef1 = xa_coefs[frame_header & 0x0F][0];
|
||||
coef2 = xa_coefs[frame_header & 0x0F][1];
|
||||
hist2 = (int16_t)((frame_header >> 0) & 0xFFF0);
|
||||
hist1 = (int16_t)((frame_header >> 16) & 0xFFF0);
|
||||
shift = 20 - ((frame_header >> 16) & 0x0F);
|
||||
shift = (frame_header >> 16) & 0x0F;
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
|
@ -119,12 +158,14 @@ void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
|
|||
|
||||
/* process nibbles */
|
||||
for (i = 0; i < 0x0f*2; i++) {
|
||||
uint8_t sample_byte = (uint8_t)read_8bit(frame_offset + 0x02 + 0x02 + i/2, stream->streamfile);
|
||||
uint8_t nibbles = frame[0x02 + 0x02 + i/2];
|
||||
int sample;
|
||||
|
||||
sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */
|
||||
sample = sample << shift;
|
||||
sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8;
|
||||
sample = i&1 ? /* high nibble first */
|
||||
(nibbles >> 0) & 0x0f :
|
||||
(nibbles >> 4) & 0x0f;
|
||||
sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */
|
||||
sample = sample + hist1 * coef1 + hist2 * coef2;
|
||||
sample = clamp16(sample);
|
||||
|
||||
if (sample_count >= first_sample && samples_done < samples_to_do) {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
/* internal sizes, can be any value */
|
||||
#define FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE 2048
|
||||
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
|
||||
|
||||
|
||||
|
@ -28,12 +26,14 @@ static void g_init_ffmpeg() {
|
|||
g_ffmpeg_initialized = 1;
|
||||
av_log_set_flags(AV_LOG_SKIP_REPEATED);
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
//av_register_all(); /* not needed in newer versions */
|
||||
//#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100)
|
||||
// av_register_all(); /* not needed in newer versions */
|
||||
//#endif
|
||||
g_ffmpeg_initialized = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void remap_audio(sample_t *outbuf, int sample_count, int channels, int channel_mappings[]) {
|
||||
static void remap_audio(sample_t *outbuf, int sample_count, int channels, int *channel_mappings) {
|
||||
int ch_from,ch_to,s;
|
||||
sample_t temp;
|
||||
for (s = 0; s < sample_count; s++) {
|
||||
|
@ -52,68 +52,6 @@ static void remap_audio(sample_t *outbuf, int sample_count, int channels, int ch
|
|||
}
|
||||
}
|
||||
|
||||
static void invert_audio(sample_t *outbuf, int sample_count, int channels) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sample_count*channels; i++) {
|
||||
outbuf[i] = -outbuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */
|
||||
static void convert_audio_pcm16(sample_t *outbuf, const uint8_t *inbuf, int fullSampleCount, int bitsPerSample, int floatingPoint) {
|
||||
int s;
|
||||
switch (bitsPerSample) {
|
||||
case 8: {
|
||||
for (s = 0; s < fullSampleCount; s++) {
|
||||
*outbuf++ = ((int)(*(inbuf++))-0x80) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
int16_t *s16 = (int16_t *)inbuf;
|
||||
for (s = 0; s < fullSampleCount; s++) {
|
||||
*outbuf++ = *(s16++);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
if (!floatingPoint) {
|
||||
int32_t *s32 = (int32_t *)inbuf;
|
||||
for (s = 0; s < fullSampleCount; s++) {
|
||||
*outbuf++ = (*(s32++)) >> 16;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float *s32 = (float *)inbuf;
|
||||
for (s = 0; s < fullSampleCount; s++) {
|
||||
float sample = *s32++;
|
||||
int s16 = (int)(sample * 32768.0f);
|
||||
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
|
||||
s16 = (s16 >> 31) ^ 0x7FFF;
|
||||
}
|
||||
*outbuf++ = s16;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 64: {
|
||||
if (floatingPoint) {
|
||||
double *s64 = (double *)inbuf;
|
||||
for (s = 0; s < fullSampleCount; s++) {
|
||||
double sample = *s64++;
|
||||
int s16 = (int)(sample * 32768.0f);
|
||||
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
|
||||
s16 = (s16 >> 31) ^ 0x7FFF;
|
||||
}
|
||||
*outbuf++ = s16;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special patching for FFmpeg's buggy seek code.
|
||||
*
|
||||
|
@ -134,7 +72,7 @@ static int init_seek(ffmpeg_codec_data * data) {
|
|||
int distance = 0; /* always 0 ("duration") */
|
||||
|
||||
AVStream * stream = data->formatCtx->streams[data->streamIndex];
|
||||
AVPacket * pkt = data->lastReadPacket;
|
||||
AVPacket * pkt = data->packet;
|
||||
|
||||
|
||||
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
|
||||
|
@ -239,7 +177,7 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) {
|
|||
if (max_to_copy > read_size)
|
||||
max_to_copy = read_size;
|
||||
|
||||
memcpy(buf, data->header_insert_block + data->logical_offset, max_to_copy);
|
||||
memcpy(buf, data->header_block + data->logical_offset, max_to_copy);
|
||||
buf += max_to_copy;
|
||||
read_size -= max_to_copy;
|
||||
data->logical_offset += max_to_copy;
|
||||
|
@ -323,13 +261,9 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t *
|
|||
* Stream index can be passed if the file has multiple audio streams that FFmpeg can demux (1=first).
|
||||
*/
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) {
|
||||
char filename[PATH_LIMIT];
|
||||
ffmpeg_codec_data * data = NULL;
|
||||
int errcode;
|
||||
|
||||
AVStream *stream;
|
||||
AVRational tb;
|
||||
|
||||
|
||||
/* check values */
|
||||
if ((header && !header_size) || (!header && header_size))
|
||||
|
@ -341,7 +275,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui
|
|||
}
|
||||
|
||||
|
||||
/* ffmpeg global setup */
|
||||
/* initial FFmpeg setup */
|
||||
g_init_ffmpeg();
|
||||
|
||||
|
||||
|
@ -349,15 +283,14 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui
|
|||
data = calloc(1, sizeof(ffmpeg_codec_data));
|
||||
if (!data) return NULL;
|
||||
|
||||
streamFile->get_name( streamFile, filename, sizeof(filename) );
|
||||
data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
data->streamfile = reopen_streamfile(streamFile, 0);
|
||||
if (!data->streamfile) goto fail;
|
||||
|
||||
/* fake header to trick FFmpeg into demuxing/decoding the stream */
|
||||
if (header_size > 0) {
|
||||
data->header_size = header_size;
|
||||
data->header_insert_block = av_memdup(header, header_size);
|
||||
if (!data->header_insert_block) goto fail;
|
||||
data->header_block = av_memdup(header, header_size);
|
||||
if (!data->header_block) goto fail;
|
||||
}
|
||||
|
||||
data->start = start;
|
||||
|
@ -371,103 +304,59 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui
|
|||
errcode = init_ffmpeg_config(data, target_subsong, 0);
|
||||
if (errcode < 0) goto fail;
|
||||
|
||||
stream = data->formatCtx->streams[data->streamIndex];
|
||||
/* reset non-zero values */
|
||||
data->read_packet = 1;
|
||||
|
||||
/* setup other values */
|
||||
{
|
||||
AVStream *stream = data->formatCtx->streams[data->streamIndex];
|
||||
AVRational tb = {0};
|
||||
|
||||
/* derive info */
|
||||
data->sampleRate = data->codecCtx->sample_rate;
|
||||
data->channels = data->codecCtx->channels;
|
||||
data->bitrate = (int)(data->codecCtx->bit_rate);
|
||||
data->floatingPoint = 0;
|
||||
switch (data->codecCtx->sample_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
data->bitsPerSample = 8;
|
||||
break;
|
||||
/* derive info */
|
||||
data->sampleRate = data->codecCtx->sample_rate;
|
||||
data->channels = data->codecCtx->channels;
|
||||
data->bitrate = (int)(data->codecCtx->bit_rate);
|
||||
#if 0
|
||||
data->blockAlign = data->codecCtx->block_align;
|
||||
data->frameSize = data->codecCtx->frame_size;
|
||||
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
|
||||
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
|
||||
#endif
|
||||
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
data->bitsPerSample = 16;
|
||||
break;
|
||||
/* try to guess frames/samples (duration isn't always set) */
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->totalSamples < 0)
|
||||
data->totalSamples = 0; /* caller must consider this */
|
||||
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
data->bitsPerSample = 32;
|
||||
break;
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
data->bitsPerSample = 32;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
data->bitsPerSample = 64;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
|
||||
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
|
||||
//VGM_ASSERT(data->codecCtx->internal->skip_samples > 0, ...); /* for codec use, not accessible */
|
||||
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
|
||||
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
|
||||
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
|
||||
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
|
||||
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
|
||||
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
|
||||
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
|
||||
/* also negative timestamp for formats like OGG/OPUS */
|
||||
/* not using it: BINK, FLAC, ATRAC3, XMA, MPC, WMA (may use internal skip samples) */
|
||||
//todo: double check Opus behavior
|
||||
}
|
||||
|
||||
/* setup decode buffer */
|
||||
data->sampleBufferBlock = FFMPEG_DEFAULT_SAMPLE_BUFFER_SIZE;
|
||||
data->sampleBuffer = av_malloc(data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels);
|
||||
if (!data->sampleBuffer) goto fail;
|
||||
|
||||
|
||||
/* try to guess frames/samples (duration isn't always set) */
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->totalSamples < 0)
|
||||
data->totalSamples = 0; /* caller must consider this */
|
||||
|
||||
data->blockAlign = data->codecCtx->block_align;
|
||||
data->frameSize = data->codecCtx->frame_size;
|
||||
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
|
||||
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
|
||||
|
||||
|
||||
/* reset */
|
||||
data->readNextPacket = 1;
|
||||
data->bytesConsumedFromDecodedFrame = INT_MAX;
|
||||
data->endOfStream = 0;
|
||||
data->endOfAudio = 0;
|
||||
|
||||
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
|
||||
|
||||
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
|
||||
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
|
||||
//VGM_ASSERT(data->codecCtx->internal->skip_samples > 0, ...); /* for codec use, not accessible */
|
||||
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
|
||||
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
|
||||
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
|
||||
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
|
||||
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
|
||||
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
|
||||
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
|
||||
/* also negative timestamp for formats like OGG/OPUS */
|
||||
/* not using it: BINK, FLAC, ATRAC3, XMA, MPC, WMA (may use internal skip samples) */
|
||||
//todo: double check Opus behavior
|
||||
|
||||
|
||||
/* setup decent seeking for faulty formats */
|
||||
errcode = init_seek(data);
|
||||
if (errcode < 0) {
|
||||
VGM_LOG("FFMPEG: can't init_seek, error=%i\n", errcode);
|
||||
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players)
|
||||
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
|
||||
data->force_seek = 1;
|
||||
reset_ffmpeg_internal(data); /* reset state from trying to seek */
|
||||
//stream = data->formatCtx->streams[data->streamIndex];
|
||||
VGM_LOG("FFMPEG: can't init_seek, error=%i (using force_seek)\n", errcode);
|
||||
ffmpeg_set_force_seek(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -547,15 +436,16 @@ static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int
|
|||
if (errcode < 0) goto fail;
|
||||
|
||||
/* prepare codec and frame/packet buffers */
|
||||
data->lastDecodedFrame = av_frame_alloc();
|
||||
if (!data->lastDecodedFrame) goto fail;
|
||||
av_frame_unref(data->lastDecodedFrame);
|
||||
|
||||
data->lastReadPacket = av_malloc(sizeof(AVPacket)); /* av_packet_alloc? */
|
||||
if (!data->lastReadPacket) goto fail;
|
||||
av_new_packet(data->lastReadPacket, 0);
|
||||
data->packet = av_malloc(sizeof(AVPacket)); /* av_packet_alloc? */
|
||||
if (!data->packet) goto fail;
|
||||
av_new_packet(data->packet, 0);
|
||||
//av_packet_unref?
|
||||
|
||||
data->frame = av_frame_alloc();
|
||||
if (!data->frame) goto fail;
|
||||
av_frame_unref(data->frame);
|
||||
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (errcode < 0)
|
||||
|
@ -563,191 +453,280 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* decodes a new frame to internal data */
|
||||
static int decode_ffmpeg_frame(ffmpeg_codec_data *data) {
|
||||
int errcode;
|
||||
int frame_error = 0;
|
||||
|
||||
|
||||
if (data->bad_init) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* ignore once file is done (but not on EOF as FFmpeg can output samples until end_of_audio) */
|
||||
if (/*data->end_of_stream ||*/ data->end_of_audio) {
|
||||
VGM_LOG("FFMPEG: decode after end of audio\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* read data packets until valid is found */
|
||||
while (data->read_packet && !data->end_of_audio) {
|
||||
if (!data->end_of_stream) {
|
||||
/* reset old packet */
|
||||
av_packet_unref(data->packet);
|
||||
|
||||
/* read encoded data from demuxer into packet */
|
||||
errcode = av_read_frame(data->formatCtx, data->packet);
|
||||
if (errcode < 0) {
|
||||
if (errcode == AVERROR_EOF) {
|
||||
data->end_of_stream = 1; /* no more data to read (but may "drain" samples) */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode);
|
||||
frame_error = 1; //goto fail;
|
||||
}
|
||||
|
||||
if (data->formatCtx->pb && data->formatCtx->pb->error) {
|
||||
VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error);
|
||||
frame_error = 1; //goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* ignore non-selected streams */
|
||||
if (data->packet->stream_index != data->streamIndex)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */
|
||||
errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
|
||||
if (errcode < 0) {
|
||||
if (errcode != AVERROR(EAGAIN)) {
|
||||
VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode);
|
||||
frame_error = 1; //goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
data->read_packet = 0; /* got data */
|
||||
}
|
||||
|
||||
/* decode frame samples from sent packet or "drain" samples*/
|
||||
if (!frame_error) {
|
||||
/* receive uncompressed sample data from decoded frame */
|
||||
errcode = avcodec_receive_frame(data->codecCtx, data->frame);
|
||||
if (errcode < 0) {
|
||||
if (errcode == AVERROR_EOF) {
|
||||
data->end_of_audio = 1; /* no more audio, file is fully decoded */
|
||||
}
|
||||
else if (errcode == AVERROR(EAGAIN)) {
|
||||
data->read_packet = 1; /* 0 samples, request more encoded data */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode);
|
||||
frame_error = 1;//goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output
|
||||
* (ex. BlazBlue X360 022_btl_az.xwb) */
|
||||
|
||||
|
||||
data->samples_consumed = 0;
|
||||
data->samples_filled = data->frame->nb_samples;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* sample copy helpers, using different functions to minimize branches.
|
||||
*
|
||||
* in theory, small optimizations like *outbuf++ vs outbuf[i] or alt clamping
|
||||
* would matter for performance, but in practice aren't very noticeable;
|
||||
* keep it simple for now until more tests are done.
|
||||
*
|
||||
* in normal (interleaved) formats samples are laid out straight
|
||||
* (ibuf[s*chs+ch], ex. 4ch with 8s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3)
|
||||
* in "p" (planar) formats samples are in planes per channel
|
||||
* (ibuf[ch][s], ex. 4ch with 8s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3)
|
||||
*
|
||||
* alt float clamping:
|
||||
* clamp_float(f32)
|
||||
* int s16 = (int)(f32 * 32768.0f);
|
||||
* if ((unsigned)(s16 + 0x8000) & 0xFFFF0000)
|
||||
* s16 = (s16 >> 31) ^ 0x7FFF;
|
||||
*
|
||||
* when casting float to int, value is simply truncated:
|
||||
* - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1
|
||||
* alts for more accurate rounding could be:
|
||||
* - (int)floor(f32 * 32768.0) //not quite ok negatives
|
||||
* - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style
|
||||
* - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f)
|
||||
* - (((int) (f1 + 32768.5)) - 32768)
|
||||
* - etc
|
||||
* but since +-1 isn't really audible we'll just cast as it's the fastest
|
||||
*/
|
||||
|
||||
static void samples_silence_s16(sample_t* obuf, int ochs, int samples) {
|
||||
int s, total_samples = samples * ochs;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = 0; /* memset'd */
|
||||
}
|
||||
}
|
||||
|
||||
static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) {
|
||||
int s, total_samples = samples * ichs;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = ((int)ibuf[skip*ichs + s] - 0x80) << 8;
|
||||
}
|
||||
}
|
||||
static void samples_u8p_to_s16(sample_t* obuf, uint8_t** ibuf, int ichs, int samples, int skip) {
|
||||
int s, ch;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
obuf[s*ichs + ch] = ((int)ibuf[ch][skip + s] - 0x80) << 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void samples_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) {
|
||||
int s, total_samples = samples * ichs;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */
|
||||
}
|
||||
}
|
||||
static void samples_s16p_to_s16(sample_t* obuf, int16_t** ibuf, int ichs, int samples, int skip) {
|
||||
int s, ch;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
obuf[s*ichs + ch] = ibuf[ch][skip + s];
|
||||
}
|
||||
}
|
||||
}
|
||||
static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) {
|
||||
int s, total_samples = samples * ichs;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = ibuf[skip*ichs + s] >> 16;
|
||||
}
|
||||
}
|
||||
static void samples_s32p_to_s16(sample_t* obuf, int32_t** ibuf, int ichs, int samples, int skip) {
|
||||
int s, ch;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
obuf[s*ichs + ch] = ibuf[ch][skip + s] >> 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) {
|
||||
int s, total_samples = samples * ichs;
|
||||
float scale = invert ? -32768.0f : 32768.0f;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = clamp16(ibuf[skip*ichs + s] * scale);
|
||||
}
|
||||
}
|
||||
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) {
|
||||
int s, ch;
|
||||
float scale = invert ? -32768.0f : 32768.0f;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
obuf[s*ichs + ch] = clamp16(ibuf[ch][skip + s] * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void samples_dbl_to_s16(sample_t* obuf, double* ibuf, int ichs, int samples, int skip) {
|
||||
int s, total_samples = samples * ichs;
|
||||
for (s = 0; s < total_samples; s++) {
|
||||
obuf[s] = clamp16(ibuf[skip*ichs + s] * 32768.0);
|
||||
}
|
||||
}
|
||||
static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int samples, int skip) {
|
||||
int s, ch;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
obuf[s*ichs + ch] = clamp16(inbuf[ch][skip + s] * 32768.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_samples(ffmpeg_codec_data *data, sample_t *outbuf, int samples_to_do) {
|
||||
int channels = data->codecCtx->channels;
|
||||
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
|
||||
void* ibuf;
|
||||
|
||||
if (is_planar) {
|
||||
ibuf = data->frame->extended_data;
|
||||
}
|
||||
else {
|
||||
ibuf = data->frame->data[0];
|
||||
}
|
||||
|
||||
switch (data->codecCtx->sample_fmt) {
|
||||
/* unused? */
|
||||
case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
case AV_SAMPLE_FMT_U8P: samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
/* common */
|
||||
case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
case AV_SAMPLE_FMT_S16P: samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
/* possibly FLAC and other lossless codecs */
|
||||
case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
case AV_SAMPLE_FMT_S32P: samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
/* mainly MDCT-like codecs (Ogg, AAC, etc) */
|
||||
case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break;
|
||||
case AV_SAMPLE_FMT_FLTP: samples_fltp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break;
|
||||
/* possibly PCM64 only (not enabled) */
|
||||
case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
case AV_SAMPLE_FMT_DBLP: samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (data->channel_remap_set)
|
||||
remap_audio(outbuf, samples_to_do, channels, data->channel_remap);
|
||||
}
|
||||
|
||||
/* decode samples of any kind of FFmpeg format */
|
||||
void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) {
|
||||
ffmpeg_codec_data *data = vgmstream->codec_data;
|
||||
int samplesReadNow;
|
||||
//todo use either channels / data->channels / codecCtx->channels
|
||||
|
||||
AVFormatContext *formatCtx = data->formatCtx;
|
||||
AVCodecContext *codecCtx = data->codecCtx;
|
||||
AVPacket *packet = data->lastReadPacket;
|
||||
AVFrame *frame = data->lastDecodedFrame;
|
||||
|
||||
int readNextPacket = data->readNextPacket;
|
||||
int endOfStream = data->endOfStream;
|
||||
int endOfAudio = data->endOfAudio;
|
||||
int bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame;
|
||||
|
||||
int planar = 0;
|
||||
int bytesPerSample = data->bitsPerSample / 8;
|
||||
int bytesRead, bytesToRead;
|
||||
|
||||
|
||||
if (data->bad_init) {
|
||||
memset(outbuf, 0, samples_to_do * channels * sizeof(sample));
|
||||
return;
|
||||
}
|
||||
while (samples_to_do > 0) {
|
||||
|
||||
/* ignore once file is done (but not at endOfStream as FFmpeg can still output samples until endOfAudio) */
|
||||
if (/*endOfStream ||*/ endOfAudio) {
|
||||
VGM_LOG("FFMPEG: decode after end of audio\n");
|
||||
memset(outbuf, 0, samples_to_do * channels * sizeof(sample));
|
||||
return;
|
||||
}
|
||||
if (data->samples_consumed < data->samples_filled) {
|
||||
/* consume samples */
|
||||
int samples_to_get = (data->samples_filled - data->samples_consumed);
|
||||
|
||||
planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
|
||||
bytesRead = 0;
|
||||
bytesToRead = samples_to_do * (bytesPerSample * codecCtx->channels);
|
||||
|
||||
|
||||
/* keep reading and decoding packets until the requested number of samples (in bytes for FFmpeg calcs) */
|
||||
while (bytesRead < bytesToRead) {
|
||||
int dataSize, toConsume, errcode;
|
||||
|
||||
/* get sample data size from current frame (dataSize will be < 0 when nb_samples = 0) */
|
||||
dataSize = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1);
|
||||
if (dataSize < 0)
|
||||
dataSize = 0;
|
||||
|
||||
/* read new data packet when requested */
|
||||
while (readNextPacket && !endOfAudio) {
|
||||
if (!endOfStream) {
|
||||
/* reset old packet */
|
||||
av_packet_unref(packet);
|
||||
|
||||
/* get compressed data from demuxer into packet */
|
||||
errcode = av_read_frame(formatCtx, packet);
|
||||
if (errcode < 0) {
|
||||
if (errcode == AVERROR_EOF) {
|
||||
endOfStream = 1; /* no more data, but may still output samples */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("FFMPEG: av_read_frame errcode %i\n", errcode);
|
||||
}
|
||||
|
||||
if (formatCtx->pb && formatCtx->pb->error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet->stream_index != data->streamIndex)
|
||||
continue; /* ignore non-selected streams */
|
||||
}
|
||||
|
||||
/* send compressed data to decoder in packet (NULL at EOF to "drain") */
|
||||
errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : packet);
|
||||
if (errcode < 0) {
|
||||
if (errcode != AVERROR(EAGAIN)) {
|
||||
VGM_LOG("FFMPEG: avcodec_send_packet errcode %i\n", errcode);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
readNextPacket = 0; /* got compressed data */
|
||||
}
|
||||
|
||||
/* decode packet into frame's sample data (if we don't have bytes to consume from previous frame) */
|
||||
if (dataSize <= bytesConsumedFromDecodedFrame) {
|
||||
if (endOfAudio) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytesConsumedFromDecodedFrame = 0;
|
||||
|
||||
/* receive uncompressed sample data from decoder in frame */
|
||||
errcode = avcodec_receive_frame(codecCtx, frame);
|
||||
if (errcode < 0) {
|
||||
if (errcode == AVERROR_EOF) {
|
||||
endOfAudio = 1; /* no more samples, file is fully decoded */
|
||||
break;
|
||||
}
|
||||
else if (errcode == AVERROR(EAGAIN)) {
|
||||
readNextPacket = 1; /* request more compressed data */
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
VGM_LOG("FFMPEG: avcodec_receive_frame errcode %i\n", errcode);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* get sample data size of current frame */
|
||||
dataSize = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1);
|
||||
if (dataSize < 0)
|
||||
dataSize = 0;
|
||||
}
|
||||
|
||||
toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
|
||||
|
||||
|
||||
/* discard decoded frame if needed (fully or partially) */
|
||||
if (data->samplesToDiscard) {
|
||||
int samplesDataSize = dataSize / (bytesPerSample * channels);
|
||||
|
||||
if (data->samplesToDiscard >= samplesDataSize) {
|
||||
/* discard all of the frame's samples and continue to the next */
|
||||
bytesConsumedFromDecodedFrame = dataSize;
|
||||
data->samplesToDiscard -= samplesDataSize;
|
||||
continue;
|
||||
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 {
|
||||
/* discard part of the frame and copy the rest below */
|
||||
int bytesToDiscard = data->samplesToDiscard * (bytesPerSample * channels);
|
||||
int dataSizeLeft = dataSize - bytesToDiscard;
|
||||
/* get max samples and copy */
|
||||
if (samples_to_get > samples_to_do)
|
||||
samples_to_get = samples_to_do;
|
||||
|
||||
bytesConsumedFromDecodedFrame += bytesToDiscard;
|
||||
data->samplesToDiscard = 0;
|
||||
if (toConsume > dataSizeLeft)
|
||||
toConsume = dataSizeLeft;
|
||||
copy_samples(data, outbuf, samples_to_get);
|
||||
|
||||
//samples_done += samples_to_get;
|
||||
samples_to_do -= samples_to_get;
|
||||
outbuf += samples_to_get * channels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* copy decoded sample data to buffer */
|
||||
if (!planar || channels == 1) { /* 1 sample per channel, already mixed */
|
||||
memmove(data->sampleBuffer + bytesRead, (frame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
|
||||
/* mark consumed samples */
|
||||
data->samples_consumed += samples_to_get;
|
||||
}
|
||||
else { /* N samples per channel, mix to 1 sample per channel */
|
||||
uint8_t * out = (uint8_t *) data->sampleBuffer + bytesRead;
|
||||
int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / channels;
|
||||
int toConsumePerPlane = toConsume / channels;
|
||||
int s, ch;
|
||||
for (s = 0; s < toConsumePerPlane; s += bytesPerSample) {
|
||||
for (ch = 0; ch < channels; ++ch) {
|
||||
memcpy(out, frame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
|
||||
out += bytesPerSample;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int ok = decode_ffmpeg_frame(data);
|
||||
if (!ok) goto decode_fail;
|
||||
}
|
||||
|
||||
/* consume */
|
||||
bytesConsumedFromDecodedFrame += toConsume;
|
||||
bytesRead += toConsume;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
end:
|
||||
/* convert native sample format into PCM16 outbuf */
|
||||
samplesReadNow = bytesRead / (bytesPerSample * channels);
|
||||
convert_audio_pcm16(outbuf, data->sampleBuffer, samplesReadNow * channels, data->bitsPerSample, data->floatingPoint);
|
||||
if (data->channel_remap_set)
|
||||
remap_audio(outbuf, samplesReadNow, data->channels, data->channel_remap);
|
||||
if (data->invert_audio_set)
|
||||
invert_audio(outbuf, samplesReadNow, data->channels);
|
||||
|
||||
/* clean buffer when requested more samples than possible */
|
||||
if (endOfAudio && samplesReadNow < samples_to_do) {
|
||||
VGM_LOG("FFMPEG: decode after end of audio %i samples\n", (samples_to_do - samplesReadNow));
|
||||
memset(outbuf + (samplesReadNow * channels), 0, (samples_to_do - samplesReadNow) * channels * sizeof(sample));
|
||||
}
|
||||
|
||||
/* copy state back */
|
||||
data->readNextPacket = readNextPacket;
|
||||
data->endOfStream = endOfStream;
|
||||
data->endOfAudio = endOfAudio;
|
||||
data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame;
|
||||
decode_fail:
|
||||
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do);
|
||||
samples_silence_s16(outbuf, channels, samples_to_do);
|
||||
}
|
||||
|
||||
|
||||
|
@ -766,7 +745,7 @@ void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample) {
|
|||
if (!data) return;
|
||||
|
||||
/* Start from 0 and discard samples until sample (slower but not too noticeable).
|
||||
* Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps). */
|
||||
* Due to many FFmpeg quirks seeking to a sample is erratic at best in most formats. */
|
||||
|
||||
if (data->force_seek) {
|
||||
int errcode;
|
||||
|
@ -787,21 +766,22 @@ void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample) {
|
|||
avcodec_flush_buffers(data->codecCtx);
|
||||
}
|
||||
|
||||
data->samplesToDiscard = num_sample;
|
||||
data->samples_consumed = 0;
|
||||
data->samples_filled = 0;
|
||||
data->samples_discard = num_sample;
|
||||
|
||||
data->readNextPacket = 1;
|
||||
data->bytesConsumedFromDecodedFrame = INT_MAX;
|
||||
data->endOfStream = 0;
|
||||
data->endOfAudio = 0;
|
||||
data->read_packet = 1;
|
||||
data->end_of_stream = 0;
|
||||
data->end_of_audio = 0;
|
||||
|
||||
/* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */
|
||||
if (data->skipSamplesSet) {
|
||||
if (data->skip_samples_set) {
|
||||
AVStream *stream = data->formatCtx->streams[data->streamIndex];
|
||||
/* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */
|
||||
stream->skip_samples = 0;
|
||||
stream->start_skip_samples = 0;
|
||||
|
||||
data->samplesToDiscard += data->skipSamples;
|
||||
data->samples_discard += data->skipSamples;
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -819,15 +799,15 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) {
|
|||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (data->lastReadPacket) {
|
||||
av_packet_unref(data->lastReadPacket);
|
||||
av_free(data->lastReadPacket);
|
||||
data->lastReadPacket = NULL;
|
||||
if (data->packet) {
|
||||
av_packet_unref(data->packet);
|
||||
av_free(data->packet);
|
||||
data->packet = NULL;
|
||||
}
|
||||
if (data->lastDecodedFrame) {
|
||||
av_frame_unref(data->lastDecodedFrame);
|
||||
av_free(data->lastDecodedFrame);
|
||||
data->lastDecodedFrame = NULL;
|
||||
if (data->frame) {
|
||||
av_frame_unref(data->frame);
|
||||
av_free(data->frame);
|
||||
data->frame = NULL;
|
||||
}
|
||||
if (data->codecCtx) {
|
||||
avcodec_close(data->codecCtx);
|
||||
|
@ -841,7 +821,7 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) {
|
|||
}
|
||||
if (data->ioCtx) {
|
||||
/* buffer passed in is occasionally freed and replaced.
|
||||
// the replacement must be free'd as well (below) */
|
||||
* the replacement must be free'd as well (below) */
|
||||
data->buffer = data->ioCtx->buffer;
|
||||
avio_context_free(&data->ioCtx);
|
||||
//av_free(data->ioCtx); /* done in context_free (same thing) */
|
||||
|
@ -852,7 +832,7 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) {
|
|||
data->buffer = NULL;
|
||||
}
|
||||
|
||||
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option (not happening in gcc builds)
|
||||
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option
|
||||
}
|
||||
|
||||
void free_ffmpeg(ffmpeg_codec_data *data) {
|
||||
|
@ -861,13 +841,9 @@ void free_ffmpeg(ffmpeg_codec_data *data) {
|
|||
|
||||
free_ffmpeg_config(data);
|
||||
|
||||
if (data->sampleBuffer) {
|
||||
av_free(data->sampleBuffer);
|
||||
data->sampleBuffer = NULL;
|
||||
}
|
||||
if (data->header_insert_block) {
|
||||
av_free(data->header_insert_block);
|
||||
data->header_insert_block = NULL;
|
||||
if (data->header_block) {
|
||||
av_free(data->header_block);
|
||||
data->header_block = NULL;
|
||||
}
|
||||
|
||||
close_streamfile(data->streamfile);
|
||||
|
@ -895,8 +871,8 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) {
|
|||
stream->skip_samples = 0; /* skip_samples can be used for any packet */
|
||||
|
||||
/* set skip samples with our internal discard */
|
||||
data->skipSamplesSet = 1;
|
||||
data->samplesToDiscard = skip_samples;
|
||||
data->skip_samples_set = 1;
|
||||
data->samples_discard = skip_samples;
|
||||
|
||||
/* expose (info only) */
|
||||
data->skipSamples = skip_samples;
|
||||
|
@ -923,4 +899,24 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap)
|
|||
data->channel_remap_set = 1;
|
||||
}
|
||||
|
||||
const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data) {
|
||||
if (!data || !data->codec)
|
||||
return NULL;
|
||||
if (data->codec->long_name)
|
||||
return data->codec->long_name;
|
||||
if (data->codec->name)
|
||||
return data->codec->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffmpeg_set_force_seek(ffmpeg_codec_data * data) {
|
||||
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
|
||||
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
|
||||
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
|
||||
;VGM_LOG("1\n");
|
||||
data->force_seek = 1;
|
||||
reset_ffmpeg_internal(data); /* reset state from trying to seek */
|
||||
//stream = data->formatCtx->streams[data->streamIndex];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -66,7 +66,7 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t
|
|||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_data->invert_audio_set = 1;
|
||||
ffmpeg_data->invert_floats_set = 1;
|
||||
}
|
||||
|
||||
return ffmpeg_data;
|
||||
|
@ -159,7 +159,7 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o
|
|||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_data->invert_audio_set = 1;
|
||||
ffmpeg_data->invert_floats_set = 1;
|
||||
}
|
||||
|
||||
/* multichannel fix: LFE channel should be reordered on decode (ATRAC3Plus only, only 1/2/6/8ch exist):
|
||||
|
|
|
@ -1124,11 +1124,14 @@ size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
|
|||
}
|
||||
|
||||
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
|
||||
int mod;
|
||||
int block_align = 0x24 * channels;
|
||||
if (channels <= 0) return 0;
|
||||
|
||||
mod = bytes % block_align;
|
||||
/* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
|
||||
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
|
||||
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
|
||||
+ ((mod > 0 && mod > 0x04*channels) ? (mod - 0x04*channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
|
||||
}
|
||||
|
||||
size_t dat4_ima_bytes_to_samples(size_t bytes, int channels) {
|
||||
|
|
|
@ -1,69 +1,103 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i=first_sample;
|
||||
int32_t sample_count;
|
||||
|
||||
int framesin = first_sample/14;
|
||||
|
||||
int8_t header = read_8bit(framesin*8+stream->offset,stream->streamfile);
|
||||
int32_t scale = 1 << (header & 0xf);
|
||||
int coef_index = (header >> 4) & 0xf;
|
||||
uint8_t frame[0x08] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int coef_index, scale, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_16;
|
||||
int32_t hist2 = stream->adpcm_history2_16;
|
||||
int coef1 = stream->adpcm_coef[coef_index*2];
|
||||
int coef2 = stream->adpcm_coef[coef_index*2+1];
|
||||
|
||||
first_sample = first_sample%14;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(framesin*8+stream->offset+1+i/2,stream->streamfile);
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x08;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
outbuf[sample_count] = clamp16((
|
||||
(((i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale)<<11) + 1024 +
|
||||
(coef1 * hist1 + coef2 * hist2))>>11
|
||||
);
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
scale = 1 << ((frame[0] >> 0) & 0xf);
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs at %x\n", (uint32_t)frame_offset);
|
||||
//if (coef_index > 8) //todo not correctly clamped in original decoder?
|
||||
// coef_index = 8;
|
||||
|
||||
coef1 = stream->adpcm_coef[coef_index*2 + 0];
|
||||
coef2 = stream->adpcm_coef[coef_index*2 + 1];
|
||||
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
|
||||
sample = i&1 ? /* high nibble first */
|
||||
get_low_nibble_signed(nibbles) :
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = ((sample * scale) << 11);
|
||||
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
|
||||
sample = clamp16(sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16 = hist1;
|
||||
stream->adpcm_history2_16 = hist2;
|
||||
}
|
||||
|
||||
/* read from memory rather than a file */
|
||||
static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * mem) {
|
||||
int i=first_sample;
|
||||
int32_t sample_count;
|
||||
|
||||
int8_t header = mem[0];
|
||||
int32_t scale = 1 << (header & 0xf);
|
||||
int coef_index = (header >> 4) & 0xf;
|
||||
/* read from memory rather than a file */
|
||||
static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * frame) {
|
||||
int i, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int coef_index, scale, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_16;
|
||||
int32_t hist2 = stream->adpcm_history2_16;
|
||||
int coef1 = stream->adpcm_coef[coef_index*2];
|
||||
int coef2 = stream->adpcm_coef[coef_index*2+1];
|
||||
|
||||
first_sample = first_sample%14;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = mem[1 + i/2];
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x08;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
VGM_ASSERT_ONCE(samples_to_do > samples_per_frame, "DSP: layout error, too many samples\n");
|
||||
|
||||
outbuf[sample_count] = clamp16((
|
||||
(((i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
get_high_nibble_signed(sample_byte)
|
||||
) * scale)<<11) + 1024 +
|
||||
(coef1 * hist1 + coef2 * hist2))>>11
|
||||
);
|
||||
/* parse frame header */
|
||||
scale = 1 << ((frame[0] >> 0) & 0xf);
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs\n");
|
||||
//if (coef_index > 8) //todo not correctly clamped in original decoder?
|
||||
// coef_index = 8;
|
||||
|
||||
coef1 = stream->adpcm_coef[coef_index*2 + 0];
|
||||
coef2 = stream->adpcm_coef[coef_index*2 + 1];
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
|
||||
sample = i&1 ?
|
||||
get_low_nibble_signed(nibbles) :
|
||||
get_high_nibble_signed(nibbles);
|
||||
sample = ((sample * scale) << 11);
|
||||
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
|
||||
sample = clamp16(sample);
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = outbuf[sample_count];
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16 = hist1;
|
||||
|
@ -72,22 +106,21 @@ static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t *
|
|||
|
||||
/* decode DSP with byte-interleaved frames (ex. 0x08: 1122112211221122) */
|
||||
void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave) {
|
||||
uint8_t sample_data[0x08];
|
||||
uint8_t frame[0x08];
|
||||
int i;
|
||||
int frames_in = first_sample / 14;
|
||||
|
||||
int framesin = first_sample/14;
|
||||
|
||||
for (i=0; i < 0x08; i++) {
|
||||
for (i = 0; i < 0x08; i++) {
|
||||
/* base + current frame + subint section + subint byte + channel adjust */
|
||||
sample_data[i] = read_8bit(
|
||||
frame[i] = read_8bit(
|
||||
stream->offset
|
||||
+ framesin*(0x08*channelspacing)
|
||||
+ frames_in*(0x08*channelspacing)
|
||||
+ i/interleave * interleave * channelspacing
|
||||
+ i%interleave
|
||||
+ interleave * channel, stream->streamfile);
|
||||
}
|
||||
|
||||
decode_ngc_dsp_subint_internal(stream, outbuf, channelspacing, first_sample, samples_to_do, sample_data);
|
||||
decode_ngc_dsp_subint_internal(stream, outbuf, channelspacing, first_sample, samples_to_do, frame);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../util.h"
|
||||
|
||||
/* PSVita ADPCM table */
|
||||
static const int16_t HEVAG_coefs[128][4] = {
|
||||
static const int16_t hevag_coefs[128][4] = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 7680, 0, 0, 0 },
|
||||
{ 14720, -6656, 0, 0 },
|
||||
|
@ -141,59 +141,58 @@ static const int16_t HEVAG_coefs[128][4] = {
|
|||
*
|
||||
* Original research and algorithm by id-daemon / daemon1.
|
||||
*/
|
||||
void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
|
||||
uint8_t predict_nr, shift, flag, byte;
|
||||
int32_t scale = 0;
|
||||
|
||||
int32_t sample;
|
||||
void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int coef_index, shift_factor, flag;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int32_t hist3 = stream->adpcm_history3_32;
|
||||
int32_t hist4 = stream->adpcm_history4_32;
|
||||
|
||||
int i, sample_count;
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
int framesin = first_sample / 28;
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
coef_index = ((frame[1] >> 0) & 0xf0) | coef_index;
|
||||
flag = (frame[1] >> 0) & 0xf; /* same flags */
|
||||
|
||||
/* 4 byte header: predictor = 3rd and 1st, shift = 2nd, flag = 4th */
|
||||
byte = (uint8_t)read_8bit(stream->offset+framesin*16+0,stream->streamfile);
|
||||
predict_nr = byte >> 4;
|
||||
shift = byte & 0x0f;
|
||||
byte = (uint8_t)read_8bit(stream->offset+framesin*16+1,stream->streamfile);
|
||||
predict_nr = (byte & 0xF0) | predict_nr;
|
||||
flag = byte & 0x0f; /* no change in flags */
|
||||
VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 127)
|
||||
coef_index = 127; /* ? */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* ? */
|
||||
|
||||
first_sample = first_sample % 28;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0, scale = 0;
|
||||
|
||||
if (first_sample & 1) { /* if first sample is odd, read byte first */
|
||||
byte = read_8bit(stream->offset+(framesin*16)+2+first_sample/2,stream->streamfile);
|
||||
}
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
|
||||
sample = 0;
|
||||
|
||||
if (flag < 7 && predict_nr < 128) {
|
||||
|
||||
if (i & 1) {/* odd/even nibble */
|
||||
scale = byte >> 4;
|
||||
} else {
|
||||
byte = read_8bit(stream->offset+(framesin*16)+2+i/2,stream->streamfile);
|
||||
scale = byte & 0x0f;
|
||||
}
|
||||
if (scale > 7) { /* sign extend */
|
||||
scale = scale - 16;
|
||||
}
|
||||
|
||||
sample = (hist1 * HEVAG_coefs[predict_nr][0] +
|
||||
hist2 * HEVAG_coefs[predict_nr][1] +
|
||||
hist3 * HEVAG_coefs[predict_nr][2] +
|
||||
hist4 * HEVAG_coefs[predict_nr][3] ) / 32;
|
||||
sample = (sample + (scale << (20 - shift)) + 128) >> 8;
|
||||
scale = i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles);
|
||||
sample = (hist1 * hevag_coefs[coef_index][0] +
|
||||
hist2 * hevag_coefs[coef_index][1] +
|
||||
hist3 * hevag_coefs[coef_index][2] +
|
||||
hist4 * hevag_coefs[coef_index][3] ) / 32;
|
||||
sample = (sample + (scale << (20 - shift_factor)) + 128) >> 8;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = clamp16(sample);
|
||||
outbuf[sample_count] = sample;
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist4 = hist3;
|
||||
hist3 = hist2;
|
||||
hist2 = hist1;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
/* PS-ADPCM table, defined as rational numbers (as in the spec) */
|
||||
static const double ps_adpcm_coefs_f[5][2] = {
|
||||
static const float ps_adpcm_coefs_f[5][2] = {
|
||||
{ 0.0 , 0.0 }, //{ 0.0 , 0.0 },
|
||||
{ 0.9375 , 0.0 }, //{ 60.0 / 64.0 , 0.0 },
|
||||
{ 1.796875 , -0.8125 }, //{ 115.0 / 64.0 , -52.0 / 64.0 },
|
||||
|
@ -44,6 +44,7 @@ static const int ps_adpcm_coefs_i[5][2] = {
|
|||
|
||||
/* standard PS-ADPCM (float math version) */
|
||||
void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
|
@ -51,6 +52,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing
|
|||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
|
||||
|
@ -58,10 +60,11 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing
|
|||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
|
||||
flag = (uint8_t)read_8bit(frame_offset+0x01,stream->streamfile); /* only lower nibble needed */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
flag = frame[1]; /* only lower nibble needed */
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 5) /* needed by inFamous (PS3) (maybe it's supposed to use more filters?) */
|
||||
|
@ -73,18 +76,19 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing
|
|||
flag = 0;
|
||||
VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %x\n", (uint32_t)frame_offset); /* meta should use PSX-badflags */
|
||||
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x02+i/2,stream->streamfile);
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
sample = i&1 ? /* low nibble first */
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
sample = (int)(sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2);
|
||||
sample = (int32_t)(sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2);
|
||||
sample = clamp16(sample);
|
||||
}
|
||||
|
||||
|
@ -105,6 +109,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing
|
|||
*
|
||||
* Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */
|
||||
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) {
|
||||
uint8_t frame[0x50] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
|
@ -112,6 +117,7 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
|||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
|
@ -119,9 +125,10 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
|||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 5) /* needed by Afrika (PS3) (maybe it's supposed to use more filters?) */
|
||||
|
@ -129,10 +136,11 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
|||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* supposedly, from Nocash PSX docs */
|
||||
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile);
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
|
||||
sample = i&1 ? /* low nibble first */
|
||||
(nibbles >> 4) & 0x0f :
|
||||
|
@ -154,6 +162,7 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
|||
|
||||
/* PS-ADPCM from Pivotal games, exactly like psx_cfg but with float math (reverse engineered from the exe) */
|
||||
void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) {
|
||||
uint8_t frame[0x50] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
|
@ -162,6 +171,7 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe
|
|||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
float scale;
|
||||
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
|
@ -169,21 +179,24 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe
|
|||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM-piv: incorrect coefs/shift\n");
|
||||
if (coef_index > 5) /* just in case */
|
||||
coef_index = 5;
|
||||
if (shift_factor > 12) /* same */
|
||||
shift_factor = 12;
|
||||
|
||||
scale = (float)(1.0 / (double)(1 << shift_factor));
|
||||
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile);
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
|
||||
sample = !(i&1) ? /* low nibble first */
|
||||
(nibbles >> 0) & 0x0f :
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
// May be implemented like the SNES/SPC700 BRR.
|
||||
|
||||
/* XA ADPCM gain values */
|
||||
static const double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const double K1[4] = { 0.0, 0.0, -0.8125,-0.859375};
|
||||
/* K0/1 floats to int, K*2^10 = K*(1<<10) = K*1024 */
|
||||
static int get_IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); }
|
||||
static int get_IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
|
||||
#if 0
|
||||
static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
#endif
|
||||
/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */
|
||||
static const int IK0[4] = { 0, -960, -1840, -1568 };
|
||||
static const int IK1[4] = { 0, 0, 832, 880 };
|
||||
|
||||
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
|
||||
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
|
||||
|
@ -35,23 +37,22 @@ static int get_IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
|
|||
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
|
||||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found.
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://gitlab.com/higan/higan/blob/master/higan/sfc/dsp/brr.cpp
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
*/
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
off_t frame_offset, sp_offset;
|
||||
int i,j, frames_in, samples_done = 0, sample_count = 0;
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x80] = {0};
|
||||
off_t frame_offset;
|
||||
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*8 / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
|
||||
|
@ -72,12 +73,19 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
|
|||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
|
||||
if (read_32bitBE(frame_offset+0x00,stream->streamfile) != read_32bitBE(frame_offset+0x04,stream->streamfile) ||
|
||||
read_32bitBE(frame_offset+0x08,stream->streamfile) != read_32bitBE(frame_offset+0x0c,stream->streamfile)) {
|
||||
VGM_LOG("bad frames at %x\n", (uint32_t)frame_offset);
|
||||
}
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*8 / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC),
|
||||
"bad frames at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
|
||||
/* decode subframes */
|
||||
|
@ -86,18 +94,18 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
|
|||
uint8_t coef_index, shift_factor;
|
||||
|
||||
/* parse current subframe (sound unit)'s header (sound parameters) */
|
||||
sp_offset = frame_offset + 0x04 + i*channelspacing + channel;
|
||||
coef_index = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(sp_offset,stream->streamfile) >> 0) & 0xf;
|
||||
sp_pos = 0x04 + i*channelspacing + channel;
|
||||
coef_index = (frame[sp_pos] >> 4) & 0xf;
|
||||
shift_factor = (frame[sp_pos] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)sp_offset);
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
if (coef_index > 4)
|
||||
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* supposedly, from Nocash PSX docs */
|
||||
|
||||
coef1 = get_IK0(coef_index);
|
||||
coef2 = get_IK1(coef_index);
|
||||
coef1 = IK0[coef_index];
|
||||
coef2 = IK1[coef_index];
|
||||
|
||||
|
||||
/* decode subframe nibbles */
|
||||
|
@ -105,9 +113,9 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
|
|||
uint8_t nibbles;
|
||||
int32_t new_sample;
|
||||
|
||||
off_t su_offset = (channelspacing==1) ?
|
||||
frame_offset + 0x10 + j*0x04 + (i/2) : /* mono */
|
||||
frame_offset + 0x10 + j*0x04 + i; /* stereo */
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
|
@ -118,11 +126,11 @@ void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, i
|
|||
continue;
|
||||
}
|
||||
|
||||
nibbles = (uint8_t)read_8bit(su_offset,stream->streamfile);
|
||||
nibbles = frame[su_pos];
|
||||
|
||||
new_sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles ) & 0x0f;
|
||||
(nibbles >> 0) & 0x0f;
|
||||
|
||||
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
new_sample = new_sample << 4;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "vgmstream.h"
|
||||
#include "coding/coding.h"
|
||||
|
||||
|
||||
/* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
|
||||
|
@ -282,6 +283,7 @@ static const char* extension_list[] = {
|
|||
"mihb",
|
||||
"mnstr",
|
||||
"mogg",
|
||||
//"mp+", //common [Moonshine Runners (PC)]
|
||||
//"mp2", //common
|
||||
//"mp3", //common
|
||||
//"mp4", //common
|
||||
|
@ -584,6 +586,7 @@ static const char* common_extension_list[] = {
|
|||
"bin", //common
|
||||
"flac", //common
|
||||
"gsf", //conflicts with GBA gsf plugins?
|
||||
"mp+", //common [Moonshine Runners (PC)]
|
||||
"mp2", //common
|
||||
"mp3", //common
|
||||
"mp4", //common
|
||||
|
@ -942,6 +945,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_XMU, "Outrage XMU header"},
|
||||
{meta_XVAS, "Konami .XVAS header"},
|
||||
{meta_PS2_XA2, "Acclaim XA2 Header"},
|
||||
{meta_SAP, "VING .SAP header"},
|
||||
{meta_DC_IDVI, "Capcom IDVI header"},
|
||||
{meta_KRAW, "Geometry Wars: Galaxies KRAW header"},
|
||||
{meta_NGC_YMF, "YMF DSP Header"},
|
||||
|
@ -1259,22 +1263,10 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou
|
|||
switch (vgmstream->coding_type) {
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case coding_FFmpeg:
|
||||
{
|
||||
ffmpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
if (data) {
|
||||
if (data->codec && data->codec->long_name) {
|
||||
description = data->codec->long_name;
|
||||
} else if (data->codec && data->codec->name) {
|
||||
description = data->codec->name;
|
||||
} else {
|
||||
description = "FFmpeg (unknown codec)";
|
||||
}
|
||||
} else {
|
||||
description = ffmpeg_get_codec_name(vgmstream->codec_data);
|
||||
if (description == NULL)
|
||||
description = "FFmpeg";
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
list_length = sizeof(coding_info_list) / sizeof(coding_info);
|
||||
|
|
|
@ -70,19 +70,51 @@ fail:
|
|||
|
||||
/* ************************************** */
|
||||
|
||||
#define ACB_TABLE_BUFFER_SIZE 0x4000
|
||||
|
||||
STREAMFILE* setup_acb_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(temp_streamFile, buffer_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE *acbFile; /* original reference, don't close */
|
||||
|
||||
/* keep track of these tables so they can be closed when done */
|
||||
utf_context *Header;
|
||||
|
||||
utf_context *CueNameTable;
|
||||
utf_context *CueTable;
|
||||
utf_context *BlockTable;
|
||||
utf_context *SequenceTable;
|
||||
utf_context *TrackTable;
|
||||
utf_context *TrackEventTable;
|
||||
utf_context *CommandTable;
|
||||
utf_context *TrackCommandTable;
|
||||
utf_context *SynthTable;
|
||||
utf_context *WaveformTable;
|
||||
|
||||
STREAMFILE *CueNameSf;
|
||||
STREAMFILE *CueSf;
|
||||
STREAMFILE *BlockSf;
|
||||
STREAMFILE *SequenceSf;
|
||||
STREAMFILE *TrackSf;
|
||||
STREAMFILE *TrackCommandSf;
|
||||
STREAMFILE *SynthSf;
|
||||
STREAMFILE *WaveformSf;
|
||||
|
||||
/* config */
|
||||
int is_memory;
|
||||
int target_waveid;
|
||||
|
@ -102,16 +134,21 @@ typedef struct {
|
|||
|
||||
} acb_header;
|
||||
|
||||
static int load_utf_subtable(STREAMFILE *acbFile, acb_header* acb, utf_context* *Table, const char* TableName, int* rows) {
|
||||
static int open_utf_subtable(acb_header* acb, STREAMFILE* *TableSf, utf_context* *Table, const char* TableName, int* rows) {
|
||||
uint32_t offset = 0;
|
||||
|
||||
/* already loaded */
|
||||
if (*Table != NULL)
|
||||
return 1;
|
||||
|
||||
if (!utf_query_data(acbFile, acb->Header, 0, TableName, &offset, NULL))
|
||||
if (!utf_query_data(acb->acbFile, acb->Header, 0, TableName, &offset, NULL))
|
||||
goto fail;
|
||||
*Table = utf_open(acbFile, offset, rows, NULL);
|
||||
|
||||
/* open a buffered streamfile to avoid so much IO back and forth between all the tables */
|
||||
*TableSf = setup_acb_streamfile(acb->acbFile, ACB_TABLE_BUFFER_SIZE);
|
||||
if (!*TableSf) goto fail;
|
||||
|
||||
*Table = utf_open(*TableSf, offset, rows, NULL);
|
||||
if (!*Table) goto fail;
|
||||
|
||||
//;VGM_LOG("ACB: loaded table %s\n", TableName);
|
||||
|
@ -121,7 +158,7 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_Streaming) {
|
||||
static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) {
|
||||
//todo safe string ops
|
||||
|
||||
/* ignore name repeats */
|
||||
|
@ -154,23 +191,23 @@ static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_S
|
|||
}
|
||||
|
||||
|
||||
static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_waveform(acb_header* acb, int16_t Index) {
|
||||
int16_t Waveform_Id;
|
||||
int8_t Waveform_Streaming;
|
||||
|
||||
/* read Waveform[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->WaveformSf, &acb->WaveformTable, "WaveformTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */
|
||||
if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */
|
||||
if (acb->is_memory) {
|
||||
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id))
|
||||
if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id))
|
||||
if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (!utf_query_s8(acbFile, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming))
|
||||
if (!utf_query_s8(acb->WaveformSf, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming);
|
||||
|
||||
|
@ -182,7 +219,7 @@ static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int16_t Index
|
|||
return 1;
|
||||
|
||||
/* aaand finally get name (phew) */
|
||||
add_acb_name(acbFile, acb, Waveform_Streaming);
|
||||
add_acb_name(acb, Waveform_Streaming);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
|
@ -190,9 +227,9 @@ fail:
|
|||
}
|
||||
|
||||
/* define here for Synths pointing to Sequences */
|
||||
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index);
|
||||
static int load_acb_sequence(acb_header* acb, int16_t Index);
|
||||
|
||||
static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_synth(acb_header* acb, int16_t Index) {
|
||||
int i, count;
|
||||
int8_t Synth_Type;
|
||||
uint32_t Synth_ReferenceItems_offset;
|
||||
|
@ -200,11 +237,11 @@ static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
|
||||
|
||||
/* read Synth[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->SynthSf, &acb->SynthTable, "SynthTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s8(acbFile, acb->SynthTable, Index, "Type", &Synth_Type))
|
||||
if (!utf_query_s8(acb->SynthSf, acb->SynthTable, Index, "Type", &Synth_Type))
|
||||
goto fail;
|
||||
if (!utf_query_data(acbFile, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size))
|
||||
if (!utf_query_data(acb->SynthSf, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size);
|
||||
|
||||
|
@ -232,8 +269,8 @@ static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
|
||||
count = Synth_ReferenceItems_size / 0x04;
|
||||
for (i = 0; i < count; i++) {
|
||||
uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acbFile);
|
||||
uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acbFile);
|
||||
uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acb->SynthSf);
|
||||
uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acb->SynthSf);
|
||||
//;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index);
|
||||
|
||||
switch(Synth_ReferenceItem_type) {
|
||||
|
@ -242,17 +279,17 @@ static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
break;
|
||||
|
||||
case 0x01: /* Waveform (most common) */
|
||||
if (!load_acb_waveform(acbFile, acb, Synth_ReferenceItem_index))
|
||||
if (!load_acb_waveform(acb, Synth_ReferenceItem_index))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */
|
||||
if (!load_acb_synth(acbFile, acb, Synth_ReferenceItem_index))
|
||||
if (!load_acb_synth(acb, Synth_ReferenceItem_index))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */
|
||||
if (!load_acb_sequence(acbFile, acb, Synth_ReferenceItem_index))
|
||||
if (!load_acb_sequence(acb, Synth_ReferenceItem_index))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
|
@ -271,33 +308,33 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_track_event_command(acb_header* acb, int16_t Index) {
|
||||
int16_t Track_EventIndex;
|
||||
uint32_t Track_Command_offset;
|
||||
uint32_t Track_Command_size;
|
||||
|
||||
|
||||
/* read Track[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->TrackSf, &acb->TrackTable, "TrackTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->TrackTable, Index, "EventIndex", &Track_EventIndex))
|
||||
if (!utf_query_s16(acb->TrackSf, acb->TrackTable, Index, "EventIndex", &Track_EventIndex))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex);
|
||||
|
||||
/* next link varies with version, check by table existence */
|
||||
if (acb->has_CommandTable) { /* <=v1.27 */
|
||||
/* read Command[EventIndex] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "CommandTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_data(acbFile, acb->CommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
|
||||
if (!utf_query_data(acb->TrackCommandSf, acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
|
||||
}
|
||||
else if (acb->has_TrackEventTable) { /* >=v1.28 */
|
||||
/* read TrackEvent[EventIndex] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "TrackEventTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_data(acbFile, acb->TrackEventTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
|
||||
if (!utf_query_data(acb->TrackCommandSf, acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
|
||||
}
|
||||
|
@ -315,8 +352,8 @@ static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, in
|
|||
|
||||
|
||||
while (offset < max_offset) {
|
||||
tlv_code = read_u16be(offset + 0x00, acbFile);
|
||||
tlv_size = read_u8 (offset + 0x02, acbFile);
|
||||
tlv_code = read_u16be(offset + 0x00, acb->TrackCommandSf);
|
||||
tlv_size = read_u8 (offset + 0x02, acb->TrackCommandSf);
|
||||
offset += 0x03;
|
||||
|
||||
if (tlv_code == 0x07D0) {
|
||||
|
@ -325,20 +362,20 @@ static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, in
|
|||
break;
|
||||
}
|
||||
|
||||
tlv_type = read_u16be(offset + 0x00, acbFile);
|
||||
tlv_index = read_u16be(offset + 0x02, acbFile);
|
||||
tlv_type = read_u16be(offset + 0x00, acb->TrackCommandSf);
|
||||
tlv_index = read_u16be(offset + 0x02, acb->TrackCommandSf);
|
||||
//;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index);
|
||||
|
||||
/* probably same as Synth_ReferenceItem_type */
|
||||
switch(tlv_type) {
|
||||
|
||||
case 0x02: /* Synth (common) */
|
||||
if (!load_acb_synth(acbFile, acb, tlv_index))
|
||||
if (!load_acb_synth(acb, tlv_index))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */
|
||||
if (!load_acb_sequence(acbFile, acb, tlv_index))
|
||||
if (!load_acb_sequence(acb, tlv_index))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
|
@ -360,7 +397,7 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_sequence(acb_header* acb, int16_t Index) {
|
||||
int i;
|
||||
int16_t Sequence_NumTracks;
|
||||
uint32_t Sequence_TrackIndex_offset;
|
||||
|
@ -368,11 +405,11 @@ static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index
|
|||
|
||||
|
||||
/* read Sequence[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->SequenceSf, &acb->SequenceTable, "SequenceTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks))
|
||||
if (!utf_query_s16(acb->SequenceSf, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks))
|
||||
goto fail;
|
||||
if (!utf_query_data(acbFile, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size))
|
||||
if (!utf_query_data(acb->SequenceSf, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size);
|
||||
|
||||
|
@ -390,9 +427,9 @@ static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index
|
|||
|
||||
/* read Tracks inside Sequence */
|
||||
for (i = 0; i < Sequence_NumTracks; i++) {
|
||||
int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acbFile);
|
||||
int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acb->SequenceSf);
|
||||
|
||||
if (!load_acb_track_event_command(acbFile, acb, Sequence_TrackIndex_index))
|
||||
if (!load_acb_track_event_command(acb, Sequence_TrackIndex_index))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -403,7 +440,7 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_block(acb_header* acb, int16_t Index) {
|
||||
int i;
|
||||
int16_t Block_NumTracks;
|
||||
uint32_t Block_TrackIndex_offset;
|
||||
|
@ -411,11 +448,11 @@ static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
|
||||
|
||||
/* read Block[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->BlockTable, "BlockTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->BlockSf, &acb->BlockTable, "BlockTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->BlockTable, Index, "NumTracks", &Block_NumTracks))
|
||||
if (!utf_query_s16(acb->BlockSf, acb->BlockTable, Index, "NumTracks", &Block_NumTracks))
|
||||
goto fail;
|
||||
if (!utf_query_data(acbFile, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size))
|
||||
if (!utf_query_data(acb->BlockSf, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size);
|
||||
|
||||
|
@ -426,9 +463,9 @@ static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
|
||||
/* read Tracks inside Block */
|
||||
for (i = 0; i < Block_NumTracks; i++) {
|
||||
int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acbFile);
|
||||
int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acb->BlockSf);
|
||||
|
||||
if (!load_acb_track_event_command(acbFile, acb, Block_TrackIndex_index))
|
||||
if (!load_acb_track_event_command(acb, Block_TrackIndex_index))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -438,17 +475,17 @@ fail:
|
|||
|
||||
}
|
||||
|
||||
static int load_acb_cue(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_cue(acb_header* acb, int16_t Index) {
|
||||
int8_t Cue_ReferenceType;
|
||||
int16_t Cue_ReferenceIndex;
|
||||
|
||||
|
||||
/* read Cue[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->CueSf, &acb->CueTable, "CueTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s8 (acbFile, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType))
|
||||
if (!utf_query_s8 (acb->CueSf, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex))
|
||||
if (!utf_query_s16(acb->CueSf, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex);
|
||||
|
||||
|
@ -457,22 +494,22 @@ static int load_acb_cue(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
|||
switch(Cue_ReferenceType) {
|
||||
|
||||
case 1: /* Cue > Waveform (ex. PES 2015) */
|
||||
if (!load_acb_waveform(acbFile, acb, Cue_ReferenceIndex))
|
||||
if (!load_acb_waveform(acb, Cue_ReferenceIndex))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 2: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */
|
||||
if (!load_acb_synth(acbFile, acb, Cue_ReferenceIndex))
|
||||
if (!load_acb_synth(acb, Cue_ReferenceIndex))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 3: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */
|
||||
if (!load_acb_sequence(acbFile, acb, Cue_ReferenceIndex))
|
||||
if (!load_acb_sequence(acb, Cue_ReferenceIndex))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 8: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */
|
||||
if (!load_acb_block(acbFile, acb, Cue_ReferenceIndex))
|
||||
if (!load_acb_block(acb, Cue_ReferenceIndex))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
|
@ -488,17 +525,17 @@ fail:
|
|||
|
||||
}
|
||||
|
||||
static int load_acb_cuename(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
|
||||
static int load_acb_cuename(acb_header* acb, int16_t Index) {
|
||||
int16_t CueName_CueIndex;
|
||||
const char* CueName_CueName;
|
||||
|
||||
|
||||
/* read CueName[Index] */
|
||||
if (!load_utf_subtable(acbFile, acb, &acb->CueNameTable, "CueNameTable", NULL))
|
||||
if (!open_utf_subtable(acb, &acb->CueNameSf, &acb->CueNameTable, "CueNameTable", NULL))
|
||||
goto fail;
|
||||
if (!utf_query_s16(acbFile, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex))
|
||||
if (!utf_query_s16(acb->CueNameSf, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex))
|
||||
goto fail;
|
||||
if (!utf_query_string(acbFile, acb->CueNameTable, Index, "CueName", &CueName_CueName))
|
||||
if (!utf_query_string(acb->CueNameSf, acb->CueNameTable, Index, "CueName", &CueName_CueName))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName);
|
||||
|
||||
|
@ -507,7 +544,7 @@ static int load_acb_cuename(STREAMFILE *acbFile, acb_header* acb, int16_t Index)
|
|||
acb->cuename_index = Index;
|
||||
acb->cuename_name = CueName_CueName;
|
||||
|
||||
if (!load_acb_cue(acbFile, acb, CueName_CueIndex))
|
||||
if (!load_acb_cue(acb, CueName_CueIndex))
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
|
@ -516,12 +553,12 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory) {
|
||||
void load_acb_wave_name(STREAMFILE *streamFile, VGMSTREAM* vgmstream, int waveid, int is_memory) {
|
||||
acb_header acb = {0};
|
||||
int i, CueName_rows;
|
||||
|
||||
|
||||
if (!acbFile || !vgmstream || waveid < 0)
|
||||
if (!streamFile || !vgmstream || waveid < 0)
|
||||
return;
|
||||
|
||||
/* Normally games load a .acb + .awb, and asks the .acb to play a cue by name or index.
|
||||
|
@ -548,21 +585,23 @@ void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, i
|
|||
|
||||
//;VGM_LOG("ACB: find waveid=%i\n", waveid);
|
||||
|
||||
acb.Header = utf_open(acbFile, 0x00, NULL, NULL);
|
||||
acb.acbFile = streamFile;
|
||||
|
||||
acb.Header = utf_open(acb.acbFile, 0x00, NULL, NULL);
|
||||
if (!acb.Header) goto fail;
|
||||
|
||||
acb.target_waveid = waveid;
|
||||
acb.is_memory = is_memory;
|
||||
acb.has_TrackEventTable = utf_query_data(acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL);
|
||||
acb.has_CommandTable = utf_query_data(acbFile, acb.Header, 0, "CommandTable", NULL,NULL);
|
||||
acb.has_TrackEventTable = utf_query_data(acb.acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL);
|
||||
acb.has_CommandTable = utf_query_data(acb.acbFile, acb.Header, 0, "CommandTable", NULL,NULL);
|
||||
|
||||
|
||||
/* read all possible cue names and find which waveids are referenced by it */
|
||||
if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows))
|
||||
if (!open_utf_subtable(&acb, &acb.CueNameSf, &acb.CueNameTable, "CueNameTable", &CueName_rows))
|
||||
goto fail;
|
||||
for (i = 0; i < CueName_rows; i++) {
|
||||
|
||||
if (!load_acb_cuename(acbFile, &acb, i))
|
||||
if (!load_acb_cuename(&acb, i))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -574,12 +613,20 @@ void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, i
|
|||
|
||||
fail:
|
||||
utf_close(acb.Header);
|
||||
|
||||
utf_close(acb.CueNameTable);
|
||||
utf_close(acb.CueTable);
|
||||
utf_close(acb.SequenceTable);
|
||||
utf_close(acb.TrackTable);
|
||||
utf_close(acb.TrackEventTable);
|
||||
utf_close(acb.CommandTable);
|
||||
utf_close(acb.TrackCommandTable);
|
||||
utf_close(acb.SynthTable);
|
||||
utf_close(acb.WaveformTable);
|
||||
|
||||
close_streamfile(acb.CueNameSf);
|
||||
close_streamfile(acb.CueSf);
|
||||
close_streamfile(acb.SequenceSf);
|
||||
close_streamfile(acb.TrackSf);
|
||||
close_streamfile(acb.TrackCommandSf);
|
||||
close_streamfile(acb.SynthSf);
|
||||
close_streamfile(acb.WaveformSf);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,12 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
|
|||
num_samples = mpeg_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
|
||||
}
|
||||
|
||||
/* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */
|
||||
if (read_32bitBE(0x00, streamFile) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
|
||||
read_32bitBE(0x00, streamFile) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
|
||||
ffmpeg_set_force_seek(data);
|
||||
}
|
||||
|
||||
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
|
||||
if (!num_samples) {
|
||||
num_samples = data->totalSamples;
|
||||
|
|
|
@ -294,6 +294,12 @@ static const hcakey_info hcakey_list[] = {
|
|||
/* Uta Macross SmaPho De Culture (Android) */
|
||||
{396798934275978741}, // 0581B68744C5F5F5
|
||||
|
||||
/* Touhou Cannonball (Android) */
|
||||
{5465717035832233}, // 00136B0A6A5D13A9
|
||||
|
||||
/* Love Live! School idol festival ALL STARS (Android) */
|
||||
{6498535309877346413}, // 5A2F6F6F0192806D
|
||||
|
||||
/* Dragalia Lost (Cygames) [iOS/Android] */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
|
|
|
@ -1,67 +1,46 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* SAP (from Bubble_Symphony) */
|
||||
/* SAP - from Bubble Symphony (SAT) */
|
||||
VGMSTREAM * init_vgmstream_sat_sap(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int num_samples;
|
||||
int loop_flag = 0, channel_count;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("sap",filename_extension(filename))) goto fail;
|
||||
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x0A,streamFile) != 0x0010400E) /* "0010400E" */
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "sap"))
|
||||
goto fail;
|
||||
|
||||
|
||||
loop_flag = 0; /* (read_32bitLE(0x08,streamFile)!=0); */
|
||||
num_samples = read_32bitBE(0x00,streamFile); /* first for I/O reasons */
|
||||
channel_count = read_32bitBE(0x04,streamFile);
|
||||
if (channel_count != 1) goto fail; /* unknown layout */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
if (read_32bitBE(0x08,streamFile) != 0x10) /* bps? */
|
||||
goto fail;
|
||||
if (read_16bitBE(0x0c,streamFile) != 0x400E) /* ? */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
start_offset = 0x800;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x800;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->meta_type = meta_SAP;
|
||||
vgmstream->sample_rate = (uint16_t)read_16bitBE(0x0E,streamFile);
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->num_samples = read_32bitBE(0x00,streamFile);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0; /* (read_32bitLE(0x08,streamFile)-1)*28; */
|
||||
vgmstream->loop_end_sample = read_32bitBE(0x00,streamFile);
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
vgmstream->meta_type = meta_SAT_SAP;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile) {
|
|||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
uint32_t data_size, loop_start, loop_end, codec_id;
|
||||
uint32_t data_size, loop_start, loop_end, codec_id, asc_chunk;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
/* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */
|
||||
|
@ -149,30 +149,31 @@ VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00, streamFile) != 0x41414320) /* "AAC " */
|
||||
goto fail;
|
||||
|
||||
/* Haven't Found a codec flag yet. Let's just use this for now */
|
||||
if (read_32bitBE(0x10000, streamFile) != 0x41534320) /* "ASC " */
|
||||
/* Find the ASC chunk, That's where the goodies are */
|
||||
asc_chunk = read_32bitBE(0x40, streamFile);
|
||||
if (read_32bitBE(asc_chunk, streamFile) != 0x41534320) /* "ASC " */
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x10104, streamFile) != 0xFFFFFFFF)
|
||||
if (read_32bitBE(asc_chunk+0x104, streamFile) != 0xFFFFFFFF)
|
||||
loop_flag = 1;
|
||||
else
|
||||
loop_flag = 0;
|
||||
|
||||
channel_count = read_32bitBE(0x100F4, streamFile);
|
||||
codec_id = read_32bitBE(0x100F0, streamFile);
|
||||
channel_count = read_32bitBE(asc_chunk + 0xF4, streamFile);
|
||||
codec_id = read_32bitBE(asc_chunk + 0xF0, streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* Useless header, let's play the guessing game */
|
||||
start_offset = 0x10110;
|
||||
vgmstream->sample_rate = read_32bitBE(0x100FC, streamFile);
|
||||
/* ASC header */
|
||||
start_offset = asc_chunk + 0x110;
|
||||
vgmstream->sample_rate = read_32bitBE(asc_chunk + 0xFC, streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->meta_type = meta_TA_AAC_PS3;
|
||||
data_size = read_32bitBE(0x100F8, streamFile);
|
||||
loop_start = read_32bitBE(0x10104, streamFile);
|
||||
loop_end = read_32bitBE(0x10108, streamFile);
|
||||
data_size = read_32bitBE(asc_chunk + 0xF8, streamFile);
|
||||
loop_start = read_32bitBE(asc_chunk + 0x104, streamFile);
|
||||
loop_end = read_32bitBE(asc_chunk + 0x108, streamFile);
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
|
|
|
@ -552,8 +552,15 @@ static VGMSTREAM *init_subfile(txth_header * txth) {
|
|||
STREAMFILE * streamSubfile = NULL;
|
||||
|
||||
|
||||
if (txth->subfile_size == 0)
|
||||
txth->subfile_size = txth->data_size - txth->subfile_offset;
|
||||
if (txth->subfile_size == 0) {
|
||||
if (txth->data_size_set)
|
||||
txth->subfile_size = txth->data_size;
|
||||
else
|
||||
txth->subfile_size = txth->data_size - txth->subfile_offset;
|
||||
if (txth->subfile_size + txth->subfile_offset > get_streamfile_size(txth->streamBody))
|
||||
txth->subfile_size = get_streamfile_size(txth->streamBody) - txth->subfile_offset;
|
||||
}
|
||||
|
||||
if (txth->subfile_extension[0] == '\0')
|
||||
get_streamfile_ext(txth->streamFile,txth->subfile_extension,sizeof(txth->subfile_extension));
|
||||
|
||||
|
@ -586,7 +593,8 @@ static VGMSTREAM *init_subfile(txth_header * txth) {
|
|||
vgmstream_force_loop(vgmstream, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (txth->chunk_count && txth->subsong_count) {
|
||||
/* assumes won't point to subfiles with subsongs */
|
||||
if (/*txth->chunk_count &&*/ txth->subsong_count) {
|
||||
vgmstream->num_streams = txth->subsong_count;
|
||||
}
|
||||
//todo: other combos with subsongs + subfile?
|
||||
|
@ -1249,7 +1257,7 @@ static int is_substring(const char * val, const char * cmp, int inline_field) {
|
|||
chr = val[len];
|
||||
|
||||
/* "val" can end with math for inline fields (like interleave*0x10) */
|
||||
if (inline_field && (chr == '+' || chr == '-' || chr == '*' || chr == '/'))
|
||||
if (inline_field && (chr == '+' || chr == '-' || chr == '*' || chr == '/' || chr == '&'))
|
||||
return len;
|
||||
|
||||
/* otherwise "val" ends in space or eof (to tell "interleave" and "interleave_last" apart) */
|
||||
|
@ -1525,7 +1533,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
|
|||
brackets--;
|
||||
n = 1;
|
||||
}
|
||||
else if (type == '+' || type == '-' || type == '/' || type == '*') { /* op */
|
||||
else if (type == '+' || type == '-' || type == '/' || type == '*' || type == '&') { /* op */
|
||||
op = type;
|
||||
n = 1;
|
||||
}
|
||||
|
@ -1593,6 +1601,8 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
|
|||
else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample;
|
||||
else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count;
|
||||
else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset;
|
||||
else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset;
|
||||
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
|
||||
//todo whatever, improve
|
||||
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
|
||||
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
|
||||
|
@ -1624,6 +1634,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
|
|||
case '-': value = result - value; break;
|
||||
case '*': value = result * value; break;
|
||||
case '/': if (value == 0) goto fail; value = result / value; break;
|
||||
case '&': value = result & value; break;
|
||||
default: break;
|
||||
}
|
||||
op = ' '; /* consume */
|
||||
|
|
|
@ -91,6 +91,45 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx)
|
|||
snprintf(buf,buf_size, "%s/%i/%08x-%08x/%s", "hx", hx->header_index, hx->cuuid1,hx->cuuid2, grp_name);
|
||||
}
|
||||
|
||||
#define TXT_LINE_MAX 0x1000
|
||||
|
||||
/* get name */
|
||||
static int parse_name_bnh(ubi_hx_header * hx, STREAMFILE *sf, uint32_t cuuid1, uint32_t cuuid2) {
|
||||
STREAMFILE *sf_t;
|
||||
off_t txt_offset = 0;
|
||||
char line[TXT_LINE_MAX];
|
||||
char cuuid[40];
|
||||
|
||||
sf_t = open_streamfile_by_ext(sf,"bnh");
|
||||
if (sf_t == NULL) goto fail;
|
||||
|
||||
snprintf(cuuid,sizeof(cuuid), "cuuid( 0x%08x, 0x%08x )", cuuid1, cuuid2);
|
||||
|
||||
/* each .bnh line has a cuuid, a bunch of repeated fields and name (sometimes name is filename or "bad name") */
|
||||
while (txt_offset < get_streamfile_size(sf)) {
|
||||
int line_read, bytes_read;
|
||||
|
||||
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,sf_t, &line_read);
|
||||
if (!line_read) break;
|
||||
txt_offset += bytes_read;
|
||||
|
||||
if (strncmp(line,cuuid,31) != 0)
|
||||
continue;
|
||||
if (bytes_read <= 79)
|
||||
goto fail;
|
||||
|
||||
/* cuuid found, copy name (lines are fixed and always starts from the same position) */
|
||||
strcpy(hx->internal_name, &line[79]);
|
||||
|
||||
close_streamfile(sf_t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* get referenced name from WavRes, using the index again (abridged) */
|
||||
static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
||||
|
@ -107,6 +146,7 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
off_t header_offset;
|
||||
size_t class_size;
|
||||
int j, link_count, language_count, is_found = 0;
|
||||
uint32_t cuuid1, cuuid2;
|
||||
|
||||
|
||||
class_size = read_32bit(offset + 0x00, sf);
|
||||
|
@ -114,6 +154,9 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */
|
||||
offset += 0x04 + class_size;
|
||||
|
||||
cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf);
|
||||
cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf);
|
||||
|
||||
header_offset = read_32bit(offset + 0x08, sf);
|
||||
offset += 0x10;
|
||||
|
||||
|
@ -159,10 +202,18 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
resclass_size = read_32bit(wavres_offset, sf);
|
||||
wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */
|
||||
|
||||
internal_size = read_32bit(wavres_offset + 0x00, sf); /* usually 0 in consoles */
|
||||
internal_size = read_32bit(wavres_offset + 0x00, sf);
|
||||
if (internal_size > sizeof(hx->internal_name)+1) goto fail;
|
||||
read_string(hx->internal_name,internal_size+1, wavres_offset + 0x04, sf);
|
||||
return 1;
|
||||
|
||||
/* usually 0 in consoles */
|
||||
if (internal_size != 0) {
|
||||
read_string(hx->internal_name,internal_size+1, wavres_offset + 0x04, sf);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
parse_name_bnh(hx, sf, cuuid1, cuuid2);
|
||||
return 1; /* ignore error */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +232,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
|
||||
//todo cleanup/unify common readings
|
||||
|
||||
//;VGM_LOG("UBI HX: header class %s, o=%lx, s=%x\n\n", class_name, header_offset, header_size);
|
||||
//;VGM_LOG("UBI HX: header o=%lx, s=%x\n\n", offset, size);
|
||||
|
||||
hx->header_index = index;
|
||||
hx->header_offset = offset;
|
||||
|
@ -307,6 +358,8 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
hx->stream_size = read_32bit(offset + 0x04, sf);
|
||||
offset += 0x08;
|
||||
|
||||
//todo some dummy files have 0 size
|
||||
|
||||
if (read_32bit(offset + 0x00, sf) != 0x01) goto fail;
|
||||
/* 0x04: some kind of parent id shared by multiple Waves, or 0 */
|
||||
offset += 0x08;
|
||||
|
@ -454,6 +507,10 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
|
|||
}
|
||||
|
||||
//todo figure out CProgramResData sequences
|
||||
// Format is pretty complex list of values and some offsets in between, then field names
|
||||
// then more values and finally a list of linked IDs Links are the same as in the index,
|
||||
// but doesn't seem to be a straight sequence list. Seems it can be used for other config too.
|
||||
|
||||
/* identify all possible names so unknown platforms fail */
|
||||
if (strcmp(class_name, "CEventResData") == 0 || /* play/stop/etc event */
|
||||
strcmp(class_name, "CProgramResData") == 0 || /* some kind of map/object-like config to make sequences in some cases? */
|
||||
|
|
|
@ -643,20 +643,71 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_wbh_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf) {
|
||||
int selected_stream = target_subsong - 1;
|
||||
int version, name_count;
|
||||
off_t offset, name_number;
|
||||
|
||||
if (read_32bitBE(0x00, sf) != 0x57424844) /* "WBHD" */
|
||||
goto fail;
|
||||
version = read_32bitLE(0x04, sf);
|
||||
if (version != 1)
|
||||
goto fail;
|
||||
name_count = read_32bitLE(0x08, sf);
|
||||
|
||||
if (selected_stream > name_count)
|
||||
goto fail;
|
||||
|
||||
/* next table:
|
||||
* - 0x00: wave id? (ordered from 0 to N)
|
||||
* - 0x04: always 0 */
|
||||
offset = 0x10 + 0x08 * name_count;
|
||||
|
||||
name_number = 0;
|
||||
while (offset < get_streamfile_size(sf)) {
|
||||
size_t name_len = read_string(buf, maxsize, offset, sf) + 1;
|
||||
|
||||
if (name_len == 0)
|
||||
goto fail;
|
||||
if (name_number == selected_stream)
|
||||
break;
|
||||
|
||||
name_number++;
|
||||
offset += name_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) {
|
||||
STREAMFILE *streamXsb = NULL;
|
||||
STREAMFILE *sf_name = NULL;
|
||||
int name_found;
|
||||
|
||||
/* try to get the stream name in the .xwb, though they are very rarely included */
|
||||
name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamXwb);
|
||||
if (name_found) return;
|
||||
|
||||
/* try again in a companion .xsb file, a comically complex cue format */
|
||||
streamXsb = open_xsb_filename_pair(streamXwb);
|
||||
if (!streamXsb) return; /* not all xwb have xsb though */
|
||||
/* try again in a companion files */
|
||||
|
||||
if (xwb->version == 1) {
|
||||
/* .wbh, a simple name container */
|
||||
sf_name = open_streamfile_by_ext(streamXwb, "wbh");
|
||||
if (!sf_name) return; /* rarely found [Pac-Man World 2 (Xbox)] */
|
||||
|
||||
name_found = get_wbh_name(buf, maxsize, target_subsong, xwb, sf_name);
|
||||
close_streamfile(sf_name);
|
||||
}
|
||||
else {
|
||||
/* .xsb, a comically complex cue format */
|
||||
sf_name = open_xsb_filename_pair(streamXwb);
|
||||
if (!sf_name) return; /* not all xwb have xsb though */
|
||||
|
||||
name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, sf_name);
|
||||
close_streamfile(sf_name);
|
||||
}
|
||||
|
||||
name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, streamXsb);
|
||||
close_streamfile(streamXsb);
|
||||
|
||||
if (!name_found) {
|
||||
buf[0] = '\0';
|
||||
|
|
|
@ -1095,6 +1095,11 @@ void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmst
|
|||
|
||||
/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */
|
||||
int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
/* Value returned here is the max (or less) that vgmstream will ask a decoder per
|
||||
* "decode_x" call. Decoders with variable samples per frame or internal discard
|
||||
* may return 0 here and handle arbitrary samples_to_do values internally
|
||||
* (or some internal sample buffer max too). */
|
||||
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_CRI_ADX:
|
||||
case coding_CRI_ADX_fixed:
|
||||
|
@ -1241,14 +1246,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case coding_FFmpeg:
|
||||
if (vgmstream->codec_data) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data*)vgmstream->codec_data;
|
||||
return data->sampleBufferBlock; /* must know the full block size for edge loops */
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
return 0;
|
||||
#endif
|
||||
case coding_MTAF:
|
||||
return 128*2;
|
||||
|
@ -1495,37 +1493,15 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_CRI_ADX:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,
|
||||
vgmstream->interleave_block_size);
|
||||
}
|
||||
|
||||
break;
|
||||
case coding_CRI_ADX_exp:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_adx_exp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,
|
||||
vgmstream->interleave_block_size);
|
||||
}
|
||||
|
||||
break;
|
||||
case coding_CRI_ADX_fixed:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_adx_fixed(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,
|
||||
vgmstream->interleave_block_size);
|
||||
}
|
||||
|
||||
break;
|
||||
case coding_CRI_ADX_enc_8:
|
||||
case coding_CRI_ADX_enc_9:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_adx_enc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,
|
||||
vgmstream->interleave_block_size);
|
||||
vgmstream->interleave_block_size, vgmstream->coding_type);
|
||||
}
|
||||
|
||||
break;
|
||||
case coding_NGC_DSP:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
|
@ -2417,7 +2393,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||
}
|
||||
|
||||
/* codecs with configurable frame size */
|
||||
if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) {
|
||||
if (vgmstream->interleave_block_size > 0) {
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_MSADPCM:
|
||||
case coding_MSADPCM_int:
|
||||
|
@ -2813,6 +2789,23 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
|||
return 1;
|
||||
#endif
|
||||
|
||||
if ((vgmstream->coding_type == coding_PSX_cfg ||
|
||||
vgmstream->coding_type == coding_PSX_pivotal) &&
|
||||
(vgmstream->interleave_block_size == 0 || vgmstream->interleave_block_size > 0x50)) {
|
||||
VGM_LOG("VGMSTREAM: PSX-cfg decoder with wrong frame size %x\n", vgmstream->interleave_block_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((vgmstream->coding_type == coding_CRI_ADX ||
|
||||
vgmstream->coding_type == coding_CRI_ADX_enc_8 ||
|
||||
vgmstream->coding_type == coding_CRI_ADX_enc_9 ||
|
||||
vgmstream->coding_type == coding_CRI_ADX_exp ||
|
||||
vgmstream->coding_type == coding_CRI_ADX_fixed) &&
|
||||
(vgmstream->interleave_block_size == 0 || vgmstream->interleave_block_size > 0x12)) {
|
||||
VGM_LOG("VGMSTREAM: ADX decoder with wrong frame size %x\n", vgmstream->interleave_block_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if interleave is big enough keep a buffer per channel */
|
||||
if (vgmstream->interleave_block_size * vgmstream->channels >= STREAMFILE_DEFAULT_BUFFER_SIZE) {
|
||||
use_streamfile_per_channel = 1;
|
||||
|
|
|
@ -409,7 +409,7 @@ typedef enum {
|
|||
meta_DC_STR, /* SEGA Stream Asset Builder */
|
||||
meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */
|
||||
meta_NGC_BH2PCM, /* Bio Hazard 2 */
|
||||
meta_SAT_SAP, /* Bubble Symphony */
|
||||
meta_SAP,
|
||||
meta_DC_IDVI, /* Eldorado Gate */
|
||||
meta_KRAW, /* Geometry Wars - Galaxies */
|
||||
meta_PS2_OMU, /* PS2 Int file with Header */
|
||||
|
@ -1188,32 +1188,26 @@ typedef struct {
|
|||
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
|
||||
|
||||
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
|
||||
uint8_t *header_insert_block; // fake header data (ie. RIFF)
|
||||
uint8_t* header_block; // fake header data (ie. RIFF)
|
||||
|
||||
/*** "public" API (read-only) ***/
|
||||
// stream info
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
int floatingPoint;
|
||||
int sampleRate;
|
||||
int bitrate;
|
||||
// extra info: 0 if unknown or not fixed
|
||||
int64_t totalSamples; // estimated count (may not be accurate for some demuxers)
|
||||
int64_t blockAlign; // coded block of bytes, counting channels (the block can be joint stereo)
|
||||
int64_t frameSize; // decoded samples per block
|
||||
int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments
|
||||
int streamCount; // number of FFmpeg audio streams
|
||||
|
||||
/*** internal state ***/
|
||||
// config
|
||||
int channel_remap_set;
|
||||
int channel_remap[32]; /* map of channel > new position */
|
||||
int invert_audio_set;
|
||||
|
||||
// intermediate byte buffer
|
||||
uint8_t *sampleBuffer;
|
||||
// max samples we can held (can be less or more than frameSize)
|
||||
size_t sampleBufferBlock;
|
||||
int channel_remap[32]; /* map of channel > new position */
|
||||
int invert_floats_set;
|
||||
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
|
||||
int force_seek; /* flags for special seeking in faulty formats */
|
||||
int bad_init;
|
||||
|
||||
// FFmpeg context used for metadata
|
||||
AVCodec *codec;
|
||||
|
@ -1224,20 +1218,17 @@ typedef struct {
|
|||
int streamIndex;
|
||||
AVFormatContext *formatCtx;
|
||||
AVCodecContext *codecCtx;
|
||||
AVFrame *lastDecodedFrame;
|
||||
AVPacket *lastReadPacket;
|
||||
int bytesConsumedFromDecodedFrame;
|
||||
int readNextPacket;
|
||||
int endOfStream;
|
||||
int endOfAudio;
|
||||
int skipSamplesSet; // flag to know skip samples were manually added from vgmstream
|
||||
AVFrame *frame; /* last decoded frame */
|
||||
AVPacket *packet; /* last read data packet */
|
||||
|
||||
// Seeking is not ideal, so rollback is necessary
|
||||
int samplesToDiscard;
|
||||
int read_packet;
|
||||
int end_of_stream;
|
||||
int end_of_audio;
|
||||
|
||||
// Flags for special seeking in faulty formats
|
||||
int force_seek;
|
||||
int bad_init;
|
||||
/* sample state */
|
||||
int32_t samples_discard;
|
||||
int32_t samples_consumed;
|
||||
int32_t samples_filled;
|
||||
|
||||
} ffmpeg_codec_data;
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue