/* 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 . */ #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, (s64)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; }