working WS decoder

git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@283 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2008-07-04 00:06:51 +00:00
parent 0903e46346
commit e62c8a90cb
5 changed files with 144 additions and 14 deletions

View File

@ -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

View File

@ -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;
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;
}
stream->adpcm_history1_32=hist;
} 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;
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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 */