-
Notifications
You must be signed in to change notification settings - Fork 176
/
ngc_dsp_decoder.c
184 lines (148 loc) · 6.69 KB
/
ngc_dsp_decoder.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include "coding.h"
#include "../util.h"
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
uint8_t frame[0x08] = {0};
off_t frame_offset;
int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
int coef_index, scale, coef1, coef2;
int32_t hist1 = stream->adpcm_history1_16;
int32_t hist2 = stream->adpcm_history2_16;
/* external interleave (fixed size), mono */
bytes_per_frame = 0x08;
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
/* parse frame header */
frame_offset = stream->offset + bytes_per_frame * frames_in;
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
scale = 1 << ((frame[0] >> 0) & 0xf);
coef_index = (frame[0] >> 4) & 0xf;
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs at %x\n", (uint32_t)frame_offset);
//if (coef_index > 8) //todo not correctly clamped in original decoder?
// coef_index = 8;
coef1 = stream->adpcm_coef[coef_index*2 + 0];
coef2 = stream->adpcm_coef[coef_index*2 + 1];
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t sample = 0;
uint8_t nibbles = frame[0x01 + i/2];
sample = i&1 ? /* high nibble first */
get_low_nibble_signed(nibbles) :
get_high_nibble_signed(nibbles);
sample = ((sample * scale) << 11);
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
sample = clamp16(sample);
outbuf[sample_count] = sample;
sample_count += channelspacing;
hist2 = hist1;
hist1 = sample;
}
stream->adpcm_history1_16 = hist1;
stream->adpcm_history2_16 = hist2;
}
/* read from memory rather than a file */
static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * frame) {
int i, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
int coef_index, scale, coef1, coef2;
int32_t hist1 = stream->adpcm_history1_16;
int32_t hist2 = stream->adpcm_history2_16;
/* external interleave (fixed size), mono */
bytes_per_frame = 0x08;
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 14 */
first_sample = first_sample % samples_per_frame;
VGM_ASSERT_ONCE(samples_to_do > samples_per_frame, "DSP: layout error, too many samples\n");
/* parse frame header */
scale = 1 << ((frame[0] >> 0) & 0xf);
coef_index = (frame[0] >> 4) & 0xf;
VGM_ASSERT_ONCE(coef_index > 8, "DSP: incorrect coefs\n");
//if (coef_index > 8) //todo not correctly clamped in original decoder?
// coef_index = 8;
coef1 = stream->adpcm_coef[coef_index*2 + 0];
coef2 = stream->adpcm_coef[coef_index*2 + 1];
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t sample = 0;
uint8_t nibbles = frame[0x01 + i/2];
sample = i&1 ?
get_low_nibble_signed(nibbles) :
get_high_nibble_signed(nibbles);
sample = ((sample * scale) << 11);
sample = (sample + 1024 + coef1*hist1 + coef2*hist2) >> 11;
sample = clamp16(sample);
outbuf[sample_count] = sample;
sample_count += channelspacing;
hist2 = hist1;
hist1 = sample;
}
stream->adpcm_history1_16 = hist1;
stream->adpcm_history2_16 = hist2;
}
/* decode DSP with byte-interleaved frames (ex. 0x08: 1122112211221122) */
void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave) {
uint8_t frame[0x08];
int i;
int frames_in = first_sample / 14;
for (i = 0; i < 0x08; i++) {
/* base + current frame + subint section + subint byte + channel adjust */
frame[i] = read_8bit(
stream->offset
+ frames_in*(0x08*channelspacing)
+ i/interleave * interleave * channelspacing
+ i%interleave
+ interleave * channel, stream->streamfile);
}
decode_ngc_dsp_subint_internal(stream, outbuf, channelspacing, first_sample, samples_to_do, frame);
}
/*
* The original DSP spec uses nibble counts for loop points, and some
* variants don't have a proper sample count, so we (who are interested
* in sample counts) need to do this conversion occasionally.
*/
int32_t dsp_nibbles_to_samples(int32_t nibbles) {
int32_t whole_frames = nibbles/16;
int32_t remainder = nibbles%16;
if (remainder>0) return whole_frames*14+remainder-2;
else return whole_frames*14;
}
size_t dsp_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
return bytes / channels / 8 * 14;
}
/* reads DSP coefs built in the streamfile */
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 1);
}
void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 0);
}
void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
int ch, i;
/* get ADPCM coefs */
for (ch=0; ch < vgmstream->channels; ch++) {
for (i=0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = be ?
read_16bitBE(offset + ch*spacing + i*2, streamFile) :
read_16bitLE(offset + ch*spacing + i*2, streamFile);
}
}
}
/* reads DSP initial hist built in the streamfile */
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 1);
}
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 0);
}
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
int ch;
/* get ADPCM hist */
for (ch=0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].adpcm_history1_16 = be ?
read_16bitBE(offset + ch*spacing + 0*2, streamFile) :
read_16bitLE(offset + ch*spacing + 0*2, streamFile);;
vgmstream->ch[ch].adpcm_history2_16 = be ?
read_16bitBE(offset + ch*spacing + 1*2, streamFile) :
read_16bitLE(offset + ch*spacing + 1*2, streamFile);;
}
}