-
Notifications
You must be signed in to change notification settings - Fork 6
/
audio_decoder.h
188 lines (163 loc) · 8.04 KB
/
audio_decoder.h
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
184
185
186
187
188
//OpenRevolution audio decoders
//Copyright (C) 2020 IC
#pragma once
//dataType will be 1 for disk streaming mode (fileData will be null) so brstm_getblock
//will know to do disk streaming stuff instead of just getting a slice of fileData
void brstm_decode_block(Brstm* brstmi,unsigned long b,unsigned int c,const unsigned char* fileData,bool dataType,int16_t** decodeDest,unsigned long decodeDestOff) {
unsigned long posOffset;
if(brstmi->audio_stream_format == 0) {
posOffset = (brstmi->blocks_size*c);
posOffset+=b*(brstmi->blocks_size*brstmi->num_channels);
} else {
posOffset = 0;
posOffset+=b*(brstmi->blocks_size);
}
unsigned long outputPos = 0; //position in decoded PCM samples output array
unsigned long c_writtensamples = 0;
//Read block
unsigned int currentBlockSize = brstmi->blocks_size;
unsigned int currentBlockSamples = brstmi->blocks_samples;
//Final block
if(b==brstmi->total_blocks-1) {
currentBlockSize = brstmi->final_block_size;
currentBlockSamples = brstmi->final_block_samples;
}
if(brstmi->audio_stream_format != 1) {
if(b>=brstmi->total_blocks-1 && c>0) {
//Go back to the previous position
posOffset-=brstmi->blocks_size*brstmi->num_channels;
//Go to the next block in position of first channel
posOffset+=brstmi->blocks_size*(brstmi->num_channels-c);
//Jump to the correct channel in the final block
posOffset+=brstmi->final_block_size_p*c;
}
}
//Get data from just the current block
//This function exists in the including file (brstm.h)
unsigned char* blockData = brstm_getblock(brstmi, fileData, dataType, brstmi->audio_offset+posOffset, currentBlockSize);
//Decode the audio
if(brstmi->codec == 0) {
//8 bit PCM
if(brstmi->audio_stream_format == 0) {
for(unsigned long sampleIndex=0;sampleIndex<currentBlockSamples;sampleIndex++) {
decodeDest[c][decodeDestOff+(outputPos++)] = ((int16_t)blockData[sampleIndex])*256;
c_writtensamples++;
}
} else if(brstmi->audio_stream_format == 1) {
for(unsigned long sampleIndex=0;sampleIndex<currentBlockSamples;sampleIndex++) {
decodeDest[c][decodeDestOff+(outputPos++)] = ((int16_t)blockData[sampleIndex*brstmi->num_channels+c])*256;
c_writtensamples++;
}
}
} else if(brstmi->codec == 1) {
//16 bit PCM
if(brstmi->audio_stream_format == 0) {
for(unsigned long sampleIndex=0;sampleIndex<currentBlockSamples;sampleIndex++) {
decodeDest[c][decodeDestOff+(outputPos++)] = brstm_getSliceAsInt16Sample(blockData,sampleIndex*2,brstmi->BOM);
c_writtensamples++;
}
} else if(brstmi->audio_stream_format == 1) {
for(unsigned long sampleIndex=0;sampleIndex<currentBlockSamples;sampleIndex++) {
decodeDest[c][decodeDestOff+(outputPos++)] = brstm_getSliceAsInt16Sample(blockData,sampleIndex*2*brstmi->num_channels+(c*2),brstmi->BOM);
c_writtensamples++;
}
}
} else if(brstmi->codec == 2 && brstmi->audio_stream_format != 1) {
//4 bit DSPADPCM (does not support stream format 1)
const unsigned char ps = blockData[0];
const signed int yn1 = brstmi->ADPCM_hsamples_1[c][b], yn2 = brstmi->ADPCM_hsamples_2[c][b];
//Magic adapted from brawllib's ADPCMState.cs
signed int
cps = ps,
cyn1 = yn1,
cyn2 = yn2;
unsigned long dataIndex = 0;
int16_t* coefs = brstmi->ADPCM_coefs[c];
for (unsigned long sampleIndex=0;sampleIndex<currentBlockSamples;) {
long outSample = 0;
if (sampleIndex % 14 == 0) {
cps = blockData[dataIndex++];
}
if ((sampleIndex++ & 1) == 0) {
outSample = blockData[dataIndex] >> 4;
} else {
outSample = blockData[dataIndex++] & 0x0f;
}
if (outSample >= 8) {
outSample -= 16;
}
const long scale = 1 << (cps & 0x0f);
const long cIndex = (cps >> 4) << 1;
outSample = (0x400 + ((scale * outSample) << 11) + coefs[brstm_clamp(cIndex, 0, 15)] * cyn1 + coefs[brstm_clamp(cIndex + 1, 0, 15)] * cyn2) >> 11;
cyn2 = cyn1;
cyn1 = brstm_clamp16(outSample);
decodeDest[c][decodeDestOff+(outputPos++)] = cyn1;
c_writtensamples++;
}
//Overwrite loaded history samples with decoded samples
if(b<brstmi->total_blocks-1) {
brstmi->ADPCM_hsamples_1[c][b+1] = decodeDest[c][decodeDestOff+c_writtensamples-1];
brstmi->ADPCM_hsamples_2[c][b+1] = decodeDest[c][decodeDestOff+c_writtensamples-2];
}
}
posOffset+=brstmi->blocks_size*brstmi->num_channels;
}
//Default decoding into PCM_blockbuffer
void brstm_decode_block(Brstm* brstmi,unsigned long b,const unsigned char* fileData,bool dataType) {
for(unsigned int c=0;c<brstmi->num_channels;c++) {
//Create new array of samples for the current channel
delete[] brstmi->PCM_blockbuffer[c];
brstmi->PCM_blockbuffer[c] = new int16_t[brstmi->blocks_samples];
brstm_decode_block(brstmi,b,c,fileData,dataType,brstmi->PCM_blockbuffer,0);
}
brstmi->PCM_blockbuffer_currentBlock = b;
}
//Standard full audio decoding/writing for readers.
unsigned char brstm_doStandardAudioWrite(Brstm* brstmi, const unsigned char* fileData, signed int debugLevel, uint8_t decodeAudio) {
if(decodeAudio == 0) return 0;
if(decodeAudio == 2 && brstmi->codec != 2) {
if(debugLevel >= 0) std::cout << "Cannot write raw ADPCM data because the codec is not ADPCM.\n";
return 222;
}
unsigned long posOffset = 0;
unsigned long chdatabytes = (brstmi->total_blocks-1) * brstmi->blocks_size + brstmi->final_block_size;
for(unsigned int c=0; c<brstmi->num_channels; c++) {
//Create new array of samples for the current channel
switch(decodeAudio) {
case 1: brstmi->PCM_samples[c] = new int16_t[brstmi->total_samples]; break;
case 2: brstmi->ADPCM_data [c] = new unsigned char[chdatabytes]; break;
}
posOffset = 0 + (brstmi->blocks_size*c);
unsigned long outputPos = 0; //position in PCM samples or ADPCM data output array
for(unsigned long b=0; b<brstmi->total_blocks; b++) {
//Read every block
unsigned int currentBlockSize = brstmi->blocks_size;
//Final block
if(b == brstmi->total_blocks-1) {
currentBlockSize = brstmi->final_block_size;
}
if(b >= brstmi->total_blocks-1 && c > 0) {
//Go back to the previous position
posOffset -= brstmi->blocks_size * brstmi->num_channels;
//Go to the next block in position of first channel
posOffset += brstmi->blocks_size * (brstmi->num_channels-c);
//Jump to the correct channel in the final block
posOffset += brstmi->final_block_size_p * c;
}
if(decodeAudio == 1) {
//Decode audio normally
brstm_decode_block(brstmi, b, c, fileData, 0, brstmi->PCM_samples, b*brstmi->blocks_samples);
}
else if(decodeAudio == 2) {
//Write raw ADPCM data to ADPCM_data
//Get data from just the current block
unsigned char* blockData = brstm_getSlice(fileData, brstmi->audio_offset + posOffset, currentBlockSize);
for(unsigned int i=0; i<currentBlockSize; i++) {
brstmi->ADPCM_data[c][outputPos++] = blockData[i];
}
}
posOffset += brstmi->blocks_size * brstmi->num_channels;
}
}
return 0;
}