mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Added 4-bit PCM decoder
This commit is contained in:
parent
a7f379d370
commit
5190b362a9
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"},
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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) */
|
||||
|
Loading…
x
Reference in New Issue
Block a user