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