pcsx2/plugins/spu2-x/src/ADSR.cpp

243 lines
5.6 KiB
C++
Raw Normal View History

/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "spu2.h"
static const s32 ADSR_MAX_VOL = 0x7fffffff;
static const int InvExpOffsets[] = { 0,4,6,8,9,10,11,12 };
static u32 PsxRates[160];
void InitADSR() // INIT ADSR
{
for (int i=0; i<(32+128); i++)
{
int shift=(i-32)>>2;
s64 rate=(i&3)+4;
if (shift<0)
rate >>= -shift;
else
rate <<= shift;
PsxRates[i] = (int)min( rate, 0x3fffffffLL );
}
}
#define VOL(x) (((s32)x)) //24.8 volume
// Returns the linear slide value for AR and SR inputs.
// (currently not used, it's buggy)
static int GetLinearSrAr( uint SrAr )
{
// The Sr/Ar settings work in quarter steps, which means
// the bottom 2 bits go on the left side of the shift, and
// the right side of the shift gets divided by 4:
const uint newSr = 0x7f - SrAr;
return ((1|(newSr&3)) << (newSr>>2));
}
bool V_ADSR::Calculate()
{
jASSUME( Phase != 0 );
if(Releasing && (Phase < 5))
Phase = 5;
switch (Phase)
{
case 1: // attack
if( Value == ADSR_MAX_VOL )
{
// Already maxed out. Progress phase and nothing more:
Phase++;
break;
}
// Case 1 below is for pseudo exponential below 75%.
// Pseudo Exp > 75% and Linear are the same.
if (AttackMode && (Value>=0x60000000))
Value += PsxRates[(AttackRate^0x7f)-0x18+32];
else //if( AttackRate < 0x7f )
Value+=PsxRates[(AttackRate^0x7f)-0x10+32];
//Value += GetLinearSrAr( AttackRate );
if( Value < 0 )
{
// We hit the ceiling.
Phase++;
Value = ADSR_MAX_VOL;
}
break;
case 2: // decay
{
u32 off = InvExpOffsets[(Value>>28)&7];
Value-=PsxRates[((DecayRate^0x1f)*4)-0x18+off+32];
// calculate sustain level as a factor of the ADSR maximum volume.
s32 suslev = ADSR_MAX_VOL / (0x10 - SustainLevel);
if( Value <= suslev )
{
if (Value < 0)
Value = 0;
Phase++;
}
}
break;
case 3: // sustain
{
// 0x7f disables sustain (infinite sustain)
if( SustainRate == 0x7f ) return true;
if (SustainMode&2) // decreasing
{
if (SustainMode&4) // exponential
{
u32 off = InvExpOffsets[(Value>>28)&7];
Value-=PsxRates[(SustainRate^0x7f)-0x1b+off+32];
}
else // linear
{
Value-=PsxRates[(SustainRate^0x7f)-0xf+32];
//Value -= GetLinearSrAr( SustainRate );
}
if( Value <= 0 )
{
Value = 0;
Phase++;
}
}
else // increasing
{
if( (SustainMode&4) && (Value>=0x60000000) )
Value+=PsxRates[(SustainRate^0x7f)-0x18+32];
else
{
// linear / Pseudo below 75% (they're the same)
Value+=PsxRates[(SustainRate^0x7f)-0x10+32];
//Value += GetLinearSrAr( SustainRate );
}
if( Value < 0 )
{
Value = ADSR_MAX_VOL;
Phase++;
}
}
}
break;
case 4: // sustain end
Value = (SustainMode&2) ? 0 : ADSR_MAX_VOL;
if(Value==0)
Phase=6;
break;
case 5: // release
if (ReleaseMode) // exponential
{
u32 off=InvExpOffsets[(Value>>28)&7];
Value-=PsxRates[((ReleaseRate^0x1f)*4)-0x18+off+32];
}
else // linear
{
//Value-=PsxRates[((ReleaseRate^0x1f)*4)-0xc+32];
if( ReleaseRate != 0x1f )
Value -= (1 << (0x1f-ReleaseRate));
}
if( Value <= 0 )
{
Value=0;
Phase++;
}
break;
case 6: // release end
Value=0;
break;
jNO_DEFAULT
}
// returns true if the voice is active, or false if it's stopping.
return Phase != 6;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
#define VOLFLAG_REVERSE_PHASE (1ul<<0)
#define VOLFLAG_DECREMENT (1ul<<1)
#define VOLFLAG_EXPONENTIAL (1ul<<2)
#define VOLFLAG_SLIDE_ENABLE (1ul<<3)
void V_Volume::Update()
{
if( !(Mode & VOLFLAG_SLIDE_ENABLE) ) return;
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
// instead of multi-stage)
if (Mode & VOLFLAG_DECREMENT)
{
// Decrement
if(Mode & VOLFLAG_EXPONENTIAL)
{
u32 off = InvExpOffsets[(Value>>28)&7];
Value -= PsxRates[(Increment^0x7f)-0x1b+off+32];
}
else
Value -= Increment;
if (Value < 0)
{
Value = 0;
Mode = 0; // disable slide
}
}
else
{
// Increment
// Pseudo-exponential increments, as done by the SPU2 (really!)
// Above 75% slides slow, below 75% slides fast. It's exponential, pseudo'ly speaking.
if( (Mode & VOLFLAG_EXPONENTIAL) && (Value>=0x60000000))
Value += PsxRates[(Increment^0x7f)-0x18+32];
else
Value += Increment;
if( Value < 0 ) // wrapped around the "top"?
{
Value = 0x7fffffff;
Mode = 0; // disable slide
}
}
}