Merge remote-tracking branch 'origin/master' into patch-1

This commit is contained in:
NicknineTheEagle 2018-07-18 05:18:35 +03:00
commit 51146cd612
13 changed files with 209 additions and 35 deletions

64
src/coding/asf_decoder.c Normal file
View File

@ -0,0 +1,64 @@
#include "coding.h"
/* Decodec Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform
* looks almost correct, but should reverse engineer asfcodec.adl (DLL) for accuracy. */
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset;
int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
uint32_t shift, mode;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32;
/* external interleave (fixed size), mono */
bytes_per_frame = 0x11;
samples_per_frame = (bytes_per_frame - 0x01) * 2;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
/* parse header */
frame_offset = stream->offset + bytes_per_frame*frames_in;
shift = (read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
mode = (read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
/* decoder nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile);
new_sample = i&1 ? /* high nibble first */
get_low_nibble_signed(nibbles):
get_high_nibble_signed(nibbles);
new_sample = new_sample << shift;
switch(mode) {
case 0x00:
new_sample = new_sample + hist1;
//new_sample = (new_sample + (hist1 << 6)) >> 6; /* maybe? */
break;
case 0x04:
new_sample = new_sample + hist1*2 - hist2;
//new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6; /* maybe? */
break;
default: /* other modes (ex 0x02/09) seem only at last frame as 0 */
//VGM_LOG("ASF: unknown mode %x at %lx\n", mode,frame_offset);
//new_sample = 0; /* maybe? */
break;
}
//new_sample = clamp16(new_sample); /* must not */
new_sample = new_sample & 0xFFFF; /* probably unnecessary */
outbuf[sample_count] = new_sample;
sample_count += channelspacing;
hist2 = hist1;
hist1 = new_sample;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_history2_32 = hist2;
}

View File

@ -147,6 +147,9 @@ void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbu
/* fadpcm_decoder */
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* asf_decoder */
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channel_count, int type);
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);

View File

