/* 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 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 (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 } } }