ScummVM API documentation
psxspu.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 /*
22  * VGMTrans (c) 2002-2019
23  * Licensed under the zlib license,
24  * refer to the included VGMTrans_LICENSE.txt file
25  */
26 #ifndef AUDIO_SOUNDFONT_PSXSPU_H
27 #define AUDIO_SOUNDFONT_PSXSPU_H
28 
29 #include "audio/soundfont/common.h"
30 #include "common/str.h"
31 #include "common/util.h"
32 #include "audio/soundfont/vgminstrset.h"
33 #include "audio/soundfont/vgmsamp.h"
34 #include "audio/soundfont/vgmitem.h"
35 
36 // All of the ADSR calculations herein (except where inaccurate) are derived from Neill Corlett's
37 // work in reverse-engineering the Playstation 1/2 SPU unit.
38 
39 //**************************************************************************************************
40 // Type Redefinitions
41 
42 typedef void v0;
43 
44 #ifdef __cplusplus
45 #if defined __BORLANDC__
46 typedef bool b8;
47 #else
48 typedef unsigned char b8;
49 #endif
50 #else
51 typedef char b8;
52 #endif
53 
54 typedef float f32;
55 //***********************************************************************************************
56 
57 static unsigned long RateTable[160];
58 static bool bRateTableInitialized = 0;
59 
60 // VAG format -----------------------------------
61 
62 // Sample Block
63 typedef struct _VAGBlk {
64  uint8 range;
65  uint8 filter;
66 
67  struct {
68  b8 end: 1; // End block
69  b8 looping: 1; // VAG loops
70  b8 loop: 1; // Loop start point
71  } flag;
72 
73  int8 brr[14]; // Compressed samples
74 } VAGBlk;
75 
76 double LinAmpDecayTimeToLinDBDecayTime(double secondsToFullAtten, int linearVolumeRange);
77 
78 // InitADSR is shamelessly ripped from P.E.Op.S
79 static inline void InitADSR() {
80  unsigned long r, rs, rd;
81  int i;
82 
83  // build the rate table according to Neill's rules
84  memset(RateTable, 0, sizeof(unsigned long) * 160);
85 
86  r = 3;
87  rs = 1;
88  rd = 0;
89 
90  // we start at pos 32 with the real values... everything before is 0
91  for (i = 32; i < 160; i++) {
92  if (r < 0x3FFFFFFF) {
93  r += rs;
94  rd++;
95  if (rd == 5) {
96  rd = 1;
97  rs *= 2;
98  }
99  }
100  if (r > 0x3FFFFFFF)
101  r = 0x3FFFFFFF;
102 
103  RateTable[i] = r;
104  }
105 }
106 
107 inline int RoundToZero(int val) {
108  if (val < 0)
109  val = 0;
110  return val;
111 }
112 
113 template<class T>
114 void PSXConvADSR(T *realADSR, unsigned short ADSR1, unsigned short ADSR2, bool bPS2) {
115  uint8 Am = (ADSR1 & 0x8000) >> 15; // if 1, then Exponential, else linear
116  uint8 Ar = (ADSR1 & 0x7F00) >> 8;
117  uint8 Dr = (ADSR1 & 0x00F0) >> 4;
118  uint8 Sl = ADSR1 & 0x000F;
119  uint8 Rm = (ADSR2 & 0x0020) >> 5;
120  uint8 Rr = ADSR2 & 0x001F;
121 
122  // The following are unimplemented in conversion (because DLS and SF2 do not support Sustain
123  // Rate)
124  uint8 Sm = (ADSR2 & 0x8000) >> 15;
125  uint8 Sd = (ADSR2 & 0x4000) >> 14;
126  uint8 Sr = (ADSR2 >> 6) & 0x7F;
127 
128  PSXConvADSR(realADSR, Am, Ar, Dr, Sl, Sm, Sd, Sr, Rm, Rr, bPS2);
129 }
130 
131 template<class T>
132 void PSXConvADSR(T *realADSR, uint8 Am, uint8 Ar, uint8 Dr, uint8 Sl, uint8 Sm,
133  uint8 Sd, uint8 Sr, uint8 Rm, uint8 Rr, bool bPS2) {
134  // Make sure all the ADSR values are within the valid ranges
135  if (((Am & ~0x01) != 0) || ((Ar & ~0x7F) != 0) || ((Dr & ~0x0F) != 0) || ((Sl & ~0x0F) != 0) ||
136  ((Rm & ~0x01) != 0) || ((Rr & ~0x1F) != 0) || ((Sm & ~0x01) != 0) || ((Sd & ~0x01) != 0) ||
137  ((Sr & ~0x7F) != 0)) {
138  error("ADSR parameter(s) out of range");
139  }
140 
141  // PS1 games use 44k, PS2 uses 48k
142  double sampleRate = bPS2 ? 48000 : 44100;
143 
144  long envelope_level;
145  double samples = 0.0;
146  unsigned long rate;
147  unsigned long remainder;
148  double timeInSecs;
149  int l;
150 
151  if (!bRateTableInitialized) {
152  InitADSR();
153  bRateTableInitialized = true;
154  }
155 
156  // to get the dls 32 bit time cents, take log base 2 of number of seconds * 1200 * 65536
157  // (dls1v11a.pdf p25).
158 
159  // if (RateTable[(Ar^0x7F)-0x10 + 32] == 0)
160  // realADSR->attack_time = 0;
161  // else
162  // {
163  if ((Ar ^ 0x7F) < 0x10)
164  Ar = 0;
165  // if linear Ar Mode
166  if (Am == 0) {
167  rate = RateTable[RoundToZero((Ar ^ 0x7F) - 0x10) + 32];
168  samples = ceil(0x7FFFFFFF / (double) rate);
169  } else if (Am == 1) {
170  rate = RateTable[RoundToZero((Ar ^ 0x7F) - 0x10) + 32];
171  samples = (unsigned long)(0x60000000 / rate);
172  remainder = 0x60000000 % rate;
173  rate = RateTable[RoundToZero((Ar ^ 0x7F) - 0x18) + 32];
174  samples += ceil(MAX<double>(0, 0x1FFFFFFF - (long) remainder) / (double) rate);
175  }
176  timeInSecs = samples / sampleRate;
177  realADSR->_attack_time = timeInSecs;
178  // }
179 
180  // Decay Time
181 
182  envelope_level = 0x7FFFFFFF;
183 
184  bool bSustainLevFound = false;
185  uint32 realSustainLevel = 0x7FFFFFFF;
186  // DLS decay rate value is to -96db (silence) not the sustain level
187  for (l = 0; envelope_level > 0; l++) {
188  if (4 * (Dr ^ 0x1F) < 0x18)
189  Dr = 0;
190  switch ((envelope_level >> 28) & 0x7) {
191  case 0:
192  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 0) + 32];
193  break;
194  case 1:
195  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 4) + 32];
196  break;
197  case 2:
198  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 6) + 32];
199  break;
200  case 3:
201  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 8) + 32];
202  break;
203  case 4:
204  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 9) + 32];
205  break;
206  case 5:
207  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 10) + 32];
208  break;
209  case 6:
210  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 11) + 32];
211  break;
212  case 7:
213  envelope_level -= RateTable[RoundToZero((4 * (Dr ^ 0x1F)) - 0x18 + 12) + 32];
214  break;
215  }
216  if (!bSustainLevFound && ((envelope_level >> 27) & 0xF) <= Sl) {
217  realSustainLevel = envelope_level;
218  bSustainLevFound = true;
219  }
220  }
221  samples = l;
222  timeInSecs = samples / sampleRate;
223  realADSR->_decay_time = timeInSecs;
224 
225  // Sustain Rate
226 
227  envelope_level = 0x7FFFFFFF;
228  // increasing... we won't even bother
229  if (Sd == 0) {
230  realADSR->_sustain_time = -1;
231  } else {
232  if (Sr == 0x7F)
233  realADSR->_sustain_time = -1; // this is actually infinite
234  else {
235  // linear
236  if (Sm == 0) {
237  rate = RateTable[RoundToZero((Sr ^ 0x7F) - 0x0F) + 32];
238  samples = ceil(0x7FFFFFFF / (double) rate);
239  } else {
240  l = 0;
241  // DLS decay rate value is to -96db (silence) not the sustain level
242  while (envelope_level > 0) {
243  long envelope_level_diff;
244  long envelope_level_target;
245 
246  switch ((envelope_level >> 28) & 0x7) {
247  case 0:
248  default:
249  envelope_level_target = 0x00000000;
250  envelope_level_diff =
251  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 0) + 32];
252  break;
253  case 1:
254  envelope_level_target = 0x0fffffff;
255  envelope_level_diff =
256  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 4) + 32];
257  break;
258  case 2:
259  envelope_level_target = 0x1fffffff;
260  envelope_level_diff =
261  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 6) + 32];
262  break;
263  case 3:
264  envelope_level_target = 0x2fffffff;
265  envelope_level_diff =
266  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 8) + 32];
267  break;
268  case 4:
269  envelope_level_target = 0x3fffffff;
270  envelope_level_diff =
271  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 9) + 32];
272  break;
273  case 5:
274  envelope_level_target = 0x4fffffff;
275  envelope_level_diff =
276  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 10) + 32];
277  break;
278  case 6:
279  envelope_level_target = 0x5fffffff;
280  envelope_level_diff =
281  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 11) + 32];
282  break;
283  case 7:
284  envelope_level_target = 0x6fffffff;
285  envelope_level_diff =
286  RateTable[RoundToZero((Sr ^ 0x7F) - 0x1B + 12) + 32];
287  break;
288  }
289 
290  long steps =
291  (envelope_level - envelope_level_target + (envelope_level_diff - 1)) /
292  envelope_level_diff;
293  envelope_level -= (envelope_level_diff * steps);
294  l += steps;
295  }
296  samples = l;
297  }
298  timeInSecs = samples / sampleRate;
299  realADSR->_sustain_time =
300  /*Sm ? timeInSecs : */ LinAmpDecayTimeToLinDBDecayTime(timeInSecs, 0x800);
301  }
302  }
303 
304  // Sustain Level
305  // realADSR->sustain_level =
306  // (double)envelope_level/(double)0x7FFFFFFF;//(long)ceil((double)envelope_level *
307  // 0.030517578139210854); //in DLS, sustain level is measured as a percentage
308  if (Sl == 0)
309  realSustainLevel = 0x07FFFFFF;
310  realADSR->_sustain_level = realSustainLevel / (double) 0x7FFFFFFF;
311 
312  // If decay is going unused, and there's a sustain rate with sustain level close to max...
313  // we'll put the sustain_rate in place of the decay rate.
314  if ((realADSR->_decay_time < 2 || (Dr == 0x0F && Sl >= 0x0C)) && Sr < 0x7E && Sd == 1) {
315  realADSR->_sustain_level = 0;
316  realADSR->_decay_time = realADSR->_sustain_time;
317  // realADSR->decay_time = 0.5;
318  }
319 
320  // Release Time
321 
322  // sustain_envelope_level = envelope_level;
323 
324  // We do this because we measure release time from max volume to 0, not from sustain level to 0
325  envelope_level = 0x7FFFFFFF;
326 
327  // if linear Rr Mode
328  if (Rm == 0) {
329  rate = RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x0C) + 32];
330 
331  if (rate != 0)
332  samples = ceil((double) envelope_level / (double) rate);
333  else
334  samples = 0;
335  } else if (Rm == 1) {
336  if ((Rr ^ 0x1F) * 4 < 0x18)
337  Rr = 0;
338  for (l = 0; envelope_level > 0; l++) {
339  switch ((envelope_level >> 28) & 0x7) {
340  case 0:
341  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 0) + 32];
342  break;
343  case 1:
344  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 4) + 32];
345  break;
346  case 2:
347  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 6) + 32];
348  break;
349  case 3:
350  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 8) + 32];
351  break;
352  case 4:
353  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 9) + 32];
354  break;
355  case 5:
356  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 10) + 32];
357  break;
358  case 6:
359  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 11) + 32];
360  break;
361  case 7:
362  envelope_level -= RateTable[RoundToZero((4 * (Rr ^ 0x1F)) - 0x18 + 12) + 32];
363  break;
364  }
365  }
366  samples = l;
367  }
368  timeInSecs = samples / sampleRate;
369 
370  // theRate = timeInSecs / sustain_envelope_level;
371  // timeInSecs = 0x7FFFFFFF * theRate; //the release time value is more like a rate. It is the
372  // time from max value to 0, not from sustain level. if (Rm == 0) // if it's linear timeInSecs *=
373  //LINEAR_RELEASE_COMPENSATION;
374 
375  realADSR->_release_time =
376  /*Rm ? timeInSecs : */ LinAmpDecayTimeToLinDBDecayTime(timeInSecs, 0x800);
377 
378  // We need to compensate the decay and release times to represent them as the time from full vol
379  // to -100db where the drop in db is a fixed amount per time unit (SoundFont2 spec for vol
380  // envelopes, pg44.)
381  // We assume the psx envelope is using a linear scale wherein envelope_level / 2 == half
382  // loudness. For a linear release mode (Rm == 0), the time to reach half volume is simply half
383  // the time to reach 0.
384  // Half perceived loudness is -10db. Therefore, time_to_half_vol * 10 == full_time * 5 == the
385  // correct SF2 time
386  // realADSR->decay_time = LinAmpDecayTimeToLinDBDecayTime(realADSR->decay_time, 0x800);
387  // realADSR->sustain_time = LinAmpDecayTimeToLinDBDecayTime(realADSR->sustain_time, 0x800);
388  // realADSR->release_time = LinAmpDecayTimeToLinDBDecayTime(realADSR->release_time, 0x800);
389 
390  // Calculations are done, so now add the articulation data
391  // artic->AddADSR(attack_time, Am, decay_time, sustain_lev, release_time, 0);
392 }
393 
394 class PSXSampColl : public VGMSampColl {
395 public:
396  PSXSampColl(VGMInstrSet *instrset, uint32 offset, uint32 length,
397  const Common::Array<SizeOffsetPair> &vagLocations);
398 
399  virtual bool
400  GetSampleInfo(); // retrieve sample info, including pointers to data, # channels, rate, etc.
401 
402 protected:
403  Common::Array<SizeOffsetPair> _vagLocations;
404 };
405 
406 class PSXSamp : public VGMSamp {
407 public:
408  PSXSamp(VGMSampColl *sampColl, uint32 offset, uint32 length, uint32 dataOffset,
409  uint32 dataLen, uint8 nChannels, uint16 theBPS, uint32 theRate,
410  Common::String name, bool bSetLoopOnConversion = true);
411 
412  ~PSXSamp() override {}
413 
414  // ratio of space conserved. should generally be > 1
415  // used to calculate both uncompressed sample size and loopOff after conversion
416  double GetCompressionRatio() override;
417 
418  void ConvertToStdWave(uint8 *buf) override;
419 
420 private:
421  void DecompVAGBlk(int16 *pSmp, VAGBlk *pVBlk, f32 *prev1, f32 *prev2);
422 
423 public:
424 
425  bool _setLoopOnConversion;
426 };
427 
428 #endif // AUDIO_SOUNDFONT_PSXSPU_H
Definition: str.h:59
Definition: psxspu.h:63
Definition: psxspu.h:394
Definition: vgmsamp.h:77
Definition: vgmsamp.h:41
Definition: vgminstrset.h:45
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
Definition: psxspu.h:406