1e397af7851efe4feb2e6ed11ef71b37e5a2090e
[pcsx_rearmed.git] / plugins / dfsound / adsr.c
1 /***************************************************************************\r
2                           adsr.c  -  description\r
3                              -------------------\r
4     begin                : Wed May 15 2002\r
5     copyright            : (C) 2002 by Pete Bernert\r
6     email                : BlackDove@addcom.de\r
7  ***************************************************************************/\r
8 /***************************************************************************\r
9  *                                                                         *\r
10  *   This program is free software; you can redistribute it and/or modify  *\r
11  *   it under the terms of the GNU General Public License as published by  *\r
12  *   the Free Software Foundation; either version 2 of the License, or     *\r
13  *   (at your option) any later version. See also the license.txt file for *\r
14  *   additional informations.                                              *\r
15  *                                                                         *\r
16  ***************************************************************************/\r
17 \r
18 #include "stdafx.h"\r
19 \r
20 #define _IN_ADSR\r
21 \r
22 // will be included from spu.c\r
23 #ifdef _IN_SPU\r
24 \r
25 ////////////////////////////////////////////////////////////////////////\r
26 // ADSR func\r
27 ////////////////////////////////////////////////////////////////////////\r
28 \r
29 unsigned long RateTable[160];\r
30 \r
31 void InitADSR(void)                                    // INIT ADSR\r
32 {\r
33  unsigned long r,rs,rd;int i;\r
34 \r
35  memset(RateTable,0,sizeof(unsigned long)*160);        // build the rate table according to Neill's rules (see at bottom of file)\r
36 \r
37  r=3;rs=1;rd=0;\r
38 \r
39  for(i=32;i<160;i++)                                   // we start at pos 32 with the real values... everything before is 0\r
40   {\r
41    if(r<0x3FFFFFFF)\r
42     {\r
43      r+=rs;\r
44      rd++;if(rd==5) {rd=1;rs*=2;}\r
45     }\r
46    if(r>0x3FFFFFFF) r=0x3FFFFFFF;\r
47 \r
48    RateTable[i]=r;\r
49   }\r
50 }\r
51 \r
52 ////////////////////////////////////////////////////////////////////////\r
53 \r
54 INLINE void StartADSR(int ch)                          // MIX ADSR\r
55 {\r
56  s_chan[ch].ADSRX.State=0;                             // and init some adsr vars\r
57  s_chan[ch].ADSRX.EnvelopeVol=0;\r
58 }\r
59 \r
60 ////////////////////////////////////////////////////////////////////////\r
61 \r
62 INLINE int MixADSR(int ch)                             // MIX ADSR\r
63 {    \r
64  if(s_chan[ch].bStop)                                  // should be stopped:\r
65   {                                                    // do release\r
66    if(s_chan[ch].ADSRX.ReleaseModeExp)\r
67     {\r
68      switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)\r
69       {\r
70        case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +0 + 32]; break;\r
71        case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +4 + 32]; break;\r
72        case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +6 + 32]; break;\r
73        case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +8 + 32]; break;\r
74        case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +9 + 32]; break;\r
75        case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +10+ 32]; break;\r
76        case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +11+ 32]; break;\r
77        case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +12+ 32]; break;\r
78       }\r
79     }\r
80    else\r
81     {\r
82      s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x0C + 32];\r
83     }\r
84 \r
85    if(s_chan[ch].ADSRX.EnvelopeVol<0) \r
86     {\r
87      s_chan[ch].ADSRX.EnvelopeVol=0;\r
88      // don't stop if this chan can still cause irqs\r
89      if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq))\r
90       //s_chan[ch].bOn=0;\r
91       s_chan[ch].pCurr=(unsigned char *)-1;\r
92      //s_chan[ch].bReverb=0;\r
93      //s_chan[ch].bNoise=0;\r
94     }\r
95 \r
96    return s_chan[ch].ADSRX.EnvelopeVol>>21;\r
97   }\r
98  else                                                  // not stopped yet?\r
99   {\r
100    if(s_chan[ch].ADSRX.State==0)                       // -> attack\r
101     {\r
102      if(s_chan[ch].ADSRX.AttackModeExp)\r
103       {\r
104        if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) \r
105         s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];\r
106        else\r
107         s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x18 + 32];\r
108       }\r
109      else\r
110       {\r
111        s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];\r
112       }\r
113 \r
114      if(s_chan[ch].ADSRX.EnvelopeVol<0) \r
115       {\r
116        s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;\r
117        s_chan[ch].ADSRX.State=1;\r
118       }\r
119 \r
120      return s_chan[ch].ADSRX.EnvelopeVol>>21;\r
121     }\r
122    //--------------------------------------------------//\r
123    if(s_chan[ch].ADSRX.State==1)                       // -> decay\r
124     {\r
125      switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)\r
126       {\r
127        case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+0 + 32]; break;\r
128        case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+4 + 32]; break;\r
129        case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+6 + 32]; break;\r
130        case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+8 + 32]; break;\r
131        case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+9 + 32]; break;\r
132        case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+10+ 32]; break;\r
133        case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+11+ 32]; break;\r
134        case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+12+ 32]; break;\r
135       }\r
136 \r
137      if(s_chan[ch].ADSRX.EnvelopeVol<0) s_chan[ch].ADSRX.EnvelopeVol=0;\r
138      if(((s_chan[ch].ADSRX.EnvelopeVol>>27)&0xF) <= s_chan[ch].ADSRX.SustainLevel)\r
139       {\r
140        s_chan[ch].ADSRX.State=2;\r
141       }\r
142 \r
143      return s_chan[ch].ADSRX.EnvelopeVol>>21;\r
144     }\r
145    //--------------------------------------------------//\r
146    if(s_chan[ch].ADSRX.State==2)                       // -> sustain\r
147     {\r
148      if(s_chan[ch].ADSRX.SustainIncrease)\r
149       {\r
150        if(s_chan[ch].ADSRX.SustainModeExp)\r
151         {\r
152          if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) \r
153           s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];\r
154          else\r
155           s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x18 + 32];\r
156         }\r
157        else\r
158         {\r
159          s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];\r
160         }\r
161 \r
162        if(s_chan[ch].ADSRX.EnvelopeVol<0) \r
163         {\r
164          s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;\r
165         }\r
166       }\r
167      else\r
168       {\r
169        if(s_chan[ch].ADSRX.SustainModeExp)\r
170         {\r
171          switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)\r
172           {\r
173            case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +0 + 32];break;\r
174            case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +4 + 32];break;\r
175            case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +6 + 32];break;\r
176            case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +8 + 32];break;\r
177            case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +9 + 32];break;\r
178            case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +10+ 32];break;\r
179            case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +11+ 32];break;\r
180            case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +12+ 32];break;\r
181           }\r
182         }\r
183        else\r
184         {\r
185          s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x0F + 32];\r
186         }\r
187 \r
188        if(s_chan[ch].ADSRX.EnvelopeVol<0) \r
189         {\r
190          s_chan[ch].ADSRX.EnvelopeVol=0;\r
191         }\r
192       }\r
193      return s_chan[ch].ADSRX.EnvelopeVol>>21;\r
194     }\r
195   }\r
196  return 0;\r
197 }\r
198 \r
199 #endif\r
200 \r
201 /*\r
202 James Higgs ADSR investigations:\r
203 \r
204 PSX SPU Envelope Timings\r
205 ~~~~~~~~~~~~~~~~~~~~~~~~\r
206 \r
207 First, here is an extract from doomed's SPU doc, which explains the basics\r
208 of the SPU "volume envelope": \r
209 \r
210 *** doomed doc extract start ***\r
211 \r
212 --------------------------------------------------------------------------\r
213 Voices.\r
214 --------------------------------------------------------------------------\r
215 The SPU has 24 hardware voices. These voices can be used to reproduce sample\r
216 data, noise or can be used as frequency modulator on the next voice.\r
217 Each voice has it's own programmable ADSR envelope filter. The main volume\r
218 can be programmed independently for left and right output.\r
219 \r
220 The ADSR envelope filter works as follows:\r
221 Ar = Attack rate, which specifies the speed at which the volume increases\r
222      from zero to it's maximum value, as soon as the note on is given. The\r
223      slope can be set to lineair or exponential.\r
224 Dr = Decay rate specifies the speed at which the volume decreases to the\r
225      sustain level. Decay is always decreasing exponentially.\r
226 Sl = Sustain level, base level from which sustain starts.\r
227 Sr = Sustain rate is the rate at which the volume of the sustained note\r
228      increases or decreases. This can be either lineair or exponential.\r
229 Rr = Release rate is the rate at which the volume of the note decreases\r
230      as soon as the note off is given.\r
231 \r
232      lvl |\r
233        ^ |     /\Dr     __\r
234      Sl _| _  / _ \__---  \\r
235          |   /       ---__ \ Rr\r
236          |  /Ar       Sr  \ \\r
237          | /                \\\r
238          |/___________________\________\r
239                                   ->time\r
240 \r
241 The overal volume can also be set to sweep up or down lineairly or\r
242 exponentially from it's current value. This can be done seperately\r
243 for left and right.\r
244 \r
245 Relevant SPU registers:\r
246 -------------------------------------------------------------\r
247 $1f801xx8         Attack/Decay/Sustain level\r
248 bit  |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|\r
249 desc.|Am|         Ar         |Dr         |Sl         |\r
250 \r
251 Am       0        Attack mode Linear\r
252          1                    Exponential\r
253 \r
254 Ar       0-7f     attack rate\r
255 Dr       0-f      decay rate\r
256 Sl       0-f      sustain level\r
257 -------------------------------------------------------------\r
258 $1f801xxa         Sustain rate, Release Rate.\r
259 bit  |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|\r
260 desc.|Sm|Sd| 0|   Sr               |Rm|Rr            |\r
261 \r
262 Sm       0        sustain rate mode linear\r
263          1                          exponential\r
264 Sd       0        sustain rate mode increase\r
265          1                          decrease\r
266 Sr       0-7f     Sustain Rate\r
267 Rm       0        Linear decrease\r
268          1        Exponential decrease\r
269 Rr       0-1f     Release Rate\r
270 \r
271 Note: decay mode is always Expontial decrease, and thus cannot\r
272 be set.\r
273 -------------------------------------------------------------\r
274 $1f801xxc         Current ADSR volume\r
275 bit  |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|\r
276 desc.|ADSRvol                                        |\r
277 \r
278 ADSRvol           Returns the current envelope volume when\r
279                   read.\r
280 -- James' Note: return range: 0 -> 32767\r
281 \r
282 *** doomed doc extract end *** \r
283 \r
284 By using a small PSX proggie to visualise the envelope as it was played,\r
285 the following results for envelope timing were obtained:\r
286 \r
287 1. Attack rate value (linear mode)\r
288 \r
289    Attack value range: 0 -> 127\r
290 \r
291    Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |    | 80 |\r
292    -----------------------------------------------------------------\r
293    Frames | 11 | 21 | 42 | 84 | 169| 338| 676|    |2890|\r
294 \r
295    Note: frames is no. of PAL frames to reach full volume (100%\r
296    amplitude)\r
297 \r
298    Hmm, noticing that the time taken to reach full volume doubles\r
299    every time we add 4 to our attack value, we know the equation is\r
300    of form:\r
301              frames = k * 2 ^ (value / 4)\r
302 \r
303    (You may ponder about envelope generator hardware at this point,\r
304    or maybe not... :)\r
305 \r
306    By substituting some stuff and running some checks, we get:\r
307 \r
308        k = 0.00257              (close enuf)\r
309 \r
310    therefore,\r
311              frames = 0.00257 * 2 ^ (value / 4)\r
312    If you just happen to be writing an emulator, then you can probably\r
313    use an equation like:\r
314 \r
315        %volume_increase_per_tick = 1 / frames\r
316 \r
317 \r
318    ------------------------------------\r
319    Pete:\r
320    ms=((1<<(value>>2))*514)/10000\r
321    ------------------------------------\r
322 \r
323 2. Decay rate value (only has log mode)\r
324 \r
325    Decay value range: 0 -> 15\r
326 \r
327    Value  |  8 |  9 | 10 | 11 | 12 | 13 | 14 | 15 |\r
328    ------------------------------------------------\r
329    frames |    |    |    |    |  6 | 12 | 24 | 47 |\r
330 \r
331    Note: frames here is no. of PAL frames to decay to 50% volume.\r
332 \r
333    formula: frames = k * 2 ^ (value)\r
334 \r
335    Substituting, we get: k = 0.00146\r
336 \r
337    Further info on logarithmic nature:\r
338    frames to decay to sustain level 3  =  3 * frames to decay to \r
339    sustain level 9\r
340 \r
341    Also no. of frames to 25% volume = roughly 1.85 * no. of frames to\r
342    50% volume.\r
343 \r
344    Frag it - just use linear approx.\r
345 \r
346    ------------------------------------\r
347    Pete:\r
348    ms=((1<<value)*292)/10000\r
349    ------------------------------------\r
350 \r
351 \r
352 3. Sustain rate value (linear mode)\r
353 \r
354    Sustain rate range: 0 -> 127\r
355 \r
356    Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |\r
357    -------------------------------------------\r
358    frames |  9 | 19 | 37 | 74 | 147| 293| 587|\r
359 \r
360    Here, frames = no. of PAL frames for volume amplitude to go from 100%\r
361    to 0% (or vice-versa).\r
362 \r
363    Same formula as for attack value, just a different value for k:\r
364 \r
365    k = 0.00225\r
366 \r
367    ie: frames = 0.00225 * 2 ^ (value / 4)\r
368 \r
369    For emulation purposes:\r
370 \r
371    %volume_increase_or_decrease_per_tick = 1 / frames\r
372 \r
373    ------------------------------------\r
374    Pete:\r
375    ms=((1<<(value>>2))*450)/10000\r
376    ------------------------------------\r
377 \r
378 \r
379 4. Release rate (linear mode)\r
380 \r
381    Release rate range: 0 -> 31\r
382 \r
383    Value  | 13 | 14 | 15 | 16 | 17 |\r
384    ---------------------------------------------------------------\r
385    frames | 18 | 36 | 73 | 146| 292|\r
386 \r
387    Here, frames = no. of PAL frames to decay from 100% vol to 0% vol\r
388    after "note-off" is triggered.\r
389 \r
390    Formula: frames = k * 2 ^ (value)\r
391 \r
392    And so: k = 0.00223\r
393 \r
394    ------------------------------------\r
395    Pete:\r
396    ms=((1<<value)*446)/10000\r
397    ------------------------------------\r
398 \r
399 \r
400 Other notes:   \r
401 \r
402 Log stuff not figured out. You may get some clues from the "Decay rate"\r
403 stuff above. For emu purposes it may not be important - use linear\r
404 approx.\r
405 \r
406 To get timings in millisecs, multiply frames by 20.\r
407 \r
408 \r
409 \r
410 - James Higgs 17/6/2000\r
411 james7780@yahoo.com\r
412 \r
413 //---------------------------------------------------------------\r
414 \r
415 OLD adsr mixing according to james' rules... has to be called\r
416 every one millisecond\r
417 \r
418 \r
419  long v,v2,lT,l1,l2,l3;\r
420 \r
421  if(s_chan[ch].bStop)                                  // psx wants to stop? -> release phase\r
422   {\r
423    if(s_chan[ch].ADSR.ReleaseVal!=0)                   // -> release not 0: do release (if 0: stop right now)\r
424     {\r
425      if(!s_chan[ch].ADSR.ReleaseVol)                   // --> release just started? set up the release stuff\r
426       {\r
427        s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;\r
428        s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;\r
429        s_chan[ch].ADSR.ReleaseTime =                   // --> calc how long does it take to reach the wanted sus level\r
430          (s_chan[ch].ADSR.ReleaseTime*\r
431           s_chan[ch].ADSR.ReleaseVol)/1024;\r
432       }\r
433                                                        // -> NO release exp mode used (yet)\r
434      v=s_chan[ch].ADSR.ReleaseVol;                     // -> get last volume\r
435      lT=s_chan[ch].ADSR.lTime-                         // -> how much time is past?\r
436         s_chan[ch].ADSR.ReleaseStartTime;\r
437      l1=s_chan[ch].ADSR.ReleaseTime;\r
438                                                        \r
439      if(lT<l1)                                         // -> we still have to release\r
440       {\r
441        v=v-((v*lT)/l1);                                // --> calc new volume\r
442       }\r
443      else                                              // -> release is over: now really stop that sample\r
444       {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}\r
445     }\r
446    else                                                // -> release IS 0: release at once\r
447     {\r
448      v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;\r
449     }\r
450   }\r
451  else                                               \r
452   {//--------------------------------------------------// not in release phase:\r
453    v=1024;\r
454    lT=s_chan[ch].ADSR.lTime;\r
455    l1=s_chan[ch].ADSR.AttackTime;\r
456                                                        \r
457    if(lT<l1)                                           // attack\r
458     {                                                  // no exp mode used (yet)\r
459 //     if(s_chan[ch].ADSR.AttackModeExp)\r
460 //      {\r
461 //       v=(v*lT)/l1;\r
462 //      }\r
463 //     else\r
464       {\r
465        v=(v*lT)/l1;\r
466       }\r
467      if(v==0) v=1;\r
468     }\r
469    else                                                // decay\r
470     {                                                  // should be exp, but who cares? ;)\r
471      l2=s_chan[ch].ADSR.DecayTime;\r
472      v2=s_chan[ch].ADSR.SustainLevel;\r
473 \r
474      lT-=l1;\r
475      if(lT<l2)\r
476       {\r
477        v-=(((v-v2)*lT)/l2);\r
478       }\r
479      else                                              // sustain\r
480       {                                                // no exp mode used (yet)\r
481        l3=s_chan[ch].ADSR.SustainTime;\r
482        lT-=l2;\r
483        if(s_chan[ch].ADSR.SustainModeDec>0)\r
484         {\r
485          if(l3!=0) v2+=((v-v2)*lT)/l3;\r
486          else      v2=v;\r
487         }\r
488        else\r
489         {\r
490          if(l3!=0) v2-=(v2*lT)/l3;\r
491          else      v2=v;\r
492         }\r
493 \r
494        if(v2>v)  v2=v;\r
495        if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}\r
496 \r
497        v=v2;\r
498       }\r
499     }\r
500   }\r
501 \r
502  //----------------------------------------------------// \r
503  // ok, done for this channel, so increase time\r
504 \r
505  s_chan[ch].ADSR.lTime+=1;                             // 1 = 1.020408f ms;      \r
506 \r
507  if(v>1024)     v=1024;                                // adjust volume\r
508  if(v<0)        v=0;                                  \r
509  s_chan[ch].ADSR.lVolume=v;                            // store act volume\r
510 \r
511  return v;                                             // return the volume factor\r
512 */\r
513 \r
514 \r
515 //-----------------------------------------------------------------------------\r
516 //-----------------------------------------------------------------------------\r
517 //-----------------------------------------------------------------------------\r
518 \r
519 \r
520 /*\r
521 -----------------------------------------------------------------------------\r
522 Neill Corlett\r
523 Playstation SPU envelope timing notes\r
524 -----------------------------------------------------------------------------\r
525 \r
526 This is preliminary.  This may be wrong.  But the model described herein fits\r
527 all of my experimental data, and it's just simple enough to sound right.\r
528 \r
529 ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.\r
530 The value returned by channel reg 0xC is (envelope_level>>16).\r
531 \r
532 Each sample, an increment or decrement value will be added to or\r
533 subtracted from this envelope level.\r
534 \r
535 Create the rate log table.  The values double every 4 entries.\r
536    entry #0 = 4\r
537 \r
538     4, 5, 6, 7,\r
539     8,10,12,14,\r
540    16,20,24,28, ...\r
541 \r
542    entry #40 = 4096...\r
543    entry #44 = 8192...\r
544    entry #48 = 16384...\r
545    entry #52 = 32768...\r
546    entry #56 = 65536...\r
547 \r
548 increments and decrements are in terms of ratelogtable[n]\r
549 n may exceed the table bounds (plan on n being between -32 and 127).\r
550 table values are all clipped between 0x00000000 and 0x3FFFFFFF\r
551 \r
552 when you "voice on", the envelope is always fully reset.\r
553 (yes, it may click. the real thing does this too.)\r
554 \r
555 envelope level begins at zero.\r
556 \r
557 each state happens for at least 1 cycle\r
558 (transitions are not instantaneous)\r
559 this may result in some oddness: if the decay rate is uberfast, it will cut\r
560 the envelope from full down to half in one sample, potentially skipping over\r
561 the sustain level\r
562 \r
563 ATTACK\r
564 ------\r
565 - if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and\r
566   proceed to DECAY.\r
567 \r
568 Linear attack mode:\r
569 - line extends upward to 0x7FFFFFFF\r
570 - increment per sample is ratelogtable[(Ar^0x7F)-0x10]\r
571 \r
572 Logarithmic attack mode:\r
573 if envelope_level < 0x60000000:\r
574   - line extends upward to 0x60000000\r
575   - increment per sample is ratelogtable[(Ar^0x7F)-0x10]\r
576 else:\r
577   - line extends upward to 0x7FFFFFFF\r
578   - increment per sample is ratelogtable[(Ar^0x7F)-0x18]\r
579 \r
580 DECAY\r
581 -----\r
582 - if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.\r
583   Do not clip to the sustain level.\r
584 - current line ends at (envelope_level & 0x07FFFFFF)\r
585 - decrement per sample depends on (envelope_level>>28)&0x7\r
586   0: ratelogtable[(4*(Dr^0x1F))-0x18+0]\r
587   1: ratelogtable[(4*(Dr^0x1F))-0x18+4]\r
588   2: ratelogtable[(4*(Dr^0x1F))-0x18+6]\r
589   3: ratelogtable[(4*(Dr^0x1F))-0x18+8]\r
590   4: ratelogtable[(4*(Dr^0x1F))-0x18+9]\r
591   5: ratelogtable[(4*(Dr^0x1F))-0x18+10]\r
592   6: ratelogtable[(4*(Dr^0x1F))-0x18+11]\r
593   7: ratelogtable[(4*(Dr^0x1F))-0x18+12]\r
594   (note that this is the same as the release rate formula, except that\r
595    decay rates 10-1F aren't possible... those would be slower in theory)\r
596 \r
597 SUSTAIN\r
598 -------\r
599 - no terminating condition except for voice off\r
600 - Sd=0 (increase) behavior is identical to ATTACK for both log and linear.\r
601 - Sd=1 (decrease) behavior:\r
602 Linear sustain decrease:\r
603 - line extends to 0x00000000\r
604 - decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]\r
605 Logarithmic sustain decrease:\r
606 - current line ends at (envelope_level & 0x07FFFFFF)\r
607 - decrement per sample depends on (envelope_level>>28)&0x7\r
608   0: ratelogtable[(Sr^0x7F)-0x1B+0]\r
609   1: ratelogtable[(Sr^0x7F)-0x1B+4]\r
610   2: ratelogtable[(Sr^0x7F)-0x1B+6]\r
611   3: ratelogtable[(Sr^0x7F)-0x1B+8]\r
612   4: ratelogtable[(Sr^0x7F)-0x1B+9]\r
613   5: ratelogtable[(Sr^0x7F)-0x1B+10]\r
614   6: ratelogtable[(Sr^0x7F)-0x1B+11]\r
615   7: ratelogtable[(Sr^0x7F)-0x1B+12]\r
616 \r
617 RELEASE\r
618 -------\r
619 - if the envelope level has overflowed to negative, clip to 0 and QUIT.\r
620 \r
621 Linear release mode:\r
622 - line extends to 0x00000000\r
623 - decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]\r
624 \r
625 Logarithmic release mode:\r
626 - line extends to (envelope_level & 0x0FFFFFFF)\r
627 - decrement per sample depends on (envelope_level>>28)&0x7\r
628   0: ratelogtable[(4*(Rr^0x1F))-0x18+0]\r
629   1: ratelogtable[(4*(Rr^0x1F))-0x18+4]\r
630   2: ratelogtable[(4*(Rr^0x1F))-0x18+6]\r
631   3: ratelogtable[(4*(Rr^0x1F))-0x18+8]\r
632   4: ratelogtable[(4*(Rr^0x1F))-0x18+9]\r
633   5: ratelogtable[(4*(Rr^0x1F))-0x18+10]\r
634   6: ratelogtable[(4*(Rr^0x1F))-0x18+11]\r
635   7: ratelogtable[(4*(Rr^0x1F))-0x18+12]\r
636 \r
637 -----------------------------------------------------------------------------\r
638 */\r
639 \r