mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
adding cd-xa support (preliminary)
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@97 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
f39503701a
commit
9e70f0ec5c
65
src/coding/xa_decoder.c
Normal file
65
src/coding/xa_decoder.c
Normal file
@ -0,0 +1,65 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
const int SH = 4;
|
||||
const int SHC = 10;
|
||||
|
||||
double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125};
|
||||
double K1[4] = { 0.0, 0.0, -0.8125,-0.859375};
|
||||
|
||||
int IK0(int fid)
|
||||
{ return ((int)((-K0[fid]) * (1 << SHC))); }
|
||||
|
||||
int IK1(int fid)
|
||||
{ return ((int)((-K1[fid]) * (1 << SHC))); }
|
||||
|
||||
int CLAMP(int value, int Minim, int Maxim)
|
||||
{
|
||||
if (value < Minim) value = Minim;
|
||||
if (value > Maxim) value = Maxim;
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint8_t get_high_nibble=1;
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
|
||||
int predict_nr, shift_factor, sample;
|
||||
int32_t hist1=stream->adpcm_history1_32;
|
||||
int32_t hist2=stream->adpcm_history2_32;
|
||||
int HeadTable[8]={0,2,8,10};
|
||||
|
||||
short scale;
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
|
||||
int framesin = first_sample / 28;
|
||||
|
||||
get_high_nibble=!get_high_nibble;
|
||||
|
||||
predict_nr = read_8bit(stream->offset+HeadTable[framesin]+get_high_nibble,stream->streamfile) >> 4;
|
||||
shift_factor = read_8bit(stream->offset+HeadTable[framesin]+get_high_nibble,stream->streamfile) & 0xf;
|
||||
|
||||
first_sample = first_sample % 28;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
short sample_byte = (short)read_8bit(stream->offset+16+framesin+(i*4),stream->streamfile);
|
||||
|
||||
scale = ((get_high_nibble ?
|
||||
sample_byte >> 4 :
|
||||
sample_byte & 0x0f)<<12);
|
||||
|
||||
sample = (short)(scale & 0xf000) >> shift_factor;
|
||||
sample <<= SH;
|
||||
sample -= (IK0(predict_nr) * hist1 + (IK1(predict_nr) * hist2)) >> SHC;
|
||||
|
||||
hist2=hist1;
|
||||
hist1=sample;
|
||||
|
||||
sample = CLAMP(sample, -32768 << SH, 32767 << SH);
|
||||
outbuf[sample_count] = (short)(sample >> SH);
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32=hist1;
|
||||
stream->adpcm_history2_32=hist2;
|
||||
}
|
@ -48,6 +48,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
|
||||
else
|
||||
vgmstream->current_block_offset=-1;
|
||||
break;
|
||||
case layout_xa_blocked:
|
||||
xa_block_update(vgmstream->next_block_offset,vgmstream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
47
src/layout/xa_blocked.c
Normal file
47
src/layout/xa_blocked.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "layout.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* set up for the block at the given offset */
|
||||
void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
|
||||
int i;
|
||||
uint8_t currentChannel=0;
|
||||
uint8_t subAudio=0;
|
||||
|
||||
// i used interleave_block_size to check sector read length
|
||||
if(vgmstream->samples_into_block!=0)
|
||||
// don't change this variable in the init process
|
||||
vgmstream->interleave_block_size+=128;
|
||||
|
||||
// We get to the end of a sector ?
|
||||
if(vgmstream->interleave_block_size==(18*128)) {
|
||||
vgmstream->interleave_block_size=0;
|
||||
|
||||
// 0x30 of unused bytes/sector :(
|
||||
block_offset+=0x30;
|
||||
begin:
|
||||
// Search for selected channel & valid audio
|
||||
currentChannel=read_8bit(block_offset-7,vgmstream->ch[0].streamfile);
|
||||
subAudio=read_8bit(block_offset-6,vgmstream->ch[0].streamfile);
|
||||
|
||||
// audio is coded as 0x64
|
||||
if((subAudio!=0x64) || (currentChannel!=vgmstream->xa_channel)) {
|
||||
// go to next sector
|
||||
block_offset+=2352;
|
||||
if(currentChannel!=-1) goto begin;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
|
||||
// Quid : how to stop the current channel ???
|
||||
// i set up 0 to current_block_size to make vgmstream not playing bad samples
|
||||
// another way to do it ???
|
||||
// (as the number of samples can be false in cd-xa due to multi-channels)
|
||||
vgmstream->current_block_size = (currentChannel==-1?0:112);
|
||||
|
||||
vgmstream->next_block_offset = vgmstream->current_block_offset+128;
|
||||
for (i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].offset = vgmstream->current_block_offset;
|
||||
}
|
||||
}
|
@ -250,6 +250,10 @@
|
||||
RelativePath=".\meta\ps2_npsf.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\psx_cdxa.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\rs03.c"
|
||||
>
|
||||
@ -314,6 +318,10 @@
|
||||
RelativePath=".\coding\psx_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\xa_decoder.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
@ -350,6 +358,10 @@
|
||||
RelativePath=".\layout\nolayout.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\xa_blocked.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
</Files>
|
||||
|
@ -35,4 +35,6 @@ VGMSTREAM * init_vgmstream_rsf(const char * const filename);
|
||||
|
||||
VGMSTREAM * init_vgmstream_rwsd(const char * const filename);
|
||||
|
||||
VGMSTREAM * init_vgmstream_cdxa(const char * const filename);
|
||||
|
||||
#endif
|
||||
|
106
src/meta/psx_cdxa.c
Normal file
106
src/meta/psx_cdxa.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* Sony PSX CD-XA */
|
||||
/* No looped file ! */
|
||||
|
||||
uint8_t AUDIO_CODING_GET_STEREO(uint8_t value) {
|
||||
return (uint8_t)(value & 3);
|
||||
}
|
||||
|
||||
uint8_t AUDIO_CODING_GET_FREQ(uint8_t value) {
|
||||
return (uint8_t)((value >> 2) & 3);
|
||||
}
|
||||
|
||||
VGMSTREAM * init_vgmstream_cdxa(const char * const filename) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * infile = NULL;
|
||||
|
||||
int channel_count;
|
||||
uint8_t bCoding;
|
||||
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (strcasecmp("xa",filename_extension(filename))) goto fail;
|
||||
|
||||
/* try to open the file for header reading */
|
||||
infile = open_streamfile(filename);
|
||||
if (!infile) goto fail;
|
||||
|
||||
/* check RIFF Header */
|
||||
if (!((read_32bitBE(0x00,infile) == 0x52494646) &&
|
||||
(read_32bitBE(0x08,infile) == 0x43445841) &&
|
||||
(read_32bitBE(0x0C,infile) == 0x666D7420)))
|
||||
goto fail;
|
||||
|
||||
bCoding = read_8bit(0x3F,infile);
|
||||
|
||||
switch (AUDIO_CODING_GET_STEREO(bCoding)) {
|
||||
case 0: channel_count = 1; break;
|
||||
case 1: channel_count = 2; break;
|
||||
default: channel_count = 0; break;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
|
||||
switch (AUDIO_CODING_GET_FREQ(bCoding)) {
|
||||
case 0: vgmstream->sample_rate = 37800; break;
|
||||
case 1: vgmstream->sample_rate = 19800; break;
|
||||
default: vgmstream->sample_rate = 0; break;
|
||||
}
|
||||
|
||||
/* Check for Compression Scheme */
|
||||
vgmstream->coding_type = coding_XA;
|
||||
vgmstream->num_samples = (int32_t)((((get_streamfile_size(infile) - 0x3C)/2352)*0x1F80)/(2*channel_count));
|
||||
|
||||
vgmstream->layout_type = layout_xa_blocked;
|
||||
vgmstream->meta_type = meta_PSX_XA;
|
||||
|
||||
close_streamfile(infile); infile=NULL;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = open_streamfile_buffer(filename,0x8000);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
xa_block_update(init_xa_channel(0),vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (infile) close_streamfile(infile);
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// in extension, we can use this fonction to set the channel
|
||||
// we want to listen to ...
|
||||
off_t init_xa_channel(int channel,VGMSTREAM* vgmstream) {
|
||||
|
||||
off_t block_offset=0x44;
|
||||
|
||||
uint8_t currentChannel;
|
||||
uint8_t subAudio;
|
||||
|
||||
vgmstream->xa_channel=channel;
|
||||
|
||||
begin:
|
||||
currentChannel=read_8bit(block_offset-7,vgmstream->ch[0].streamfile);
|
||||
subAudio=read_8bit(block_offset-6,vgmstream->ch[0].streamfile);
|
||||
if ((currentChannel!=channel) && (subAudio==0x64)) {
|
||||
block_offset+=2352;
|
||||
goto begin;
|
||||
}
|
||||
return block_offset;
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
* List of functions that will recognize files. These should correspond pretty
|
||||
* directly to the metadata types
|
||||
*/
|
||||
#define INIT_VGMSTREAM_FCNS 16
|
||||
#define INIT_VGMSTREAM_FCNS 17
|
||||
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
||||
init_vgmstream_adx,
|
||||
init_vgmstream_brstm,
|
||||
@ -33,6 +33,7 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
||||
init_vgmstream_ps2_ads,
|
||||
init_vgmstream_ps2_npsf,
|
||||
init_vgmstream_rwsd,
|
||||
init_vgmstream_cdxa,
|
||||
};
|
||||
|
||||
|
||||
@ -153,6 +154,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
||||
break;
|
||||
case layout_ast_blocked:
|
||||
case layout_halpst_blocked:
|
||||
case layout_xa_blocked:
|
||||
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
}
|
||||
@ -177,6 +179,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_NGC_AFC:
|
||||
return 16;
|
||||
case coding_PSX:
|
||||
case coding_XA:
|
||||
return 28;
|
||||
default:
|
||||
return 0;
|
||||
@ -213,6 +216,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
return 9;
|
||||
case coding_PSX:
|
||||
return 16;
|
||||
case coding_XA:
|
||||
return 28;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -302,6 +307,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_XA:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,6 +468,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
snprintf(temp,TEMPSIZE,"Gamecube \"AFC\" 4-bit ADPCM");
|
||||
break;
|
||||
case coding_PSX:
|
||||
case coding_XA:
|
||||
snprintf(temp,TEMPSIZE,"Playstation 4-bit ADPCM");
|
||||
break;
|
||||
default:
|
||||
@ -484,6 +497,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
break;
|
||||
case layout_halpst_blocked:
|
||||
snprintf(temp,TEMPSIZE,"HALPST blocked");
|
||||
break;
|
||||
case layout_xa_blocked:
|
||||
snprintf(temp,TEMPSIZE,"CD-XA");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
|
||||
@ -563,6 +579,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case meta_RWSD:
|
||||
snprintf(temp,TEMPSIZE,"Nintendo RWSD header (single stream)");
|
||||
break;
|
||||
case meta_PSX_XA:
|
||||
snprintf(temp,TEMPSIZE,"RIFF/CDXA Header");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ typedef enum {
|
||||
coding_G721, /* CCITT G.721 ADPCM */
|
||||
coding_NGC_AFC, /* NGC ADPCM, called AFC */
|
||||
coding_PSX, /* PSX & PS2 ADPCM */
|
||||
coding_XA, /* PSX CD-XA */
|
||||
} coding_t;
|
||||
|
||||
/* The layout type specifies how the sound data is laid out in the file */
|
||||
@ -38,6 +39,7 @@ typedef enum {
|
||||
/* headered blocks */
|
||||
layout_ast_blocked, /* .ast STRM with BLCK blocks*/
|
||||
layout_halpst_blocked, /* blocks with HALPST-format header */
|
||||
layout_xa_blocked,
|
||||
#if 0
|
||||
layout_strm_blocked, /* */
|
||||
#endif
|
||||
@ -74,6 +76,8 @@ typedef enum {
|
||||
|
||||
meta_PS2_SShd, /* .ADS with SShd header */
|
||||
meta_PS2_NPSF, /* Namco Production Sound File */
|
||||
|
||||
meta_PSX_XA,
|
||||
} meta_t;
|
||||
|
||||
typedef struct {
|
||||
@ -151,6 +155,7 @@ typedef struct {
|
||||
size_t loop_block_size; /* saved from current_block_size */
|
||||
off_t loop_next_block_offset; /* saved from next_block_offset */
|
||||
|
||||
uint8_t xa_channel; /* Selected XA Channel */
|
||||
} VGMSTREAM;
|
||||
|
||||
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
|
||||
|
@ -45,7 +45,7 @@ int fade_samples = 0;
|
||||
|
||||
#define EXTENSION_LIST_SIZE 1024
|
||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||
#define EXTENSION_COUNT 15
|
||||
#define EXTENSION_COUNT 16
|
||||
char * extension_list[EXTENSION_COUNT] = {
|
||||
"adx\0ADX Audio File (*.ADX)\0",
|
||||
"afc\0AFC Audio File (*.AFC)\0",
|
||||
@ -62,6 +62,7 @@ char * extension_list[EXTENSION_COUNT] = {
|
||||
"ss2\0PS2 SS2 Audio File (*.SS2)\0",
|
||||
"npsf\0PS2 NPSF Audio File (*.NPSF)\0",
|
||||
"rwsd\0RWSD Audio File (*.RWSD)\0",
|
||||
"xa\0PSX CD-XA File (*.XA)\0",
|
||||
};
|
||||
|
||||
/* stubs, we don't do anything fancy yet */
|
||||
|
Loading…
x
Reference in New Issue
Block a user