mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +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(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_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_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(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_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);
|
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) {
|
static int expand_ulaw(uint8_t ulawbyte) {
|
||||||
int sign, segment, quantization, new_sample;
|
int sign, segment, quantization, new_sample;
|
||||||
const int bias = 0x84;
|
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) {
|
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_PCM16LE, "Little Endian 16-bit PCM"},
|
||||||
{coding_PCM16BE, "Big Endian 16-bit PCM"},
|
{coding_PCM16BE, "Big Endian 16-bit PCM"},
|
||||||
{coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"},
|
{coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"},
|
||||||
{coding_PCM8, "8-bit PCM"},
|
{coding_PCM8, "8-bit signed PCM"},
|
||||||
{coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"},
|
{coding_PCM8_int, "8-bit signed PCM with 1 byte interleave (block)"},
|
||||||
{coding_PCM8_U, "8-bit unsigned PCM"},
|
{coding_PCM8_U, "8-bit unsigned PCM"},
|
||||||
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"},
|
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"},
|
||||||
{coding_PCM8_SB, "8-bit PCM with sign bit"},
|
{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, "8-bit u-Law"},
|
||||||
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
|
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
|
||||||
{coding_ALAW, "8-bit a-Law"},
|
{coding_ALAW, "8-bit a-Law"},
|
||||||
|
@ -31,6 +31,8 @@ typedef enum {
|
|||||||
FFMPEG = 22, /* any headered FFmpeg format */
|
FFMPEG = 22, /* any headered FFmpeg format */
|
||||||
AC3 = 23, /* AC3/SPDIF */
|
AC3 = 23, /* AC3/SPDIF */
|
||||||
PCFX = 24, /* PC-FX ADPCM */
|
PCFX = 24, /* PC-FX ADPCM */
|
||||||
|
PCM4 = 25, /* 4bit signed PCM */
|
||||||
|
PCM4_U = 26, /* 4bit unsigned PCM */
|
||||||
} genh_type;
|
} genh_type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -114,6 +116,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
case FFMPEG: coding = coding_FFmpeg; break;
|
case FFMPEG: coding = coding_FFmpeg; break;
|
||||||
#endif
|
#endif
|
||||||
case PCFX: coding = coding_PCFX; break;
|
case PCFX: coding = coding_PCFX; break;
|
||||||
|
case PCM4: coding = coding_PCM4; break;
|
||||||
|
case PCM4_U: coding = coding_PCM4_U; break;
|
||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -137,6 +141,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
case coding_PCM16BE:
|
case coding_PCM16BE:
|
||||||
case coding_PCM8:
|
case coding_PCM8:
|
||||||
case coding_PCM8_U:
|
case coding_PCM8_U:
|
||||||
|
case coding_PCM4:
|
||||||
|
case coding_PCM4_U:
|
||||||
case coding_SDX2:
|
case coding_SDX2:
|
||||||
case coding_PSX:
|
case coding_PSX:
|
||||||
case coding_PSX_badflags:
|
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;
|
break;
|
||||||
|
|
||||||
case coding_PCFX:
|
case coding_PCFX:
|
||||||
|
@ -31,6 +31,8 @@ typedef enum {
|
|||||||
FFMPEG = 22, /* any headered FFmpeg format */
|
FFMPEG = 22, /* any headered FFmpeg format */
|
||||||
AC3 = 23, /* AC3/SPDIF */
|
AC3 = 23, /* AC3/SPDIF */
|
||||||
PCFX = 24, /* PC-FX ADPCM */
|
PCFX = 24, /* PC-FX ADPCM */
|
||||||
|
PCM4 = 25, /* 4bit signed PCM */
|
||||||
|
PCM4_U = 26, /* 4bit unsigned PCM */
|
||||||
} txth_type;
|
} txth_type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -176,6 +178,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
case FFMPEG: coding = coding_FFmpeg; break;
|
case FFMPEG: coding = coding_FFmpeg; break;
|
||||||
#endif
|
#endif
|
||||||
case PCFX: coding = coding_PCFX; break;
|
case PCFX: coding = coding_PCFX; break;
|
||||||
|
case PCM4: coding = coding_PCM4; break;
|
||||||
|
case PCM4_U: coding = coding_PCM4_U; break;
|
||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -212,6 +216,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
case coding_PCM16BE:
|
case coding_PCM16BE:
|
||||||
case coding_PCM8:
|
case coding_PCM8:
|
||||||
case coding_PCM8_U:
|
case coding_PCM8_U:
|
||||||
|
case coding_PCM4:
|
||||||
|
case coding_PCM4_U:
|
||||||
case coding_SDX2:
|
case coding_SDX2:
|
||||||
case coding_PSX:
|
case coding_PSX:
|
||||||
case coding_PSX_badflags:
|
case coding_PSX_badflags:
|
||||||
@ -260,6 +266,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
vgmstream->ch[i].adpcm_step_index = 0x7f;
|
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;
|
break;
|
||||||
|
|
||||||
case coding_PCFX:
|
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,"FFMPEG")) txth->codec = FFMPEG;
|
||||||
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
|
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
|
||||||
else if (0==strcmp(val,"PCFX")) txth->codec = PCFX;
|
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 goto fail;
|
||||||
}
|
}
|
||||||
else if (0==strcmp(key,"codec_mode")) {
|
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_int:
|
||||||
case PCM8_U:
|
case PCM8_U:
|
||||||
return pcm_bytes_to_samples(bytes, txth->channels, 8);
|
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:
|
case MSADPCM:
|
||||||
if (!txth->interleave) return 0;
|
if (!txth->interleave) return 0;
|
||||||
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
|
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);
|
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case coding_ULAW:
|
||||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
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, /* 8-bit PCM, unsigned (0x80 = 0) */
|
||||||
coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */
|
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_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, /* 8-bit u-Law (non-linear PCM) */
|
||||||
coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */
|
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