Merge pull request #476 from bnnm/rws-aica-etc

rws aica etc
This commit is contained in:
bnnm 2019-09-21 21:02:01 +02:00 committed by GitHub
commit 3090c08e18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 458 additions and 278 deletions

View File

@ -371,7 +371,7 @@ are used in few games.
- Microsoft MS IMA ADPCM (standard, Xbox, NDS, Radical, Wwise, FSB, WV6, etc)
- Microsoft MS ADPCM (standard, Cricket Audio)
- Westwood VBR ADPCM
- Yamaha ADPCM (standard, Aska)
- Yamaha ADPCM (AICA, Aska)
- Procyon Studio ADPCM
- Level-5 0x555 ADPCM
- lsf ADPCM
@ -562,7 +562,7 @@ This list is not complete and many other files are supported.
- .xmu
- .xvas
- .xwav
- Yamaha ADPCM:
- Yamaha AICA ADPCM:
- .adpcm
- .dcs+.dcsw
- .str

View File

@ -126,25 +126,25 @@ bool read_data(const char * filename, Tuple & tuple) {
STREAMFILE *streamfile = open_vfs(filename);
if (!streamfile) return false;
VGMSTREAM *vgmstream = init_vgmstream_from_STREAMFILE(streamfile);
if (!vgmstream) {
VGMSTREAM *infostream = init_vgmstream_from_STREAMFILE(streamfile);
if (!infostream) {
close_streamfile(streamfile);
return false;
}
tuple.set_filename(filename); //may leak string???
int rate = get_vgmstream_average_bitrate(vgmstream);
tuple.set_int(Tuple::Bitrate, rate);
int bitrate = get_vgmstream_average_bitrate(infostream);
tuple.set_int(Tuple::Bitrate, bitrate);
int ms = get_vgmstream_play_samples(vgmstream_cfg.loop_count, vgmstream_cfg.fade_length, vgmstream_cfg.fade_delay, vgmstream);
ms = ms* 1000LL / vgmstream->sample_rate;
int ms = get_vgmstream_play_samples(vgmstream_cfg.loop_count, vgmstream_cfg.fade_length, vgmstream_cfg.fade_delay, infostream);
ms = ms* 1000LL / infostream->sample_rate;
tuple.set_int(Tuple::Length, ms);
tuple.set_str(Tuple::Codec, "vgmstream codec");//doesn't show?
// here we could call describe_vgmstream() and get substring to add tags and stuff
close_streamfile(streamfile);
close_vgmstream(vgmstream);
close_vgmstream(infostream);
return true;
}
@ -169,7 +169,11 @@ bool VgmstreamPlugin::play(const char *filename, VFSFile &file) {
debugMessage("start play");
int current_sample_pos = 0;
int rate;
int bitrate;
// just in case
if (vgmstream)
close_vgmstream(vgmstream);
STREAMFILE *streamfile = open_vfs(filename);
if (!streamfile) {
@ -194,9 +198,9 @@ bool VgmstreamPlugin::play(const char *filename, VFSFile &file) {
int stream_samples_amount = get_vgmstream_play_samples(
vgmstream_cfg.loop_count, vgmstream_cfg.fade_length,
vgmstream_cfg.fade_delay, vgmstream);
rate = get_vgmstream_average_bitrate(vgmstream);
bitrate = get_vgmstream_average_bitrate(vgmstream);
set_stream_bitrate(rate);
set_stream_bitrate(bitrate);
open_audio(FMT_S16_LE, vgmstream->sample_rate, vgmstream->channels);
int fade_samples = vgmstream_cfg.fade_length * vgmstream->sample_rate;

View File

@ -12,7 +12,6 @@ typedef struct _VFSSTREAMFILE {
VFSFile *vfsFile;
off_t offset;
char name[32768];
//char realname[32768];
} VFSSTREAMFILE;
static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path);
@ -35,7 +34,7 @@ static size_t read_vfs(VFSSTREAMFILE *streamfile, uint8_t *dest, off_t offset,
static void close_vfs(VFSSTREAMFILE *streamfile) {
debugMessage("close_vfs");
free(streamfile->vfsFile);
delete streamfile->vfsFile; //fcloses the internal file too
free(streamfile);
}

View File

@ -2,7 +2,7 @@
## Build requirements
**CMake**: Needs v3.5 or later
**CMake**: Needs v3.6 or later
- https://cmake.org/download/
**Git**: optional, to generate version numbers:

View File

@ -93,8 +93,8 @@ as explained below, but often will use default values. Accepted codec strings:
# * Special interleave is multiple of 0x1, often +0x80
# - DVI_IMA IMA ADPCM (DVI order)
# * Variation with modified encoding
# - YAMAHA|AICA Yamaha ADPCM (mono/stereo)
# * For some Dreamcast games, and some arcade games
# - AICA Yamaha AICA ADPCM (mono/stereo)
# * For some Dreamcast games, and some arcade (Naomi) games
# * Special interleave is multiple of 0x1
# - APPLE_IMA4 Apple Quicktime IMA ADPCM
# * For some Mac/iOS games

View File

@ -224,7 +224,7 @@ music_Home.ps3.scd#C3 4
```
### Custom play settings
### Play settings
**`#l(loops)`**, **`#f(fade)`**, **`#d(fade-delay)`**, **`#i(ignore loop)`**, **`#F(ignore fade)`**, **`#E(end-to-end loop)`**
Those setting should override player's defaults if set (except "loop forever"). They are equivalent to some test.exe options.
@ -259,8 +259,29 @@ boss2_3ningumi_ver6.adx#l1.5#d1#f5
```
### Time modifications
**`#t(time)`**: trims the file so base duration (before applying loops/fades/etc) is `(time)`. If value is negative substracts `(time)` to duration. Loop end is adjusted when necessary, and ignored if value is bigger than possible (use `#l(loops)` config to extend time instead).
Time values can be `M:S(.n)` (minutes and seconds), `S.n` (seconds with dot), `0xN` (samples in hex format) or `N` (samples). Beware of the subtle difference between 10.0 (ten seconds) and 10 (ten samples).
Some segments have padding/silence at the end for some reason, that don't allow smooth transitions. You can fix it like this:
```
intro.fsb #t -1.0 #intro segment has 1 second of silence.
main.fsb
```
Similarly other games don't use loop points, but rather repeat/loops the song internally many times:
```
intro.vag #t3:20 #i #l1.0 #trim + combine with forced loops for easy fades
```
Note that if you need to remove very few samples (like 1) to get smooth transitions it may be a bug in vgmstream, consider reporting.
### Force sample rate
**`#h(sample rate)`**: for a few games that set a sample rate value in the header but actually play with other (applying some of pitch or just forcing it).
**`#h(sample rate)`**: changes sample rate to selected value (within some limits).
Needed for a few games set a sample rate value in the header but actually play with other (applying some of pitch or just forcing it).
**Super Paper Mario (Wii)**
```

View File

@ -125,8 +125,7 @@ static void get_name_foo(FOO_STREAMFILE *streamfile,char *buffer,size_t length)
}
}
static void close_foo(FOO_STREAMFILE * streamfile) {
if (streamfile->m_file_opened)
streamfile->m_file.release();
streamfile->m_file.release(); //release alloc'ed ptr
free(streamfile->name);
free(streamfile->buffer);
free(streamfile);
@ -215,7 +214,7 @@ static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size
streamFile = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buffersize, p_abort);
if (!streamFile) {
//m_file.release(); //todo not needed after g_open_read?
//m_file.release(); //refcounted and cleaned after it goes out of scope
}
return streamFile;

View File

@ -38,7 +38,7 @@ extern "C" {
"https://github.com/kode54/vgmstream/\n" \
"https://sourceforge.net/projects/vgmstream/ (original)"
// called every time a file is added to the playlist (to get info) or when playing
input_vgmstream::input_vgmstream() {
vgmstream = NULL;
subsong = 0; // 0 = not set, will be properly changed on first setup_vgmstream
@ -67,12 +67,13 @@ input_vgmstream::input_vgmstream() {
load_settings();
}
// called on stop or when playlist info has been read
input_vgmstream::~input_vgmstream() {
close_vgmstream(vgmstream);
vgmstream = NULL;
}
// called first when a new file is opened
// called first when a new file is accepted, before playing it
void input_vgmstream::open(service_ptr_t<file> p_filehint, const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
if (!p_path) { // shouldn't be possible
@ -132,10 +133,12 @@ unsigned input_vgmstream::get_subsong_count() {
return subsong_count;
}
// called after get_subsong_count to play subsong N (even when count is 1)
t_uint32 input_vgmstream::get_subsong(unsigned p_index) {
return p_index + 1; // translates index (0..N < subsong_count) for vgmstream: 1=first
}
// called before playing to get info
void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) {
int length_in_ms=0, channels = 0, samplerate = 0;
int total_samples = -1;
@ -252,6 +255,7 @@ void input_vgmstream::decode_initialize(t_uint32 p_subsong, unsigned p_flags, ab
decode_seek( 0, p_abort );
};
// called when audio buffer needs to be filled
bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
if (!decoding) return false;
if (!vgmstream) return false;
@ -306,6 +310,7 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
}
}
// called when seeking
void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
seek_pos_samples = (int) audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
@ -377,6 +382,8 @@ void input_vgmstream::retag_set_info(t_uint32 p_subsong, const file_info & p_inf
void input_vgmstream::retag_commit(abort_callback & p_abort) { /*throw exception_io_data();*/ }
bool input_vgmstream::g_is_our_content_type(const char * p_content_type) {return false;}
// called to check if file can be processed by the plugin
bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension) {
const char ** ext_list;
size_t ext_list_len;
@ -397,7 +404,7 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension
return 0;
}
// internal util to create a VGMSTREAM
VGMSTREAM * input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort) {
VGMSTREAM *vgmstream = NULL;
@ -410,6 +417,7 @@ VGMSTREAM * input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char *
return vgmstream;
}
// internal util to initialize vgmstream
void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
// close first in case of changing subsongs
if (vgmstream) {
@ -447,6 +455,7 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
fade_samples = (int)(config.song_fade_time * vgmstream->sample_rate);
}
// internal util to get info
void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & title, int *length_in_ms, int *total_samples, int *loop_flag, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort) {
VGMSTREAM * infostream = NULL;
bool is_infostream = false;
@ -601,29 +610,26 @@ void input_vgmstream::apply_config(VGMSTREAM * vgmstream, foobar_song_config *cu
}
}
GUID input_vgmstream::g_get_guid()
{
GUID input_vgmstream::g_get_guid() {
static const GUID guid = { 0x9e7263c7, 0x4cdd, 0x482c,{ 0x9a, 0xec, 0x5e, 0x71, 0x28, 0xcb, 0xc3, 0x4 } };
return guid;
}
const char * input_vgmstream::g_get_name()
{
const char * input_vgmstream::g_get_name() {
return "vgmstream";
}
GUID input_vgmstream::g_get_preferences_guid()
{
GUID input_vgmstream::g_get_preferences_guid() {
static const GUID guid = { 0x2b5d0302, 0x165b, 0x409c,{ 0x94, 0x74, 0x2c, 0x8c, 0x2c, 0xd7, 0x6a, 0x25 } };;
return guid;
}
bool input_vgmstream::g_is_low_merit()
{
// checks priority (foobar 1.4+)
bool input_vgmstream::g_is_low_merit() {
return true;
}
/* foobar plugin defs */
// foobar plugin defs
static input_factory_t<input_vgmstream> g_input_vgmstream_factory;
DECLARE_COMPONENT_VERSION(APP_NAME,PLUGIN_VERSION,PLUGIN_DESCRIPTION);

View File

@ -133,7 +133,7 @@ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspac
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
/* yamaha_decoder */
void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
size_t yamaha_bytes_to_samples(size_t bytes, int channels);

View File

@ -1,30 +1,88 @@
#include "../util.h"
#include "coding.h"
/* fixed point (.8) amount to scale the current step size by */
/* part of the same series as used in MS ADPCM "ADPCMTable" */
static const unsigned int scale_step[16] = {
/* fixed point amount to scale the current step size */
static const unsigned int scale_step_aica[16] = {
230, 230, 230, 230, 307, 409, 512, 614,
230, 230, 230, 230, 307, 409, 512, 614
};
/* actually implemented with if-else/switchs but that's too goofy */
static const int scale_step_aska[8] = {
static const int scale_step_adpcmb[16] = {
57, 57, 57, 57, 77, 102, 128, 153,
57, 57, 57, 57, 77, 102, 128, 153,
};
/* expand an unsigned four bit delta to a wider signed range */
/* look-up for 'mul' IMA's sign*((code&7) * 2 + 1) for every code */
static const int scale_delta[16] = {
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9,-11,-13,-15
};
/* Yamaha ADPCM-B (aka DELTA-T) expand used in YM2608/YM2610/etc (cross referenced with various sources and .so) */
static void yamaha_adpcmb_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) {
int code, delta, sample;
/* raw Yamaha ADPCM a.k.a AICA as it's prominently used in Naomi/Dreamcast's Yamaha AICA sound chip,
* also found in Windows RIFF and older Yamaha's arcade sound chips. */
void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
int i, sample_count;
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
delta = ((((code & 0x7) * 2) + 1) * (*step_size)) >> 3; /* like 'mul' IMA */
if (code & 8)
delta = -delta;
sample = *hist1 + delta;
sample = clamp16(sample); /* this may not be needed (not done in Aska) but no byte changes */
*step_size = ((*step_size) * scale_step_adpcmb[code]) >> 6;
if (*step_size < 0x7f) *step_size = 0x7f;
else if (*step_size > 0x6000) *step_size = 0x6000;
*out_sample = sample;
*hist1 = sample;
}
/* Yamaha AICA expand, slightly filtered vs "ACM" Yamaha ADPCM, same as Creative ADPCM
* (some info from https://github.com/vgmrips/vgmplay, https://wiki.multimedia.cx/index.php/Creative_ADPCM) */
static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) {
int code, delta, sample;
*hist1 = *hist1 * 254 / 256; /* hist filter is vital to get correct waveform but not done in many emus */
code = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf;
delta = (*step_size * scale_delta[code]) / 8; /* 'mul' IMA with table (not sure if part of encoder) */
sample = *hist1 + delta;
sample = clamp16(sample); /* apparently done by official encoder */
*step_size = ((*step_size) * scale_step_aica[code]) >> 8;
if (*step_size < 0x7f) *step_size = 0x7f;
else if (*step_size > 0x6000) *step_size = 0x6000;
*out_sample = sample;
*hist1 = sample;
}
/* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j')
* - possibly RIFF codec 0x20
* - simply called "Yamaha ADPCM Codec" (even though not quite like Yamaha ADPCM-B)
* - block_align = (sample_rate / 0x3C + 0x04) * channels (ex. 0x2E6 for 22050 stereo, probably given in RIFF)
* - low nibble first, stereo or mono modes (no interleave)
* - expand (old IMA 'shift+add' style, not 'mul' style):
* delta = step_size >> 3;
* if (code & 1) delta += step_size >> 2;
* if (code & 2) delta += step_size >> 1;
* if (code & 4) delta += step_size;
* if (code & 8) delta = -delta;
* sample = hist + clamp16(delta);
* though compiled more like:
* sample = hist + (1-2*(code & 8) * (step_size/8 + step_size/2 * (code&2) + step_size/4 * (code&1) + step_size * (code&4))
* - step_size update:
* step_size = clamp_range(step_size * scale_step_aica[code] >> 8, 0x7F, 0x6000)
*/
/* Yamaha AICA ADPCM (also used in YMZ280B with high nibble first) */
void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
int i, sample_count = 0;
int16_t out_sample;
int32_t hist1 = stream->adpcm_history1_16;
int step_size = stream->adpcm_step_index;
@ -32,8 +90,7 @@ void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspac
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */
stream->offset + i/2; /* mono: consecutive nibbles */
@ -41,26 +98,20 @@ void decode_yamaha(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspac
(!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */
(!(i&1) ? 0:4); /* low nibble first */
/* Yamaha/AICA expand, but same result as IMA's (((delta * 2 + 1) * step) >> 3) */
sample_nibble = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf;
sample_delta = (step_size * scale_delta[sample_nibble]) / 8;
sample_decoded = hist1 + sample_delta;
outbuf[sample_count] = clamp16(sample_decoded);
hist1 = outbuf[sample_count];
step_size = (step_size * scale_step[sample_nibble]) >> 8;
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
yamaha_aica_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample);
outbuf[sample_count] = out_sample;
sample_count += channelspacing;
}
stream->adpcm_history1_16 = hist1;
stream->adpcm_step_index = step_size;
}
/* tri-Ace Aska ADPCM, same-ish with modified step table (reversed from Android SO's .so) */
/* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so)
* implements table with if-else/switchs too but that's too goofy */
void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count, num_frame;
int i, sample_count = 0, num_frame;
int16_t out_sample;
int32_t hist1 = stream->adpcm_history1_32;
int step_size = stream->adpcm_step_index;
@ -75,13 +126,13 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_size = read_16bitLE(header_offset+0x02,stream->streamfile);
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
/* in most files 1st frame has step 0 but it seems ok and accounted for */
//if (step_size < 0x7f) step_size = 0x7f;
//else if (step_size > 0x6000) step_size = 0x6000;
}
/* decode nibbles (layout: varies) */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = (channelspacing == 2) ?
(stream->offset + 0x40*num_frame + 0x04*channelspacing) + i : /* stereo: one nibble per channel */
(stream->offset + 0x40*num_frame + 0x04*channelspacing) + i/2; /* mono: consecutive nibbles */
@ -89,26 +140,19 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
(!(channel&1) ? 0:4) :
(!(i&1) ? 0:4); /* even = low, odd = high */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
sample_delta = ((((sample_nibble & 0x7) * 2) | 1) * step_size) >> 3; /* like 'mul' IMA with 'or' */
if (sample_nibble & 8) sample_delta = -sample_delta;
sample_decoded = hist1 + sample_delta;
outbuf[sample_count] = sample_decoded; /* not clamped */
hist1 = outbuf[sample_count];
step_size = (step_size * scale_step_aska[sample_nibble & 0x07]) >> 6;
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample);
outbuf[sample_count] = out_sample;
sample_count += channelspacing;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_size;
}
/* Yamaha ADPCM with unknown expand variation (noisy), step size is double of normal Yamaha? */
void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count, num_frame;
int i, sample_count = 0, num_frame;
int32_t hist1 = stream->adpcm_history1_32;
int step_size = stream->adpcm_step_index;
@ -124,26 +168,27 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_size = read_16bitLE(header_offset+0x02,stream->streamfile);
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
else if (step_size > 0x6000) step_size = 0x6000;
}
/* decode nibbles (layout: all nibbles from one channel) */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_nibble, sample_decoded, sample_delta;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int code, delta, sample;
off_t byte_offset = (stream->offset + 0x40*num_frame + 0x04) + i/2;
int nibble_shift = (i&1?4:0); /* low nibble first */
int nibble_shift = (i&1?4:0); /* low nibble first? */
/* Yamaha expand? */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
sample_delta = (step_size * scale_delta[sample_nibble] / 4) / 8; //todo not ok
sample_decoded = hist1 + sample_delta;
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
delta = (step_size * scale_delta[code]) / 8; //todo wrong
sample = hist1 + delta;
outbuf[sample_count] = clamp16(sample_decoded);
outbuf[sample_count] = clamp16(sample);
hist1 = outbuf[sample_count];
step_size = (step_size * scale_step[sample_nibble]) >> 8;
step_size = (step_size * scale_step_aica[code]) / 260.0; //todo wrong
if (step_size < 0x7f) step_size = 0x7f;
if (step_size > 0x6000) step_size = 0x6000;
else if (step_size > 0x6000) step_size = 0x6000;
sample_count += channelspacing;
}
stream->adpcm_history1_32 = hist1;

View File

@ -676,8 +676,8 @@ static const coding_info coding_info_list[] = {
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_YAMAHA, "Yamaha 4-bit ADPCM"},
{coding_YAMAHA_int, "Yamaha 4-bit ADPCM (mono/interleave)"},
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
{coding_ASKA, "tri-Ace Aska 4-bit ADPCM"},
{coding_NXAP, "Nex NXAP 4-bit ADPCM"},
{coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"},

View File

@ -39,7 +39,7 @@ VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile) {
/* fill in the vital statistics */
switch (samples) {
case 4:
vgmstream->coding_type = coding_YAMAHA_int;
vgmstream->coding_type = coding_AICA_int;
vgmstream->num_samples = read_32bitLE(0x14,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = 0;

View File

@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_dcs_wav(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_DCS_WAV;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = yamaha_bytes_to_samples(get_streamfile_size(streamFile), channel_count);
vgmstream->coding_type = coding_YAMAHA_int;
vgmstream->coding_type = coding_AICA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4000;

View File

@ -16,7 +16,7 @@ typedef enum {
DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM (low nibble first) */
YAMAHA = 10, /* YAMAHA (AICA) ADPCM (Dreamcast games) */
AICA = 10, /* YAMAHA AICA ADPCM (Dreamcast games) */
MSADPCM = 11, /* MS ADPCM (Windows games) */
NGC_DSP = 12, /* NGC DSP (Nintendo games) */
PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */
@ -102,7 +102,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */
#endif
case IMA: coding = coding_IMA; break;
case YAMAHA: coding = coding_YAMAHA; break;
case AICA: coding = coding_AICA; break;
case MSADPCM: coding = coding_MSADPCM; break;
case NGC_DSP: coding = coding_NGC_DSP; break;
case PCM8_U_int: coding = coding_PCM8_U_int; break;
@ -153,7 +153,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_PSX_badflags:
case coding_DVI_IMA:
case coding_IMA:
case coding_YAMAHA:
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
@ -172,8 +172,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
coding = coding_DVI_IMA_int;
if (coding == coding_IMA)
coding = coding_IMA_int;
if (coding == coding_YAMAHA)
coding = coding_YAMAHA_int;
if (coding == coding_AICA)
coding = coding_AICA_int;
}
/* to avoid endless loops */
@ -190,11 +190,11 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
}
/* to avoid problems with dual stereo files (_L+_R) for codecs with stereo modes */
if (coding == coding_YAMAHA && genh.channels == 1)
coding = coding_YAMAHA_int;
if (coding == coding_AICA && genh.channels == 1)
coding = coding_AICA_int;
/* setup adpcm */
if (coding == coding_YAMAHA || coding == coding_YAMAHA_int) {
if (coding == coding_AICA || coding == coding_AICA_int) {
int ch;
for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].adpcm_step_index = 0x7f;

View File

@ -29,7 +29,7 @@ VGMSTREAM * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) {
vgmstream->sample_rate = 44100;
vgmstream->num_samples = yamaha_bytes_to_samples(data_size, channel_count);
vgmstream->coding_type = coding_YAMAHA_int;
vgmstream->coding_type = coding_AICA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = data_size / channel_count;
vgmstream->meta_type = meta_NAOMI_ADPCM;

View File

@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) {
break;
case 0x03: /* standard */
vgmstream->coding_type = coding_YAMAHA_int;
vgmstream->coding_type = coding_AICA_int;
vgmstream->num_samples = yamaha_bytes_to_samples(data_size,channel_count);
vgmstream->loop_start_sample = /*read_32bitLE(0x2c,streamFile) +*/ yamaha_bytes_to_samples(0x2000*channel_count,channel_count);
vgmstream->loop_end_sample = vgmstream->num_samples;

View File

@ -135,10 +135,10 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
}
switch (fmt->codec) {
case 0x00: /* Yamaha ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */
case 0x00: /* Yamaha AICA ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */
if (fmt->bps != 4) goto fail;
if (fmt->block_size != 0x02*fmt->channel_count) goto fail;
fmt->coding_type = coding_YAMAHA_int;
fmt->coding_type = coding_AICA_int;
fmt->interleave = 0x01;
break;
@ -174,9 +174,11 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
fmt->coding_type = coding_MS_IMA;
break;
case 0x20: /* Yamaha ADPCM (raw) [Takuyo/Dynamix/etc DC games] */
case 0x20: /* Yamaha AICA ADPCM (raw) [Takuyo/Dynamix/etc DC games] */
if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_YAMAHA;
fmt->coding_type = coding_AICA;
/* official RIFF spec has 0x20 as 'Yamaha ADPCM', but data is probably not pure AICA
* (maybe with headered frames and would need extra detection?) */
break;
case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */
@ -598,8 +600,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->num_samples = fact_sample_count;
break;
case coding_YAMAHA:
case coding_YAMAHA_int:
case coding_AICA:
case coding_AICA_int:
vgmstream->num_samples = yamaha_bytes_to_samples(data_size, fmt.channel_count);
break;
@ -675,7 +677,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
case coding_YAMAHA:
case coding_AICA:
case coding_XBOX_IMA:
case coding_IMA:
#ifdef VGM_USE_FFMPEG

View File

@ -7,20 +7,24 @@ static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile);
typedef struct {
int big_endian;
uint32_t codec;
int channel_count;
int codec;
int sample_rate;
off_t file_name_offset;
int total_segments;
int target_segment;
off_t segment_offset;
size_t segment_size;
size_t segment_layers_size;
off_t segment_name_offset;
int total_layers;
int target_layer;
off_t layer_offset;
size_t layer_size;
off_t layer_start;
//size_t layer_size;
off_t layer_name_offset;
size_t file_size;
@ -28,14 +32,13 @@ typedef struct {
size_t data_size;
off_t data_offset;
//size_t stream_size;
size_t usable_size;
size_t block_size;
size_t block_size_total;
size_t stream_size_full;
size_t block_layers_size;
off_t coefs_offset;
int use_segment_subsongs; /* otherwise play the whole thing */
char readable_name[STREAM_NAME_SIZE];
} rws_header;
@ -43,7 +46,6 @@ typedef struct {
VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, offset;
off_t stream_offset, name_offset;
size_t stream_size;
int loop_flag;
int i;
@ -56,99 +58,112 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (!check_extensions(streamFile,"rws"))
goto fail;
/* parse chunks (always LE) */
/* RWS are made of a file chunk with header and data chunks (other chunks exist for non-audio .RWS).
* A chunk is: id, size, RW version (no real diffs), data of size (version is repeated but same for all chunks).
* Version: 16b main + 16b build (can vary between files) ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */
/* Audio .RWS is made of file + header + data chunks (non-audio .RWS with other chunks exist).
* Chunk format (LE): id, size, RW version, data of size (version is repeated but same for all chunks).
* Version is 16b main + 16b build (possibly shifted), no known differences between versions,
* and can vary between files of a game. ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */
if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file chunk id */
/* parse audio chunks */
if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file id */
goto fail;
rws.file_size = read_32bitLE(0x04,streamFile); /* audio file chunk size */
rws.file_size = read_32bitLE(0x04, streamFile); /* audio file size */
if (rws.file_size + 0x0c != get_streamfile_size(streamFile))
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header chunk id */
if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header id */
goto fail;
rws.header_size = read_32bitLE(0x10,streamFile); /* header chunk size */
rws.header_size = read_32bitLE(0x10, streamFile); /* header size */
rws.data_offset = 0x0c + 0x0c + rws.header_size; /* usually 0x800 but not always */
if (read_32bitLE(rws.data_offset+0x00,streamFile) != 0x0000080f) /* data chunk id */
if (read_32bitLE(rws.data_offset + 0x00, streamFile) != 0x0000080f) /* data chunk id */
goto fail;
rws.data_size = read_32bitLE(rws.data_offset+0x04,streamFile); /* data chunk size */
rws.data_size = read_32bitLE(rws.data_offset + 0x04, streamFile); /* data chunk size */
if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(streamFile))
goto fail;
/* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */
/* inside header chunk (many unknown fields are probably IDs/config/garbage,
* as two files of the same size vary a lot) */
offset = 0x0c + 0x0c;
rws.big_endian = guess_endianness32bit(offset + 0x00, streamFile); /* GC/Wii/X360 */
read_32bit = rws.big_endian ? read_32bitBE : read_32bitLE;
/* base header */
/* 0x00: actual header size (less than chunk size), 0x04/08/10: sizes of various sections?, 0x14/18/24/2C: commands?
* 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */
read_32bit = (read_32bitLE(offset+0x00,streamFile) > rws.header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */
rws.total_segments = read_32bit(offset+0x20,streamFile);
rws.total_layers = read_32bit(offset+0x28,streamFile);
if (rws.total_segments > 1 && rws.total_layers > 1) {
VGM_LOG("RWS: unknown segments+layers layout\n");
{
/* 0x00: actual header size (less than chunk size) */
/* 0x04/08/10: sizes of various sections? */
/* 0x14/18: config? */
/* 0x1c: null? */
rws.total_segments = read_32bit(offset + 0x20, streamFile);
/* 0x24: config? */
rws.total_layers = read_32bit(offset + 0x28, streamFile);
/* 0x2c: config? */
/* 0x30: 0x800? */
/* 0x34: block_layers_size? */
/* 0x38: data offset */
/* 0x3c: 0? */
/* 0x40-50: file uuid */
offset += 0x50;
}
/* audio file name */
offset += 0x50 + get_rws_string_size(offset+0x50, streamFile);
{
rws.file_name_offset = offset;
offset += get_rws_string_size(offset, streamFile);
}
/* RWS data can be divided in two ways:
* - "substreams" (layers): interleaved blocks, for fake multichannel L/R+C/S+LS/RS [Burnout 2 (GC/Xbox)]
* or song variations [Neighbours From Hell (Xbox/GC)]. Last layer may have padding to keep chunks aligned:
* ex.- 0x1700 data of substream_0 2ch, 0x1700 data + 0x200 pad of substream1 2ch, repeat until end
* - "segments": cues/divisions within data, like intro+main/loop [[Max Payne 2 (PS2), Nana (PS2)]
* or voice1+2+..N, song1+2+..N [Madagascar (PS2), The Legend of Spyro: Dawn of the Dragon (X360)]
* - "streams" (layers): interleaved blocks, like L/R+C/S+LS/RS [Burnout 2 (GC/Xbox)]. Last stream has padding:
* ex.- 1 block: 0x1800 data of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch.
*
* Layers seem only used to fake multichannel, but as they are given sample rate/channel/codec/coefs/etc
* they are treated as subsongs. Similarly segments can be treated as subsongs in some cases.
* They don't seem used at the same time, though could be possible. */
* As each layer is given sample rate/channel/codec/etc they are treated as full subsongs, though usually
* all layers are the same. Segments are just divisions and can be played one after another, but are useful
* to play as subsongs. Since both can exist at the same time (rarely) we make layers*segments=subsongs.
* Ex.- subsong1=layer1 blocks in segment1, subsong2=layer2 blocks in segment1, subsong3=layer1 blocks in segment2, ...
*
* Segment1 Segment2
* +-------------------------------------------+-----------------
* |Layer1|Layer2|(pad)|...|Layer1|Layer2|(pad)|Layer1|Layer2|...
* --------------------------------------------------------------
*/
/* Use either layers or segments as subsongs (with layers having priority). This divides Nana (PS2)
* or Max Payne 2 (PS2) intro+main, so it could be adjusted to >2 (>4 for Max Payne 2) if undesired */
if (target_subsong == 0) target_subsong = 1;
rws.use_segment_subsongs = (rws.total_layers == 1 && rws.total_segments > 1);
if (rws.use_segment_subsongs) {
rws.target_layer = 1;
rws.target_segment = target_subsong;
total_subsongs = rws.total_segments;
}
else {
rws.target_layer = target_subsong;
rws.target_segment = 0; /* none = play all */
total_subsongs = rws.total_layers;
}
rws.target_layer = ((target_subsong-1) % rws.total_layers) + 1;
rws.target_segment = ((target_subsong-1) / rws.total_layers) + 1;
total_subsongs = rws.total_layers * rws.total_segments;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* segment info, for all layers */
/* 0x00/04/0c: command?, 0x18: full segment size (including padding), 0x1c: offset, others: ?) */
/* segment info */
for (i = 0; i < rws.total_segments; i++) {
if (i+1 == rws.target_segment) {
rws.segment_offset = read_32bit(offset + 0x20*i + 0x1c,streamFile);
/* 0x00/04/0c: config? */
/* others: ? */
rws.segment_layers_size = read_32bit(offset + 0x18, streamFile); /* sum of all including padding */
rws.segment_offset = read_32bit(offset + 0x1c, streamFile);
}
rws.stream_size_full += read_32bit(offset + 0x20*i + 0x18,streamFile);
offset += 0x20;
}
offset += 0x20 * rws.total_segments;
/* usable segment/layer sizes (assumed either one, sometimes incorrect size?) */
for (i = 0; i < (rws.total_segments * rws.total_layers); i++) { /* sum usable segment sizes (no padding) */
size_t usable_size = read_32bit(offset + 0x04*i,streamFile); /* size without padding */
if (i+1 == rws.target_segment) {
rws.segment_size = usable_size;
}
if (i+1 == rws.target_layer || rws.total_layers == 1) {
rws.layer_size += usable_size;
/* usable layer sizes per segment */
for (i = 0; i < (rws.total_segments * rws.total_layers); i++) {
size_t usable_size = read_32bit(offset, streamFile); /* without padding */
/* size order: segment1 layer1 size, ..., segment1 layerN size, segment2 layer1 size, etc */
if (i+1 == target_subsong) { /* order matches our subsong order */
rws.usable_size = usable_size;
}
offset += 0x04;
}
offset += 0x04 * (rws.total_segments * rws.total_layers);
/* segment uuids */
offset += 0x10 * rws.total_segments;
{
offset += 0x10 * rws.total_segments;
}
/* segment names */
for (i = 0; i < rws.total_segments; i++) {
@ -158,46 +173,53 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
offset += get_rws_string_size(offset, streamFile);
}
/* layer info */
/* 0x00/04/14: command?, 0x08: null? 0x0c: related to samples per frame? (XADPCM=07, VAG=1C, DSP=0E, PCM=01)
* 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */
for (i = 0; i < rws.total_layers; i++) { /* get block_sizes */
rws.block_size_total += read_32bit(offset + 0x10 + 0x28*i, streamFile); /* for all layers, to skip during decode */
if (i+1 == rws.target_layer) {
//block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */
rws.block_size = read_32bit(offset + 0x20 + 0x28*i, streamFile); /* without padding */
rws.layer_offset = read_32bit(offset + 0x24 + 0x28*i, streamFile); /* within data */
/* 0x00/04: config? */
/* 0x08: null? */
/* 0x0c: related to samples per frame? (XBOX-IMA=07, PSX=1C, DSP=0E, PCM=01) */
//block_size_pad = read_32bit(offset + 0x10, streamFile); /* with padding, can be different per layer */
/* 0x14/18: ? */
/* 0x1c: codec related? */
rws.block_size = read_32bit(offset + 0x20, streamFile); /* without padding */
rws.layer_start = read_32bit(offset + 0x24, streamFile); /* skip data */
}
rws.block_layers_size += read_32bit(offset + 0x10, streamFile); /* needed to skip during decode */
offset += 0x28;
}
offset += 0x28 * rws.total_layers;
/* layer config */
/* 0x04: command?, 0x0c(1): bits per sample, others: null? */
for (i = 0; i < rws.total_layers; i++) { /* size depends on codec so we must parse it */
int prev_codec = 0;
for (i = 0; i < rws.total_layers; i++) {
uint32_t layer_codec = 0;
if (i+1 == rws.target_layer) {
rws.sample_rate = read_32bit(offset+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */
rws.channel_count = read_8bit(offset+0x0d, streamFile);
rws.codec = read_32bitBE(offset+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
rws.sample_rate = read_32bit(offset + 0x00, streamFile);
/* 0x04: config? */
//rws.layer_size = read_32bit(offset + 0x08, streamFile); /* same or close to usable size */
/* 0x0c: bits per sample */
rws.channel_count = read_8bit(offset + 0x0d, streamFile);
/* others: ? */
rws.codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */
}
prev_codec = read_32bitBE(offset+0x1c, streamFile);
layer_codec = (uint32_t)read_32bit(offset + 0x1c, streamFile);
offset += 0x2c;
if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per layer */
/* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */
/* DSP has an extra field per layer */
if (layer_codec == 0xF86215B0) {
/* 0x00: approx num samples? */
/* 0x04: approx size/loop related? (can be 0) */
if (i+1 == rws.target_layer) {
rws.coefs_offset = offset + 0x1c;
}
offset += 0x60;
}
offset += 0x04; /* padding/garbage */
}
/* layer uuids */
offset += 0x10 * rws.total_layers;
{
offset += 0x10 * rws.total_layers;
}
/* layer names */
for (i = 0; i < rws.total_layers; i++) {
@ -207,35 +229,49 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
offset += get_rws_string_size(offset, streamFile);
}
/* rest is padding/garbage until chunk end (may contain strings and weird stuff) */
/* rest is padding/garbage until chunk end (may contain strings and uninitialized memory) */
// ...
start_offset = rws.data_offset + 0x0c + (rws.segment_offset + rws.layer_start);
stream_size = rws.usable_size;
if (rws.use_segment_subsongs) {
stream_offset = rws.segment_offset;
stream_size = rws.segment_size;
name_offset = rws.segment_name_offset;
}
else {
stream_offset = rws.layer_offset;
stream_size = rws.layer_size;
name_offset = rws.layer_name_offset;
}
start_offset = rws.data_offset + 0x0c + stream_offset;
/* sometimes it's wrong in XBOX-IMA for no apparent reason (probably a bug in RWS) */
if (!rws.use_segment_subsongs) {
size_t stream_size_expected = (rws.stream_size_full / rws.block_size_total) * (rws.block_size * rws.total_layers) / rws.total_layers;
if (stream_size > stream_size_expected) {
VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected);
stream_size = stream_size_expected;
/* sometimes segment/layers go over file size in XBOX-IMA for no apparent reason, with usable_size bigger
* than segment_layers_size yet data_size being correct (bug in RWS header? maybe stops decoding on file end) */
{
size_t expected_size = (rws.segment_layers_size / rws.block_layers_size) * (rws.block_size * rws.total_layers) / rws.total_layers;
if (stream_size > expected_size) {
VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, expected_size);
stream_size = expected_size;
}
}
/* build readable name */
{
char base_name[STREAM_NAME_SIZE], file_name[STREAM_NAME_SIZE], segment_name[STREAM_NAME_SIZE], layer_name[STREAM_NAME_SIZE];
loop_flag = 0; /* RWX doesn't seem to include actual looping (so devs may fake it with segments) */
get_streamfile_basename(streamFile, base_name, sizeof(base_name));
/* null terminated */
read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, streamFile);
read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, streamFile);
read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, streamFile);
/* some internal names aren't very interesting and are stuff like "SubStream" */
if (strcmp(base_name, file_name) == 0) {
if (rws.total_layers > 1)
snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s", segment_name, layer_name);
else
snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s", segment_name);
}
else {
if (rws.total_layers > 1)
snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s/%s", file_name, segment_name, layer_name);
else
snprintf(rws.readable_name,STREAM_NAME_SIZE, "%s/%s", file_name, segment_name);
}
}
/* seemingly no actual looping supported (devs may fake it with segments) */
loop_flag = 0;
/* build the VGMSTREAM */
@ -246,25 +282,23 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->sample_rate = rws.sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
strcpy(vgmstream->stream_name, rws.readable_name);
vgmstream->layout_type = layout_blocked_rws;
vgmstream->current_block_size = rws.block_size / vgmstream->channels;
vgmstream->full_block_size = rws.block_size_total;
vgmstream->full_block_size = rws.block_layers_size;
switch(rws.codec) {
case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */
case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */
/* ex. D.i.R.T. - Origin of the Species (PC), The Legend of Spyro (X360) */
case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360 */
/* ex. D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360) */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = (rws.codec == 0xD01BD217); /* X360: BE */
vgmstream->interleave_block_size = 0x02; /* only used to setup channels */
vgmstream->codec_endian = (rws.big_endian);
vgmstream->interleave_block_size = 0x02; /* only to setup channels */
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, rws.channel_count, 16);
break;
case 0x9897EAD9: /* PS-ADPCM PS2 (9897EAD9 BCBB7B44 96B26547 59102E16) */
case 0xD9EA9798: /* {D9EA9798,BBBC,447B,96,B2,65,47,59,10,2E,16} PS-ADPCM PS2 */
/* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */
vgmstream->coding_type = coding_PSX;
vgmstream->interleave_block_size = rws.block_size / 2;
@ -272,21 +306,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channel_count);
break;
case 0xF86215B0: /* DSP GC/Wii (F86215B0 31D54C29 BD37CDBF 9BD10C53) */
case 0xF86215B0: /* {F86215B0,31D5,4C29,BD,37,CD,BF,9B,D1,0C,53} DSP GC/Wii */
/* ex. Burnout 2 (GC), Alice in Wonderland (Wii) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->interleave_block_size = rws.block_size / 2;
/* get coefs (all channels share them so 0 spacing; also seem fixed for all RWS) */
dsp_read_coefs_be(vgmstream,streamFile,rws.coefs_offset, 0);
/* get coefs (all channels share them; also seem fixed for all RWS) */
dsp_read_coefs_be(vgmstream, streamFile, rws.coefs_offset, 0);
vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channel_count);
break;
case 0x936538EF: /* XBOX-IMA PC (936538EF 11B62D43 957FA71A DE44227A) */
case 0x2BA22F63: /* XBOX-IMA Xbox (2BA22F63 DD118F45 AA27A5C3 46E9790E) */
case 0xEF386593: /* {EF386593,B611,432D,95,7F,A7,1A,DE,44,22,7A} XBOX-IMA PC */
case 0x632FA22B: /* {632FA22B,11DD,458F,AA,27,A5,C3,46,E9,79,0E} XBOX-IMA Xbox */
/* ex. Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */
vgmstream->coding_type = coding_XBOX_IMA; /* PC and Xbox share the same data */
vgmstream->coding_type = coding_XBOX_IMA; /* same data though different uuid */
vgmstream->interleave_block_size = 0;
vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channel_count);
@ -298,7 +332,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
@ -308,11 +342,11 @@ fail:
}
/* rws-strings are null-terminated then padded to 0x10 (weirdly the padding contains garbage) */
/* rws-strings are null-terminated then padded to 0x10 (weirdly enough the padding contains garbage) */
static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile) {
int i;
for (i = 0; i < 0x800; i++) { /* 0x800=arbitrary max */
if (read_8bit(offset+i,streamFile) == 0) { /* null terminator */
for (i = 0; i < 255; i++) { /* arbitrary max */
if (read_8bit(offset+i, streamFile) == 0) { /* null terminator */
return i + (0x10 - (i % 0x10)); /* size is padded */
}
}

View File

@ -17,7 +17,7 @@ typedef enum {
DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM (low nibble first) */
YAMAHA = 10, /* YAMAHA (AICA) ADPCM (Dreamcast games) */
AICA = 10, /* YAMAHA AICA ADPCM (Dreamcast games) */
MSADPCM = 11, /* MS ADPCM (Windows games) */
NGC_DSP = 12, /* NGC DSP (Nintendo games) */
PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */
@ -200,7 +200,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */
#endif
case IMA: coding = coding_IMA; break;
case YAMAHA: coding = coding_YAMAHA; break;
case AICA: coding = coding_AICA; break;
case MSADPCM: coding = coding_MSADPCM; break;
case NGC_DSP: coding = coding_NGC_DSP; break;
case PCM8_U_int: coding = coding_PCM8_U_int; break;
@ -264,7 +264,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_PSX_badflags:
case coding_DVI_IMA:
case coding_IMA:
case coding_YAMAHA:
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
@ -283,8 +283,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
coding = coding_DVI_IMA_int;
if (coding == coding_IMA)
coding = coding_IMA_int;
if (coding == coding_YAMAHA)
coding = coding_YAMAHA_int;
if (coding == coding_AICA)
coding = coding_AICA_int;
}
/* to avoid endless loops */
@ -294,7 +294,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
coding == coding_IMA_int ||
coding == coding_DVI_IMA_int ||
coding == coding_SDX2_int ||
coding == coding_YAMAHA_int) ) {
coding == coding_AICA_int) ) {
goto fail;
}
} else {
@ -302,11 +302,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
}
/* to avoid problems with dual stereo files (_L+_R) for codecs with stereo modes */
if (coding == coding_YAMAHA && txth.channels == 1)
coding = coding_YAMAHA_int;
if (coding == coding_AICA && txth.channels == 1)
coding = coding_AICA_int;
/* setup adpcm */
if (coding == coding_YAMAHA || coding == coding_YAMAHA_int) {
if (coding == coding_AICA || coding == coding_AICA_int) {
int ch;
for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].adpcm_step_index = 0x7f;
@ -813,8 +813,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else if (is_string(val,"DVI_IMA")) txth->codec = DVI_IMA;
else if (is_string(val,"MPEG")) txth->codec = MPEG;
else if (is_string(val,"IMA")) txth->codec = IMA;
else if (is_string(val,"YAMAHA")) txth->codec = YAMAHA;
else if (is_string(val,"AICA")) txth->codec = YAMAHA;
else if (is_string(val,"AICA")) txth->codec = AICA;
else if (is_string(val,"MSADPCM")) txth->codec = MSADPCM;
else if (is_string(val,"NGC_DSP")) txth->codec = NGC_DSP;
else if (is_string(val,"DSP")) txth->codec = NGC_DSP;
@ -1302,7 +1301,7 @@ static int is_string_match(const char * text, const char * pattern) {
int t_pos = 0, p_pos = 0;
int p_size, t_size;
uint16_t p_char, t_char;
;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern);
//;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern);
/* compares 2 strings (case insensitive, to a point) allowing wildcards
* ex. for "test": match = "Test*", "*est", "*teSt","T*ES*T"; fail = "tst", "teest"
@ -1321,7 +1320,7 @@ static int is_string_match(const char * text, const char * pattern) {
while(text[t_pos] != '\0') {
t_char = get_string_wchar(text, t_pos, &t_size);
;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) );
//;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) );
if (t_char == p_char)
break;
@ -1704,7 +1703,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case IMA:
case DVI_IMA:
return ima_bytes_to_samples(bytes, txth->channels);
case YAMAHA:
case AICA:
return yamaha_bytes_to_samples(bytes, txth->channels);
case PCFX:
case OKI16:

View File

@ -83,13 +83,18 @@ typedef struct {
int config_ignore_fade;
int sample_rate;
int loop_install;
int loop_install_set;
int loop_end_max;
double loop_start_second;
int32_t loop_start_sample;
double loop_end_second;
int32_t loop_end_sample;
int trim_set;
double trim_second;
int32_t trim_sample;
} txtp_entry;
@ -442,7 +447,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
if (current->sample_rate > 0)
vgmstream->sample_rate = current->sample_rate;
if (current->loop_install) {
if (current->loop_install_set) {
if (current->loop_start_second > 0 || current->loop_end_second > 0) {
current->loop_start_sample = current->loop_start_second * vgmstream->sample_rate;
current->loop_end_sample = current->loop_end_second * vgmstream->sample_rate;
@ -455,9 +460,27 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
current->loop_end_sample = vgmstream->num_samples;
}
vgmstream_force_loop(vgmstream, current->loop_install, current->loop_start_sample, current->loop_end_sample);
vgmstream_force_loop(vgmstream, current->loop_install_set, current->loop_start_sample, current->loop_end_sample);
}
if (current->trim_set) {
if (current->trim_second != 0.0) {
current->trim_sample = current->trim_second * vgmstream->sample_rate;
}
if (current->trim_sample < 0) {
vgmstream->num_samples += current->trim_sample; /* trim from end (add negative) */
}
else if (vgmstream->num_samples > current->trim_sample) {
vgmstream->num_samples = current->trim_sample; /* trim to value */
}
/* readjust after triming if it went over (could check for more edge cases but eh) */
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
}
/* add macro to mixing list */
if (current->channel_mask) {
int ch;
@ -625,7 +648,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
m = sscanf(config, " %i%c%i%n", &temp_i1,&temp_c,&temp_i2,&n);
if (m == 3 && (temp_c == ':' || temp_c == '_')) {
m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n);
if (m != 3 || temp_f1 < 0.0 || temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0)
if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0)
return 0;
*value_f = temp_f1 * 60.0 + temp_f2;
@ -636,7 +659,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
m = sscanf(config, " %i.%i%n", &temp_i1,&temp_i2,&n);
if (m == 2) {
m = sscanf(config, " %lf%n", &temp_f1,&n);
if (m != 1 || temp_f1 < 0.0)
if (m != 1 /*|| temp_f1 < 0.0*/)
return 0;
*value_f = temp_f1;
return n;
@ -896,14 +919,20 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
current->sample_rate = cfg->sample_rate;
}
if (cfg->loop_install) {
current->loop_install = cfg->loop_install;
if (cfg->loop_install_set) {
current->loop_install_set = cfg->loop_install_set;
current->loop_end_max = cfg->loop_end_max;
current->loop_start_sample = cfg->loop_start_sample;
current->loop_start_second = cfg->loop_start_second;
current->loop_end_sample = cfg->loop_end_sample;
current->loop_end_second = cfg->loop_end_second;
}
if (cfg->trim_set) {
current->trim_set = cfg->trim_set;
current->trim_second = cfg->trim_second;
current->trim_sample = cfg->trim_sample;
}
}
static void parse_config(txtp_entry *cfg, char *config) {
@ -1084,12 +1113,17 @@ static void parse_config(txtp_entry *cfg, char *config) {
}
config += n;
cfg->loop_install = 1;
cfg->loop_install_set = 1;
}
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max,
// cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second);
}
else if (strcmp(command,"t") == 0) {
n = get_time(config, &cfg->trim_second, &cfg->trim_sample);
cfg->trim_set = (n > 0);
//;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample);
}
//todo cleanup
else if (strcmp(command,"@volume") == 0) {
txtp_mix_data mix = {0};

View File

@ -47,7 +47,7 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) {
/* checks */
/* .hxd: Rayman Arena (all), PK: Out of Shadows (all)
/* .hxd: Rayman M/Arena (all), PK: Out of Shadows (all)
* .hxc: Rayman 3 (PC), XIII (PC)
* .hx2: Rayman 3 (PS2), XIII (PS2)
* .hxg: Rayman 3 (GC), XIII (GC)
@ -176,6 +176,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = hx->big_endian ? read_16bitBE : read_16bitLE;
off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size;
size_t chunk_size;
int cue_flag = 0;
//todo cleanup/unify common readings
@ -274,10 +275,18 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
/* find "datx" (external) or "data" (internal) also in machine endianness */
if (hx->is_external) {
if (!find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian))
if (find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) {
hx->stream_size = read_32bit(chunk_offset + 0x00, sf);
hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust;
}
else if ((flag_type == 0x01 || flag_type == 0x02) && /* Rayman M (not Arena) uses "data" instead */
find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) {
hx->stream_size = chunk_size;
hx->stream_offset = read_32bit(chunk_offset + 0x00, sf) + stream_adjust;
}
else {
goto fail;
hx->stream_size = read_32bit(chunk_offset + 0x00, sf);
hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust;
}
}
else {
if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian))

View File

@ -11,6 +11,7 @@ struct VGMSTREAM_TAGS {
char val[VGMSTREAM_TAGS_LINE_MAX];
/* file to find tags for */
int targetname_len;
char targetname[VGMSTREAM_TAGS_LINE_MAX];
/* path of targetname */
char targetpath[VGMSTREAM_TAGS_LINE_MAX];
@ -59,17 +60,21 @@ void vgmstream_tags_close(VGMSTREAM_TAGS *tags) {
free(tags);
}
/* Tags are divided in two: "global" @TAGS and "file" %TAGS for target filename. To extract both
* we find the filename's tag "section": (other_filename) ..(#tag section).. (target_filename).
* When a new "other_filename" is found that offset is marked as section_start, and when target_filename
* is found it's marked as section_end. Then we can begin extracting tags within that section, until
* all tags are exhausted. Global tags are extracted while searching, so they always go first, and
* also meaning any tags after the section is found are ignored. */
/* Find next tag and return 1 if found.
*
* Tags can be "global" @TAGS, "command" $TAGS, and "file" %TAGS for a target filename.
* To extract tags we must find either global tags, or the filename's tag "section"
* where tags apply: (# @TAGS ) .. (other_filename) ..(# %TAGS section).. (target_filename).
* When a new "other_filename" is found that offset is marked as section_start, and when
* target_filename is found it's marked as section_end. Then we can begin extracting tags
* within that section, until all tags are exhausted. Global tags are extracted as found,
* so they always go first, also meaning any tags after file's section are ignored.
* Command tags have special meanings and are output after all section tags. */
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
off_t file_size = get_streamfile_size(tagfile);
char currentname[VGMSTREAM_TAGS_LINE_MAX] = {0};
char line[VGMSTREAM_TAGS_LINE_MAX] = {0};
int ok, bytes_read, line_done;
int ok, bytes_read, line_done, n1,n2;
if (!tags)
return 0;
@ -92,7 +97,7 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
/* read lines */
while (tags->offset <= file_size) {
/* no more tags to extract */
/* after section: no more tags to extract */
if (tags->section_found && tags->offset >= tags->section_end) {
/* write extra tags after all regular tags */
@ -163,10 +168,32 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
continue; /* next line */
}
/* find possible filename and section start/end */
ok = sscanf(line, " %[^\r\n] ", currentname);
/* find possible filename and section start/end
* (.m3u seem to allow filenames with whitespaces before, make sure to trim) */
ok = sscanf(line, " %n%[^\r\n]%n ", &n1, currentname, &n2);
if (ok == 1) {
if (strcasecmp(tags->targetname,currentname) == 0) { /* looks ok even for UTF-8 */
int currentname_len = n2 - n1;
int filename_found = 0;
/* we want to find file with the same name (case insensitive), OR a virtual .txtp with
* the filename inside (so 'file.adx' gets tags from 'file.adx#i.txtp', reading
* tags even if we don't open !tags.m3u with virtual .txtp directly) */
/* strcasecmp works ok even for UTF-8 */
if (currentname_len >= tags->targetname_len && /* starts with targetname */
strncasecmp(currentname, tags->targetname, tags->targetname_len) == 0) {
if (currentname_len == tags->targetname_len) { /* exact match */
filename_found = 1;
}
else if (vgmstream_is_virtual_filename(currentname)) { /* ends with .txth */
char c = currentname[tags->targetname_len];
/* tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */
filename_found = (c==' ' || c == '.' || c == '#');
}
}
if (filename_found) {
/* section ok, start would be set before this (or be 0) */
tags->section_end = tags->offset;
tags->section_found = 1;
@ -223,6 +250,7 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) {
tags->targetpath[0] = '\0';
strcpy(tags->targetname, target_filename);
}
tags->targetname_len = strlen(tags->targetname);
}
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {

View File

@ -1214,9 +1214,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
case coding_YAMAHA:
case coding_AICA:
return 1;
case coding_YAMAHA_int:
case coding_AICA_int:
return 2;
case coding_ASKA:
return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels;
@ -1410,8 +1410,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return vgmstream->interleave_block_size;
case coding_WS:
return vgmstream->current_block_size;
case coding_YAMAHA:
case coding_YAMAHA_int:
case coding_AICA:
case coding_AICA_int:
return 0x01;
case coding_ASKA:
case coding_NXAP:
@ -2010,12 +2010,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch);
}
break;
case coding_YAMAHA:
case coding_YAMAHA_int:
case coding_AICA:
case coding_AICA_int:
for (ch = 0; ch < vgmstream->channels; ch++) {
int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_YAMAHA);
int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA);
decode_yamaha(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch,
is_stereo);
}

View File

@ -148,8 +148,8 @@ typedef enum {
coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_YAMAHA, /* Yamaha ADPCM (stereo) */
coding_YAMAHA_int, /* Yamaha ADPCM (mono/interleave) */
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */
coding_ASKA, /* Aska ADPCM */
coding_NXAP, /* NXAP ADPCM */