1 #include <algorithm>
2 #include "streamsource.h"
3 #include "fileio.h"
4
5 /**
6 * PlayStation XA (ADPCM) source support for MultiVoc
7 * Adapted and remixed from superxa2wav
8 *
9 * taken from EDuke32 and adapted for GZDoom by Christoph Oelckers
10 */
11
12
13 //#define NO_XA_HEADER
14
15 enum
16 {
17 kNumOfSamples = 224,
18 kNumOfSGs = 18,
19 TTYWidth = 80,
20
21 kBufSize = (kNumOfSGs*kNumOfSamples),
22 kSamplesMono = (kNumOfSGs*kNumOfSamples),
23 kSamplesStereo = (kNumOfSGs*kNumOfSamples/2),
24
25 /* ADPCM */
26 XA_DATA_START = (0x44-48)
27 };
28
DblToPCMF(double dt)29 inline float constexpr DblToPCMF(double dt) { return float(dt) * (1.f/32768.f); }
30
31 typedef struct {
32 MusicIO::FileInterface *reader;
33 size_t committed;
34 size_t length;
35 bool blockIsMono;
36 bool blockIs18K;
37 bool finished;
38
39 double t1, t2;
40 double t1_x, t2_x;
41
42 float block[kBufSize];
43 } xa_data;
44
45 typedef int8_t SoundGroup[128];
46
47 typedef struct XASector {
48 int8_t sectorFiller[48];
49 SoundGroup SoundGroups[18];
50 } XASector;
51
52 static double K0[4] = {
53 0.0,
54 0.9375,
55 1.796875,
56 1.53125
57 };
58 static double K1[4] = {
59 0.0,
60 0.0,
61 -0.8125,
62 -0.859375
63 };
64
65
66
getSoundData(int8_t * buf,int32_t unit,int32_t sample)67 static int8_t getSoundData(int8_t *buf, int32_t unit, int32_t sample)
68 {
69 int8_t ret;
70 int8_t *p;
71 int32_t offset, shift;
72
73 p = buf;
74 shift = (unit%2) * 4;
75
76 offset = 16 + (unit / 2) + (sample * 4);
77 p += offset;
78
79 ret = (*p >> shift) & 0x0F;
80
81 if (ret > 7) {
82 ret -= 16;
83 }
84 return ret;
85 }
86
getFilter(const int8_t * buf,int32_t unit)87 static int8_t getFilter(const int8_t *buf, int32_t unit)
88 {
89 return (*(buf + 4 + unit) >> 4) & 0x03;
90 }
91
92
getRange(const int8_t * buf,int32_t unit)93 static int8_t getRange(const int8_t *buf, int32_t unit)
94 {
95 return *(buf + 4 + unit) & 0x0F;
96 }
97
98
decodeSoundSectMono(XASector * ssct,xa_data * xad)99 static void decodeSoundSectMono(XASector *ssct, xa_data * xad)
100 {
101 size_t count = 0;
102 int8_t snddat, filt, range;
103 int32_t unit, sample;
104 int32_t sndgrp;
105 double tmp2, tmp3, tmp4, tmp5;
106 auto &decodeBuf = xad->block;
107
108 for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++)
109 {
110 for (unit = 0; unit < 8; unit++)
111 {
112 range = getRange(ssct->SoundGroups[sndgrp], unit);
113 filt = getFilter(ssct->SoundGroups[sndgrp], unit);
114 for (sample = 0; sample < 28; sample++)
115 {
116 snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample);
117 tmp2 = (double)(1 << (12 - range));
118 tmp3 = (double)snddat * tmp2;
119 tmp4 = xad->t1 * K0[filt];
120 tmp5 = xad->t2 * K1[filt];
121 xad->t2 = xad->t1;
122 xad->t1 = tmp3 + tmp4 + tmp5;
123 decodeBuf[count++] = DblToPCMF(xad->t1);
124 }
125 }
126 }
127 }
128
decodeSoundSectStereo(XASector * ssct,xa_data * xad)129 static void decodeSoundSectStereo(XASector *ssct, xa_data * xad)
130 {
131 size_t count = 0;
132 int8_t snddat, filt, range;
133 int8_t filt1, range1;
134 int32_t unit, sample;
135 int32_t sndgrp;
136 double tmp2, tmp3, tmp4, tmp5;
137 auto &decodeBuf = xad->block;
138
139 for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++)
140 {
141 for (unit = 0; unit < 8; unit+= 2)
142 {
143 range = getRange(ssct->SoundGroups[sndgrp], unit);
144 filt = getFilter(ssct->SoundGroups[sndgrp], unit);
145 range1 = getRange(ssct->SoundGroups[sndgrp], unit+1);
146 filt1 = getFilter(ssct->SoundGroups[sndgrp], unit+1);
147
148 for (sample = 0; sample < 28; sample++)
149 {
150 // Channel 1
151 snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample);
152 tmp2 = (double)(1 << (12 - range));
153 tmp3 = (double)snddat * tmp2;
154 tmp4 = xad->t1 * K0[filt];
155 tmp5 = xad->t2 * K1[filt];
156 xad->t2 = xad->t1;
157 xad->t1 = tmp3 + tmp4 + tmp5;
158 decodeBuf[count++] = DblToPCMF(xad->t1);
159
160 // Channel 2
161 snddat = getSoundData(ssct->SoundGroups[sndgrp], unit+1, sample);
162 tmp2 = (double)(1 << (12 - range1));
163 tmp3 = (double)snddat * tmp2;
164 tmp4 = xad->t1_x * K0[filt1];
165 tmp5 = xad->t2_x * K1[filt1];
166 xad->t2_x = xad->t1_x;
167 xad->t1_x = tmp3 + tmp4 + tmp5;
168 decodeBuf[count++] = DblToPCMF(xad->t1_x);
169 }
170 }
171 }
172 }
173
174 //==========================================================================
175 //
176 // Get one decoded block of data
177 //
178 //==========================================================================
179
getNextXABlock(xa_data * xad,bool looping)180 static void getNextXABlock(xa_data *xad, bool looping )
181 {
182 XASector ssct;
183 int coding;
184 const int SUBMODE_REAL_TIME_SECTOR = (1 << 6);
185 const int SUBMODE_FORM = (1 << 5);
186 const int SUBMODE_AUDIO_DATA = (1 << 2);
187
188 do
189 {
190 size_t bytes = xad->length - xad->reader->tell();
191
192 if (sizeof(XASector) < bytes)
193 bytes = sizeof(XASector);
194
195 xad->reader->read(&ssct, (int)bytes);
196 }
197 while (ssct.sectorFiller[46] != (SUBMODE_REAL_TIME_SECTOR | SUBMODE_FORM | SUBMODE_AUDIO_DATA));
198
199 coding = ssct.sectorFiller[47];
200
201 xad->committed = 0;
202 xad->blockIsMono = (coding & 3) == 0;
203 xad->blockIs18K = (((coding >> 2) & 3) == 1);
204
205 if (!xad->blockIsMono)
206 {
207 decodeSoundSectStereo(&ssct, xad);
208 }
209 else
210 {
211 decodeSoundSectMono(&ssct, xad);
212 }
213
214 if (xad->length == xad->reader->tell())
215 {
216 if (looping)
217 {
218 xad->reader->seek(XA_DATA_START, SEEK_SET);
219 xad->t1 = xad->t2 = xad->t1_x = xad->t2_x = 0;
220 }
221 else
222 xad->finished = true;
223 }
224
225 xad->finished = false;
226 }
227
228 //==========================================================================
229 //
230 // XASong
231 //
232 //==========================================================================
233
234 class XASong : public StreamSource
235 {
236 public:
237 XASong(MusicIO::FileInterface *readr);
238 SoundStreamInfo GetFormat() override;
239 bool Start() override;
240 bool GetData(void *buffer, size_t len) override;
241
242 protected:
243 xa_data xad;
244 };
245
246 //==========================================================================
247 //
248 // XASong - Constructor
249 //
250 //==========================================================================
251
XASong(MusicIO::FileInterface * reader)252 XASong::XASong(MusicIO::FileInterface * reader)
253 {
254 reader->seek(0, SEEK_END);
255 xad.length = reader->tell();
256 reader->seek(XA_DATA_START, SEEK_SET);
257 xad.reader = reader;
258 xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0;
259
260 getNextXABlock(&xad, false);
261 }
262
GetFormat()263 SoundStreamInfo XASong::GetFormat()
264 {
265 auto SampleRate = xad.blockIs18K? 18900 : 37800;
266 return { 64*1024, SampleRate, 2};
267 }
268
269 //==========================================================================
270 //
271 // XASong :: Play
272 //
273 //==========================================================================
274
Start()275 bool XASong::Start()
276 {
277 if (xad.finished && m_Looping)
278 {
279 xad.reader->seek(XA_DATA_START, SEEK_SET);
280 xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0;
281 xad.finished = false;
282 }
283 return true;
284 }
285
286 //==========================================================================
287 //
288 // XASong :: Read
289 //
290 //==========================================================================
291
GetData(void * vbuff,size_t len)292 bool XASong::GetData(void *vbuff, size_t len)
293 {
294 float *dest = (float*)vbuff;
295 while (len > 0)
296 {
297 auto ptr = xad.committed;
298 auto block = xad.block + ptr;
299 if (ptr < kBufSize)
300 {
301 // commit the data
302 if (xad.blockIsMono)
303 {
304 size_t numsamples = len / 8;
305 size_t availdata = kBufSize - ptr;
306
307 for(size_t tocopy = std::min(numsamples, availdata); tocopy > 0; tocopy--)
308 {
309 float f = *block++;
310 *dest++ = f;
311 *dest++ = f;
312 len -= 8;
313 ptr++;
314 }
315 xad.committed = ptr;
316 }
317 else
318 {
319 size_t availdata = (kBufSize - ptr) * 4;
320 size_t tocopy = std::min(availdata, len);
321 memcpy(dest, block, tocopy);
322 dest += tocopy / 4;
323 len -= tocopy;
324 xad.committed += tocopy / 4;
325 }
326 }
327 if (xad.finished)
328 {
329 memset(dest, 0, len);
330 return true;
331 }
332 if (len > 0)
333 {
334 // we ran out of data and need more
335 getNextXABlock(&xad, m_Looping);
336 // repeat until done.
337 }
338 else break;
339
340 }
341 return !xad.finished;
342 }
343
344 //==========================================================================
345 //
346 // XA_OpenSong
347 //
348 //==========================================================================
349
XA_OpenSong(MusicIO::FileInterface * reader)350 StreamSource *XA_OpenSong(MusicIO::FileInterface *reader)
351 {
352 return new XASong(reader);
353 }
354
355