@ -15,21 +15,21 @@ static const int8_t fadpcm_coefs[8][2] = {
/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout.
* Code/layout could be simplified but tries to emulate FMOD's code.
* Algorithm and tables debugged from their PC DLLs. */
* Algorithm and tables debugged from their PC DLLs (byte-accurate). */
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset;
int i, j, k;
int block_samples, num_frame, samples_done = 0, sample_count = 0;
int block_samples, frames_in, samples_done = 0, sample_count = 0;
uint32_t coefs, shifts;
int32_t hist1; //= stream->adpcm_history1_32;
int32_t hist2; //= stream->adpcm_history2_32;
/* external interleave (fixed size), mono */
block_samples = (0x8c - 0xc) * 2;
num_frame = first_sample / block_samples;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + 0x8c*num_frame;
frame_offset = stream->offset + 0x8c*frames_in;
/* parse 0xc header (header samples are not written to outbuf) */
@ -50,7 +50,7 @@ void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
coef1 = fadpcm_coefs[coef_index][0];
coef2 = fadpcm_coefs[coef_index][1];
shift = 0x16 - shift_factor;
shift = 0x16 - shift_factor; /* pre-adjust for 32b sign extend */
for (j = 0; j < 4; j++) {
uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile);
@ -59,9 +59,9 @@ void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
int32_t new_sample;
new_sample = (nibbles >> k*4) & 0x0f;
new_sample = (new_sample << 28) >> shift; /* sign extend + scale */
new_sample = (new_sample << 28) >> shift; /* 32b sign extend + scale */
new_sample = (new_sample - hist2*coef2 + hist1*coef1);
new_sample = new_sample >> 6; /* (new_sample / 64) has minor rounding differences */
new_sample = new_sample >> 6;
new_sample = clamp16(new_sample);
if (sample_count >= first_sample && samples_done < samples_to_do) {

View File

@ -39,10 +39,16 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
/* read and transform SNS/EA-XMA blocks into XMA packets */
while (buf_done < buf_size) {
int s, p, bytes_to_copy, max_packets;
size_t data_size = 0, gap_size = 0;
size_t block_size = read_32bitBE(real_offset, data->streamfile);
/* 0x04(4): decoded samples */
off_t packets_offset = real_offset + 0x08;
size_t block_size, data_size = 0, gap_size = 0;
uint32_t block_flag;
off_t packets_offset;
block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile);
block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF;
packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */
if (block_flag == 0x45) /* exit on last block just in case, though should reach real_size */
break;
max_packets = get_block_max_packets(num_streams, packets_offset, data->streamfile);
if (max_packets == 0) goto fail;
@ -115,12 +121,11 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
/* move when block is fully done */
if (data_size == bytes_to_copy + gap_size) {
real_offset += (block_size & 0x00FFFFFF);
real_offset += block_size;
virtual_base += data_size;
}
/* exit on last block just in case, though should reach real_size */
if ((block_size & 0x80000000) || (block_size & 0x45000000))
if (block_flag == 0x80) /* exit on last block just in case, though should reach real_size */
break;
}
@ -153,10 +158,16 @@ int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset
/* find target block */
while (virtual_base < seek_virtual_offset) {
size_t data_size, extra_size = 0;
size_t block_size = read_32bitBE(real_offset, data->streamfile);
size_t block_size, data_size, extra_size = 0;
uint32_t block_flag;
data_size = (block_size & 0x00FFFFFF) - 0x0c;
block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile);
block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF;
if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */
break;
data_size = block_size - 0x0c;
if (data_size % EAXMA_XMA_PACKET_SIZE)
extra_size = EAXMA_XMA_PACKET_SIZE - (data_size % EAXMA_XMA_PACKET_SIZE);
@ -164,8 +175,11 @@ int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset
if (data_size + extra_size > seek_virtual_offset)
break;
real_offset += (block_size & 0x00FFFFFF);
real_offset += block_size;
virtual_base += data_size + extra_size;
if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */
break;
}
/* closest we can use for reads */
@ -195,13 +209,16 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea
/* count all SNS/EAXMA blocks size + padding size */
while (real_offset < real_end_offset) {
int max_packets;
size_t block_size = read_32bitBE(real_offset + 0x00, streamFile);
/* 0x04(4): decoded samples */
off_t packets_offset = real_offset + 0x08;
uint32_t block_flag, block_size;
off_t packets_offset;
block_flag = (uint8_t)read_8bit(real_offset+0x00,streamFile);
block_size = read_32bitBE(real_offset+0x00,streamFile) & 0x00FFFFFF;
packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */
if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */
break;
/* At 0x00(1): block flag
* - in SNS: 0x00=normal block, 0x80=last block (not mandatory)
* - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */
max_packets = get_block_max_packets(num_streams, packets_offset, streamFile);
if (max_packets == 0) goto fail;
@ -209,10 +226,9 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea
/* fixed data_size per block for multichannel, see reads */
virtual_size += max_packets * num_streams * EAXMA_XMA_PACKET_SIZE;
real_offset += (block_size & 0x00FFFFFF);
real_offset += block_size;
/* exit on last block just in case, though should reach real_size */
if ((block_size & 0x80000000) || (block_size & 0x45000000))
if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */
break;
}

View File

@ -2,13 +2,13 @@
#include "../util.h"
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
// XA coefs int math in different ways (see comments below), so may not be 100% accurate.
// May be implemented like the SNES/SPC700 BRR (per-filter code?).
// XA coefs int math in different ways (see comments below), not be 100% accurate.
// May be implemented like the SNES/SPC700 BRR (see BSNES' brr.cpp, hardware-tested).
/* 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};
static int IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); } /* K0/1 floats to int, K*2^10 = K*1024 */
static int IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); } /* K0/1 floats to int, K*2^10 = K*(1<<10) = K*1024 */
static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
@ -21,15 +21,16 @@ static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
* short sample = ((nibble << 12) & 0xf000) >> shift
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
* (sample + K0*2^N*hist1 + K1*2^N*hist1 + [(2^N)/2]) / 2^N
* (sample + K0*2^N*hist1 + K1*2^N*hist1 + [(2^N)/2]) >> N
* sample + (K0<<N*hist1 + K1<<N*hist1)>>N
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist1)>>N
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
* etc
* (rounding differences should be inaudible, so public implementations may be approximations)
*
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
* differently, using one of the above methods.
* differently, using one of the above methods in software/CPU, but in XA's case may be done like
* the SNES/SPC700 BRR, with specific per-filter ops.
* 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.
*

View File

