mirror of https://github.com/PCSX2/pcsx2.git
240 lines
5.5 KiB
C++
240 lines
5.5 KiB
C++
/* 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]
|
|
*
|
|
* SPU2-X 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 Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or 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 SPU2-X. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Global.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)std::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
|
|
Value += PsxRates[(AttackRate^0x7f)-0x10+32];
|
|
|
|
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 = ((0x80000000 / 0x10 ) * (SustainLevel+1)) - 1;
|
|
|
|
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];
|
|
|
|
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];
|
|
|
|
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_VolumeSlide::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( Increment == 0x7f ) return;
|
|
|
|
s32 value = abs(Value);
|
|
|
|
if (Mode & VOLFLAG_DECREMENT)
|
|
{
|
|
// Decrement
|
|
|
|
if(Mode & VOLFLAG_EXPONENTIAL)
|
|
{
|
|
u32 off = InvExpOffsets[(value>>28)&7];
|
|
value -= PsxRates[(Increment^0x7f)-0x1b+off+32];
|
|
}
|
|
else
|
|
value -= PsxRates[(Increment^0x7f)-0xf+32];
|
|
|
|
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
|
|
// linear / Pseudo below 75% (they're the same)
|
|
value += PsxRates[(Increment^0x7f)-0x10+32];
|
|
|
|
if( value < 0 ) // wrapped around the "top"?
|
|
{
|
|
value = 0x7fffffff;
|
|
Mode = 0; // disable slide
|
|
}
|
|
}
|
|
|
|
Value = (Value < 0) ? -value : value;
|
|
}
|