2008-05-06 05:35:37 +02:00
|
|
|
#include "coding.h"
|
2008-02-05 02:28:52 +01:00
|
|
|
#include "../util.h"
|
2017-09-24 18:45:39 +02:00
|
|
|
#include <math.h>
|
2008-02-05 02:28:52 +01:00
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-02-05 02:28:52 +01:00
|
|
|
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]=read_16bitLE(stream->offset+i*2,stream->streamfile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-02-05 02:28:52 +01:00
|
|
|
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]=read_16bitBE(stream->offset+i*2,stream->streamfile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) {
|
2018-08-26 16:36:08 +02:00
|
|
|
int i, sample_count;
|
|
|
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
outbuf[sample_count]=read_16bit(stream->offset+i*2*channelspacing,stream->streamfile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm8(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-02-05 02:28:52 +01:00
|
|
|
int i;
|
|
|
|
int32_t sample_count;
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2008-02-05 08:38:00 +01:00
|
|
|
outbuf[sample_count]=read_8bit(stream->offset+i,stream->streamfile)*0x100;
|
2008-02-05 02:28:52 +01:00
|
|
|
}
|
|
|
|
}
|
2008-07-14 21:21:45 +02:00
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-07-14 21:21:45 +02:00
|
|
|
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]=read_8bit(stream->offset+i*channelspacing,stream->streamfile)*0x100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-07-30 22:58:50 +02:00
|
|
|
int i;
|
|
|
|
int32_t sample_count;
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
2018-08-26 16:36:08 +02:00
|
|
|
int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile);
|
|
|
|
outbuf[sample_count] = v*0x100 - 0x8000;
|
2008-07-30 22:58:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2008-11-23 13:21:36 +01:00
|
|
|
int i;
|
|
|
|
int32_t sample_count;
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
int16_t v = (uint8_t)read_8bit(stream->offset+i*channelspacing,stream->streamfile);
|
|
|
|
outbuf[sample_count] = v*0x100 - 0x8000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2009-04-28 18:52:49 +02:00
|
|
|
int i;
|
|
|
|
int32_t sample_count;
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile);
|
2018-08-26 16:36:08 +02:00
|
|
|
if (v&0x80) v = 0-(v&0x7f);
|
|
|
|
outbuf[sample_count] = v*0x100;
|
2008-07-14 21:21:45 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-28 03:43:40 +02:00
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
2019-01-09 20:30:15 +01:00
|
|
|
int i, nibble_shift, is_high_first, is_stereo;
|
|
|
|
int32_t sample_count;
|
2019-01-10 00:54:52 +01:00
|
|
|
int16_t v;
|
2019-01-09 20:30:15 +01:00
|
|
|
off_t byte_offset;
|
|
|
|
|
|
|
|
is_high_first = (vgmstream->codec_config & 1);
|
|
|
|
is_stereo = (vgmstream->channels != 1);
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
byte_offset = is_stereo ?
|
|
|
|
stream->offset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */
|
|
|
|
stream->offset + i/2; /* mono: consecutive nibbles */
|
|
|
|
nibble_shift = is_high_first ?
|
|
|
|
is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */
|
|
|
|
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
|
|
|
|
|
2019-01-10 00:54:52 +01:00
|
|
|
v = (int16_t)read_8bit(byte_offset, stream->streamfile);
|
|
|
|
v = (v >> nibble_shift) & 0x0F;
|
|
|
|
outbuf[sample_count] = v*0x11*0x100;
|
2019-01-09 20:30:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
2019-01-09 20:30:15 +01:00
|
|
|
int i, nibble_shift, is_high_first, is_stereo;
|
|
|
|
int32_t sample_count;
|
2019-01-10 00:54:52 +01:00
|
|
|
int16_t v;
|
2019-01-09 20:30:15 +01:00
|
|
|
off_t byte_offset;
|
|
|
|
|
|
|
|
is_high_first = (vgmstream->codec_config & 1);
|
|
|
|
is_stereo = (vgmstream->channels != 1);
|
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
byte_offset = is_stereo ?
|
|
|
|
stream->offset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */
|
|
|
|
stream->offset + i/2; /* mono: consecutive nibbles */
|
|
|
|
nibble_shift = is_high_first ?
|
|
|
|
is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */
|
|
|
|
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
|
|
|
|
|
2019-01-10 00:54:52 +01:00
|
|
|
v = (int16_t)read_8bit(byte_offset, stream->streamfile);
|
|
|
|
v = (v >> nibble_shift) & 0x0F;
|
|
|
|
outbuf[sample_count] = v*0x11*0x100 - 0x8000;
|
2019-01-09 20:30:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-24 01:39:24 +01:00
|
|
|
static int expand_ulaw(uint8_t ulawbyte) {
|
2019-03-02 19:23:37 +01:00
|
|
|
int sign, segment, quantization, sample;
|
2017-06-09 22:26:09 +02:00
|
|
|
const int bias = 0x84;
|
|
|
|
|
2017-12-24 01:39:24 +01:00
|
|
|
ulawbyte = ~ulawbyte; /* stored in complement */
|
|
|
|
sign = (ulawbyte & 0x80);
|
|
|
|
segment = (ulawbyte & 0x70) >> 4; /* exponent */
|
|
|
|
quantization = ulawbyte & 0x0F; /* mantissa */
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
sample = (quantization << 3) + bias; /* add bias */
|
|
|
|
sample <<= segment;
|
|
|
|
sample = (sign) ? (bias - sample) : (sample - bias); /* remove bias */
|
2017-12-24 01:39:24 +01:00
|
|
|
|
|
|
|
#if 0 // the above follows Sun's implementation, but this works too
|
|
|
|
{
|
|
|
|
static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */
|
|
|
|
new_sample = exp_lut[segment] + (quantization << (segment + 3));
|
|
|
|
if (sign != 0) new_sample = -new_sample;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
return sample;
|
2017-12-24 01:39:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* decodes u-law (ITU G.711 non-linear PCM), from g711.c */
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-12-24 01:39:24 +01:00
|
|
|
int i, sample_count;
|
2017-06-09 22:26:09 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
uint8_t ulawbyte = read_8bit(stream->offset+i,stream->streamfile);
|
2017-12-24 01:39:24 +01:00
|
|
|
outbuf[sample_count] = expand_ulaw(ulawbyte);
|
|
|
|
}
|
|
|
|
}
|
2017-06-09 22:26:09 +02:00
|
|
|
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-12-24 01:39:24 +01:00
|
|
|
int i, sample_count;
|
2017-06-09 22:26:09 +02:00
|
|
|
|
2017-12-24 01:39:24 +01:00
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
uint8_t ulawbyte = read_8bit(stream->offset+i*channelspacing,stream->streamfile);
|
|
|
|
outbuf[sample_count] = expand_ulaw(ulawbyte);
|
|
|
|
}
|
|
|
|
}
|
2017-06-09 22:26:09 +02:00
|
|
|
|
2017-12-24 01:39:24 +01:00
|
|
|
static int expand_alaw(uint8_t alawbyte) {
|
2019-03-02 19:23:37 +01:00
|
|
|
int sign, segment, quantization, sample;
|
2017-12-24 01:39:24 +01:00
|
|
|
|
|
|
|
alawbyte ^= 0x55;
|
|
|
|
sign = (alawbyte & 0x80);
|
|
|
|
segment = (alawbyte & 0x70) >> 4; /* exponent */
|
|
|
|
quantization = alawbyte & 0x0F; /* mantissa */
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
sample = (quantization << 4);
|
2017-12-24 01:39:24 +01:00
|
|
|
switch (segment) {
|
|
|
|
case 0:
|
2019-03-02 19:23:37 +01:00
|
|
|
sample += 8;
|
2017-12-24 01:39:24 +01:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-03-02 19:23:37 +01:00
|
|
|
sample += 0x108;
|
2017-12-24 01:39:24 +01:00
|
|
|
break;
|
|
|
|
default:
|
2019-03-02 19:23:37 +01:00
|
|
|
sample += 0x108;
|
|
|
|
sample <<= segment - 1;
|
2017-12-24 01:39:24 +01:00
|
|
|
break;
|
2017-06-09 22:26:09 +02:00
|
|
|
}
|
2019-03-02 19:23:37 +01:00
|
|
|
sample = (sign) ? sample : -sample;
|
2017-12-24 01:39:24 +01:00
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
return sample;
|
2017-06-09 22:26:09 +02:00
|
|
|
}
|
|
|
|
|
2017-10-08 17:51:54 +02:00
|
|
|
/* decodes a-law (ITU G.711 non-linear PCM), from g711.c */
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_alaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
2017-12-24 01:39:24 +01:00
|
|
|
int i, sample_count;
|
2017-10-08 17:51:54 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
uint8_t alawbyte = read_8bit(stream->offset+i,stream->streamfile);
|
2017-12-24 01:39:24 +01:00
|
|
|
outbuf[sample_count] = expand_alaw(alawbyte);;
|
2017-10-08 17:51:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:23:37 +01:00
|
|
|
void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) {
|
2017-08-18 18:54:21 +02:00
|
|
|
int i, sample_count;
|
2017-08-27 22:17:13 +02:00
|
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
2017-08-18 18:54:21 +02:00
|
|
|
|
|
|
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
|
|
|
uint32_t sample_int = read_32bit(stream->offset+i*4,stream->streamfile);
|
2017-08-27 21:54:43 +02:00
|
|
|
float* sample_float;
|
2017-08-18 18:54:21 +02:00
|
|
|
int sample_pcm;
|
|
|
|
|
2017-08-27 21:54:43 +02:00
|
|
|
sample_float = (float*)&sample_int;
|
2018-03-03 19:07:59 +01:00
|
|
|
sample_pcm = (int)floor((*sample_float) * 32767.f + .5f);
|
2017-08-18 18:54:21 +02:00
|
|
|
|
|
|
|
outbuf[sample_count] = clamp16(sample_pcm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-07 16:23:43 +02:00
|
|
|
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
|
2019-03-11 01:34:36 +01:00
|
|
|
if (channels <= 0 || bits_per_sample <= 0) return 0;
|
2019-02-24 09:52:48 +01:00
|
|
|
return ((int64_t)bytes * 8) / channels / bits_per_sample;
|
2017-04-07 16:23:43 +02:00
|
|
|
}
|