Added 4-bit PCM decoder

This commit is contained in:
NicknineTheEagle 2019-01-09 22:30:15 +03:00
parent a7f379d370
commit 5190b362a9
7 changed files with 93 additions and 3 deletions

View File

@ -71,6 +71,8 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);

View File

@ -78,6 +78,52 @@ void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
}
}
void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, nibble_shift, is_high_first, is_stereo;
int32_t sample_count;
uint16_t sample;
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 */
sample = (uint16_t)read_8bit(byte_offset, stream->streamfile);
sample = (sample >> nibble_shift) & 0x0F;
outbuf[sample_count] = sample*0x11*0x100;
}
}
void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, nibble_shift, is_high_first, is_stereo;
int32_t sample_count;
uint16_t sample;
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 */
sample = (uint16_t)read_8bit(byte_offset, stream->streamfile);
sample = (sample >> nibble_shift) & 0x0F;
outbuf[sample_count] = sample*0x11*0x100 - 0x8000;
}
}
static int expand_ulaw(uint8_t ulawbyte) {
int sign, segment, quantization, new_sample;
const int bias = 0x84;
@ -175,5 +221,5 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
}
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
return bytes / channels / (bits_per_sample/8);
return (bytes * 8) / channels / bits_per_sample;
}

View File

@ -551,11 +551,13 @@ static const coding_info coding_info_list[] = {
{coding_PCM16LE, "Little Endian 16-bit PCM"},
{coding_PCM16BE, "Big Endian 16-bit PCM"},
{coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"},
{coding_PCM8, "8-bit PCM"},
{coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"},
{coding_PCM8, "8-bit signed PCM"},
{coding_PCM8_int, "8-bit signed PCM with 1 byte interleave (block)"},
{coding_PCM8_U, "8-bit unsigned PCM"},
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"},
{coding_PCM8_SB, "8-bit PCM with sign bit"},
{coding_PCM4, "4-bit signed PCM"},
{coding_PCM4_U, "4-bit unsigned PCM"},
{coding_ULAW, "8-bit u-Law"},
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
{coding_ALAW, "8-bit a-Law"},

View File

@ -31,6 +31,8 @@ typedef enum {
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PCM4 = 25, /* 4bit signed PCM */
PCM4_U = 26, /* 4bit unsigned PCM */
} genh_type;
typedef struct {
@ -114,6 +116,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
case PCM4: coding = coding_PCM4; break;
case PCM4_U: coding = coding_PCM4_U; break;
default:
goto fail;
}
@ -137,6 +141,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_PCM16BE:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCM4:
case coding_PCM4_U:
case coding_SDX2:
case coding_PSX:
case coding_PSX_badflags:
@ -185,6 +191,10 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
}
}
if (coding == coding_PCM4 || coding == coding_PCM4_U) {
/* high nibble or low nibble first */
vgmstream->codec_config = genh.codec_mode;
}
break;
case coding_PCFX:

View File

@ -31,6 +31,8 @@ typedef enum {
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PCM4 = 25, /* 4bit signed PCM */
PCM4_U = 26, /* 4bit unsigned PCM */
} txth_type;
typedef struct {
@ -176,6 +178,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
case PCM4: coding = coding_PCM4; break;
case PCM4_U: coding = coding_PCM4_U; break;
default:
goto fail;
}
@ -212,6 +216,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_PCM16BE:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCM4:
case coding_PCM4_U:
case coding_SDX2:
case coding_PSX:
case coding_PSX_badflags:
@ -260,6 +266,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->ch[i].adpcm_step_index = 0x7f;
}
}
if (coding == coding_PCM4 || coding == coding_PCM4_U) {
/* high nibble or low nibble first */
vgmstream->codec_config = txth.codec_mode;
}
break;
case coding_PCFX:
@ -583,6 +594,8 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
else if (0==strcmp(val,"PCFX")) txth->codec = PCFX;
else if (0==strcmp(val,"PCM4")) txth->codec = PCM4;
else if (0==strcmp(val,"PCM4_U")) txth->codec = PCM4_U;
else goto fail;
}
else if (0==strcmp(key,"codec_mode")) {
@ -913,6 +926,9 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case PCM8_U_int:
case PCM8_U:
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case PCM4:
case PCM4_U:
return pcm_bytes_to_samples(bytes, txth->channels, 4);
case MSADPCM:
if (!txth->interleave) return 0;
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);

View File

@ -1550,6 +1550,18 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
break;
case coding_PCM4:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm4(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch);
}
break;
case coding_PCM4_U:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch);
}
break;
case coding_ULAW:
for (ch = 0; ch < vgmstream->channels; ch++) {

View File

@ -73,6 +73,8 @@ typedef enum {
coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */
coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */
coding_PCM8_SB, /* 8-bit PCM, sign bit (others are 2's complement) */
coding_PCM4, /* 4-bit PCM, signed */
coding_PCM4_U, /* 4-bit PCM, unsigned */
coding_ULAW, /* 8-bit u-Law (non-linear PCM) */
coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */