mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 06:50:20 +01:00
working WS decoder
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@283 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
0903e46346
commit
e62c8a90cb
@ -39,6 +39,6 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t sa
|
||||
|
||||
void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
void decode_ws(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
#endif
|
||||
|
@ -4,17 +4,143 @@
|
||||
|
||||
/* Westwood Studios ADPCM */
|
||||
|
||||
/* Based on Valery V. Ansimovsky's WS-AUD.txt */
|
||||
/* Based on Valery V. Anisimovsky's WS-AUD.txt */
|
||||
|
||||
void decode_ws(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
static char WSTable2bit[4]={-2,-1,0,1};
|
||||
static char WSTable4bit[16]={-9,-8,-6,-5,-4,-3,-2,-1,
|
||||
0, 1, 2, 3, 4, 5 ,6, 8};
|
||||
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
/* We pass in the VGMSTREAM here, unlike in other codings, because
|
||||
the decoder has to know about the block structure. */
|
||||
void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample,
|
||||
int32_t samples_to_do) {
|
||||
|
||||
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);
|
||||
int16_t hist = stream->adpcm_history1_16;
|
||||
off_t offset = stream->offset;
|
||||
int samples_left_in_frame = stream->samples_left_in_frame;
|
||||
off_t header_off = stream->frame_header_offset;
|
||||
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = 0;
|
||||
}
|
||||
stream->adpcm_history1_32=hist;
|
||||
if (vgmstream->ws_output_size == vgmstream->current_block_size) {
|
||||
/* uncompressed, we just need to convert to 16-bit */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing,offset++) {
|
||||
outbuf[sample_count]=((uint8_t)read_8bit(offset,stream->streamfile)-0x80)*0x100;
|
||||
}
|
||||
} else {
|
||||
if (first_sample == 0) {
|
||||
hist = 0x80;
|
||||
samples_left_in_frame = 0;
|
||||
}
|
||||
/* actually decompress */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; ) {
|
||||
uint8_t header;
|
||||
uint8_t count;
|
||||
|
||||
if (samples_left_in_frame == 0) {
|
||||
header_off = offset;
|
||||
offset++;
|
||||
}
|
||||
|
||||
header = read_8bit(header_off,stream->streamfile);
|
||||
count = header & 0x3f;
|
||||
switch (header>>6) { /* code */
|
||||
case 0: /* 2-bit ADPCM */
|
||||
if (samples_left_in_frame == 0) {
|
||||
samples_left_in_frame = (count + 1)*4;
|
||||
}
|
||||
for (;samples_left_in_frame>0 && /* read this frame */
|
||||
i<first_sample+samples_to_do; /* up to samples_to_do */
|
||||
i++,sample_count+=channelspacing, /* done with writing a sample */
|
||||
samples_left_in_frame--) { /* done with reading a sample */
|
||||
int twobit = ((count + 1)*4-samples_left_in_frame)%4;
|
||||
uint8_t sample;
|
||||
sample = read_8bit(offset,stream->streamfile);
|
||||
sample = (sample >> (twobit*2)) & 0x3;
|
||||
hist += WSTable2bit[sample];
|
||||
if (hist < 0) hist = 0;
|
||||
if (hist > 0xff) hist = 0xff;
|
||||
outbuf[sample_count]=(hist-0x80)*0x100;
|
||||
|
||||
if (twobit == 3)
|
||||
offset++; /* done with that byte */
|
||||
}
|
||||
break;
|
||||
case 1: /* 4-bit ADPCM */
|
||||
if (samples_left_in_frame == 0) {
|
||||
samples_left_in_frame = (count + 1)*2;
|
||||
}
|
||||
for (;samples_left_in_frame>0 && /* read this frame */
|
||||
i<first_sample+samples_to_do; /* up to samples_to_do */
|
||||
i++,sample_count+=channelspacing, /* done with writing a sample */
|
||||
samples_left_in_frame--) { /* done with reading a sample */
|
||||
int nibble = ((count + 1)*4-samples_left_in_frame)%2;
|
||||
uint8_t sample;
|
||||
sample = read_8bit(offset,stream->streamfile);
|
||||
if (nibble == 0)
|
||||
sample &= 0xf;
|
||||
else
|
||||
sample >>= 4;
|
||||
hist += WSTable4bit[sample];
|
||||
if (hist < 0) hist = 0;
|
||||
if (hist > 0xff) hist = 0xff;
|
||||
outbuf[sample_count]=(hist-0x80)*0x100;
|
||||
|
||||
if (nibble == 1)
|
||||
offset++; /* done with that byte */
|
||||
}
|
||||
break;
|
||||
case 2: /* no compression */
|
||||
if (count & 0x20) { /* delta */
|
||||
/* Note no checks against samples_to_do here,
|
||||
at the top of the for loop we can always do at
|
||||
least one sample */
|
||||
/* low 5 bits are a signed delta */
|
||||
if (count & 0x10) {
|
||||
hist -= ((count & 0xf)^0xf) + 1;
|
||||
} else {
|
||||
hist += count & 0xf;
|
||||
}
|
||||
|
||||
/* Valery doesn't specify this, but I will assume */
|
||||
if (hist < 0) hist = 0;
|
||||
if (hist > 0xff) hist = 0xff;
|
||||
|
||||
outbuf[sample_count]=(hist-0x80)*0x100;
|
||||
sample_count+=channelspacing;
|
||||
i++;
|
||||
|
||||
/* just one, and we got it */
|
||||
samples_left_in_frame = 0;
|
||||
} else { /* copy bytes verbatim */
|
||||
if (samples_left_in_frame == 0)
|
||||
samples_left_in_frame=count+1;
|
||||
for (;samples_left_in_frame>0 && /* read this frame */
|
||||
i<first_sample+samples_to_do; /* up to samples_to_do */
|
||||
offset++, /* done with a byte */
|
||||
i++,sample_count+=channelspacing, /* done with writing a sample */
|
||||
samples_left_in_frame--) { /* done with reading a sample */
|
||||
outbuf[sample_count]=((hist=(uint8_t)read_8bit(offset,stream->streamfile))-0x80)*0x100;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: /* RLE */
|
||||
if (samples_left_in_frame == 0)
|
||||
samples_left_in_frame=count+1;
|
||||
for (;samples_left_in_frame>0 && /* read this frame */
|
||||
i<first_sample+samples_to_do; /* up to samples_to_do */
|
||||
i++,sample_count+=channelspacing, /* done with writing a sample */
|
||||
samples_left_in_frame--) { /* done with reading a sample */
|
||||
outbuf[sample_count]=(hist-0x80)*0x100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream->offset = offset;
|
||||
stream->adpcm_history1_16 = hist;
|
||||
stream->samples_left_in_frame = samples_left_in_frame;
|
||||
stream->frame_header_offset = header_off;
|
||||
}
|
||||
|
@ -12,8 +12,7 @@ void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
vgmstream->current_block_size + 8;
|
||||
|
||||
if (vgmstream->coding_type == coding_WS) {
|
||||
/* this only works if the output sample size is 8 bit */
|
||||
vgmstream->ws_output_samples = read_16bitLE(
|
||||
vgmstream->ws_output_size = read_16bitLE(
|
||||
vgmstream->current_block_offset+2,
|
||||
vgmstream->ch[0].streamfile);
|
||||
}
|
||||
|
@ -313,7 +313,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_EAXA:
|
||||
return 28;
|
||||
case coding_WS:
|
||||
return vgmstream->ws_output_samples;
|
||||
/* only works if output sample size is 8 bit, which is always
|
||||
is for WS ADPCM */
|
||||
return vgmstream->ws_output_size;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -508,7 +510,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
break;
|
||||
case coding_WS:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_ws(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
}
|
||||
@ -709,7 +711,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
snprintf(temp,TEMPSIZE,"4-bit IMA ADPCM");
|
||||
break;
|
||||
case coding_WS:
|
||||
snprintf(temp,TEMPSIZE,"Westwood Studios ADPCM");
|
||||
snprintf(temp,TEMPSIZE,"Westwood Studios DPCM");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"CANNOT DECODE");
|
||||
|
@ -156,6 +156,9 @@ typedef struct {
|
||||
off_t channel_start_offset; /* where data for this channel begins */
|
||||
off_t offset; /* current location in the file */
|
||||
|
||||
off_t frame_header_offset; /* offset of the current frame header (for WS) */
|
||||
int samples_left_in_frame; /* for WS */
|
||||
|
||||
/* format specific */
|
||||
|
||||
/* adpcm */
|
||||
@ -229,7 +232,7 @@ typedef struct {
|
||||
uint8_t ea_compression_version;
|
||||
uint8_t ea_platform;
|
||||
|
||||
int32_t ws_output_samples; /* output samples for this block */
|
||||
int32_t ws_output_size; /* output bytes for this block */
|
||||
|
||||
void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user