From 24ed5ac12e531ad9c28b9e154ca7debf36631f16 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Mon, 16 Feb 2009 20:03:19 +0000 Subject: [PATCH] SPU2-X: Forgot a file (again). git-svn-id: http://pcsx2.googlecode.com/svn/trunk@510 96395faa-99c1-11dd-bbfe-3dabce05a288 --- plugins/spu2-x/src/ADSR.cpp | 242 ++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 plugins/spu2-x/src/ADSR.cpp diff --git a/plugins/spu2-x/src/ADSR.cpp b/plugins/spu2-x/src/ADSR.cpp new file mode 100644 index 0000000000..ec48228fad --- /dev/null +++ b/plugins/spu2-x/src/ADSR.cpp @@ -0,0 +1,242 @@ +/* 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 + } + } +}