Add XA 8-bit mode [Micro Machines (CD-i)]

This commit is contained in:
bnnm 2021-02-24 23:40:20 +01:00
parent b3c50be513
commit a2a4a48524
7 changed files with 163 additions and 115 deletions

View File

@ -110,8 +110,8 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
/* xa_decoder */
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, int is_form2);
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8);
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps);
/* ea_xa_decoder */

View File

@ -38,45 +38,57 @@ static const int IK1[4] = { 0, 0, 832, 880 };
* 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.
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
* Official "sound quality level" modes:
* - Level A: 37.8hz, 8-bit
* - Level B: 37.8hz, 4-bit
* - Level C: 18.9hz, 4-bit
*
* 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://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
*/
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
/* data layout (mono):
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
* (sectors handled externally, this decoder only sees N frames)
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
* (repeats may be for error correction, though probably unused)
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
*
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
*
* example:
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
* ...
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
*
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
* ...
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
*/
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) {
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;
int subframes = (is_xa8) ? 4 : 8;
/* data layout (mono):
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
* (handled externally, this decoder only gets frames)
* - a frame ("sound group") is divided into 8 subframes ("sound unit"), with
* subframe headers ("sound parameters") first then subframe nibbles ("sound data")
* - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
* (repeats may be for error correction, though probably unused)
* - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
*
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
*
* example:
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
* ...
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
*/
/* external interleave (fixed size), mono/stereo */
bytes_per_frame = 0x80;
samples_per_frame = 28*8 / channelspacing;
samples_per_frame = 28*subframes / channelspacing;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
@ -84,25 +96,27 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
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),
VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC),
"bad frames at %x\n", (uint32_t)frame_offset);
/* decode subframes */
for (i = 0; i < 8 / channelspacing; i++) {
for (i = 0; i < subframes / channelspacing; i++) {
int32_t coef1, coef2;
uint8_t coef_index, shift_factor;
/* parse current subframe (sound unit)'s header (sound parameters) */
sp_pos = 0x04 + i*channelspacing + channel;
sp_pos = is_xa8 ?
i*channelspacing + channel:
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)frame_offset + sp_pos);
/* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */
VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 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 */
if (shift_factor > (is_xa8 ? 8 : 12))
shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
coef1 = IK0[coef_index];
coef2 = IK1[coef_index];
@ -110,15 +124,7 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
/* decode subframe nibbles */
for(j = 0; j < 28; j++) {
uint8_t nibbles;
int32_t new_sample;
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) */
int32_t sample;
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
@ -126,22 +132,39 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
continue;
}
nibbles = frame[su_pos];
if (is_xa8) {
int su_pos = (channelspacing==1) ?
0x10 + j*0x04 + i : /* mono */
0x10 + j*0x04 + i*2 + channel; /* stereo */
new_sample = get_high_nibble ?
(nibbles >> 4) & 0x0f :
(nibbles >> 0) & 0x0f;
sample = frame[su_pos];
sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */
}
else {
uint8_t nibbles;
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) */
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
new_sample = new_sample << 4;
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
nibbles = frame[su_pos];
sample = get_high_nibble ?
(nibbles >> 4) & 0x0f :
(nibbles >> 0) & 0x0f;
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
}
sample = sample << 4; /* scale for current IK */
sample = sample - ((coef1*hist1 + coef2*hist2) >> 10);
hist2 = hist1;
hist1 = new_sample; /* must go before clamp, somehow */
new_sample = new_sample >> 4;
new_sample = clamp16(new_sample);
hist1 = sample; /* must go before clamp, somehow */
sample = sample >> 4;
sample = clamp16(sample);
outbuf[samples_done * channelspacing] = new_sample;
outbuf[samples_done * channelspacing] = sample;
samples_done++;
sample_count++;
@ -152,11 +175,13 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
stream->adpcm_history2_32 = hist2;
}
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) {
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps) {
int subframes = (bps == 8) ? 4 : 8;
if (is_blocked) {
return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16);
return (bytes / 0x930) * (28*subframes/ channels) * (is_form2 ? 18 : 16);
}
else {
return (bytes / 0x80) * (28*8 / channels);
return (bytes / 0x80) * (28*subframes / channels);
}
}

View File

@ -439,6 +439,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_XA:
return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */
case coding_XA8:
return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */
case coding_PSX:
case coding_PSX_badflags:
case coding_HEVAG:
@ -649,6 +651,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
return 0x00; /* variable (block-controlled) */
case coding_XA:
case coding_XA8:
return 0x80;
case coding_PSX:
case coding_PSX_badflags:
@ -985,11 +988,14 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
}
break;
case coding_XA:
case coding_XA8: {
int is_xa8 = (vgmstream->coding_type == coding_XA8);
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_xa(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8);
}
break;
}
case coding_EA_XA:
case coding_EA_XA_int: {
int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_EA_XA);

View File