@ -548,6 +548,7 @@ static const coding_info coding_info_list[] = {
{coding_MTA2, "Konami MTA2 4-bit ADPCM"},
{coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"},
{coding_ASF, "Argonaut ASF 4-bit ADPCM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
@ -1032,6 +1033,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
{meta_ASF, "Argonaut ASF header"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -292,6 +292,10 @@
RelativePath=".\meta\apple_caff.c"
>
</File>
<File
RelativePath=".\meta\asf.c"
>
</File>
<File
RelativePath=".\meta\ast.c"
>
@ -1542,6 +1546,10 @@
RelativePath=".\coding\adx_decoder.c"
>
</File>
<File
RelativePath=".\coding\asf_decoder.c"
>
</File>
<File
RelativePath=".\coding\yamaha_decoder.c"
>

View File

@ -195,6 +195,7 @@
<ClCompile Include="meta\aifc.c" />
<ClCompile Include="meta\aix.c" />
<ClCompile Include="meta\apple_caff.c" />
<ClCompile Include="meta\asf.c" />
<ClCompile Include="meta\ast.c" />
<ClCompile Include="meta\atsl.c" />
<ClCompile Include="meta\atx.c" />
@ -447,6 +448,7 @@
<ClCompile Include="meta\zwdsp.c" />
<ClCompile Include="coding\acm_decoder.c" />
<ClCompile Include="coding\adx_decoder.c" />
<ClCompile Include="coding\asf_decoder.c" />
<ClCompile Include="coding\yamaha_decoder.c" />
<ClCompile Include="coding\ea_mt_decoder.c" />
<ClCompile Include="coding\ea_xa_decoder.c" />

View File

@ -181,6 +181,9 @@
<ClCompile Include="meta\apple_caff.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\asf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ast.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -910,6 +913,9 @@
<ClCompile Include="coding\adx_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\asf_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\yamaha_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

56
src/meta/asf.c Normal file
View File

@ -0,0 +1,56 @@
#include "meta.h"
#include "../coding/coding.h"
/* ASF - Argonaut PC games [Croc 2 (PC), Aladdin: Nasira's Revenge (PC)] */
VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, version;
/* checks */
if (!check_extensions(streamFile, "asf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41534600) /* "ASF\0" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x02000100)
goto fail;
if (read_32bitLE(0x08,streamFile) != 0x01 &&
read_32bitLE(0x0c,streamFile) != 0x18 &&
read_32bitLE(0x1c,streamFile) != 0x20)
goto fail;
version = read_32bitLE(0x28,streamFile); /* assumed? */
switch(version){
case 0x0d: channel_count = 1; break; /* Aladdin: Nasira's Revenge (PC) */
case 0x0f: channel_count = 2; break; /* Croc 2 (PC), The Emperor's New Groove (PC) */
default: goto fail;
}
loop_flag = 0;
start_offset = 0x2c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x24, streamFile);
vgmstream->meta_type = meta_ASF;
vgmstream->coding_type = coding_ASF;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x11;
vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/(0x11*channel_count)*32; /* bytes_to_samples */
//vgmstream->num_samples = read_32bitLE(0x18,streamFile) * (0x20<<channel_count); /* something like this? */
read_string(vgmstream->stream_name,0x10, 0x08+1,streamFile);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -759,4 +759,6 @@ VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile);
#endif /*_META_H*/

View File

@ -414,6 +414,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_dsp_sadf,
init_vgmstream_h4m,
init_vgmstream_ps2_ads_container,
init_vgmstream_asf,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -1118,6 +1119,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 10;
case coding_FADPCM:
return 256; /* (0x8c - 0xc) * 2 */
case coding_ASF:
return 32; /* (0x11 - 0x1) * 2 */
case coding_EA_MT:
return 432;
case coding_CRI_HCA:
@ -1274,6 +1277,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x04;
case coding_FADPCM:
return 0x8c;
case coding_ASF:
return 0x11;
case coding_EA_MT:
return 0; /* variable (frames of bit counts or PCM frames) */
#ifdef VGM_USE_ATRAC9
@ -1905,6 +1910,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_ASF:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_asf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_EA_MT:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan,

View File

@ -155,6 +155,7 @@ typedef enum {
coding_MTA2, /* Konami MTA2 ADPCM */
coding_MC3, /* Paradigm MC3 3-bit ADPCM */
coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */
coding_ASF, /* Argonaut ASF 4-bit ADPCM */
/* others */
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
@ -680,6 +681,7 @@ typedef enum {
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,