summaryrefslogtreecommitdiff
path: root/plugins/dfsound/adsr.c
diff options
context:
space:
mode:
authorSND\dario86_cp <SND\dario86_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2011-03-12 18:54:28 +0000
committerSND\dario86_cp <SND\dario86_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2011-03-12 18:54:28 +0000
commita58cfdac407bc1d8fedc11acd924b275ba28cc51 (patch)
treeb4c2e08c34ef1bfe0ba947ef8eed931c9a43fc0e /plugins/dfsound/adsr.c
parent9bdd06684bcc627c06ddcf4c406f6b48f0dfe389 (diff)
downloadpcsxr-a58cfdac407bc1d8fedc11acd924b275ba28cc51.tar.gz
Commited patch in issue #8171 (by darktjm).
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@64524 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'plugins/dfsound/adsr.c')
-rw-r--r--plugins/dfsound/adsr.c1527
1 files changed, 764 insertions, 763 deletions
diff --git a/plugins/dfsound/adsr.c b/plugins/dfsound/adsr.c
index f75a5127..d0f7808e 100644
--- a/plugins/dfsound/adsr.c
+++ b/plugins/dfsound/adsr.c
@@ -1,763 +1,764 @@
-/***************************************************************************
- adsr.c - description
- -------------------
- begin : Wed May 15 2002
- copyright : (C) 2002 by Pete Bernert
- email : BlackDove@addcom.de
- ***************************************************************************/
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. See also the license.txt file for *
- * additional informations. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#define _IN_ADSR
-
-// will be included from spu.c
-#ifdef _IN_SPU
-
-////////////////////////////////////////////////////////////////////////
-// ADSR func
-////////////////////////////////////////////////////////////////////////
-
-/*
-ADSR
-- Dr. Hell (Xebra PS1 emu)
-- Accurate (!)
-- http://drhell.web.fc2.com
-
-
-Envelope increase
-0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2))
-48+: 7 - (RATE & 3) / (1 <<((RATE>> 2) - 11))
-
-Envelope decrease
-0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2))
-48+: -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11))
-
-
-Exponential increase
-0000-5FFF = (rate + 0)
-6000+ = (rate + 8)
-
-Exponential decrease
-(molecules (decrease) * level)>> 15
-
------------------------------------
-
-Fraction (release rate)
-1<<((4*32>>2)-11) = 1<<21
-
-
-Increase
-40 = (7-0)<<(11-10) = 7<<1 = 14
-41 = (7-1)<<(11-10) = 6<<1 = 12
-42 = (7-2)<<(11-10) = 5<<1 = 10
-43 = (7-3)<<(11-10) = 4<<1 = 8
-
-44 = (7-0)<<(11-11) = 7<<0 = 7
-45 = (7-1)<<(11-11) = 6<<0 = 6
-46 = (7-2)<<(11-11) = 5<<0 = 5
-47 = (7-3)<<(11-11) = 4<<0 = 4
---
-48 = (7-0) / 1<<(12-11) = 7 / 2
-49 = (7-1) / 1<<(12-11) = 6 / 2
-50 = (7-2) / 1<<(12-11) = 5 / 2
-51 = (7-3) / 1<<(12-11) = 4 / 2
-
-52 = (7-0) / 1<<(13-11) = 7 / 4
-56 = (7-0) / 1<<(14-11) = 7 / 8
-60 = (7-0) / 1<<(15-11) = 7 / 16
-
-
-Decrease
-40 = (-8+0)<<(11-10) = -8<<1 = -16
-41 = (-8+1)<<(11-10) = -7<<1 = -14
-42 = (-8+2)<<(11-10) = -6<<1 = -12
-43 = (-8+3)<<(11-10) = -5<<1 = -10
-
-44 = (-8+0)<<(11-11) = -8<<0 = -8
-45 = (-8+1)<<(11-11) = -7<<0 = -7
-46 = (-8+2)<<(11-11) = -6<<0 = -6
-47 = (-8+3)<<(11-11) = -5<<0 = -5
---
-48 = (-8+0) / 1<<(12-11) = -8 / 2
-49 = (-8+1) / 1<<(12-11) = -7 / 2
-50 = (-8+2) / 1<<(12-11) = -6 / 2
-51 = (-8+3) / 1<<(12-11) = -5 / 2
-*/
-
-
-static int RateTableAdd[128];
-static int RateTableAdd_f[128];
-static int RateTableSub[128];
-static int RateTableSub_f[128];
-static const int RateTable_denom = 1 << (( (4*32)>>2) - 11);
-
-void InitADSR(void) // INIT ADSR
-{
- int lcv;
-
- memset(RateTableAdd,0,sizeof(int)*128);
- memset(RateTableAdd_f,0,sizeof(int)*128);
- memset(RateTableSub,0,sizeof(int)*128);
- memset(RateTableSub_f,0,sizeof(int)*128);
-
-
- // Optimize table - Dr. Hell ADSR math
- for( lcv=0; lcv<48; lcv++ ) {
- RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2));
- RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2));
-
- RateTableAdd_f[lcv] = 0;
- RateTableSub_f[lcv] = 0;
- }
-
- for( lcv=48; lcv<128; lcv++ ) {
- int denom;
-
- denom = 1 << ((lcv>>2) - 11);
-
- // whole
- RateTableAdd[lcv] = (7 - (lcv&3)) / denom;
- RateTableSub[lcv] = (-8 + (lcv&3)) / denom;
-
- // fraction
- RateTableAdd_f[lcv] = (7 - (lcv&3)) % denom;
- RateTableSub_f[lcv] = (-8 + (lcv&3)) % denom;
-
- RateTableAdd_f[lcv] *= RateTable_denom / denom;
- RateTableSub_f[lcv] *= RateTable_denom / denom;
-
- // goofy compiler - mod
- if( RateTableSub_f[lcv] > 0 ) RateTableSub_f[lcv] = -RateTableSub_f[lcv];
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE void StartADSR(int ch) // MIX ADSR
-{
- s_chan[ch].ADSRX.lVolume=1; // and init some adsr vars
- s_chan[ch].ADSRX.State=0;
- s_chan[ch].ADSRX.EnvelopeVol=0;
- s_chan[ch].ADSRX.EnvelopeVol_f=0;
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE int MixADSR(int ch) // MIX ADSR
-{
- int EnvelopeVol = s_chan[ch].ADSRX.EnvelopeVol;
- int EnvelopeVol_f = s_chan[ch].ADSRX.EnvelopeVol_f;
-
-
- // dead volume - voice on
- if( s_chan[ch].iSilent == 2 ) {
- if( s_chan[ch].bStop ) s_chan[ch].bOn = 0;
- return 0;
- }
-
-
- if(s_chan[ch].bStop) // should be stopped:
- { // do release
- if(s_chan[ch].ADSRX.ReleaseModeExp)
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ] * EnvelopeVol ) >> 15;
- else
- EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
- if(EnvelopeVol<0)
- {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- // don't stop if this chan can still cause irqs
- if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq))
- s_chan[ch].bOn=0;
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- else // not stopped yet?
- {
- if(s_chan[ch].ADSRX.State==0) // -> attack
- {
- if(s_chan[ch].ADSRX.AttackModeExp)
- {
- if(EnvelopeVol>=0x6000) {
- EnvelopeVol+=RateTableAdd[s_chan[ch].ADSRX.AttackRate + 8];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 8];
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
- }
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
- }
-
- if( EnvelopeVol_f >= RateTable_denom ) {
- EnvelopeVol_f -= RateTable_denom;
- EnvelopeVol++;
- }
-
- if(EnvelopeVol>=0x8000)
- {
- EnvelopeVol=0x7FFF;
- EnvelopeVol_f=RateTable_denom;
- s_chan[ch].ADSRX.State=1;
- }
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- //--------------------------------------------------//
- if(s_chan[ch].ADSRX.State==1) // -> decay
- {
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.DecayRate * 4 ] * EnvelopeVol ) >> 15;
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.DecayRate * 4 ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
- if(EnvelopeVol<0) {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- }
-
- // FF7 cursor - use Neill's 4-bit accuracy
- if( ((EnvelopeVol>>11)&0xf) <= s_chan[ch].ADSRX.SustainLevel)
- {
- s_chan[ch].ADSRX.State=2;
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- //--------------------------------------------------//
- if(s_chan[ch].ADSRX.State==2) // -> sustain
- {
- if(s_chan[ch].ADSRX.SustainIncrease)
- {
- if(s_chan[ch].ADSRX.SustainModeExp)
- {
- if(EnvelopeVol>=0x6000) {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 8];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 8];
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
- }
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
- }
-
- if( EnvelopeVol_f >= RateTable_denom ) {
- EnvelopeVol_f -= RateTable_denom;
- EnvelopeVol++;
- }
-
- if(EnvelopeVol >= 0x8000)
- {
- EnvelopeVol=0x7FFF;
- EnvelopeVol_f=RateTable_denom;
- }
- }
- else
- {
- if(s_chan[ch].ADSRX.SustainModeExp)
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.SustainRate ] * EnvelopeVol ) >> 15;
- else
- EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.SustainRate ];
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.SustainRate ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
-
- if(EnvelopeVol<0) {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- }
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- }
- return 0;
-}
-
-#endif
-
-/*
-James Higgs ADSR investigations:
-
-PSX SPU Envelope Timings
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-First, here is an extract from doomed's SPU doc, which explains the basics
-of the SPU "volume envelope":
-
-*** doomed doc extract start ***
-
---------------------------------------------------------------------------
-Voices.
---------------------------------------------------------------------------
-The SPU has 24 hardware voices. These voices can be used to reproduce sample
-data, noise or can be used as frequency modulator on the next voice.
-Each voice has it's own programmable ADSR envelope filter. The main volume
-can be programmed independently for left and right output.
-
-The ADSR envelope filter works as follows:
-Ar = Attack rate, which specifies the speed at which the volume increases
- from zero to it's maximum value, as soon as the note on is given. The
- slope can be set to lineair or exponential.
-Dr = Decay rate specifies the speed at which the volume decreases to the
- sustain level. Decay is always decreasing exponentially.
-Sl = Sustain level, base level from which sustain starts.
-Sr = Sustain rate is the rate at which the volume of the sustained note
- increases or decreases. This can be either lineair or exponential.
-Rr = Release rate is the rate at which the volume of the note decreases
- as soon as the note off is given.
-
- lvl |
- ^ | /\Dr __
- Sl _| _ / _ \__--- \
- | / ---__ \ Rr
- | /Ar Sr \ \
- | / \\
- |/___________________\________
- ->time
-
-The overal volume can also be set to sweep up or down lineairly or
-exponentially from it's current value. This can be done seperately
-for left and right.
-
-Relevant SPU registers:
--------------------------------------------------------------
-$1f801xx8 Attack/Decay/Sustain level
-bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
-desc.|Am| Ar |Dr |Sl |
-
-Am 0 Attack mode Linear
- 1 Exponential
-
-Ar 0-7f attack rate
-Dr 0-f decay rate
-Sl 0-f sustain level
--------------------------------------------------------------
-$1f801xxa Sustain rate, Release Rate.
-bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
-desc.|Sm|Sd| 0| Sr |Rm|Rr |
-
-Sm 0 sustain rate mode linear
- 1 exponential
-Sd 0 sustain rate mode increase
- 1 decrease
-Sr 0-7f Sustain Rate
-Rm 0 Linear decrease
- 1 Exponential decrease
-Rr 0-1f Release Rate
-
-Note: decay mode is always Expontial decrease, and thus cannot
-be set.
--------------------------------------------------------------
-$1f801xxc Current ADSR volume
-bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
-desc.|ADSRvol |
-
-ADSRvol Returns the current envelope volume when
- read.
--- James' Note: return range: 0 -> 32767
-
-*** doomed doc extract end ***
-
-By using a small PSX proggie to visualise the envelope as it was played,
-the following results for envelope timing were obtained:
-
-1. Attack rate value (linear mode)
-
- Attack value range: 0 -> 127
-
- Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 |
- -----------------------------------------------------------------
- Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890|
-
- Note: frames is no. of PAL frames to reach full volume (100%
- amplitude)
-
- Hmm, noticing that the time taken to reach full volume doubles
- every time we add 4 to our attack value, we know the equation is
- of form:
- frames = k * 2 ^ (value / 4)
-
- (You may ponder about envelope generator hardware at this point,
- or maybe not... :)
-
- By substituting some stuff and running some checks, we get:
-
- k = 0.00257 (close enuf)
-
- therefore,
- frames = 0.00257 * 2 ^ (value / 4)
- If you just happen to be writing an emulator, then you can probably
- use an equation like:
-
- %volume_increase_per_tick = 1 / frames
-
-
- ------------------------------------
- Pete:
- ms=((1<<(value>>2))*514)/10000
- ------------------------------------
-
-2. Decay rate value (only has log mode)
-
- Decay value range: 0 -> 15
-
- Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
- ------------------------------------------------
- frames | | | | | 6 | 12 | 24 | 47 |
-
- Note: frames here is no. of PAL frames to decay to 50% volume.
-
- formula: frames = k * 2 ^ (value)
-
- Substituting, we get: k = 0.00146
-
- Further info on logarithmic nature:
- frames to decay to sustain level 3 = 3 * frames to decay to
- sustain level 9
-
- Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
- 50% volume.
-
- Frag it - just use linear approx.
-
- ------------------------------------
- Pete:
- ms=((1<<value)*292)/10000
- ------------------------------------
-
-
-3. Sustain rate value (linear mode)
-
- Sustain rate range: 0 -> 127
-
- Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 |
- -------------------------------------------
- frames | 9 | 19 | 37 | 74 | 147| 293| 587|
-
- Here, frames = no. of PAL frames for volume amplitude to go from 100%
- to 0% (or vice-versa).
-
- Same formula as for attack value, just a different value for k:
-
- k = 0.00225
-
- ie: frames = 0.00225 * 2 ^ (value / 4)
-
- For emulation purposes:
-
- %volume_increase_or_decrease_per_tick = 1 / frames
-
- ------------------------------------
- Pete:
- ms=((1<<(value>>2))*450)/10000
- ------------------------------------
-
-
-4. Release rate (linear mode)
-
- Release rate range: 0 -> 31
-
- Value | 13 | 14 | 15 | 16 | 17 |
- ---------------------------------------------------------------
- frames | 18 | 36 | 73 | 146| 292|
-
- Here, frames = no. of PAL frames to decay from 100% vol to 0% vol
- after "note-off" is triggered.
-
- Formula: frames = k * 2 ^ (value)
-
- And so: k = 0.00223
-
- ------------------------------------
- Pete:
- ms=((1<<value)*446)/10000
- ------------------------------------
-
-
-Other notes:
-
-Log stuff not figured out. You may get some clues from the "Decay rate"
-stuff above. For emu purposes it may not be important - use linear
-approx.
-
-To get timings in millisecs, multiply frames by 20.
-
-
-
-- James Higgs 17/6/2000
-james7780@yahoo.com
-
-//---------------------------------------------------------------
-
-OLD adsr mixing according to james' rules... has to be called
-every one millisecond
-
-
- long v,v2,lT,l1,l2,l3;
-
- if(s_chan[ch].bStop) // psx wants to stop? -> release phase
- {
- if(s_chan[ch].ADSR.ReleaseVal!=0) // -> release not 0: do release (if 0: stop right now)
- {
- if(!s_chan[ch].ADSR.ReleaseVol) // --> release just started? set up the release stuff
- {
- s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;
- s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;
- s_chan[ch].ADSR.ReleaseTime = // --> calc how long does it take to reach the wanted sus level
- (s_chan[ch].ADSR.ReleaseTime*
- s_chan[ch].ADSR.ReleaseVol)/1024;
- }
- // -> NO release exp mode used (yet)
- v=s_chan[ch].ADSR.ReleaseVol; // -> get last volume
- lT=s_chan[ch].ADSR.lTime- // -> how much time is past?
- s_chan[ch].ADSR.ReleaseStartTime;
- l1=s_chan[ch].ADSR.ReleaseTime;
-
- if(lT<l1) // -> we still have to release
- {
- v=v-((v*lT)/l1); // --> calc new volume
- }
- else // -> release is over: now really stop that sample
- {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
- }
- else // -> release IS 0: release at once
- {
- v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;
- }
- }
- else
- {//--------------------------------------------------// not in release phase:
- v=1024;
- lT=s_chan[ch].ADSR.lTime;
- l1=s_chan[ch].ADSR.AttackTime;
-
- if(lT<l1) // attack
- { // no exp mode used (yet)
-// if(s_chan[ch].ADSR.AttackModeExp)
-// {
-// v=(v*lT)/l1;
-// }
-// else
- {
- v=(v*lT)/l1;
- }
- if(v==0) v=1;
- }
- else // decay
- { // should be exp, but who cares? ;)
- l2=s_chan[ch].ADSR.DecayTime;
- v2=s_chan[ch].ADSR.SustainLevel;
-
- lT-=l1;
- if(lT<l2)
- {
- v-=(((v-v2)*lT)/l2);
- }
- else // sustain
- { // no exp mode used (yet)
- l3=s_chan[ch].ADSR.SustainTime;
- lT-=l2;
- if(s_chan[ch].ADSR.SustainModeDec>0)
- {
- if(l3!=0) v2+=((v-v2)*lT)/l3;
- else v2=v;
- }
- else
- {
- if(l3!=0) v2-=(v2*lT)/l3;
- else v2=v;
- }
-
- if(v2>v) v2=v;
- if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
-
- v=v2;
- }
- }
- }
-
- //----------------------------------------------------//
- // ok, done for this channel, so increase time
-
- s_chan[ch].ADSR.lTime+=1; // 1 = 1.020408f ms;
-
- if(v>1024) v=1024; // adjust volume
- if(v<0) v=0;
- s_chan[ch].ADSR.lVolume=v; // store act volume
-
- return v; // return the volume factor
-*/
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
-
-/*
------------------------------------------------------------------------------
-Neill Corlett
-Playstation SPU envelope timing notes
------------------------------------------------------------------------------
-
-This is preliminary. This may be wrong. But the model described herein fits
-all of my experimental data, and it's just simple enough to sound right.
-
-ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.
-The value returned by channel reg 0xC is (envelope_level>>16).
-
-Each sample, an increment or decrement value will be added to or
-subtracted from this envelope level.
-
-Create the rate log table. The values double every 4 entries.
- entry #0 = 4
-
- 4, 5, 6, 7,
- 8,10,12,14,
- 16,20,24,28, ...
-
- entry #40 = 4096...
- entry #44 = 8192...
- entry #48 = 16384...
- entry #52 = 32768...
- entry #56 = 65536...
-
-increments and decrements are in terms of ratelogtable[n]
-n may exceed the table bounds (plan on n being between -32 and 127).
-table values are all clipped between 0x00000000 and 0x3FFFFFFF
-
-when you "voice on", the envelope is always fully reset.
-(yes, it may click. the real thing does this too.)
-
-envelope level begins at zero.
-
-each state happens for at least 1 cycle
-(transitions are not instantaneous)
-this may result in some oddness: if the decay rate is uberfast, it will cut
-the envelope from full down to half in one sample, potentially skipping over
-the sustain level
-
-ATTACK
-------
-- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and
- proceed to DECAY.
-
-Linear attack mode:
-- line extends upward to 0x7FFFFFFF
-- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
-
-Logarithmic attack mode:
-if envelope_level < 0x60000000:
- - line extends upward to 0x60000000
- - increment per sample is ratelogtable[(Ar^0x7F)-0x10]
-else:
- - line extends upward to 0x7FFFFFFF
- - increment per sample is ratelogtable[(Ar^0x7F)-0x18]
-
-DECAY
------
-- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.
- Do not clip to the sustain level.
-- current line ends at (envelope_level & 0x07FFFFFF)
-- decrement per sample depends on (envelope_level>>28)&0x7
- 0: ratelogtable[(4*(Dr^0x1F))-0x18+0]
- 1: ratelogtable[(4*(Dr^0x1F))-0x18+4]
- 2: ratelogtable[(4*(Dr^0x1F))-0x18+6]
- 3: ratelogtable[(4*(Dr^0x1F))-0x18+8]
- 4: ratelogtable[(4*(Dr^0x1F))-0x18+9]
- 5: ratelogtable[(4*(Dr^0x1F))-0x18+10]
- 6: ratelogtable[(4*(Dr^0x1F))-0x18+11]
- 7: ratelogtable[(4*(Dr^0x1F))-0x18+12]
- (note that this is the same as the release rate formula, except that
- decay rates 10-1F aren't possible... those would be slower in theory)
-
-SUSTAIN
--------
-- no terminating condition except for voice off
-- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.
-- Sd=1 (decrease) behavior:
-Linear sustain decrease:
-- line extends to 0x00000000
-- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]
-Logarithmic sustain decrease:
-- current line ends at (envelope_level & 0x07FFFFFF)
-- decrement per sample depends on (envelope_level>>28)&0x7
- 0: ratelogtable[(Sr^0x7F)-0x1B+0]
- 1: ratelogtable[(Sr^0x7F)-0x1B+4]
- 2: ratelogtable[(Sr^0x7F)-0x1B+6]
- 3: ratelogtable[(Sr^0x7F)-0x1B+8]
- 4: ratelogtable[(Sr^0x7F)-0x1B+9]
- 5: ratelogtable[(Sr^0x7F)-0x1B+10]
- 6: ratelogtable[(Sr^0x7F)-0x1B+11]
- 7: ratelogtable[(Sr^0x7F)-0x1B+12]
-
-RELEASE
--------
-- if the envelope level has overflowed to negative, clip to 0 and QUIT.
-
-Linear release mode:
-- line extends to 0x00000000
-- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]
-
-Logarithmic release mode:
-- line extends to (envelope_level & 0x0FFFFFFF)
-- decrement per sample depends on (envelope_level>>28)&0x7
- 0: ratelogtable[(4*(Rr^0x1F))-0x18+0]
- 1: ratelogtable[(4*(Rr^0x1F))-0x18+4]
- 2: ratelogtable[(4*(Rr^0x1F))-0x18+6]
- 3: ratelogtable[(4*(Rr^0x1F))-0x18+8]
- 4: ratelogtable[(4*(Rr^0x1F))-0x18+9]
- 5: ratelogtable[(4*(Rr^0x1F))-0x18+10]
- 6: ratelogtable[(4*(Rr^0x1F))-0x18+11]
- 7: ratelogtable[(4*(Rr^0x1F))-0x18+12]
-
------------------------------------------------------------------------------
-*/
-
+/***************************************************************************
+ adsr.c - description
+ -------------------
+ begin : Wed May 15 2002
+ copyright : (C) 2002 by Pete Bernert
+ email : BlackDove@addcom.de
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. See also the license.txt file for *
+ * additional informations. *
+ * *
+ ***************************************************************************/
+
+#include "stdafx.h"
+#include "adsr.h"
+
+#define _IN_ADSR
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// ADSR func
+////////////////////////////////////////////////////////////////////////
+
+/*
+ADSR
+- Dr. Hell (Xebra PS1 emu)
+- Accurate (!)
+- http://drhell.web.fc2.com
+
+
+Envelope increase
+0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2))
+48+: 7 - (RATE & 3) / (1 <<((RATE>> 2) - 11))
+
+Envelope decrease
+0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2))
+48+: -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11))
+
+
+Exponential increase
+0000-5FFF = (rate + 0)
+6000+ = (rate + 8)
+
+Exponential decrease
+(molecules (decrease) * level)>> 15
+
+-----------------------------------
+
+Fraction (release rate)
+1<<((4*32>>2)-11) = 1<<21
+
+
+Increase
+40 = (7-0)<<(11-10) = 7<<1 = 14
+41 = (7-1)<<(11-10) = 6<<1 = 12
+42 = (7-2)<<(11-10) = 5<<1 = 10
+43 = (7-3)<<(11-10) = 4<<1 = 8
+
+44 = (7-0)<<(11-11) = 7<<0 = 7
+45 = (7-1)<<(11-11) = 6<<0 = 6
+46 = (7-2)<<(11-11) = 5<<0 = 5
+47 = (7-3)<<(11-11) = 4<<0 = 4
+--
+48 = (7-0) / 1<<(12-11) = 7 / 2
+49 = (7-1) / 1<<(12-11) = 6 / 2
+50 = (7-2) / 1<<(12-11) = 5 / 2
+51 = (7-3) / 1<<(12-11) = 4 / 2
+
+52 = (7-0) / 1<<(13-11) = 7 / 4
+56 = (7-0) / 1<<(14-11) = 7 / 8
+60 = (7-0) / 1<<(15-11) = 7 / 16
+
+
+Decrease
+40 = (-8+0)<<(11-10) = -8<<1 = -16
+41 = (-8+1)<<(11-10) = -7<<1 = -14
+42 = (-8+2)<<(11-10) = -6<<1 = -12
+43 = (-8+3)<<(11-10) = -5<<1 = -10
+
+44 = (-8+0)<<(11-11) = -8<<0 = -8
+45 = (-8+1)<<(11-11) = -7<<0 = -7
+46 = (-8+2)<<(11-11) = -6<<0 = -6
+47 = (-8+3)<<(11-11) = -5<<0 = -5
+--
+48 = (-8+0) / 1<<(12-11) = -8 / 2
+49 = (-8+1) / 1<<(12-11) = -7 / 2
+50 = (-8+2) / 1<<(12-11) = -6 / 2
+51 = (-8+3) / 1<<(12-11) = -5 / 2
+*/
+
+
+static int RateTableAdd[128];
+static int RateTableAdd_f[128];
+static int RateTableSub[128];
+static int RateTableSub_f[128];
+static const int RateTable_denom = 1 << (( (4*32)>>2) - 11);
+
+void InitADSR(void) // INIT ADSR
+{
+ int lcv;
+
+ memset(RateTableAdd,0,sizeof(int)*128);
+ memset(RateTableAdd_f,0,sizeof(int)*128);
+ memset(RateTableSub,0,sizeof(int)*128);
+ memset(RateTableSub_f,0,sizeof(int)*128);
+
+
+ // Optimize table - Dr. Hell ADSR math
+ for( lcv=0; lcv<48; lcv++ ) {
+ RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2));
+ RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2));
+
+ RateTableAdd_f[lcv] = 0;
+ RateTableSub_f[lcv] = 0;
+ }
+
+ for( lcv=48; lcv<128; lcv++ ) {
+ int denom;
+
+ denom = 1 << ((lcv>>2) - 11);
+
+ // whole
+ RateTableAdd[lcv] = (7 - (lcv&3)) / denom;
+ RateTableSub[lcv] = (-8 + (lcv&3)) / denom;
+
+ // fraction
+ RateTableAdd_f[lcv] = (7 - (lcv&3)) % denom;
+ RateTableSub_f[lcv] = (-8 + (lcv&3)) % denom;
+
+ RateTableAdd_f[lcv] *= RateTable_denom / denom;
+ RateTableSub_f[lcv] *= RateTable_denom / denom;
+
+ // goofy compiler - mod
+ if( RateTableSub_f[lcv] > 0 ) RateTableSub_f[lcv] = -RateTableSub_f[lcv];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StartADSR(int ch) // MIX ADSR
+{
+ s_chan[ch].ADSRX.lVolume=1; // and init some adsr vars
+ s_chan[ch].ADSRX.State=0;
+ s_chan[ch].ADSRX.EnvelopeVol=0;
+ s_chan[ch].ADSRX.EnvelopeVol_f=0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int MixADSR(int ch) // MIX ADSR
+{
+ int EnvelopeVol = s_chan[ch].ADSRX.EnvelopeVol;
+ int EnvelopeVol_f = s_chan[ch].ADSRX.EnvelopeVol_f;
+
+
+ // dead volume - voice on
+ if( s_chan[ch].iSilent == 2 ) {
+ if( s_chan[ch].bStop ) s_chan[ch].bOn = 0;
+ return 0;
+ }
+
+
+ if(s_chan[ch].bStop) // should be stopped:
+ { // do release
+ if(s_chan[ch].ADSRX.ReleaseModeExp)
+ EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ] * EnvelopeVol ) >> 15;
+ else
+ EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
+
+ EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
+ if( EnvelopeVol_f < 0 ) {
+ EnvelopeVol_f += RateTable_denom;
+ EnvelopeVol--;
+ }
+
+ if(EnvelopeVol<0)
+ {
+ EnvelopeVol=0;
+ EnvelopeVol_f=0;
+ // don't stop if this chan can still cause irqs
+ if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq))
+ s_chan[ch].bOn=0;
+ }
+
+
+ s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
+ s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
+ s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
+ return EnvelopeVol>>5;
+ }
+ else // not stopped yet?
+ {
+ if(s_chan[ch].ADSRX.State==0) // -> attack
+ {
+ if(s_chan[ch].ADSRX.AttackModeExp)
+ {
+ if(EnvelopeVol>=0x6000) {
+ EnvelopeVol+=RateTableAdd[s_chan[ch].ADSRX.AttackRate + 8];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 8];
+ }
+ else {
+ EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
+ }
+ }
+ else {
+ EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
+ }
+
+ if( EnvelopeVol_f >= RateTable_denom ) {
+ EnvelopeVol_f -= RateTable_denom;
+ EnvelopeVol++;
+ }
+
+ if(EnvelopeVol>=0x8000)
+ {
+ EnvelopeVol=0x7FFF;
+ EnvelopeVol_f=RateTable_denom;
+ s_chan[ch].ADSRX.State=1;
+ }
+
+ s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
+ s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
+ s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
+ return EnvelopeVol>>5;
+ }
+ //--------------------------------------------------//
+ if(s_chan[ch].ADSRX.State==1) // -> decay
+ {
+ EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.DecayRate * 4 ] * EnvelopeVol ) >> 15;
+
+ EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.DecayRate * 4 ];
+ if( EnvelopeVol_f < 0 ) {
+ EnvelopeVol_f += RateTable_denom;
+ EnvelopeVol--;
+ }
+
+ if(EnvelopeVol<0) {
+ EnvelopeVol=0;
+ EnvelopeVol_f=0;
+ }
+
+ // FF7 cursor - use Neill's 4-bit accuracy
+ if( ((EnvelopeVol>>11)&0xf) <= s_chan[ch].ADSRX.SustainLevel)
+ {
+ s_chan[ch].ADSRX.State=2;
+ }
+
+
+ s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
+ s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
+ s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
+ return EnvelopeVol>>5;
+ }
+ //--------------------------------------------------//
+ if(s_chan[ch].ADSRX.State==2) // -> sustain
+ {
+ if(s_chan[ch].ADSRX.SustainIncrease)
+ {
+ if(s_chan[ch].ADSRX.SustainModeExp)
+ {
+ if(EnvelopeVol>=0x6000) {
+ EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 8];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 8];
+ }
+ else {
+ EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
+ }
+ }
+ else {
+ EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
+ EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
+ }
+
+ if( EnvelopeVol_f >= RateTable_denom ) {
+ EnvelopeVol_f -= RateTable_denom;
+ EnvelopeVol++;
+ }
+
+ if(EnvelopeVol >= 0x8000)
+ {
+ EnvelopeVol=0x7FFF;
+ EnvelopeVol_f=RateTable_denom;
+ }
+ }
+ else
+ {
+ if(s_chan[ch].ADSRX.SustainModeExp)
+ EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.SustainRate ] * EnvelopeVol ) >> 15;
+ else
+ EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.SustainRate ];
+
+ EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.SustainRate ];
+ if( EnvelopeVol_f < 0 ) {
+ EnvelopeVol_f += RateTable_denom;
+ EnvelopeVol--;
+ }
+
+
+ if(EnvelopeVol<0) {
+ EnvelopeVol=0;
+ EnvelopeVol_f=0;
+ }
+ }
+
+
+ s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
+ s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
+ s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
+ return EnvelopeVol>>5;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+/*
+James Higgs ADSR investigations:
+
+PSX SPU Envelope Timings
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+First, here is an extract from doomed's SPU doc, which explains the basics
+of the SPU "volume envelope":
+
+*** doomed doc extract start ***
+
+--------------------------------------------------------------------------
+Voices.
+--------------------------------------------------------------------------
+The SPU has 24 hardware voices. These voices can be used to reproduce sample
+data, noise or can be used as frequency modulator on the next voice.
+Each voice has it's own programmable ADSR envelope filter. The main volume
+can be programmed independently for left and right output.
+
+The ADSR envelope filter works as follows:
+Ar = Attack rate, which specifies the speed at which the volume increases
+ from zero to it's maximum value, as soon as the note on is given. The
+ slope can be set to lineair or exponential.
+Dr = Decay rate specifies the speed at which the volume decreases to the
+ sustain level. Decay is always decreasing exponentially.
+Sl = Sustain level, base level from which sustain starts.
+Sr = Sustain rate is the rate at which the volume of the sustained note
+ increases or decreases. This can be either lineair or exponential.
+Rr = Release rate is the rate at which the volume of the note decreases
+ as soon as the note off is given.
+
+ lvl |
+ ^ | /\Dr __
+ Sl _| _ / _ \__--- \
+ | / ---__ \ Rr
+ | /Ar Sr \ \
+ | / \\
+ |/___________________\________
+ ->time
+
+The overal volume can also be set to sweep up or down lineairly or
+exponentially from it's current value. This can be done seperately
+for left and right.
+
+Relevant SPU registers:
+-------------------------------------------------------------
+$1f801xx8 Attack/Decay/Sustain level
+bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
+desc.|Am| Ar |Dr |Sl |
+
+Am 0 Attack mode Linear
+ 1 Exponential
+
+Ar 0-7f attack rate
+Dr 0-f decay rate
+Sl 0-f sustain level
+-------------------------------------------------------------
+$1f801xxa Sustain rate, Release Rate.
+bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
+desc.|Sm|Sd| 0| Sr |Rm|Rr |
+
+Sm 0 sustain rate mode linear
+ 1 exponential
+Sd 0 sustain rate mode increase
+ 1 decrease
+Sr 0-7f Sustain Rate
+Rm 0 Linear decrease
+ 1 Exponential decrease
+Rr 0-1f Release Rate
+
+Note: decay mode is always Expontial decrease, and thus cannot
+be set.
+-------------------------------------------------------------
+$1f801xxc Current ADSR volume
+bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
+desc.|ADSRvol |
+
+ADSRvol Returns the current envelope volume when
+ read.
+-- James' Note: return range: 0 -> 32767
+
+*** doomed doc extract end ***
+
+By using a small PSX proggie to visualise the envelope as it was played,
+the following results for envelope timing were obtained:
+
+1. Attack rate value (linear mode)
+
+ Attack value range: 0 -> 127
+
+ Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 |
+ -----------------------------------------------------------------
+ Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890|
+
+ Note: frames is no. of PAL frames to reach full volume (100%
+ amplitude)
+
+ Hmm, noticing that the time taken to reach full volume doubles
+ every time we add 4 to our attack value, we know the equation is
+ of form:
+ frames = k * 2 ^ (value / 4)
+
+ (You may ponder about envelope generator hardware at this point,
+ or maybe not... :)
+
+ By substituting some stuff and running some checks, we get:
+
+ k = 0.00257 (close enuf)
+
+ therefore,
+ frames = 0.00257 * 2 ^ (value / 4)
+ If you just happen to be writing an emulator, then you can probably
+ use an equation like:
+
+ %volume_increase_per_tick = 1 / frames
+
+
+ ------------------------------------
+ Pete:
+ ms=((1<<(value>>2))*514)/10000
+ ------------------------------------
+
+2. Decay rate value (only has log mode)
+
+ Decay value range: 0 -> 15
+
+ Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
+ ------------------------------------------------
+ frames | | | | | 6 | 12 | 24 | 47 |
+
+ Note: frames here is no. of PAL frames to decay to 50% volume.
+
+ formula: frames = k * 2 ^ (value)
+
+ Substituting, we get: k = 0.00146
+
+ Further info on logarithmic nature:
+ frames to decay to sustain level 3 = 3 * frames to decay to
+ sustain level 9
+
+ Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
+ 50% volume.
+
+ Frag it - just use linear approx.
+
+ ------------------------------------
+ Pete:
+ ms=((1<<value)*292)/10000
+ ------------------------------------
+
+
+3. Sustain rate value (linear mode)
+
+ Sustain rate range: 0 -> 127
+
+ Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 |
+ -------------------------------------------
+ frames | 9 | 19 | 37 | 74 | 147| 293| 587|
+
+ Here, frames = no. of PAL frames for volume amplitude to go from 100%
+ to 0% (or vice-versa).
+
+ Same formula as for attack value, just a different value for k:
+
+ k = 0.00225
+
+ ie: frames = 0.00225 * 2 ^ (value / 4)
+
+ For emulation purposes:
+
+ %volume_increase_or_decrease_per_tick = 1 / frames
+
+ ------------------------------------
+ Pete:
+ ms=((1<<(value>>2))*450)/10000
+ ------------------------------------
+
+
+4. Release rate (linear mode)
+
+ Release rate range: 0 -> 31
+
+ Value | 13 | 14 | 15 | 16 | 17 |
+ ---------------------------------------------------------------
+ frames | 18 | 36 | 73 | 146| 292|
+
+ Here, frames = no. of PAL frames to decay from 100% vol to 0% vol
+ after "note-off" is triggered.
+
+ Formula: frames = k * 2 ^ (value)
+
+ And so: k = 0.00223
+
+ ------------------------------------
+ Pete:
+ ms=((1<<value)*446)/10000
+ ------------------------------------
+
+
+Other notes:
+
+Log stuff not figured out. You may get some clues from the "Decay rate"
+stuff above. For emu purposes it may not be important - use linear
+approx.
+
+To get timings in millisecs, multiply frames by 20.
+
+
+
+- James Higgs 17/6/2000
+james7780@yahoo.com
+
+//---------------------------------------------------------------
+
+OLD adsr mixing according to james' rules... has to be called
+every one millisecond
+
+
+ long v,v2,lT,l1,l2,l3;
+
+ if(s_chan[ch].bStop) // psx wants to stop? -> release phase
+ {
+ if(s_chan[ch].ADSR.ReleaseVal!=0) // -> release not 0: do release (if 0: stop right now)
+ {
+ if(!s_chan[ch].ADSR.ReleaseVol) // --> release just started? set up the release stuff
+ {
+ s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;
+ s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;
+ s_chan[ch].ADSR.ReleaseTime = // --> calc how long does it take to reach the wanted sus level
+ (s_chan[ch].ADSR.ReleaseTime*
+ s_chan[ch].ADSR.ReleaseVol)/1024;
+ }
+ // -> NO release exp mode used (yet)
+ v=s_chan[ch].ADSR.ReleaseVol; // -> get last volume
+ lT=s_chan[ch].ADSR.lTime- // -> how much time is past?
+ s_chan[ch].ADSR.ReleaseStartTime;
+ l1=s_chan[ch].ADSR.ReleaseTime;
+
+ if(lT<l1) // -> we still have to release
+ {
+ v=v-((v*lT)/l1); // --> calc new volume
+ }
+ else // -> release is over: now really stop that sample
+ {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+ }
+ else // -> release IS 0: release at once
+ {
+ v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;
+ }
+ }
+ else
+ {//--------------------------------------------------// not in release phase:
+ v=1024;
+ lT=s_chan[ch].ADSR.lTime;
+ l1=s_chan[ch].ADSR.AttackTime;
+
+ if(lT<l1) // attack
+ { // no exp mode used (yet)
+// if(s_chan[ch].ADSR.AttackModeExp)
+// {
+// v=(v*lT)/l1;
+// }
+// else
+ {
+ v=(v*lT)/l1;
+ }
+ if(v==0) v=1;
+ }
+ else // decay
+ { // should be exp, but who cares? ;)
+ l2=s_chan[ch].ADSR.DecayTime;
+ v2=s_chan[ch].ADSR.SustainLevel;
+
+ lT-=l1;
+ if(lT<l2)
+ {
+ v-=(((v-v2)*lT)/l2);
+ }
+ else // sustain
+ { // no exp mode used (yet)
+ l3=s_chan[ch].ADSR.SustainTime;
+ lT-=l2;
+ if(s_chan[ch].ADSR.SustainModeDec>0)
+ {
+ if(l3!=0) v2+=((v-v2)*lT)/l3;
+ else v2=v;
+ }
+ else
+ {
+ if(l3!=0) v2-=(v2*lT)/l3;
+ else v2=v;
+ }
+
+ if(v2>v) v2=v;
+ if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+
+ v=v2;
+ }
+ }
+ }
+
+ //----------------------------------------------------//
+ // ok, done for this channel, so increase time
+
+ s_chan[ch].ADSR.lTime+=1; // 1 = 1.020408f ms;
+
+ if(v>1024) v=1024; // adjust volume
+ if(v<0) v=0;
+ s_chan[ch].ADSR.lVolume=v; // store act volume
+
+ return v; // return the volume factor
+*/
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+
+/*
+-----------------------------------------------------------------------------
+Neill Corlett
+Playstation SPU envelope timing notes
+-----------------------------------------------------------------------------
+
+This is preliminary. This may be wrong. But the model described herein fits
+all of my experimental data, and it's just simple enough to sound right.
+
+ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.
+The value returned by channel reg 0xC is (envelope_level>>16).
+
+Each sample, an increment or decrement value will be added to or
+subtracted from this envelope level.
+
+Create the rate log table. The values double every 4 entries.
+ entry #0 = 4
+
+ 4, 5, 6, 7,
+ 8,10,12,14,
+ 16,20,24,28, ...
+
+ entry #40 = 4096...
+ entry #44 = 8192...
+ entry #48 = 16384...
+ entry #52 = 32768...
+ entry #56 = 65536...
+
+increments and decrements are in terms of ratelogtable[n]
+n may exceed the table bounds (plan on n being between -32 and 127).
+table values are all clipped between 0x00000000 and 0x3FFFFFFF
+
+when you "voice on", the envelope is always fully reset.
+(yes, it may click. the real thing does this too.)
+
+envelope level begins at zero.
+
+each state happens for at least 1 cycle
+(transitions are not instantaneous)
+this may result in some oddness: if the decay rate is uberfast, it will cut
+the envelope from full down to half in one sample, potentially skipping over
+the sustain level
+
+ATTACK
+------
+- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and
+ proceed to DECAY.
+
+Linear attack mode:
+- line extends upward to 0x7FFFFFFF
+- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+
+Logarithmic attack mode:
+if envelope_level < 0x60000000:
+ - line extends upward to 0x60000000
+ - increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+else:
+ - line extends upward to 0x7FFFFFFF
+ - increment per sample is ratelogtable[(Ar^0x7F)-0x18]
+
+DECAY
+-----
+- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.
+ Do not clip to the sustain level.
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+ 0: ratelogtable[(4*(Dr^0x1F))-0x18+0]
+ 1: ratelogtable[(4*(Dr^0x1F))-0x18+4]
+ 2: ratelogtable[(4*(Dr^0x1F))-0x18+6]
+ 3: ratelogtable[(4*(Dr^0x1F))-0x18+8]
+ 4: ratelogtable[(4*(Dr^0x1F))-0x18+9]
+ 5: ratelogtable[(4*(Dr^0x1F))-0x18+10]
+ 6: ratelogtable[(4*(Dr^0x1F))-0x18+11]
+ 7: ratelogtable[(4*(Dr^0x1F))-0x18+12]
+ (note that this is the same as the release rate formula, except that
+ decay rates 10-1F aren't possible... those would be slower in theory)
+
+SUSTAIN
+-------
+- no terminating condition except for voice off
+- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.
+- Sd=1 (decrease) behavior:
+Linear sustain decrease:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]
+Logarithmic sustain decrease:
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+ 0: ratelogtable[(Sr^0x7F)-0x1B+0]
+ 1: ratelogtable[(Sr^0x7F)-0x1B+4]
+ 2: ratelogtable[(Sr^0x7F)-0x1B+6]
+ 3: ratelogtable[(Sr^0x7F)-0x1B+8]
+ 4: ratelogtable[(Sr^0x7F)-0x1B+9]
+ 5: ratelogtable[(Sr^0x7F)-0x1B+10]
+ 6: ratelogtable[(Sr^0x7F)-0x1B+11]
+ 7: ratelogtable[(Sr^0x7F)-0x1B+12]
+
+RELEASE
+-------
+- if the envelope level has overflowed to negative, clip to 0 and QUIT.
+
+Linear release mode:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]
+
+Logarithmic release mode:
+- line extends to (envelope_level & 0x0FFFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+ 0: ratelogtable[(4*(Rr^0x1F))-0x18+0]
+ 1: ratelogtable[(4*(Rr^0x1F))-0x18+4]
+ 2: ratelogtable[(4*(Rr^0x1F))-0x18+6]
+ 3: ratelogtable[(4*(Rr^0x1F))-0x18+8]
+ 4: ratelogtable[(4*(Rr^0x1F))-0x18+9]
+ 5: ratelogtable[(4*(Rr^0x1F))-0x18+10]
+ 6: ratelogtable[(4*(Rr^0x1F))-0x18+11]
+ 7: ratelogtable[(4*(Rr^0x1F))-0x18+12]
+
+-----------------------------------------------------------------------------
+*/
+