OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
wave.cpp
Go to the documentation of this file.
1#include "soundfont.h"
2#include "wave.h"
3
4#include <Tempest/File>
5#include <Tempest/MemReader>
6#include <Tempest/MemWriter>
7#include <Tempest/Log>
8
9#include <stdexcept>
10#include <memory>
11#include <cassert>
12
13template<class T>
14static T clip(T v,T low,T hi){
15 if(v<low)
16 return low;
17 if(v>hi)
18 return hi;
19 return v;
20 }
21
22using namespace Dx8;
23
25 if(!input.is("LIST"))
26 throw std::runtime_error("not a list");
27 input.readListId("wave");
28 implRead(input);
29 }
30
31Wave::Wave(const char *dbg) {
32 Tempest::RFile fin(dbg);
33 std::vector<uint8_t> v(size_t(fin.size()));
34 fin.read(&v[0],v.size());
35
36 auto input = Dx8::Riff(v.data(),v.size());
37 if(!input.is("RIFF"))
38 throw std::runtime_error("not a list");
39 input.readListId("WAVE");
40 implRead(input);
41 }
42
43Wave::Wave(const int16_t *pcm, size_t count) {
44 wavedata.resize(count*sizeof(int16_t));
45 std::memcpy(&wavedata[0],pcm,wavedata.size());
46
48 wfmt.wChannels = 2;
50 wfmt.dwAvgBytesPerSec = wfmt.dwSamplesPerSec * uint32_t(wfmt.wChannels * sizeof(int16_t));
51 wfmt.wBlockAlign = 2;
53 }
54
55void Wave::implRead(Riff &input) {
56 input.read([this](Riff& c){
57 implParse(c);
58 });
59
61 uint16_t samplesPerBlock=0;
62 std::unique_ptr<int16_t[]> coeffTable;
63 uint16_t nCoefs=0;
64 int errct=0;
65
66 {
67 static const int16_t msAdpcmIcoef[7][2] = {
68 { 256, 0},
69 { 512,-256},
70 { 0, 0},
71 { 192, 64},
72 { 240, 0},
73 { 460,-208},
74 { 392,-232}
75 };
76
77 Tempest::MemReader f(extra.data(),extra.size());
78 f.read(&samplesPerBlock,sizeof(samplesPerBlock));
79 f.read(&nCoefs,sizeof(nCoefs));
80 if(nCoefs<7 || nCoefs>0x100)
81 throw std::runtime_error("invalid MS ADPCM sound");
82 if(extra.size()<size_t(4+4*nCoefs))
83 throw std::runtime_error("invalid MS ADPCM sound");
84 coeffTable.reset(new int16_t[nCoefs*2]);
85 for(size_t i=0; i<2u*nCoefs; i++) {
86 f.read(&coeffTable[i],2);
87 if(i<14)
88 errct += (coeffTable[i] != msAdpcmIcoef[i/2][i%2]);
89 }
90 }
91 if(errct>0)
92 Tempest::Log::i("");
93
94 size_t blockCount = (wavedata.size()+wfmt.wBlockAlign-1) / wfmt.wBlockAlign;
95
96 /* We decode two samples per byte. There will be blockCount headers in the data chunk.
97 * This is enough to know how to calculate the total PCM frame count. */
98 size_t totalBlockHeaderSizeInBytes = blockCount * (6*wfmt.wChannels);
99 size_t totalPCMFrameCount = ((wavedata.size() - totalBlockHeaderSizeInBytes) * 2) / wfmt.wChannels;
100
101 std::vector<uint8_t> src = std::move(wavedata);
102 wavedata.resize(totalPCMFrameCount*wfmt.wChannels*sizeof(int16_t));
103
104 Tempest::MemReader f(src.data(),src.size());
105 decodeAdpcm(f,totalPCMFrameCount,wfmt.wBlockAlign,wfmt.wChannels,reinterpret_cast<int16_t*>(wavedata.data()));
106
108 wfmt.wChannels = 2;
109 //wfmt.dwSamplesPerSec = wavedata.size()/2;
110 wfmt.dwAvgBytesPerSec = wfmt.dwSamplesPerSec * uint32_t(wfmt.wChannels * sizeof(int16_t));
111 wfmt.wBlockAlign = 2;
112 wfmt.wBitsPerSample = 16;
113 }
114 }
115
116void Wave::implParse(Riff& input) {
117 if(input.is("data"))
118 input.read(wavedata);
119 if(input.is("fmt ")) {
120 input.read(&wfmt,sizeof(wfmt));
121 if(input.remaning()>=2){
122 uint16_t cbSize=0;
123 input.read(&cbSize,2);
124 extra.resize(cbSize);
125 if(cbSize>0)
126 input.read(&extra[0],extra.size());
127 }
128 }
129 if(input.is("wsmp")){
130 input.read(&waveSample,sizeof(waveSample));
132 for(auto& i:loop){
133 input.read(&i,sizeof(i));
134 input.skip(i.cbSize-sizeof(i));
135 }
136 }
137 if(input.is("LIST") && input.isListId("INFO")) {
138 info = Info(input);
139 }
140 }
141
142size_t Wave::decodeAdpcm(Tempest::MemReader& rd, const size_t framesToRead,
143 uint16_t blockAlign, uint16_t channels, int16_t* pBufferOut) {
144 size_t total = 0;
145 while(total<framesToRead) {
146 size_t decoded = decodeAdpcmBlock(rd,framesToRead-total,blockAlign,channels,pBufferOut);
147 if(decoded==0)
148 break;
149 total += decoded;
150 pBufferOut += decoded*channels;
151 }
152 return total;
153 }
154
155int32_t Wave::decodeADPCMFrame(AdpcChannel& msadpcm, int32_t nibble) {
156 static const int32_t adaptationTable[] = {
157 230, 230, 230, 230, 307, 409, 512, 614,
158 768, 614, 512, 409, 307, 230, 230, 230
159 };
160
161 static const int32_t coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
162 static const int32_t coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
163
164 int32_t sample=0;
165 sample = ((msadpcm.prevFrames[1] * coeff1Table[msadpcm.predictor]) +
166 (msadpcm.prevFrames[0] * coeff2Table[msadpcm.predictor])) >> 8;
167
168 int32_t nibbleMul = (nibble & 0x08) ? (nibble-16) : nibble;
169 sample += nibbleMul * msadpcm.delta;
170 sample = clip(sample, -32768, 32767);
171
172 msadpcm.delta = (adaptationTable[nibble] * msadpcm.delta) >> 8;
173 if(msadpcm.delta<16)
174 msadpcm.delta = 16;
175
176 msadpcm.prevFrames[0] = msadpcm.prevFrames[1];
177 msadpcm.prevFrames[1] = sample;
178
179 return sample;
180 }
181
182size_t Wave::decodeAdpcmBlock(Tempest::MemReader& rd, const size_t framesToRead,
183 uint16_t blockAlign, uint16_t channels, int16_t* pBufferOut) {
184 size_t totalFramesRead = 0;
185 AdpcState msadpcm = {};
186 size_t bytesRemaining = 0;
187
188 if(channels == 1) {
189 /* Mono. */
190 uint8_t predictor;
191 AdpcHdrMono header;
192 if(rd.read(&predictor,1)!=1 ||
193 rd.read(&header,sizeof(header))!=sizeof(header))
194 return totalFramesRead;
195 bytesRemaining = blockAlign-sizeof(header)-1;
196
197 AdpcChannel& c = msadpcm.channel[0];
198 c.predictor = predictor;
199 c.delta = header.delta;
200 c.prevFrames[1] = header.prevFrames1;
201 c.prevFrames[0] = header.prevFrames0;
202
203 msadpcm.cachedFrames[2] = c.prevFrames[0];
204 msadpcm.cachedFrames[3] = c.prevFrames[1];
205 msadpcm.cachedFrameCount = 2;
206 } else {
207 /* Stereo. */
208 uint8_t predictor[2];
209 AdpcHdrStereo header;
210 if(rd.read(&predictor,2)!=2 ||
211 rd.read(&header,sizeof(header))!=sizeof(header))
212 return totalFramesRead;
213
214 bytesRemaining = blockAlign-sizeof(header)-2;
215
216 AdpcChannel& c0 = msadpcm.channel[0];
217 AdpcChannel& c1 = msadpcm.channel[0];
218
219 c0.predictor = predictor[0];
220 c1.predictor = predictor[1];
221 c0.delta = header.delta[0];
222 c1.delta = header.delta[1];
223 c0.prevFrames[1] = header.prevFrames1[0];
224 c1.prevFrames[1] = header.prevFrames1[1];
225 c0.prevFrames[0] = header.prevFrames0[0];
226 c1.prevFrames[0] = header.prevFrames0[1];
227
228 msadpcm.cachedFrames[0] = c0.prevFrames[0];
229 msadpcm.cachedFrames[1] = c1.prevFrames[0];
230 msadpcm.cachedFrames[2] = c0.prevFrames[1];
231 msadpcm.cachedFrames[3] = c1.prevFrames[1];
232 msadpcm.cachedFrameCount = 2;
233 }
234
235 while(true) {
236 // flush cache
237 while(msadpcm.cachedFrameCount>0 && totalFramesRead<framesToRead) {
238 const int32_t* cache = msadpcm.cachedFrames+MAX_CACHED_FRAMES-(msadpcm.cachedFrameCount*channels);
239 for(uint16_t i=0; i<channels; i++)
240 pBufferOut[i] = int16_t(cache[i]);
241
242 pBufferOut += channels;
243 totalFramesRead += 1;
244 msadpcm.cachedFrameCount -= 1;
245 }
246
247 if(totalFramesRead>=framesToRead)
248 return totalFramesRead;
249
250 if(bytesRemaining==0)
251 return totalFramesRead;
252
253 // fill cache
254 uint8_t nibbles=0;
255 if(rd.read(&nibbles,1)!=1)
256 return totalFramesRead;
257
258 bytesRemaining--;
259 int32_t nibble0 = ((nibbles & 0xF0) >> 4);
260 int32_t nibble1 = ((nibbles & 0x0F) >> 0);
261
262 if(channels==1) {
263 /* Mono. */
264 msadpcm.cachedFrames[2] = decodeADPCMFrame(msadpcm.channel[0],nibble0);
265 msadpcm.cachedFrames[3] = decodeADPCMFrame(msadpcm.channel[0],nibble1);
266 msadpcm.cachedFrameCount = 2;
267 } else {
268 /* Stereo. */
269 msadpcm.cachedFrames[2] = decodeADPCMFrame(msadpcm.channel[0],nibble0);
270 msadpcm.cachedFrames[3] = decodeADPCMFrame(msadpcm.channel[1],nibble1);
271 msadpcm.cachedFrameCount = 1;
272 }
273 }
274 }
275
276void Wave::toFloatSamples(float* out) const {
277 const int16_t* smp = reinterpret_cast<const int16_t*>(wavedata.data());
278 for(size_t i=0;i<wavedata.size();i+=sizeof(int16_t), ++smp) {
279 *out = (*smp)/32767.f;
280 ++out;
281 }
282 }
283
284void Wave::save(const char *path) const {
285 Tempest::WFile f(path);
286 f.write("RIFF",4);
287
288 uint32_t fmtSize = uint32_t(sizeof(wfmt)) + uint32_t(extra.size()>0 ? (2+extra.size()) : 0);
289 uint32_t sz = 8u + fmtSize + 8u + uint32_t(wavedata.size());
290 f.write(reinterpret_cast<const char*>(&sz),4);
291 f.write("WAVE",4);
292
293 f.write("fmt ",4);
294 sz=fmtSize;
295 f.write(&sz,4);
296 f.write(&wfmt,sizeof(wfmt));
297 if(extra.size()>0){
298 uint16_t sz=uint16_t(extra.size());
299 f.write(&sz,2);
300 f.write(&extra[0],extra.size());
301 }
302
303 f.write("data",4);
304 sz=uint32_t(wavedata.size());
305 f.write(&sz,4);
306 f.write(wavedata.data(),sz);
307 }
bool is(const char *idx) const
Definition riff.h:19
void read(std::u16string &str)
Definition riff.cpp:29
size_t remaning() const
Definition riff.h:22
void readListId()
Definition riff.cpp:13
void skip(size_t sz)
Definition riff.cpp:76
bool isListId(const char *id)
Definition riff.cpp:24
WaveSample waveSample
Definition wave.h:59
@ ADPCM
Definition wave.h:23
@ PCM
Definition wave.h:22
std::vector< uint8_t > extra
Definition wave.h:57
Wave(Riff &input)
Definition wave.cpp:24
Info info
Definition wave.h:61
void save(const char *path) const
Definition wave.cpp:284
WaveFormat wfmt
Definition wave.h:56
void toFloatSamples(float *out) const
Definition wave.cpp:276
std::vector< uint8_t > wavedata
Definition wave.h:58
std::vector< WaveSampleLoop > loop
Definition wave.h:60
Definition band.h:10
WaveFormatTag wFormatTag
Definition wave.h:27
uint32_t dwAvgBytesPerSec
Definition wave.h:30
uint16_t wBitsPerSample
Definition wave.h:32
uint32_t dwSamplesPerSec
Definition wave.h:29
uint16_t wBlockAlign
Definition wave.h:31
uint16_t wChannels
Definition wave.h:28
uint32_t cSampleLoops
Definition wave.h:41
static T clip(T v, T low, T hi)
Definition wave.cpp:14