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