@ -194,6 +194,7 @@ static const char* extension_list[] = {
"genh",
"gin",
"gms",
"grn",
"gsb",
"gsf",
"gtd",
@ -717,6 +718,7 @@ static const coding_info coding_info_list[] = {
{coding_G721, "CCITT G.721 4-bit ADPCM"},
{coding_XA, "CD-ROM XA 4-bit ADPCM"},
{coding_XA8, "CD-ROM XA 8-bit ADPCM"},
{coding_PSX, "Playstation 4-bit ADPCM"},
{coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"},
{coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"},

View File

@ -3,12 +3,12 @@
#include "../vgmstream.h"
/* parse a CD-XA raw mode2/form2 sector */
void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
void block_update_xa(off_t block_offset, VGMSTREAM* vgmstream) {
STREAMFILE* sf = vgmstream->ch[0].streamfile;
int i, is_audio;
size_t block_samples;
uint16_t xa_config, target_config;
uint8_t xa_submode;
uint8_t xa_submode, xa_header;
/* XA mode2/form2 sector, size 0x930
@ -22,8 +22,29 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
* 0x930: end
* Sectors with no data may exist near other with data
*/
xa_config = read_u16be(block_offset + 0x10, streamFile);
target_config = vgmstream->codec_config;
xa_config = read_u16be(block_offset + 0x10, sf); /* file + channel */
/* submode flag bits (typical audio value = 0x64 01100100)
* - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end)
* - 6 (0x40 01000000): real time sector (special control flag)
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
* - 4 (0x10 00010000): trigger (generates interrupt for the application)
* - 3 (0x08 00001000): data sector
* - 2 (0x04 00000100): audio sector
* - 1 (0x02 00000010): video sector
* - 0 (0x01 00000001): end of audio (optional for non-real time XAs)
* Empty sectors with no flags may exist interleaved with other with audio/data.
*/
xa_submode = read_u8(block_offset + 0x12, sf);
/* header bits:
* - 7 (0x80 10000000): reserved
* - 6 (0x40 01000000): emphasis (applies filter, same as CD-DA emphasis)
* - 4 (0x30 00110000): bits per sample (0=4-bit, 1=8-bit)
* - 2 (0x0C 00001100): sample rate (0=37.8hz, 1=18.9hz)
* - 0 (0x03 00000011): channels (0=mono, 1=stereo)
*/
xa_header = read_u8(block_offset + 0x13, sf);
/* Sector subheader's file+channel markers are used to interleave streams (music/sfx/voices)
* by reading one target file+channel while ignoring the rest. This is needed to adjust
@ -38,36 +59,23 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
* Extractors deinterleave and split .xa using file + channel + EOF flags.
* 'Channel' here doesn't mean "audio channel", just a fancy name for substreams (mono or stereo).
* Files can go up to 255, normally file 0=sequential, 1+=interleaved */
/* submode flag bits (typical audio value = 0x64 01100100)
* - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end)
* - 6 (0x40 01000000): real time sector (special control flag)
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
* - 4 (0x10 00010000): trigger (generates interrupt for the application)
* - 3 (0x08 00001000): data sector
* - 2 (0x04 00000100): audio sector
* - 1 (0x02 00000010): video sector
* - 0 (0x01 00000001): end of audio (optional for non-real time XAs)
* Empty sectors with no flags may exist interleaved with other with audio/data.
*/
xa_submode = read_u8(block_offset + 0x12,streamFile);
target_config = vgmstream->codec_config;
/* audio sector must set/not set certain flags, as per spec (in theory form2 only too) */
is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (xa_config != target_config) {
block_samples = 0; /* not a target sector */
}
else if (is_audio) {
int subframes = ((xa_header >> 4) & 3) == 1 ? 4 : 8; /* 8-bit mode = 4 subframes */
if (xa_submode & 0x20) {
/* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
block_samples = (28*8 / vgmstream->channels) * 18;
/* form2 audio: size 0x900, 18 frames of size 0x80 with N subframes of 28 samples */
block_samples = (28*subframes / vgmstream->channels) * 18;
}
else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */
/* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */
block_samples = (28*8 / vgmstream->channels) * 16;
/* form1 audio: size 0x800, 16 frames of size 0x80 with N subframes of 28 samples (rest is garbage/other data) */
block_samples = (28*subframes / vgmstream->channels) * 16;
}
}
else {

View File

@ -3,17 +3,17 @@
#include "../coding/coding.h"
static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2);
static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked);
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2);
static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked);
/* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count, sample_rate;
int loop_flag = 0, channels, sample_rate, bps;
int is_riff = 0, is_blocked = 0, is_form2 = 0;
size_t stream_size = 0;
int total_subsongs = 0, target_subsong = streamFile->stream_index;
int total_subsongs = 0, target_subsong = sf->stream_index;
uint16_t target_config = 0;
@ -23,26 +23,27 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
* .str: often videos and sometimes speech/music
* .adp: Phantasy Star Collection (SAT) raw XA
* .pxa: Mortal Kombat 4 (PS1)
* .grn: Micro Machines (CDi)
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
if (!check_extensions(streamFile,"xa,str,adp,pxa,"))
if (!check_extensions(sf,"xa,str,adp,pxa,grn,"))
goto fail;
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
* Also has minimal support for headerless (ISO 2048 mode1/data) mode. */
/* check RIFF header = raw (optional, added when ripping and not part of the CD data) */
if (read_u32be(0x00,streamFile) == 0x52494646 && /* "RIFF" */
read_u32be(0x08,streamFile) == 0x43445841 && /* "CDXA" */
read_u32be(0x0C,streamFile) == 0x666D7420) { /* "fmt " */
if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */
read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */
read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */
is_blocked = 1;
is_riff = 1;
start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */
}
else {
/* sector sync word = raw */
if (read_u32be(0x00,streamFile) == 0x00FFFFFF &&
read_u32be(0x04,streamFile) == 0xFFFFFFFF &&
read_u32be(0x08,streamFile) == 0xFFFFFF00) {
if (read_u32be(0x00,sf) == 0x00FFFFFF &&
read_u32be(0x04,sf) == 0xFFFFFFFF &&
read_u32be(0x08,sf) == 0xFFFFFF00) {
is_blocked = 1;
start_offset = 0x00;
}
@ -53,26 +54,26 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
}
/* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */
if (!is_riff && !xa_check_format(streamFile, start_offset, is_blocked))
if (!is_riff && !xa_check_format(sf, start_offset, is_blocked))
goto fail;
/* find subsongs as XA can interleave sectors using 'file' and 'channel' makers (see blocked_xa.c) */
if (/*!is_riff &&*/ is_blocked) {
total_subsongs = xa_read_subsongs(streamFile, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2);
total_subsongs = xa_read_subsongs(sf, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2);
if (total_subsongs <= 0) goto fail;
}
else {
stream_size = get_streamfile_size(streamFile) - start_offset;
stream_size = get_streamfile_size(sf) - start_offset;
}
/* data is ok: parse header */
if (is_blocked) {
/* parse 0x18 sector header (also see blocked_xa.c) */
uint8_t xa_header = read_u8(start_offset + 0x13,streamFile);
uint8_t xa_header = read_u8(start_offset + 0x13,sf);
switch((xa_header >> 0) & 3) { /* 0..1: mono/stereo */
case 0: channel_count = 1; break;
case 1: channel_count = 2; break;
case 0: channels = 1; break;
case 1: channels = 2; break;
default: goto fail;
}
switch((xa_header >> 2) & 3) { /* 2..3: sample rate */
@ -80,11 +81,10 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
case 1: sample_rate = 18900; break;
default: goto fail;
}
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4-bit ADPCM, 1=8-bit ADPCM) */
case 0: break;
default: /* PS1 games only do 4-bit */
VGM_LOG("XA: unknown bits per sample found\n");
goto fail;
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample */
case 0: bps = 4; break; /* PS1 games only do 4-bit ADPCM */
case 1: bps = 8; break; /* Micro Machines (CDi) */
default: goto fail;
}
switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */
case 0: break;
@ -101,38 +101,44 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
}
else {
/* headerless */
if (check_extensions(streamFile,"adp")) {
if (check_extensions(sf,"adp")) {
/* Phantasy Star Collection (SAT) raw files */
/* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */
char filename[PATH_LIMIT] = {0};
get_streamfile_filename(streamFile, filename,PATH_LIMIT);
get_streamfile_filename(sf, filename,PATH_LIMIT);
/* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */
if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') {
channel_count = 1;
channels = 1;
sample_rate = 22050;
}
else {
channel_count = 2;
channels = 2;
sample_rate = 44100;
}
bps = 4;
}
else {
/* incorrectly ripped standard XA */
channel_count = 2;
channels = 2;
sample_rate = 37800;
bps = 4;
}
}
/* untested */
if (bps == 8 && channels == 1)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XA;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_XA;
vgmstream->coding_type = bps == 8 ? coding_XA8 : coding_XA;
vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none;
if (is_blocked) {
vgmstream->codec_config = target_config;
@ -144,9 +150,9 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
}
}
vgmstream->num_samples = xa_bytes_to_samples(stream_size, channel_count, is_blocked, is_form2);
vgmstream->num_samples = xa_bytes_to_samples(stream_size, channels, is_blocked, is_form2, bps);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
@ -230,7 +236,7 @@ typedef struct xa_subsong_t {
*
* Bigfiles that paste tons of XA together are slow to parse since we need to read every sector to
* count totals, but XA subsong handling is mainly for educational purposes. */
static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2) {
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2) {
xa_subsong_t *cur_subsong = NULL;
xa_subsong_t subsongs[XA_SUBSONG_MAX] = {0};
const size_t sector_size = 0x930;

View File

@ -102,7 +102,8 @@ typedef enum {
coding_G721, /* CCITT G.721 */
coding_XA, /* CD-ROM XA */
coding_XA, /* CD-ROM XA 4-bit */
coding_XA8, /* CD-ROM XA 8-bit */
coding_PSX, /* Sony PS ADPCM (VAG) */
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */