void PS_SPU::RunEnvelope(SPU_Voice *voice)
{
SPU_ADSR *ADSR = &voice->ADSR;
int increment;
int divinco;
int16 uoflow_reset;
if(ADSR->Phase == ADSR_ATTACK && ADSR->EnvLevel == 0x7FFF)
ADSR->Phase++;
//static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool decrement, bool inv_increment, int16 Current, int &increment, int &divinco)
switch(ADSR->Phase)
{
default: assert(0);
break;
case ADSR_ATTACK:
CalcVCDelta(0x7F, ADSR->AttackRate, ADSR->AttackExp, false, false, (int16)ADSR->EnvLevel, increment, divinco);
uoflow_reset = 0x7FFF;
break;
case ADSR_DECAY:
CalcVCDelta(0x1F << 2, ADSR->DecayRate, true, true, true, (int16)ADSR->EnvLevel, increment, divinco);
uoflow_reset = 0;
break;
case ADSR_SUSTAIN:
CalcVCDelta(0x7F, ADSR->SustainRate, ADSR->SustainExp, ADSR->SustainDec, ADSR->SustainDec, (int16)ADSR->EnvLevel, increment, divinco);
uoflow_reset = ADSR->SustainDec ? 0 : 0x7FFF;
break;
case ADSR_RELEASE:
CalcVCDelta(0x1F << 2, ADSR->ReleaseRate, ADSR->ReleaseExp, true, true, (int16)ADSR->EnvLevel, increment, divinco);
uoflow_reset = 0;
break;
}
ADSR->Divider += divinco;
if(ADSR->Divider & 0x8000)
{
const uint16 prev_level = ADSR->EnvLevel;
ADSR->Divider = 0;
ADSR->EnvLevel += increment;
if(ADSR->Phase == ADSR_ATTACK)
{
// If previous the upper bit was 0, but now it's 1, handle overflow.
if(((prev_level ^ ADSR->EnvLevel) & ADSR->EnvLevel) & 0x8000)
ADSR->EnvLevel = uoflow_reset;
}
else
{
if(ADSR->EnvLevel & 0x8000)
ADSR->EnvLevel = uoflow_reset;
}
if(ADSR->Phase == ADSR_DECAY && (uint16)ADSR->EnvLevel < ADSR->SustainLevel)
ADSR->Phase++;
}
}
static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool dec_mode, bool inv_increment, int16 Current, int &increment, int &divinco)
{
increment = (7 - (speed & 0x3));
if(inv_increment)
increment = ~increment;
divinco = 32768;
if(speed < 0x2C)
increment = (unsigned)increment << ((0x2F - speed) >> 2);
if(speed >= 0x30)
divinco >>= (speed - 0x2C) >> 2;
if(log_mode)
{
if(dec_mode) // Log decrement mode
increment = (Current * increment) >> 15;
else // Log increment mode
{
if((Current & 0x7FFF) >= 0x6000)
{
if(speed < 0x28)
increment >>= 2;
else if(speed >= 0x2C)
divinco >>= 2;
else // 0x28 ... 0x2B
{
increment >>= 1;
divinco >>= 1;
}
}
}
} // end if(log_mode)
if(divinco == 0 && speed < zs) //0x7F)
divinco = 1;
}