From 114454a44f0b8f60012e448facece32a7c6c556e Mon Sep 17 00:00:00 2001 From: JoseAaronLopezGarcia Date: Mon, 24 Jul 2023 23:05:32 +0200 Subject: [PATCH] revert --- core/compat/vita/popspatch.c | 7 +- extras/modules/peops/Makefile | 2 +- extras/modules/peops/main.c | 12 + extras/modules/peops/spu/adsr.c | 1399 ++++++++++++-------------- extras/modules/peops/spu/adsr.h | 47 +- extras/modules/peops/spu/externals.c | 26 - extras/modules/peops/spu/externals.h | 648 ++++++------ extras/modules/peops/spu/gauss_i.h | 312 +++--- extras/modules/peops/spu/registers.c | 314 +----- extras/modules/peops/spu/registers.h | 338 +++---- extras/modules/peops/spu/regs.h | 63 +- extras/modules/peops/spu/reverb.c | 979 +++++++++--------- extras/modules/peops/spu/reverb.h | 51 +- extras/modules/peops/spu/spu.c | 1069 ++++++-------------- extras/modules/peops/spu/spu.h | 51 +- extras/modules/peops/spu/stdafx.h | 98 +- extras/modules/peops/spu/xa.c | 333 +++--- extras/modules/peops/spu/xa.h | 40 +- 18 files changed, 2380 insertions(+), 3409 deletions(-) delete mode 100644 extras/modules/peops/spu/externals.c diff --git a/core/compat/vita/popspatch.c b/core/compat/vita/popspatch.c index b7bd62c4e..53e03f872 100644 --- a/core/compat/vita/popspatch.c +++ b/core/compat/vita/popspatch.c @@ -126,7 +126,7 @@ static int myKernelLoadModule(char * fname, int flag, void * opt) void patchPspPopsman(SceModule2* mod){ u32 text_addr = mod->text_addr; u32 top_addr = text_addr + mod->text_size; - + /* for (u32 addr=text_addr; addrtext_addr; + /* + for (u32 addr=text_addr; addrtext_size; addr+=4){ + u32 data = _lw(addr); + // Replace Media Engine SPU Background Thread Starter + if (data == 0x24050260){ + u32 a = addr; + do { a+=4; } while (_lw(a) != 0x8FBF0004); // find end of function + MAKE_CALL(a-8, sceMeAudioInitPatched); + } + } + */ + MAKE_CALL(text_addr + 0x1A038, sceMeAudioInitPatched); REDIRECT_FUNCTION(text_addr + 0x3D264, sceMeAudioNotifyPatched); diff --git a/extras/modules/peops/spu/adsr.c b/extras/modules/peops/spu/adsr.c index b64a287e9..9a3ad0aaf 100644 --- a/extras/modules/peops/spu/adsr.c +++ b/extras/modules/peops/spu/adsr.c @@ -1,763 +1,636 @@ -/*************************************************************************** - adsr.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#include "stdafx.h" - -#define _IN_ADSR - -// will be included from spu.c -#ifdef _IN_SPU - -//////////////////////////////////////////////////////////////////////// -// ADSR func -//////////////////////////////////////////////////////////////////////// - -/* -ADSR -- Dr. Hell (Xebra PS1 emu) -- Accurate (!) -- http://drhell.web.fc2.com - - -Envelope increase -0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2)) -48+: 7 - (RATE & 3) / (1 <<((RATE>> 2) - 11)) - -Envelope decrease -0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2)) -48+: -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11)) - - -Exponential increase -0000-5FFF = (rate + 0) -6000+ = (rate + 8) - -Exponential decrease -(molecules (decrease) * level)>> 15 - ------------------------------------ - -Fraction (release rate) -1<<((4*32>>2)-11) = 1<<21 - - -Increase -40 = (7-0)<<(11-10) = 7<<1 = 14 -41 = (7-1)<<(11-10) = 6<<1 = 12 -42 = (7-2)<<(11-10) = 5<<1 = 10 -43 = (7-3)<<(11-10) = 4<<1 = 8 - -44 = (7-0)<<(11-11) = 7<<0 = 7 -45 = (7-1)<<(11-11) = 6<<0 = 6 -46 = (7-2)<<(11-11) = 5<<0 = 5 -47 = (7-3)<<(11-11) = 4<<0 = 4 --- -48 = (7-0) / 1<<(12-11) = 7 / 2 -49 = (7-1) / 1<<(12-11) = 6 / 2 -50 = (7-2) / 1<<(12-11) = 5 / 2 -51 = (7-3) / 1<<(12-11) = 4 / 2 - -52 = (7-0) / 1<<(13-11) = 7 / 4 -56 = (7-0) / 1<<(14-11) = 7 / 8 -60 = (7-0) / 1<<(15-11) = 7 / 16 - - -Decrease -40 = (-8+0)<<(11-10) = -8<<1 = -16 -41 = (-8+1)<<(11-10) = -7<<1 = -14 -42 = (-8+2)<<(11-10) = -6<<1 = -12 -43 = (-8+3)<<(11-10) = -5<<1 = -10 - -44 = (-8+0)<<(11-11) = -8<<0 = -8 -45 = (-8+1)<<(11-11) = -7<<0 = -7 -46 = (-8+2)<<(11-11) = -6<<0 = -6 -47 = (-8+3)<<(11-11) = -5<<0 = -5 --- -48 = (-8+0) / 1<<(12-11) = -8 / 2 -49 = (-8+1) / 1<<(12-11) = -7 / 2 -50 = (-8+2) / 1<<(12-11) = -6 / 2 -51 = (-8+3) / 1<<(12-11) = -5 / 2 -*/ - - -static int RateTableAdd[128]; -static int RateTableAdd_f[128]; -static int RateTableSub[128]; -static int RateTableSub_f[128]; -static const int RateTable_denom = 1 << (( (4*32)>>2) - 11); - -void InitADSR(void) // INIT ADSR -{ - int lcv; - - memset(RateTableAdd,0,sizeof(int)*128); - memset(RateTableAdd_f,0,sizeof(int)*128); - memset(RateTableSub,0,sizeof(int)*128); - memset(RateTableSub_f,0,sizeof(int)*128); - - - // Optimize table - Dr. Hell ADSR math - for( lcv=0; lcv<48; lcv++ ) { - RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2)); - RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2)); - - RateTableAdd_f[lcv] = 0; - RateTableSub_f[lcv] = 0; - } - - for( lcv=48; lcv<128; lcv++ ) { - int denom; - - denom = 1 << ((lcv>>2) - 11); - - // whole - RateTableAdd[lcv] = (7 - (lcv&3)) / denom; - RateTableSub[lcv] = (-8 + (lcv&3)) / denom; - - // fraction - RateTableAdd_f[lcv] = (7 - (lcv&3)) % denom; - RateTableSub_f[lcv] = (-8 + (lcv&3)) % denom; - - RateTableAdd_f[lcv] *= RateTable_denom / denom; - RateTableSub_f[lcv] *= RateTable_denom / denom; - - // goofy compiler - mod - if( RateTableSub_f[lcv] > 0 ) RateTableSub_f[lcv] = -RateTableSub_f[lcv]; - } -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE void StartADSR(int ch) // MIX ADSR -{ - s_chan[ch].ADSRX.lVolume=1; // and init some adsr vars - s_chan[ch].ADSRX.State=0; - s_chan[ch].ADSRX.EnvelopeVol=0; - s_chan[ch].ADSRX.EnvelopeVol_f=0; -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int MixADSR(int ch) // MIX ADSR -{ - int EnvelopeVol = s_chan[ch].ADSRX.EnvelopeVol; - int EnvelopeVol_f = s_chan[ch].ADSRX.EnvelopeVol_f; - - - // dead volume - voice on - if( s_chan[ch].iSilent == 2 ) { - if( s_chan[ch].bStop ) s_chan[ch].bOn = 0; - return 0; - } - - - if(s_chan[ch].bStop) // should be stopped: - { // do release - if(s_chan[ch].ADSRX.ReleaseModeExp) - EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ] * EnvelopeVol ) >> 15; - else - EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ]; - - EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.ReleaseRate * 4 ]; - if( EnvelopeVol_f < 0 ) { - EnvelopeVol_f += RateTable_denom; - EnvelopeVol--; - } - - if(EnvelopeVol<0) - { - EnvelopeVol=0; - EnvelopeVol_f=0; - // don't stop if this chan can still cause irqs - if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq)) - s_chan[ch].bOn=0; - } - - - s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; - s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; - s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; - return EnvelopeVol>>5; - } - else // not stopped yet? - { - if(s_chan[ch].ADSRX.State==0) // -> attack - { - if(s_chan[ch].ADSRX.AttackModeExp) - { - if(EnvelopeVol>=0x6000) { - EnvelopeVol+=RateTableAdd[s_chan[ch].ADSRX.AttackRate + 8]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 8]; - } - else { - EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0]; - } - } - else { - EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0]; - } - - if( EnvelopeVol_f >= RateTable_denom ) { - EnvelopeVol_f -= RateTable_denom; - EnvelopeVol++; - } - - if(EnvelopeVol>=0x8000) - { - EnvelopeVol=0x7FFF; - EnvelopeVol_f=RateTable_denom; - s_chan[ch].ADSRX.State=1; - } - - s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; - s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; - s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; - return EnvelopeVol>>5; - } - //--------------------------------------------------// - if(s_chan[ch].ADSRX.State==1) // -> decay - { - EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.DecayRate * 4 ] * EnvelopeVol ) >> 15; - - EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.DecayRate * 4 ]; - if( EnvelopeVol_f < 0 ) { - EnvelopeVol_f += RateTable_denom; - EnvelopeVol--; - } - - if(EnvelopeVol<0) { - EnvelopeVol=0; - EnvelopeVol_f=0; - } - - // FF7 cursor - use Neill's 4-bit accuracy - if( ((EnvelopeVol>>11)&0xf) <= s_chan[ch].ADSRX.SustainLevel) - { - s_chan[ch].ADSRX.State=2; - } - - - s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; - s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; - s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; - return EnvelopeVol>>5; - } - //--------------------------------------------------// - if(s_chan[ch].ADSRX.State==2) // -> sustain - { - if(s_chan[ch].ADSRX.SustainIncrease) - { - if(s_chan[ch].ADSRX.SustainModeExp) - { - if(EnvelopeVol>=0x6000) { - EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 8]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 8]; - } - else { - EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0]; - } - } - else { - EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0]; - EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0]; - } - - if( EnvelopeVol_f >= RateTable_denom ) { - EnvelopeVol_f -= RateTable_denom; - EnvelopeVol++; - } - - if(EnvelopeVol >= 0x8000) - { - EnvelopeVol=0x7FFF; - EnvelopeVol_f=RateTable_denom; - } - } - else - { - if(s_chan[ch].ADSRX.SustainModeExp) - EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.SustainRate ] * EnvelopeVol ) >> 15; - else - EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.SustainRate ]; - - EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.SustainRate ]; - if( EnvelopeVol_f < 0 ) { - EnvelopeVol_f += RateTable_denom; - EnvelopeVol--; - } - - - if(EnvelopeVol<0) { - EnvelopeVol=0; - EnvelopeVol_f=0; - } - } - - - s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; - s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; - s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; - return EnvelopeVol>>5; - } - } - return 0; -} - -#endif - -/* -James Higgs ADSR investigations: - -PSX SPU Envelope Timings -~~~~~~~~~~~~~~~~~~~~~~~~ - -First, here is an extract from doomed's SPU doc, which explains the basics -of the SPU "volume envelope": - -*** doomed doc extract start *** - --------------------------------------------------------------------------- -Voices. --------------------------------------------------------------------------- -The SPU has 24 hardware voices. These voices can be used to reproduce sample -data, noise or can be used as frequency modulator on the next voice. -Each voice has it's own programmable ADSR envelope filter. The main volume -can be programmed independently for left and right output. - -The ADSR envelope filter works as follows: -Ar = Attack rate, which specifies the speed at which the volume increases - from zero to it's maximum value, as soon as the note on is given. The - slope can be set to lineair or exponential. -Dr = Decay rate specifies the speed at which the volume decreases to the - sustain level. Decay is always decreasing exponentially. -Sl = Sustain level, base level from which sustain starts. -Sr = Sustain rate is the rate at which the volume of the sustained note - increases or decreases. This can be either lineair or exponential. -Rr = Release rate is the rate at which the volume of the note decreases - as soon as the note off is given. - - lvl | - ^ | /\Dr __ - Sl _| _ / _ \__--- \ - | / ---__ \ Rr - | /Ar Sr \ \ - | / \\ - |/___________________\________ - ->time - -The overal volume can also be set to sweep up or down lineairly or -exponentially from it's current value. This can be done seperately -for left and right. - -Relevant SPU registers: -------------------------------------------------------------- -$1f801xx8 Attack/Decay/Sustain level -bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00| -desc.|Am| Ar |Dr |Sl | - -Am 0 Attack mode Linear - 1 Exponential - -Ar 0-7f attack rate -Dr 0-f decay rate -Sl 0-f sustain level -------------------------------------------------------------- -$1f801xxa Sustain rate, Release Rate. -bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00| -desc.|Sm|Sd| 0| Sr |Rm|Rr | - -Sm 0 sustain rate mode linear - 1 exponential -Sd 0 sustain rate mode increase - 1 decrease -Sr 0-7f Sustain Rate -Rm 0 Linear decrease - 1 Exponential decrease -Rr 0-1f Release Rate - -Note: decay mode is always Expontial decrease, and thus cannot -be set. -------------------------------------------------------------- -$1f801xxc Current ADSR volume -bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00| -desc.|ADSRvol | - -ADSRvol Returns the current envelope volume when - read. --- James' Note: return range: 0 -> 32767 - -*** doomed doc extract end *** - -By using a small PSX proggie to visualise the envelope as it was played, -the following results for envelope timing were obtained: - -1. Attack rate value (linear mode) - - Attack value range: 0 -> 127 - - Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 | - ----------------------------------------------------------------- - Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890| - - Note: frames is no. of PAL frames to reach full volume (100% - amplitude) - - Hmm, noticing that the time taken to reach full volume doubles - every time we add 4 to our attack value, we know the equation is - of form: - frames = k * 2 ^ (value / 4) - - (You may ponder about envelope generator hardware at this point, - or maybe not... :) - - By substituting some stuff and running some checks, we get: - - k = 0.00257 (close enuf) - - therefore, - frames = 0.00257 * 2 ^ (value / 4) - If you just happen to be writing an emulator, then you can probably - use an equation like: - - %volume_increase_per_tick = 1 / frames - - - ------------------------------------ - Pete: - ms=((1<<(value>>2))*514)/10000 - ------------------------------------ - -2. Decay rate value (only has log mode) - - Decay value range: 0 -> 15 - - Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - ------------------------------------------------ - frames | | | | | 6 | 12 | 24 | 47 | - - Note: frames here is no. of PAL frames to decay to 50% volume. - - formula: frames = k * 2 ^ (value) - - Substituting, we get: k = 0.00146 - - Further info on logarithmic nature: - frames to decay to sustain level 3 = 3 * frames to decay to - sustain level 9 - - Also no. of frames to 25% volume = roughly 1.85 * no. of frames to - 50% volume. - - Frag it - just use linear approx. - - ------------------------------------ - Pete: - ms=((1< 127 - - Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | - ------------------------------------------- - frames | 9 | 19 | 37 | 74 | 147| 293| 587| - - Here, frames = no. of PAL frames for volume amplitude to go from 100% - to 0% (or vice-versa). - - Same formula as for attack value, just a different value for k: - - k = 0.00225 - - ie: frames = 0.00225 * 2 ^ (value / 4) - - For emulation purposes: - - %volume_increase_or_decrease_per_tick = 1 / frames - - ------------------------------------ - Pete: - ms=((1<<(value>>2))*450)/10000 - ------------------------------------ - - -4. Release rate (linear mode) - - Release rate range: 0 -> 31 - - Value | 13 | 14 | 15 | 16 | 17 | - --------------------------------------------------------------- - frames | 18 | 36 | 73 | 146| 292| - - Here, frames = no. of PAL frames to decay from 100% vol to 0% vol - after "note-off" is triggered. - - Formula: frames = k * 2 ^ (value) - - And so: k = 0.00223 - - ------------------------------------ - Pete: - ms=((1< release phase - { - if(s_chan[ch].ADSR.ReleaseVal!=0) // -> release not 0: do release (if 0: stop right now) - { - if(!s_chan[ch].ADSR.ReleaseVol) // --> release just started? set up the release stuff - { - s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime; - s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume; - s_chan[ch].ADSR.ReleaseTime = // --> calc how long does it take to reach the wanted sus level - (s_chan[ch].ADSR.ReleaseTime* - s_chan[ch].ADSR.ReleaseVol)/1024; - } - // -> NO release exp mode used (yet) - v=s_chan[ch].ADSR.ReleaseVol; // -> get last volume - lT=s_chan[ch].ADSR.lTime- // -> how much time is past? - s_chan[ch].ADSR.ReleaseStartTime; - l1=s_chan[ch].ADSR.ReleaseTime; - - if(lT we still have to release - { - v=v-((v*lT)/l1); // --> calc new volume - } - else // -> release is over: now really stop that sample - {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;} - } - else // -> release IS 0: release at once - { - v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0; - } - } - else - {//--------------------------------------------------// not in release phase: - v=1024; - lT=s_chan[ch].ADSR.lTime; - l1=s_chan[ch].ADSR.AttackTime; - - if(lT0) - { - if(l3!=0) v2+=((v-v2)*lT)/l3; - else v2=v; - } - else - { - if(l3!=0) v2-=(v2*lT)/l3; - else v2=v; - } - - if(v2>v) v2=v; - if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;} - - v=v2; - } - } - } - - //----------------------------------------------------// - // ok, done for this channel, so increase time - - s_chan[ch].ADSR.lTime+=1; // 1 = 1.020408f ms; - - if(v>1024) v=1024; // adjust volume - if(v<0) v=0; - s_chan[ch].ADSR.lVolume=v; // store act volume - - return v; // return the volume factor -*/ - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -/* ------------------------------------------------------------------------------ -Neill Corlett -Playstation SPU envelope timing notes ------------------------------------------------------------------------------ - -This is preliminary. This may be wrong. But the model described herein fits -all of my experimental data, and it's just simple enough to sound right. - -ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally. -The value returned by channel reg 0xC is (envelope_level>>16). - -Each sample, an increment or decrement value will be added to or -subtracted from this envelope level. - -Create the rate log table. The values double every 4 entries. - entry #0 = 4 - - 4, 5, 6, 7, - 8,10,12,14, - 16,20,24,28, ... - - entry #40 = 4096... - entry #44 = 8192... - entry #48 = 16384... - entry #52 = 32768... - entry #56 = 65536... - -increments and decrements are in terms of ratelogtable[n] -n may exceed the table bounds (plan on n being between -32 and 127). -table values are all clipped between 0x00000000 and 0x3FFFFFFF - -when you "voice on", the envelope is always fully reset. -(yes, it may click. the real thing does this too.) - -envelope level begins at zero. - -each state happens for at least 1 cycle -(transitions are not instantaneous) -this may result in some oddness: if the decay rate is uberfast, it will cut -the envelope from full down to half in one sample, potentially skipping over -the sustain level - -ATTACK ------- -- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and - proceed to DECAY. - -Linear attack mode: -- line extends upward to 0x7FFFFFFF -- increment per sample is ratelogtable[(Ar^0x7F)-0x10] - -Logarithmic attack mode: -if envelope_level < 0x60000000: - - line extends upward to 0x60000000 - - increment per sample is ratelogtable[(Ar^0x7F)-0x10] -else: - - line extends upward to 0x7FFFFFFF - - increment per sample is ratelogtable[(Ar^0x7F)-0x18] - -DECAY ------ -- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN. - Do not clip to the sustain level. -- current line ends at (envelope_level & 0x07FFFFFF) -- decrement per sample depends on (envelope_level>>28)&0x7 - 0: ratelogtable[(4*(Dr^0x1F))-0x18+0] - 1: ratelogtable[(4*(Dr^0x1F))-0x18+4] - 2: ratelogtable[(4*(Dr^0x1F))-0x18+6] - 3: ratelogtable[(4*(Dr^0x1F))-0x18+8] - 4: ratelogtable[(4*(Dr^0x1F))-0x18+9] - 5: ratelogtable[(4*(Dr^0x1F))-0x18+10] - 6: ratelogtable[(4*(Dr^0x1F))-0x18+11] - 7: ratelogtable[(4*(Dr^0x1F))-0x18+12] - (note that this is the same as the release rate formula, except that - decay rates 10-1F aren't possible... those would be slower in theory) - -SUSTAIN -------- -- no terminating condition except for voice off -- Sd=0 (increase) behavior is identical to ATTACK for both log and linear. -- Sd=1 (decrease) behavior: -Linear sustain decrease: -- line extends to 0x00000000 -- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F] -Logarithmic sustain decrease: -- current line ends at (envelope_level & 0x07FFFFFF) -- decrement per sample depends on (envelope_level>>28)&0x7 - 0: ratelogtable[(Sr^0x7F)-0x1B+0] - 1: ratelogtable[(Sr^0x7F)-0x1B+4] - 2: ratelogtable[(Sr^0x7F)-0x1B+6] - 3: ratelogtable[(Sr^0x7F)-0x1B+8] - 4: ratelogtable[(Sr^0x7F)-0x1B+9] - 5: ratelogtable[(Sr^0x7F)-0x1B+10] - 6: ratelogtable[(Sr^0x7F)-0x1B+11] - 7: ratelogtable[(Sr^0x7F)-0x1B+12] - -RELEASE -------- -- if the envelope level has overflowed to negative, clip to 0 and QUIT. - -Linear release mode: -- line extends to 0x00000000 -- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C] - -Logarithmic release mode: -- line extends to (envelope_level & 0x0FFFFFFF) -- decrement per sample depends on (envelope_level>>28)&0x7 - 0: ratelogtable[(4*(Rr^0x1F))-0x18+0] - 1: ratelogtable[(4*(Rr^0x1F))-0x18+4] - 2: ratelogtable[(4*(Rr^0x1F))-0x18+6] - 3: ratelogtable[(4*(Rr^0x1F))-0x18+8] - 4: ratelogtable[(4*(Rr^0x1F))-0x18+9] - 5: ratelogtable[(4*(Rr^0x1F))-0x18+10] - 6: ratelogtable[(4*(Rr^0x1F))-0x18+11] - 7: ratelogtable[(4*(Rr^0x1F))-0x18+12] - ------------------------------------------------------------------------------ -*/ - +/*************************************************************************** + adsr.c - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2004/09/18 - LDChen +// - Speed optimized ADSR mixing +// +// 2003/05/14 - xodnizel +// - removed stopping of reverb on sample end +// +// 2003/01/06 - Pete +// - added Neill's ADSR timings +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_ADSR + +// will be included from spu.c +#ifdef _IN_SPU + +//////////////////////////////////////////////////////////////////////// +// ADSR func +//////////////////////////////////////////////////////////////////////// + +static unsigned long int RateTable[160]; + +void InitADSR(void) // INIT ADSR +{ + unsigned long r,rs,rd;int i; + + memset(RateTable,0,sizeof(unsigned long)*160); // build the rate table according to Neill's rules (see at bottom of file) + + r=3;rs=1;rd=0; + + for(i=32;i<160;i++) // we start at pos 32 with the real values... everything before is 0 + { + if(r<0x3FFFFFFF) + { + r+=rs; + rd++;if(rd==5) {rd=1;rs*=2;} + } + if(r>0x3FFFFFFF) r=0x3FFFFFFF; + + RateTable[i]=r; + } +} + +//////////////////////////////////////////////////////////////////////// + +INLINE void StartADSR(SPUCHAN * pChannel) // MIX ADSR +{ + pChannel->ADSRX.lVolume=1; // and init some adsr vars + pChannel->ADSRX.State=0; + pChannel->ADSRX.EnvelopeVol=0; +} + +//////////////////////////////////////////////////////////////////////// + +static const unsigned long int TableDisp[] = { + -0x18+0+32,-0x18+4+32,-0x18+6+32,-0x18+8+32, // release/decay + -0x18+9+32,-0x18+10+32,-0x18+11+32,-0x18+12+32, + + -0x1B+0+32,-0x1B+4+32,-0x1B+6+32,-0x1B+8+32, // sustain + -0x1B+9+32,-0x1B+10+32,-0x1B+11+32,-0x1B+12+32, +}; + +INLINE int MixADSR(SPUCHAN *ch) +{ + unsigned long int disp; + signed long int EnvelopeVol = ch->ADSRX.EnvelopeVol; + + if(ch->bStop) // should be stopped: + { // do release + if(ch->ADSRX.ReleaseModeExp) + { + disp = TableDisp[(EnvelopeVol>>28)&0x7]; + } + else + { + disp=-0x0C+32; + } + EnvelopeVol-=RateTable[ch->ADSRX.ReleaseRate + disp]; + + if(EnvelopeVol<0) + { + EnvelopeVol=0; + ch->bOn=0; + } + + ch->ADSRX.EnvelopeVol=EnvelopeVol; + ch->ADSRX.lVolume=(EnvelopeVol>>=21); + return EnvelopeVol; + } + else // not stopped yet? + { + if(ch->ADSRX.State==0) // -> attack + { + disp = -0x10+32; + if(ch->ADSRX.AttackModeExp) + { + if(EnvelopeVol>=0x60000000) + disp = -0x18+32; + } + EnvelopeVol+=RateTable[ch->ADSRX.AttackRate+disp]; + + if(EnvelopeVol<0) + { + EnvelopeVol=0x7FFFFFFF; + ch->ADSRX.State=1; + } + + ch->ADSRX.EnvelopeVol=EnvelopeVol; + ch->ADSRX.lVolume=(EnvelopeVol>>=21); + return EnvelopeVol; + } + //--------------------------------------------------// + if(ch->ADSRX.State==1) // -> decay + { + disp = TableDisp[(EnvelopeVol>>28)&0x7]; + EnvelopeVol-=RateTable[ch->ADSRX.DecayRate+disp]; + + if(EnvelopeVol<0) EnvelopeVol=0; + if(EnvelopeVol <= ch->ADSRX.SustainLevel) + { + ch->ADSRX.State=2; + } + + ch->ADSRX.EnvelopeVol=EnvelopeVol; + ch->ADSRX.lVolume=(EnvelopeVol>>=21); + return EnvelopeVol; + } + //--------------------------------------------------// + if(ch->ADSRX.State==2) // -> sustain + { + if(ch->ADSRX.SustainIncrease) + { + disp = -0x10+32; + if(ch->ADSRX.SustainModeExp) + { + if(EnvelopeVol>=0x60000000) + disp = -0x18+32; + } + EnvelopeVol+=RateTable[ch->ADSRX.SustainRate+disp]; + + if(EnvelopeVol<0) + { + EnvelopeVol=0x7FFFFFFF; + } + } + else + { + if(ch->ADSRX.SustainModeExp) + { + disp = TableDisp[((EnvelopeVol>>28)&0x7)+8]; + } + else + { + disp=-0x0F+32; + } + EnvelopeVol-=RateTable[ch->ADSRX.SustainRate+disp]; + + if(EnvelopeVol<0) + { + EnvelopeVol=0; + } + } + ch->ADSRX.EnvelopeVol=EnvelopeVol; + ch->ADSRX.lVolume=(EnvelopeVol>>=21); + return EnvelopeVol; + } + } + return 0; +} + +#endif + +/* +James Higgs ADSR investigations: + +PSX SPU Envelope Timings +~~~~~~~~~~~~~~~~~~~~~~~~ + +First, here is an extract from doomed's SPU doc, which explains the basics +of the SPU "volume envelope": + +*** doomed doc extract start *** + +-------------------------------------------------------------------------- +Voices. +-------------------------------------------------------------------------- +The SPU has 24 hardware voices. These voices can be used to reproduce sample +data, noise or can be used as frequency modulator on the next voice. +Each voice has it's own programmable ADSR envelope filter. The main volume +can be programmed independently for left and right output. + +The ADSR envelope filter works as follows: +Ar = Attack rate, which specifies the speed at which the volume increases + from zero to it's maximum value, as soon as the note on is given. The + slope can be set to lineair or exponential. +Dr = Decay rate specifies the speed at which the volume decreases to the + sustain level. Decay is always decreasing exponentially. +Sl = Sustain level, base level from which sustain starts. +Sr = Sustain rate is the rate at which the volume of the sustained note + increases or decreases. This can be either lineair or exponential. +Rr = Release rate is the rate at which the volume of the note decreases + as soon as the note off is given. + + lvl | + ^ | /\Dr __ + Sl _| _ / _ \__--- \ + | / ---__ \ Rr + | /Ar Sr \ \ + | / \\ + |/___________________\________ + ->time + +The overal volume can also be set to sweep up or down lineairly or +exponentially from it's current value. This can be done seperately +for left and right. + +Relevant SPU registers: +------------------------------------------------------------- +$1f801xx8 Attack/Decay/Sustain level +bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00| +desc.|Am| Ar |Dr |Sl | + +Am 0 Attack mode Linear + 1 Exponential + +Ar 0-7f attack rate +Dr 0-f decay rate +Sl 0-f sustain level +------------------------------------------------------------- +$1f801xxa Sustain rate, Release Rate. +bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00| +desc.|Sm|Sd| 0| Sr |Rm|Rr | + +Sm 0 sustain rate mode linear + 1 exponential +Sd 0 sustain rate mode increase + 1 decrease +Sr 0-7f Sustain Rate +Rm 0 Linear decrease + 1 Exponential decrease +Rr 0-1f Release Rate + +Note: decay mode is always Expontial decrease, and thus cannot +be set. +------------------------------------------------------------- +$1f801xxc Current ADSR volume +bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00| +desc.|ADSRvol | + +ADSRvol Returns the current envelope volume when + read. +-- James' Note: return range: 0 -> 32767 + +*** doomed doc extract end *** + +By using a small PSX proggie to visualise the envelope as it was played, +the following results for envelope timing were obtained: + +1. Attack rate value (linear mode) + + Attack value range: 0 -> 127 + + Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 | + ----------------------------------------------------------------- + Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890| + + Note: frames is no. of PAL frames to reach full volume (100% + amplitude) + + Hmm, noticing that the time taken to reach full volume doubles + every time we add 4 to our attack value, we know the equation is + of form: + frames = k * 2 ^ (value / 4) + + (You may ponder about envelope generator hardware at this point, + or maybe not... :) + + By substituting some stuff and running some checks, we get: + + k = 0.00257 (close enuf) + + therefore, + frames = 0.00257 * 2 ^ (value / 4) + If you just happen to be writing an emulator, then you can probably + use an equation like: + + %volume_increase_per_tick = 1 / frames + + + ------------------------------------ + Pete: + ms=((1<<(value>>2))*514)/10000 + ------------------------------------ + +2. Decay rate value (only has log mode) + + Decay value range: 0 -> 15 + + Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + ------------------------------------------------ + frames | | | | | 6 | 12 | 24 | 47 | + + Note: frames here is no. of PAL frames to decay to 50% volume. + + formula: frames = k * 2 ^ (value) + + Substituting, we get: k = 0.00146 + + Further info on logarithmic nature: + frames to decay to sustain level 3 = 3 * frames to decay to + sustain level 9 + + Also no. of frames to 25% volume = roughly 1.85 * no. of frames to + 50% volume. + + Frag it - just use linear approx. + + ------------------------------------ + Pete: + ms=((1< 127 + + Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | + ------------------------------------------- + frames | 9 | 19 | 37 | 74 | 147| 293| 587| + + Here, frames = no. of PAL frames for volume amplitude to go from 100% + to 0% (or vice-versa). + + Same formula as for attack value, just a different value for k: + + k = 0.00225 + + ie: frames = 0.00225 * 2 ^ (value / 4) + + For emulation purposes: + + %volume_increase_or_decrease_per_tick = 1 / frames + + ------------------------------------ + Pete: + ms=((1<<(value>>2))*450)/10000 + ------------------------------------ + + +4. Release rate (linear mode) + + Release rate range: 0 -> 31 + + Value | 13 | 14 | 15 | 16 | 17 | + --------------------------------------------------------------- + frames | 18 | 36 | 73 | 146| 292| + + Here, frames = no. of PAL frames to decay from 100% vol to 0% vol + after "note-off" is triggered. + + Formula: frames = k * 2 ^ (value) + + And so: k = 0.00223 + + ------------------------------------ + Pete: + ms=((1< release phase + { + if(s_chan[ch].ADSR.ReleaseVal!=0) // -> release not 0: do release (if 0: stop right now) + { + if(!s_chan[ch].ADSR.ReleaseVol) // --> release just started? set up the release stuff + { + s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime; + s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume; + s_chan[ch].ADSR.ReleaseTime = // --> calc how long does it take to reach the wanted sus level + (s_chan[ch].ADSR.ReleaseTime* + s_chan[ch].ADSR.ReleaseVol)/1024; + } + // -> NO release exp mode used (yet) + v=s_chan[ch].ADSR.ReleaseVol; // -> get last volume + lT=s_chan[ch].ADSR.lTime- // -> how much time is past? + s_chan[ch].ADSR.ReleaseStartTime; + l1=s_chan[ch].ADSR.ReleaseTime; + + if(lT we still have to release + { + v=v-((v*lT)/l1); // --> calc new volume + } + else // -> release is over: now really stop that sample + {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;} + } + else // -> release IS 0: release at once + { + v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0; + } + } + else + {//--------------------------------------------------// not in release phase: + v=1024; + lT=s_chan[ch].ADSR.lTime; + l1=s_chan[ch].ADSR.AttackTime; + + if(lT0) + { + if(l3!=0) v2+=((v-v2)*lT)/l3; + else v2=v; + } + else + { + if(l3!=0) v2-=(v2*lT)/l3; + else v2=v; + } + + if(v2>v) v2=v; + if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;} + + v=v2; + } + } + } + + //----------------------------------------------------// + // ok, done for this channel, so increase time + + s_chan[ch].ADSR.lTime+=1; // 1 = 1.020408f ms; + + if(v>1024) v=1024; // adjust volume + if(v<0) v=0; + s_chan[ch].ADSR.lVolume=v; // store act volume + + return v; // return the volume factor +*/ + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +/* +----------------------------------------------------------------------------- +Neill Corlett +Playstation SPU envelope timing notes +----------------------------------------------------------------------------- + +This is preliminary. This may be wrong. But the model described herein fits +all of my experimental data, and it's just simple enough to sound right. + +ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally. +The value returned by channel reg 0xC is (envelope_level>>16). + +Each sample, an increment or decrement value will be added to or +subtracted from this envelope level. + +Create the rate log table. The values double every 4 entries. + entry #0 = 4 + + 4, 5, 6, 7, + 8,10,12,14, + 16,20,24,28, ... + + entry #40 = 4096... + entry #44 = 8192... + entry #48 = 16384... + entry #52 = 32768... + entry #56 = 65536... + +increments and decrements are in terms of ratelogtable[n] +n may exceed the table bounds (plan on n being between -32 and 127). +table values are all clipped between 0x00000000 and 0x3FFFFFFF + +when you "voice on", the envelope is always fully reset. +(yes, it may click. the real thing does this too.) + +envelope level begins at zero. + +each state happens for at least 1 cycle +(transitions are not instantaneous) +this may result in some oddness: if the decay rate is uberfast, it will cut +the envelope from full down to half in one sample, potentially skipping over +the sustain level + +ATTACK +------ +- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and + proceed to DECAY. + +Linear attack mode: +- line extends upward to 0x7FFFFFFF +- increment per sample is ratelogtable[(Ar^0x7F)-0x10] + +Logarithmic attack mode: +if envelope_level < 0x60000000: + - line extends upward to 0x60000000 + - increment per sample is ratelogtable[(Ar^0x7F)-0x10] +else: + - line extends upward to 0x7FFFFFFF + - increment per sample is ratelogtable[(Ar^0x7F)-0x18] + +DECAY +----- +- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN. + Do not clip to the sustain level. +- current line ends at (envelope_level & 0x07FFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(4*(Dr^0x1F))-0x18+0] + 1: ratelogtable[(4*(Dr^0x1F))-0x18+4] + 2: ratelogtable[(4*(Dr^0x1F))-0x18+6] + 3: ratelogtable[(4*(Dr^0x1F))-0x18+8] + 4: ratelogtable[(4*(Dr^0x1F))-0x18+9] + 5: ratelogtable[(4*(Dr^0x1F))-0x18+10] + 6: ratelogtable[(4*(Dr^0x1F))-0x18+11] + 7: ratelogtable[(4*(Dr^0x1F))-0x18+12] + (note that this is the same as the release rate formula, except that + decay rates 10-1F aren't possible... those would be slower in theory) + +SUSTAIN +------- +- no terminating condition except for voice off +- Sd=0 (increase) behavior is identical to ATTACK for both log and linear. +- Sd=1 (decrease) behavior: +Linear sustain decrease: +- line extends to 0x00000000 +- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F] +Logarithmic sustain decrease: +- current line ends at (envelope_level & 0x07FFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(Sr^0x7F)-0x1B+0] + 1: ratelogtable[(Sr^0x7F)-0x1B+4] + 2: ratelogtable[(Sr^0x7F)-0x1B+6] + 3: ratelogtable[(Sr^0x7F)-0x1B+8] + 4: ratelogtable[(Sr^0x7F)-0x1B+9] + 5: ratelogtable[(Sr^0x7F)-0x1B+10] + 6: ratelogtable[(Sr^0x7F)-0x1B+11] + 7: ratelogtable[(Sr^0x7F)-0x1B+12] + +RELEASE +------- +- if the envelope level has overflowed to negative, clip to 0 and QUIT. + +Linear release mode: +- line extends to 0x00000000 +- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C] + +Logarithmic release mode: +- line extends to (envelope_level & 0x0FFFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(4*(Rr^0x1F))-0x18+0] + 1: ratelogtable[(4*(Rr^0x1F))-0x18+4] + 2: ratelogtable[(4*(Rr^0x1F))-0x18+6] + 3: ratelogtable[(4*(Rr^0x1F))-0x18+8] + 4: ratelogtable[(4*(Rr^0x1F))-0x18+9] + 5: ratelogtable[(4*(Rr^0x1F))-0x18+10] + 6: ratelogtable[(4*(Rr^0x1F))-0x18+11] + 7: ratelogtable[(4*(Rr^0x1F))-0x18+12] + +----------------------------------------------------------------------------- +*/ diff --git a/extras/modules/peops/spu/adsr.h b/extras/modules/peops/spu/adsr.h index 1773959b7..1e365c90b 100644 --- a/extras/modules/peops/spu/adsr.h +++ b/extras/modules/peops/spu/adsr.h @@ -1,19 +1,28 @@ -/*************************************************************************** - adsr.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -static INLINE void StartADSR(int ch); -static INLINE int MixADSR(int ch); +/*************************************************************************** + adsr.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +INLINE void StartADSR(SPUCHAN * pChannel); +INLINE int MixADSR(SPUCHAN * pChannel); diff --git a/extras/modules/peops/spu/externals.c b/extras/modules/peops/spu/externals.c deleted file mode 100644 index 6ca90a13d..000000000 --- a/extras/modules/peops/spu/externals.c +++ /dev/null @@ -1,26 +0,0 @@ -/*************************************************************************** - externals.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#include - -// 15-bit value + 1-sign -int CLAMP16(int x) { - if(x > 32767) x = 32767; - else if(x < -32768) x = -32768; - - return x; -} diff --git a/extras/modules/peops/spu/externals.h b/extras/modules/peops/spu/externals.h index 40d8f6112..004de6c90 100644 --- a/extras/modules/peops/spu/externals.h +++ b/extras/modules/peops/spu/externals.h @@ -1,365 +1,283 @@ -/*************************************************************************** - externals.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#include - -///////////////////////////////////////////////////////// -// generic defines -///////////////////////////////////////////////////////// - -#define PSE_LT_SPU 4 -#define PSE_SPU_ERR_SUCCESS 0 -#define PSE_SPU_ERR -60 -#define PSE_SPU_ERR_NOTCONFIGURED PSE_SPU_ERR - 1 -#define PSE_SPU_ERR_INIT PSE_SPU_ERR - 2 -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - - - -// 15-bit value + 1-sign -extern int CLAMP16(int x); - - -//////////////////////////////////////////////////////////////////////// -// spu defines -//////////////////////////////////////////////////////////////////////// - -// sound buffer sizes -// 400 ms complete sound buffer -#define SOUNDSIZE 70560 -// 137 ms test buffer... if less than that is buffered, a new upload will happen -#define TESTSIZE 24192 - -// num of channels -#define MAXCHAN 24 - - -// ~ 1 ms of data - somewhat slower than Eternal -//#define NSSIZE 45 -//#define INTERVAL_TIME 1000 - -// ~ 0.5 ms of data - roughly Eternal maybe -//#define NSSIZE 23 -//#define INTERVAL_TIME 2000 - -// ~ 0.25 ms of data - seems a little bad..? -//#define NSSIZE 12 -//#define INTERVAL_TIME 4000 - -#define NSSIZE 10 -#define APU_CYCLES_UPDATE NSSIZE - - -// update times -#if 0 -// PEOPS DSound 1.09a - good sound cards -#define LATENCY 10 -#elif defined (_WINDOWS) -// work on most cards -#define LATENCY 25 -#else -// work on most cards -#define LATENCY 25 -#endif - - -// make sure this is bigger than cpu action - no glitchy -#define INTERVAL_TIME 4500 - - -#define CPU_CLOCK 33868800 - -/////////////////////////////////////////////////////////// -// struct defines -/////////////////////////////////////////////////////////// - -// ADSR INFOS PER CHANNEL -typedef struct -{ - int AttackModeExp; - long AttackTime; - long DecayTime; - long SustainLevel; - int SustainModeExp; - long SustainModeDec; - long SustainTime; - int ReleaseModeExp; - unsigned long ReleaseVal; - long ReleaseTime; - long ReleaseStartTime; - long ReleaseVol; - long lTime; - long lVolume; -} ADSRInfo; - -typedef struct -{ - int State; - int AttackModeExp; - int AttackRate; - int DecayRate; - int SustainLevel; - int SustainModeExp; - int SustainIncrease; - int SustainRate; - int ReleaseModeExp; - int ReleaseRate; - int EnvelopeVol; - int EnvelopeVol_f; // fraction - long lVolume; - long lDummy1; - long lDummy2; -} ADSRInfoEx; - -/////////////////////////////////////////////////////////// - -// Tmp Flags - -// used for debug channel muting -#define FLAG_MUTE 1 - -// used for simple interpolation -#define FLAG_IPOL0 2 -#define FLAG_IPOL1 4 - -/////////////////////////////////////////////////////////// - -// MAIN CHANNEL STRUCT -typedef struct -{ - // no mutexes used anymore... don't need them to sync access - //HANDLE hMutex; - - int bNew; // start flag - - int iSBPos; // mixing stuff - int spos; - int sinc; - int SB[32+32]; // Pete added another 32 dwords in 1.6 ... prevents overflow issues with gaussian/cubic interpolation (thanx xodnizel!), and can be used for even better interpolations, eh? :) - int sval; - - unsigned char * pStart; // start ptr into sound mem - unsigned char * pCurr; // current pos in sound mem - unsigned char * pLoop; // loop ptr in sound mem - - int bOn; // is channel active (sample playing?) - int bStop; // is channel stopped (sample _can_ still be playing, ADSR Release phase) - int bReverb; // can we do reverb on this channel? must have ctrl register bit, to get active - int iActFreq; // current psx pitch - int iUsedFreq; // current pc pitch - int iLeftVolume; // left volume - int iLeftVolRaw; // left psx volume value - int bLoopJump; // ignore loop bit, if an external loop address is used - int iMute; // mute mode (debug) - int iSilent; // voice on - sound on/off - int iRightVolume; // right volume - int iRightVolRaw; // right psx volume value - int iRawPitch; // raw pitch (0...3fff) - int iIrqDone; // debug irq done flag - int s_1; // last decoding infos - int s_2; - int bRVBActive; // reverb active flag - int iRVBOffset; // reverb offset - int iRVBRepeat; // reverb repeat - int bNoise; // noise active flag - int bFMod; // freq mod (0=off, 1=sound channel, 2=freq channel) - int iRVBNum; // another reverb helper - int iOldNoise; // old noise val for this channel - ADSRInfo ADSR; // active ADSR settings - ADSRInfoEx ADSRX; // next ADSR settings (will be moved to active on sample start) -} SPUCHAN; - -/////////////////////////////////////////////////////////// - -typedef struct -{ - int StartAddr; // reverb area start addr in samples - int CurrAddr; // reverb area curr addr in samples - - int VolLeft; - int VolRight; - int iLastRVBLeft; - int iLastRVBRight; - int iRVBLeft; - int iRVBRight; - - int FB_SRC_A; // (offset) - int FB_SRC_B; // (offset) - int IIR_ALPHA; // (coef.) - int ACC_COEF_A; // (coef.) - int ACC_COEF_B; // (coef.) - int ACC_COEF_C; // (coef.) - int ACC_COEF_D; // (coef.) - int IIR_COEF; // (coef.) - int FB_ALPHA; // (coef.) - int FB_X; // (coef.) - int IIR_DEST_A0; // (offset) - int IIR_DEST_A1; // (offset) - int ACC_SRC_A0; // (offset) - int ACC_SRC_A1; // (offset) - int ACC_SRC_B0; // (offset) - int ACC_SRC_B1; // (offset) - int IIR_SRC_A0; // (offset) - int IIR_SRC_A1; // (offset) - int IIR_DEST_B0; // (offset) - int IIR_DEST_B1; // (offset) - int ACC_SRC_C0; // (offset) - int ACC_SRC_C1; // (offset) - int ACC_SRC_D0; // (offset) - int ACC_SRC_D1; // (offset) - int IIR_SRC_B1; // (offset) - int IIR_SRC_B0; // (offset) - int MIX_DEST_A0; // (offset) - int MIX_DEST_A1; // (offset) - int MIX_DEST_B0; // (offset) - int MIX_DEST_B1; // (offset) - int IN_COEF_L; // (coef.) - int IN_COEF_R; // (coef.) -} REVERBInfo; - -#ifdef _WINDOWS -extern HINSTANCE hInst; -#define WM_MUTE (WM_USER+543) -#endif - -/////////////////////////////////////////////////////////// -// SPU.C globals -/////////////////////////////////////////////////////////// - -#ifndef _IN_SPU - -// psx buffers / addresses -extern unsigned short* spuMem; -extern unsigned char * spuMemC; -extern unsigned char * pSpuIrq; -extern unsigned char * pSpuBuffer; - -// user settings - -extern int iVolume; -extern int iXAPitch; -extern int iUseTimer; -extern int iSPUIRQWait; -extern int iDebugMode; -extern int iRecordMode; -extern int iUseReverb; -extern int iUseInterpolation; -extern int iDisStereo; -extern int iFreqResponse; -// MISC - -extern int iSpuAsyncWait; - -extern SPUCHAN s_chan[]; -extern REVERBInfo rvb; - -extern unsigned long dwNoiseVal; -extern unsigned long dwNoiseClock; -extern unsigned long dwNoiseCount; -extern unsigned short spuCtrl; -extern unsigned short spuStat; -extern unsigned short spuIrq; -extern unsigned long spuAddr; -extern int bEndThread; -extern int bThreadEnded; -extern int bSpuInit; -extern uint32_t dwNewChannel; -extern unsigned int bIrqHit; - -extern int SSumR[]; -extern int SSumL[]; -extern int iCycle; -extern short * pS; - -#ifdef _WINDOWS -extern HWND hWMain; // window handle -extern HWND hWDebug; -#endif - -extern void (CALLBACK *cddavCallback)(unsigned short,unsigned short); -extern void (CALLBACK *irqCallback)(void); // func of main emu, called on spu irq - -#endif - -/////////////////////////////////////////////////////////// -// DSOUND.C globals -/////////////////////////////////////////////////////////// - -#ifndef _IN_DSOUND - -#ifdef _WINDOWS -extern unsigned long LastWrite; -extern unsigned long LastPlay; -#endif - -#endif - -/////////////////////////////////////////////////////////// -// RECORD.C globals -/////////////////////////////////////////////////////////// - -#ifndef _IN_RECORD - -#ifdef _WINDOWS -extern int iDoRecord; -#endif - -#endif - -/////////////////////////////////////////////////////////// -// XA.C globals -/////////////////////////////////////////////////////////// - -#ifndef _IN_XA - -extern xa_decode_t * xapGlobal; - -extern uint32_t * XAFeed; -extern uint32_t * XAPlay; -extern uint32_t * XAStart; -extern uint32_t * XAEnd; - -extern uint32_t XARepeat; -extern uint32_t XALastVal; - -extern uint32_t * CDDAFeed; -extern uint32_t * CDDAPlay; -extern uint32_t * CDDAStart; -extern uint32_t * CDDAEnd; - -extern int iLeftXAVol; -extern int iRightXAVol; - -#endif - -/////////////////////////////////////////////////////////// -// REVERB.C globals -/////////////////////////////////////////////////////////// - -#ifndef _IN_REVERB - -extern int * sRVBPlay; -extern int * sRVBEnd; -extern int * sRVBStart; -extern int iReverbOff; -extern int iReverbRepeat; -extern int iReverbNum; - -#endif +/*************************************************************************** + externals.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/04/04 - Pete +// - increased channel struct for interpolation +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +///////////////////////////////////////////////////////// +// generic defines +///////////////////////////////////////////////////////// + +#define PSE_LT_SPU 4 +#define PSE_SPU_ERR_SUCCESS 0 +#define PSE_SPU_ERR -60 +#define PSE_SPU_ERR_NOTCONFIGURED PSE_SPU_ERR - 1 +#define PSE_SPU_ERR_INIT PSE_SPU_ERR - 2 +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +//////////////////////////////////////////////////////////////////////// +// spu defines +//////////////////////////////////////////////////////////////////////// + +// sound buffer sizes +// 400 ms complete sound buffer +#define SOUNDSIZE 70560 +// 137 ms test buffer... if less than that is buffered, a new upload will happen +#define TESTSIZE 24192 + +// num of channels +#define MAXCHAN 24 + +// ~ 1 ms of data +#define NSSIZE 45 + +/////////////////////////////////////////////////////////// +// struct defines +/////////////////////////////////////////////////////////// + +// ADSR INFOS PER CHANNEL +typedef struct +{ + int AttackModeExp; + long AttackTime; + long DecayTime; + long SustainLevel; + int SustainModeExp; + long SustainModeDec; + long SustainTime; + int ReleaseModeExp; + unsigned long ReleaseVal; + long ReleaseTime; + long ReleaseStartTime; + long ReleaseVol; + long lTime; + long lVolume; +} ADSRInfo; + +typedef struct +{ + int State; + int AttackModeExp; + int AttackRate; + int DecayRate; + int SustainLevel; + int SustainModeExp; + int SustainIncrease; + int SustainRate; + int ReleaseModeExp; + int ReleaseRate; + int EnvelopeVol; + long lVolume; + long lDummy1; + long lDummy2; +} ADSRInfoEx; + +/////////////////////////////////////////////////////////// + +// Tmp Flags + +// used for debug channel muting +#define FLAG_MUTE 1 + +// used for simple interpolation +#define FLAG_IPOL0 2 +#define FLAG_IPOL1 4 + +/////////////////////////////////////////////////////////// + +// MAIN CHANNEL STRUCT +typedef struct +{ + // no mutexes used anymore... don't need them to sync access + //HANDLE hMutex; + + int bNew; // start flag + + int iSBPos; // mixing stuff + int spos; + int sinc; + int SB[32+32]; // Pete added another 32 dwords in 1.6 ... prevents overflow issues with gaussian/cubic interpolation (thanx xodnizel!), and can be used for even better interpolations, eh? :) + int sval; + + unsigned char * pStart; // start ptr into sound mem + unsigned char * pCurr; // current pos in sound mem + unsigned char * pLoop; // loop ptr in sound mem + + int bOn; // is channel active (sample playing?) + int bStop; // is channel stopped (sample _can_ still be playing, ADSR Release phase) + int bReverb; // can we do reverb on this channel? must have ctrl register bit, to get active + int iActFreq; // current psx pitch + int iUsedFreq; // current pc pitch + int iLeftVolume; // left volume + int iLeftVolRaw; // left psx volume value + int bIgnoreLoop; // ignore loop bit, if an external loop address is used + int iRightVolume; // right volume + int iRightVolRaw; // right psx volume value + int iRawPitch; // raw pitch (0...3fff) + int s_1; // last decoding infos + int s_2; + int bRVBActive; // reverb active flag + int iRVBOffset; // reverb offset + int iRVBRepeat; // reverb repeat + int bNoise; // noise active flag + int bFMod; // freq mod (0=off, 1=sound channel, 2=freq channel) + int iRVBNum; // another reverb helper + int iOldNoise; // old noise val for this channel + ADSRInfo ADSR; // active ADSR settings + ADSRInfoEx ADSRX; // next ADSR settings (will be moved to active on sample start) + +} SPUCHAN; + +/////////////////////////////////////////////////////////// + +typedef struct +{ + int StartAddr; // reverb area start addr in samples + int CurrAddr; // reverb area curr addr in samples + + int VolLeft; + int VolRight; + int iLastRVBLeft; + int iLastRVBRight; + int iRVBLeft; + int iRVBRight; + + + int FB_SRC_A; // (offset) + int FB_SRC_B; // (offset) + int IIR_ALPHA; // (coef.) + int ACC_COEF_A; // (coef.) + int ACC_COEF_B; // (coef.) + int ACC_COEF_C; // (coef.) + int ACC_COEF_D; // (coef.) + int IIR_COEF; // (coef.) + int FB_ALPHA; // (coef.) + int FB_X; // (coef.) + int IIR_DEST_A0; // (offset) + int IIR_DEST_A1; // (offset) + int ACC_SRC_A0; // (offset) + int ACC_SRC_A1; // (offset) + int ACC_SRC_B0; // (offset) + int ACC_SRC_B1; // (offset) + int IIR_SRC_A0; // (offset) + int IIR_SRC_A1; // (offset) + int IIR_DEST_B0; // (offset) + int IIR_DEST_B1; // (offset) + int ACC_SRC_C0; // (offset) + int ACC_SRC_C1; // (offset) + int ACC_SRC_D0; // (offset) + int ACC_SRC_D1; // (offset) + int IIR_SRC_B1; // (offset) + int IIR_SRC_B0; // (offset) + int MIX_DEST_A0; // (offset) + int MIX_DEST_A1; // (offset) + int MIX_DEST_B0; // (offset) + int MIX_DEST_B1; // (offset) + int IN_COEF_L; // (coef.) + int IN_COEF_R; // (coef.) +} REVERBInfo; + +/////////////////////////////////////////////////////////// +// SPU.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_SPU + +// psx buffers / addresses + +extern unsigned char * spuMemC; +extern unsigned char * pSpuBuffer; + +// user settings + +extern int iUseXA; +extern int iVolume; +extern int iXAPitch; +extern int iUseReverb; +extern int iUseInterpolation; + +// MISC + +extern SPUCHAN s_chan[]; +extern REVERBInfo rvb; + +extern unsigned long dwNoiseVal; +extern unsigned short spuCtrl; +extern int bEndThread; +extern int bSpuInit; +extern unsigned long dwNewChannel; + +extern int SSumR[]; +extern int SSumL[]; +extern int iCycle; +extern short * pS; + +#endif + +/////////////////////////////////////////////////////////// +// XA.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_XA + +extern xa_decode_t * xapGlobal; + +extern uint32_t * XAFeed; +extern uint32_t * XAPlay; +extern uint32_t * XAStart; +extern uint32_t * XAEnd; + +extern uint32_t XARepeat; +extern uint32_t XALastVal; + +extern uint32_t * CDDAFeed; +extern uint32_t * CDDAPlay; +extern uint32_t * CDDAStart; +extern uint32_t * CDDAEnd; + +extern int iLeftXAVol; +extern int iRightXAVol; + +#endif + +/////////////////////////////////////////////////////////// +// REVERB.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_REVERB + +extern int * sRVBPlay; +extern int * sRVBEnd; +extern int * sRVBStart; +extern int iReverbOff; +extern int iReverbRepeat; +extern int iReverbNum; + +#endif diff --git a/extras/modules/peops/spu/gauss_i.h b/extras/modules/peops/spu/gauss_i.h index 0388549d0..85fc63fe1 100644 --- a/extras/modules/peops/spu/gauss_i.h +++ b/extras/modules/peops/spu/gauss_i.h @@ -1,150 +1,162 @@ -/*************************************************************************** - gauss_i.h - description - ----------------------- - begin : Sun Feb 08 2003 - copyright : (C) 2003 by Chris Moeller, eh, whatever - email : chris@kode54.tk - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#ifndef GAUSS_H -#define GAUSS_H - -const int gauss[]={ - 0x172, 0x519, 0x176, 0x000, 0x16E, 0x519, 0x17A, 0x000, - 0x16A, 0x518, 0x17D, 0x000, 0x166, 0x518, 0x181, 0x000, - 0x162, 0x518, 0x185, 0x000, 0x15F, 0x518, 0x189, 0x000, - 0x15B, 0x518, 0x18D, 0x000, 0x157, 0x517, 0x191, 0x000, - 0x153, 0x517, 0x195, 0x000, 0x150, 0x517, 0x19A, 0x000, - 0x14C, 0x516, 0x19E, 0x000, 0x148, 0x516, 0x1A2, 0x000, - 0x145, 0x515, 0x1A6, 0x000, 0x141, 0x514, 0x1AA, 0x000, - 0x13E, 0x514, 0x1AE, 0x000, 0x13A, 0x513, 0x1B2, 0x000, - 0x137, 0x512, 0x1B7, 0x001, 0x133, 0x511, 0x1BB, 0x001, - 0x130, 0x511, 0x1BF, 0x001, 0x12C, 0x510, 0x1C3, 0x001, - 0x129, 0x50F, 0x1C8, 0x001, 0x125, 0x50E, 0x1CC, 0x001, - 0x122, 0x50D, 0x1D0, 0x001, 0x11E, 0x50C, 0x1D5, 0x001, - 0x11B, 0x50B, 0x1D9, 0x001, 0x118, 0x50A, 0x1DD, 0x001, - 0x114, 0x508, 0x1E2, 0x001, 0x111, 0x507, 0x1E6, 0x002, - 0x10E, 0x506, 0x1EB, 0x002, 0x10B, 0x504, 0x1EF, 0x002, - 0x107, 0x503, 0x1F3, 0x002, 0x104, 0x502, 0x1F8, 0x002, - 0x101, 0x500, 0x1FC, 0x002, 0x0FE, 0x4FF, 0x201, 0x002, - 0x0FB, 0x4FD, 0x205, 0x003, 0x0F8, 0x4FB, 0x20A, 0x003, - 0x0F5, 0x4FA, 0x20F, 0x003, 0x0F2, 0x4F8, 0x213, 0x003, - 0x0EF, 0x4F6, 0x218, 0x003, 0x0EC, 0x4F5, 0x21C, 0x004, - 0x0E9, 0x4F3, 0x221, 0x004, 0x0E6, 0x4F1, 0x226, 0x004, - 0x0E3, 0x4EF, 0x22A, 0x004, 0x0E0, 0x4ED, 0x22F, 0x004, - 0x0DD, 0x4EB, 0x233, 0x005, 0x0DA, 0x4E9, 0x238, 0x005, - 0x0D7, 0x4E7, 0x23D, 0x005, 0x0D4, 0x4E5, 0x241, 0x005, - 0x0D2, 0x4E3, 0x246, 0x006, 0x0CF, 0x4E0, 0x24B, 0x006, - 0x0CC, 0x4DE, 0x250, 0x006, 0x0C9, 0x4DC, 0x254, 0x006, - 0x0C7, 0x4D9, 0x259, 0x007, 0x0C4, 0x4D7, 0x25E, 0x007, - 0x0C1, 0x4D5, 0x263, 0x007, 0x0BF, 0x4D2, 0x267, 0x008, - 0x0BC, 0x4D0, 0x26C, 0x008, 0x0BA, 0x4CD, 0x271, 0x008, - 0x0B7, 0x4CB, 0x276, 0x009, 0x0B4, 0x4C8, 0x27B, 0x009, - 0x0B2, 0x4C5, 0x280, 0x009, 0x0AF, 0x4C3, 0x284, 0x00A, - 0x0AD, 0x4C0, 0x289, 0x00A, 0x0AB, 0x4BD, 0x28E, 0x00A, - 0x0A8, 0x4BA, 0x293, 0x00B, 0x0A6, 0x4B7, 0x298, 0x00B, - 0x0A3, 0x4B5, 0x29D, 0x00B, 0x0A1, 0x4B2, 0x2A2, 0x00C, - 0x09F, 0x4AF, 0x2A6, 0x00C, 0x09C, 0x4AC, 0x2AB, 0x00D, - 0x09A, 0x4A9, 0x2B0, 0x00D, 0x098, 0x4A6, 0x2B5, 0x00E, - 0x096, 0x4A2, 0x2BA, 0x00E, 0x093, 0x49F, 0x2BF, 0x00F, - 0x091, 0x49C, 0x2C4, 0x00F, 0x08F, 0x499, 0x2C9, 0x00F, - 0x08D, 0x496, 0x2CE, 0x010, 0x08B, 0x492, 0x2D3, 0x010, - 0x089, 0x48F, 0x2D8, 0x011, 0x086, 0x48C, 0x2DC, 0x011, - 0x084, 0x488, 0x2E1, 0x012, 0x082, 0x485, 0x2E6, 0x013, - 0x080, 0x481, 0x2EB, 0x013, 0x07E, 0x47E, 0x2F0, 0x014, - 0x07C, 0x47A, 0x2F5, 0x014, 0x07A, 0x477, 0x2FA, 0x015, - 0x078, 0x473, 0x2FF, 0x015, 0x076, 0x470, 0x304, 0x016, - 0x075, 0x46C, 0x309, 0x017, 0x073, 0x468, 0x30E, 0x017, - 0x071, 0x465, 0x313, 0x018, 0x06F, 0x461, 0x318, 0x018, - 0x06D, 0x45D, 0x31D, 0x019, 0x06B, 0x459, 0x322, 0x01A, - 0x06A, 0x455, 0x326, 0x01B, 0x068, 0x452, 0x32B, 0x01B, - 0x066, 0x44E, 0x330, 0x01C, 0x064, 0x44A, 0x335, 0x01D, - 0x063, 0x446, 0x33A, 0x01D, 0x061, 0x442, 0x33F, 0x01E, - 0x05F, 0x43E, 0x344, 0x01F, 0x05E, 0x43A, 0x349, 0x020, - 0x05C, 0x436, 0x34E, 0x020, 0x05A, 0x432, 0x353, 0x021, - 0x059, 0x42E, 0x357, 0x022, 0x057, 0x42A, 0x35C, 0x023, - 0x056, 0x425, 0x361, 0x024, 0x054, 0x421, 0x366, 0x024, - 0x053, 0x41D, 0x36B, 0x025, 0x051, 0x419, 0x370, 0x026, - 0x050, 0x415, 0x374, 0x027, 0x04E, 0x410, 0x379, 0x028, - 0x04D, 0x40C, 0x37E, 0x029, 0x04C, 0x408, 0x383, 0x02A, - 0x04A, 0x403, 0x388, 0x02B, 0x049, 0x3FF, 0x38C, 0x02C, - 0x047, 0x3FB, 0x391, 0x02D, 0x046, 0x3F6, 0x396, 0x02E, - 0x045, 0x3F2, 0x39B, 0x02F, 0x043, 0x3ED, 0x39F, 0x030, - 0x042, 0x3E9, 0x3A4, 0x031, 0x041, 0x3E5, 0x3A9, 0x032, - 0x040, 0x3E0, 0x3AD, 0x033, 0x03E, 0x3DC, 0x3B2, 0x034, - 0x03D, 0x3D7, 0x3B7, 0x035, 0x03C, 0x3D2, 0x3BB, 0x036, - 0x03B, 0x3CE, 0x3C0, 0x037, 0x03A, 0x3C9, 0x3C5, 0x038, - 0x038, 0x3C5, 0x3C9, 0x03A, 0x037, 0x3C0, 0x3CE, 0x03B, - 0x036, 0x3BB, 0x3D2, 0x03C, 0x035, 0x3B7, 0x3D7, 0x03D, - 0x034, 0x3B2, 0x3DC, 0x03E, 0x033, 0x3AD, 0x3E0, 0x040, - 0x032, 0x3A9, 0x3E5, 0x041, 0x031, 0x3A4, 0x3E9, 0x042, - 0x030, 0x39F, 0x3ED, 0x043, 0x02F, 0x39B, 0x3F2, 0x045, - 0x02E, 0x396, 0x3F6, 0x046, 0x02D, 0x391, 0x3FB, 0x047, - 0x02C, 0x38C, 0x3FF, 0x049, 0x02B, 0x388, 0x403, 0x04A, - 0x02A, 0x383, 0x408, 0x04C, 0x029, 0x37E, 0x40C, 0x04D, - 0x028, 0x379, 0x410, 0x04E, 0x027, 0x374, 0x415, 0x050, - 0x026, 0x370, 0x419, 0x051, 0x025, 0x36B, 0x41D, 0x053, - 0x024, 0x366, 0x421, 0x054, 0x024, 0x361, 0x425, 0x056, - 0x023, 0x35C, 0x42A, 0x057, 0x022, 0x357, 0x42E, 0x059, - 0x021, 0x353, 0x432, 0x05A, 0x020, 0x34E, 0x436, 0x05C, - 0x020, 0x349, 0x43A, 0x05E, 0x01F, 0x344, 0x43E, 0x05F, - 0x01E, 0x33F, 0x442, 0x061, 0x01D, 0x33A, 0x446, 0x063, - 0x01D, 0x335, 0x44A, 0x064, 0x01C, 0x330, 0x44E, 0x066, - 0x01B, 0x32B, 0x452, 0x068, 0x01B, 0x326, 0x455, 0x06A, - 0x01A, 0x322, 0x459, 0x06B, 0x019, 0x31D, 0x45D, 0x06D, - 0x018, 0x318, 0x461, 0x06F, 0x018, 0x313, 0x465, 0x071, - 0x017, 0x30E, 0x468, 0x073, 0x017, 0x309, 0x46C, 0x075, - 0x016, 0x304, 0x470, 0x076, 0x015, 0x2FF, 0x473, 0x078, - 0x015, 0x2FA, 0x477, 0x07A, 0x014, 0x2F5, 0x47A, 0x07C, - 0x014, 0x2F0, 0x47E, 0x07E, 0x013, 0x2EB, 0x481, 0x080, - 0x013, 0x2E6, 0x485, 0x082, 0x012, 0x2E1, 0x488, 0x084, - 0x011, 0x2DC, 0x48C, 0x086, 0x011, 0x2D8, 0x48F, 0x089, - 0x010, 0x2D3, 0x492, 0x08B, 0x010, 0x2CE, 0x496, 0x08D, - 0x00F, 0x2C9, 0x499, 0x08F, 0x00F, 0x2C4, 0x49C, 0x091, - 0x00F, 0x2BF, 0x49F, 0x093, 0x00E, 0x2BA, 0x4A2, 0x096, - 0x00E, 0x2B5, 0x4A6, 0x098, 0x00D, 0x2B0, 0x4A9, 0x09A, - 0x00D, 0x2AB, 0x4AC, 0x09C, 0x00C, 0x2A6, 0x4AF, 0x09F, - 0x00C, 0x2A2, 0x4B2, 0x0A1, 0x00B, 0x29D, 0x4B5, 0x0A3, - 0x00B, 0x298, 0x4B7, 0x0A6, 0x00B, 0x293, 0x4BA, 0x0A8, - 0x00A, 0x28E, 0x4BD, 0x0AB, 0x00A, 0x289, 0x4C0, 0x0AD, - 0x00A, 0x284, 0x4C3, 0x0AF, 0x009, 0x280, 0x4C5, 0x0B2, - 0x009, 0x27B, 0x4C8, 0x0B4, 0x009, 0x276, 0x4CB, 0x0B7, - 0x008, 0x271, 0x4CD, 0x0BA, 0x008, 0x26C, 0x4D0, 0x0BC, - 0x008, 0x267, 0x4D2, 0x0BF, 0x007, 0x263, 0x4D5, 0x0C1, - 0x007, 0x25E, 0x4D7, 0x0C4, 0x007, 0x259, 0x4D9, 0x0C7, - 0x006, 0x254, 0x4DC, 0x0C9, 0x006, 0x250, 0x4DE, 0x0CC, - 0x006, 0x24B, 0x4E0, 0x0CF, 0x006, 0x246, 0x4E3, 0x0D2, - 0x005, 0x241, 0x4E5, 0x0D4, 0x005, 0x23D, 0x4E7, 0x0D7, - 0x005, 0x238, 0x4E9, 0x0DA, 0x005, 0x233, 0x4EB, 0x0DD, - 0x004, 0x22F, 0x4ED, 0x0E0, 0x004, 0x22A, 0x4EF, 0x0E3, - 0x004, 0x226, 0x4F1, 0x0E6, 0x004, 0x221, 0x4F3, 0x0E9, - 0x004, 0x21C, 0x4F5, 0x0EC, 0x003, 0x218, 0x4F6, 0x0EF, - 0x003, 0x213, 0x4F8, 0x0F2, 0x003, 0x20F, 0x4FA, 0x0F5, - 0x003, 0x20A, 0x4FB, 0x0F8, 0x003, 0x205, 0x4FD, 0x0FB, - 0x002, 0x201, 0x4FF, 0x0FE, 0x002, 0x1FC, 0x500, 0x101, - 0x002, 0x1F8, 0x502, 0x104, 0x002, 0x1F3, 0x503, 0x107, - 0x002, 0x1EF, 0x504, 0x10B, 0x002, 0x1EB, 0x506, 0x10E, - 0x002, 0x1E6, 0x507, 0x111, 0x001, 0x1E2, 0x508, 0x114, - 0x001, 0x1DD, 0x50A, 0x118, 0x001, 0x1D9, 0x50B, 0x11B, - 0x001, 0x1D5, 0x50C, 0x11E, 0x001, 0x1D0, 0x50D, 0x122, - 0x001, 0x1CC, 0x50E, 0x125, 0x001, 0x1C8, 0x50F, 0x129, - 0x001, 0x1C3, 0x510, 0x12C, 0x001, 0x1BF, 0x511, 0x130, - 0x001, 0x1BB, 0x511, 0x133, 0x001, 0x1B7, 0x512, 0x137, - 0x000, 0x1B2, 0x513, 0x13A, 0x000, 0x1AE, 0x514, 0x13E, - 0x000, 0x1AA, 0x514, 0x141, 0x000, 0x1A6, 0x515, 0x145, - 0x000, 0x1A2, 0x516, 0x148, 0x000, 0x19E, 0x516, 0x14C, - 0x000, 0x19A, 0x517, 0x150, 0x000, 0x195, 0x517, 0x153, - 0x000, 0x191, 0x517, 0x157, 0x000, 0x18D, 0x518, 0x15B, - 0x000, 0x189, 0x518, 0x15F, 0x000, 0x185, 0x518, 0x162, - 0x000, 0x181, 0x518, 0x166, 0x000, 0x17D, 0x518, 0x16A, - 0x000, 0x17A, 0x519, 0x16E, 0x000, 0x176, 0x519, 0x172}; -#endif +/*************************************************************************** + gauss_i.h - description + ----------------------- + begin : Sun Feb 08 2003 + copyright : (C) 2003 by Chris Moeller, eh, whatever + email : chris@kode54.tk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2003/02/08 - kode54 +// - generated by interleaving table from gauss.h from the libopenspc +// project; a gaussian bell curve table logged from the SPC-700, +// though Neill says he logged the same curve from a PSX SPU. Also +// says that interleaving the coefficients together runs faster. Meh. +// +//*************************************************************************// + +#ifndef GAUSS_H +#define GAUSS_H + +const int gauss[]={ + 0x172, 0x519, 0x176, 0x000, 0x16E, 0x519, 0x17A, 0x000, + 0x16A, 0x518, 0x17D, 0x000, 0x166, 0x518, 0x181, 0x000, + 0x162, 0x518, 0x185, 0x000, 0x15F, 0x518, 0x189, 0x000, + 0x15B, 0x518, 0x18D, 0x000, 0x157, 0x517, 0x191, 0x000, + 0x153, 0x517, 0x195, 0x000, 0x150, 0x517, 0x19A, 0x000, + 0x14C, 0x516, 0x19E, 0x000, 0x148, 0x516, 0x1A2, 0x000, + 0x145, 0x515, 0x1A6, 0x000, 0x141, 0x514, 0x1AA, 0x000, + 0x13E, 0x514, 0x1AE, 0x000, 0x13A, 0x513, 0x1B2, 0x000, + 0x137, 0x512, 0x1B7, 0x001, 0x133, 0x511, 0x1BB, 0x001, + 0x130, 0x511, 0x1BF, 0x001, 0x12C, 0x510, 0x1C3, 0x001, + 0x129, 0x50F, 0x1C8, 0x001, 0x125, 0x50E, 0x1CC, 0x001, + 0x122, 0x50D, 0x1D0, 0x001, 0x11E, 0x50C, 0x1D5, 0x001, + 0x11B, 0x50B, 0x1D9, 0x001, 0x118, 0x50A, 0x1DD, 0x001, + 0x114, 0x508, 0x1E2, 0x001, 0x111, 0x507, 0x1E6, 0x002, + 0x10E, 0x506, 0x1EB, 0x002, 0x10B, 0x504, 0x1EF, 0x002, + 0x107, 0x503, 0x1F3, 0x002, 0x104, 0x502, 0x1F8, 0x002, + 0x101, 0x500, 0x1FC, 0x002, 0x0FE, 0x4FF, 0x201, 0x002, + 0x0FB, 0x4FD, 0x205, 0x003, 0x0F8, 0x4FB, 0x20A, 0x003, + 0x0F5, 0x4FA, 0x20F, 0x003, 0x0F2, 0x4F8, 0x213, 0x003, + 0x0EF, 0x4F6, 0x218, 0x003, 0x0EC, 0x4F5, 0x21C, 0x004, + 0x0E9, 0x4F3, 0x221, 0x004, 0x0E6, 0x4F1, 0x226, 0x004, + 0x0E3, 0x4EF, 0x22A, 0x004, 0x0E0, 0x4ED, 0x22F, 0x004, + 0x0DD, 0x4EB, 0x233, 0x005, 0x0DA, 0x4E9, 0x238, 0x005, + 0x0D7, 0x4E7, 0x23D, 0x005, 0x0D4, 0x4E5, 0x241, 0x005, + 0x0D2, 0x4E3, 0x246, 0x006, 0x0CF, 0x4E0, 0x24B, 0x006, + 0x0CC, 0x4DE, 0x250, 0x006, 0x0C9, 0x4DC, 0x254, 0x006, + 0x0C7, 0x4D9, 0x259, 0x007, 0x0C4, 0x4D7, 0x25E, 0x007, + 0x0C1, 0x4D5, 0x263, 0x007, 0x0BF, 0x4D2, 0x267, 0x008, + 0x0BC, 0x4D0, 0x26C, 0x008, 0x0BA, 0x4CD, 0x271, 0x008, + 0x0B7, 0x4CB, 0x276, 0x009, 0x0B4, 0x4C8, 0x27B, 0x009, + 0x0B2, 0x4C5, 0x280, 0x009, 0x0AF, 0x4C3, 0x284, 0x00A, + 0x0AD, 0x4C0, 0x289, 0x00A, 0x0AB, 0x4BD, 0x28E, 0x00A, + 0x0A8, 0x4BA, 0x293, 0x00B, 0x0A6, 0x4B7, 0x298, 0x00B, + 0x0A3, 0x4B5, 0x29D, 0x00B, 0x0A1, 0x4B2, 0x2A2, 0x00C, + 0x09F, 0x4AF, 0x2A6, 0x00C, 0x09C, 0x4AC, 0x2AB, 0x00D, + 0x09A, 0x4A9, 0x2B0, 0x00D, 0x098, 0x4A6, 0x2B5, 0x00E, + 0x096, 0x4A2, 0x2BA, 0x00E, 0x093, 0x49F, 0x2BF, 0x00F, + 0x091, 0x49C, 0x2C4, 0x00F, 0x08F, 0x499, 0x2C9, 0x00F, + 0x08D, 0x496, 0x2CE, 0x010, 0x08B, 0x492, 0x2D3, 0x010, + 0x089, 0x48F, 0x2D8, 0x011, 0x086, 0x48C, 0x2DC, 0x011, + 0x084, 0x488, 0x2E1, 0x012, 0x082, 0x485, 0x2E6, 0x013, + 0x080, 0x481, 0x2EB, 0x013, 0x07E, 0x47E, 0x2F0, 0x014, + 0x07C, 0x47A, 0x2F5, 0x014, 0x07A, 0x477, 0x2FA, 0x015, + 0x078, 0x473, 0x2FF, 0x015, 0x076, 0x470, 0x304, 0x016, + 0x075, 0x46C, 0x309, 0x017, 0x073, 0x468, 0x30E, 0x017, + 0x071, 0x465, 0x313, 0x018, 0x06F, 0x461, 0x318, 0x018, + 0x06D, 0x45D, 0x31D, 0x019, 0x06B, 0x459, 0x322, 0x01A, + 0x06A, 0x455, 0x326, 0x01B, 0x068, 0x452, 0x32B, 0x01B, + 0x066, 0x44E, 0x330, 0x01C, 0x064, 0x44A, 0x335, 0x01D, + 0x063, 0x446, 0x33A, 0x01D, 0x061, 0x442, 0x33F, 0x01E, + 0x05F, 0x43E, 0x344, 0x01F, 0x05E, 0x43A, 0x349, 0x020, + 0x05C, 0x436, 0x34E, 0x020, 0x05A, 0x432, 0x353, 0x021, + 0x059, 0x42E, 0x357, 0x022, 0x057, 0x42A, 0x35C, 0x023, + 0x056, 0x425, 0x361, 0x024, 0x054, 0x421, 0x366, 0x024, + 0x053, 0x41D, 0x36B, 0x025, 0x051, 0x419, 0x370, 0x026, + 0x050, 0x415, 0x374, 0x027, 0x04E, 0x410, 0x379, 0x028, + 0x04D, 0x40C, 0x37E, 0x029, 0x04C, 0x408, 0x383, 0x02A, + 0x04A, 0x403, 0x388, 0x02B, 0x049, 0x3FF, 0x38C, 0x02C, + 0x047, 0x3FB, 0x391, 0x02D, 0x046, 0x3F6, 0x396, 0x02E, + 0x045, 0x3F2, 0x39B, 0x02F, 0x043, 0x3ED, 0x39F, 0x030, + 0x042, 0x3E9, 0x3A4, 0x031, 0x041, 0x3E5, 0x3A9, 0x032, + 0x040, 0x3E0, 0x3AD, 0x033, 0x03E, 0x3DC, 0x3B2, 0x034, + 0x03D, 0x3D7, 0x3B7, 0x035, 0x03C, 0x3D2, 0x3BB, 0x036, + 0x03B, 0x3CE, 0x3C0, 0x037, 0x03A, 0x3C9, 0x3C5, 0x038, + 0x038, 0x3C5, 0x3C9, 0x03A, 0x037, 0x3C0, 0x3CE, 0x03B, + 0x036, 0x3BB, 0x3D2, 0x03C, 0x035, 0x3B7, 0x3D7, 0x03D, + 0x034, 0x3B2, 0x3DC, 0x03E, 0x033, 0x3AD, 0x3E0, 0x040, + 0x032, 0x3A9, 0x3E5, 0x041, 0x031, 0x3A4, 0x3E9, 0x042, + 0x030, 0x39F, 0x3ED, 0x043, 0x02F, 0x39B, 0x3F2, 0x045, + 0x02E, 0x396, 0x3F6, 0x046, 0x02D, 0x391, 0x3FB, 0x047, + 0x02C, 0x38C, 0x3FF, 0x049, 0x02B, 0x388, 0x403, 0x04A, + 0x02A, 0x383, 0x408, 0x04C, 0x029, 0x37E, 0x40C, 0x04D, + 0x028, 0x379, 0x410, 0x04E, 0x027, 0x374, 0x415, 0x050, + 0x026, 0x370, 0x419, 0x051, 0x025, 0x36B, 0x41D, 0x053, + 0x024, 0x366, 0x421, 0x054, 0x024, 0x361, 0x425, 0x056, + 0x023, 0x35C, 0x42A, 0x057, 0x022, 0x357, 0x42E, 0x059, + 0x021, 0x353, 0x432, 0x05A, 0x020, 0x34E, 0x436, 0x05C, + 0x020, 0x349, 0x43A, 0x05E, 0x01F, 0x344, 0x43E, 0x05F, + 0x01E, 0x33F, 0x442, 0x061, 0x01D, 0x33A, 0x446, 0x063, + 0x01D, 0x335, 0x44A, 0x064, 0x01C, 0x330, 0x44E, 0x066, + 0x01B, 0x32B, 0x452, 0x068, 0x01B, 0x326, 0x455, 0x06A, + 0x01A, 0x322, 0x459, 0x06B, 0x019, 0x31D, 0x45D, 0x06D, + 0x018, 0x318, 0x461, 0x06F, 0x018, 0x313, 0x465, 0x071, + 0x017, 0x30E, 0x468, 0x073, 0x017, 0x309, 0x46C, 0x075, + 0x016, 0x304, 0x470, 0x076, 0x015, 0x2FF, 0x473, 0x078, + 0x015, 0x2FA, 0x477, 0x07A, 0x014, 0x2F5, 0x47A, 0x07C, + 0x014, 0x2F0, 0x47E, 0x07E, 0x013, 0x2EB, 0x481, 0x080, + 0x013, 0x2E6, 0x485, 0x082, 0x012, 0x2E1, 0x488, 0x084, + 0x011, 0x2DC, 0x48C, 0x086, 0x011, 0x2D8, 0x48F, 0x089, + 0x010, 0x2D3, 0x492, 0x08B, 0x010, 0x2CE, 0x496, 0x08D, + 0x00F, 0x2C9, 0x499, 0x08F, 0x00F, 0x2C4, 0x49C, 0x091, + 0x00F, 0x2BF, 0x49F, 0x093, 0x00E, 0x2BA, 0x4A2, 0x096, + 0x00E, 0x2B5, 0x4A6, 0x098, 0x00D, 0x2B0, 0x4A9, 0x09A, + 0x00D, 0x2AB, 0x4AC, 0x09C, 0x00C, 0x2A6, 0x4AF, 0x09F, + 0x00C, 0x2A2, 0x4B2, 0x0A1, 0x00B, 0x29D, 0x4B5, 0x0A3, + 0x00B, 0x298, 0x4B7, 0x0A6, 0x00B, 0x293, 0x4BA, 0x0A8, + 0x00A, 0x28E, 0x4BD, 0x0AB, 0x00A, 0x289, 0x4C0, 0x0AD, + 0x00A, 0x284, 0x4C3, 0x0AF, 0x009, 0x280, 0x4C5, 0x0B2, + 0x009, 0x27B, 0x4C8, 0x0B4, 0x009, 0x276, 0x4CB, 0x0B7, + 0x008, 0x271, 0x4CD, 0x0BA, 0x008, 0x26C, 0x4D0, 0x0BC, + 0x008, 0x267, 0x4D2, 0x0BF, 0x007, 0x263, 0x4D5, 0x0C1, + 0x007, 0x25E, 0x4D7, 0x0C4, 0x007, 0x259, 0x4D9, 0x0C7, + 0x006, 0x254, 0x4DC, 0x0C9, 0x006, 0x250, 0x4DE, 0x0CC, + 0x006, 0x24B, 0x4E0, 0x0CF, 0x006, 0x246, 0x4E3, 0x0D2, + 0x005, 0x241, 0x4E5, 0x0D4, 0x005, 0x23D, 0x4E7, 0x0D7, + 0x005, 0x238, 0x4E9, 0x0DA, 0x005, 0x233, 0x4EB, 0x0DD, + 0x004, 0x22F, 0x4ED, 0x0E0, 0x004, 0x22A, 0x4EF, 0x0E3, + 0x004, 0x226, 0x4F1, 0x0E6, 0x004, 0x221, 0x4F3, 0x0E9, + 0x004, 0x21C, 0x4F5, 0x0EC, 0x003, 0x218, 0x4F6, 0x0EF, + 0x003, 0x213, 0x4F8, 0x0F2, 0x003, 0x20F, 0x4FA, 0x0F5, + 0x003, 0x20A, 0x4FB, 0x0F8, 0x003, 0x205, 0x4FD, 0x0FB, + 0x002, 0x201, 0x4FF, 0x0FE, 0x002, 0x1FC, 0x500, 0x101, + 0x002, 0x1F8, 0x502, 0x104, 0x002, 0x1F3, 0x503, 0x107, + 0x002, 0x1EF, 0x504, 0x10B, 0x002, 0x1EB, 0x506, 0x10E, + 0x002, 0x1E6, 0x507, 0x111, 0x001, 0x1E2, 0x508, 0x114, + 0x001, 0x1DD, 0x50A, 0x118, 0x001, 0x1D9, 0x50B, 0x11B, + 0x001, 0x1D5, 0x50C, 0x11E, 0x001, 0x1D0, 0x50D, 0x122, + 0x001, 0x1CC, 0x50E, 0x125, 0x001, 0x1C8, 0x50F, 0x129, + 0x001, 0x1C3, 0x510, 0x12C, 0x001, 0x1BF, 0x511, 0x130, + 0x001, 0x1BB, 0x511, 0x133, 0x001, 0x1B7, 0x512, 0x137, + 0x000, 0x1B2, 0x513, 0x13A, 0x000, 0x1AE, 0x514, 0x13E, + 0x000, 0x1AA, 0x514, 0x141, 0x000, 0x1A6, 0x515, 0x145, + 0x000, 0x1A2, 0x516, 0x148, 0x000, 0x19E, 0x516, 0x14C, + 0x000, 0x19A, 0x517, 0x150, 0x000, 0x195, 0x517, 0x153, + 0x000, 0x191, 0x517, 0x157, 0x000, 0x18D, 0x518, 0x15B, + 0x000, 0x189, 0x518, 0x15F, 0x000, 0x185, 0x518, 0x162, + 0x000, 0x181, 0x518, 0x166, 0x000, 0x17D, 0x518, 0x16A, + 0x000, 0x17A, 0x519, 0x16E, 0x000, 0x176, 0x519, 0x172}; +#endif diff --git a/extras/modules/peops/spu/registers.c b/extras/modules/peops/spu/registers.c index e0154a750..962b4d4ed 100644 --- a/extras/modules/peops/spu/registers.c +++ b/extras/modules/peops/spu/registers.c @@ -5,6 +5,7 @@ copyright : (C) 2002 by Pete Bernert email : BlackDove@addcom.de ***************************************************************************/ + /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * @@ -15,6 +16,27 @@ * * ***************************************************************************/ +//*************************************************************************// +// History of changes: +// +// 2004/09/18 - LDChen +// - pre-calculated ADSRX values +// +// 2003/02/09 - kode54 +// - removed &0x3fff from reverb volume registers, fixes a few games, +// hopefully won't be breaking anything +// +// 2003/01/19 - Pete +// - added Neill's reverb +// +// 2003/01/06 - Pete +// - added Neill's ADSR timings +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + #include "stdafx.h" #define _IN_REGISTERS @@ -24,63 +46,13 @@ #include "regs.h" #include "reverb.h" -/* -// adsr time values (in ms) by James Higgs ... see the end of -// the adsr.c source for details - -#define ATTACK_MS 514L -#define DECAYHALF_MS 292L -#define DECAY_MS 584L -#define SUSTAIN_MS 450L -#define RELEASE_MS 446L -*/ - -// we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines -#define ATTACK_MS 494L -#define DECAYHALF_MS 286L -#define DECAY_MS 572L -#define SUSTAIN_MS 441L -#define RELEASE_MS 437L - - - - - - -int Check_IRQ( int addr, int force ) { - if(spuCtrl & CTRL_IRQ) // some callback and irq active? - { - if( ( bIrqHit == 0 ) && - ( force == 1 || pSpuIrq == spuMemC+addr ) ) - { - if(irqCallback) - irqCallback(); // -> call main emu - - // one-time - bIrqHit = 1; - spuStat |= STAT_IRQ; - -#if 0 - MessageBox( NULL, "IRQ", "SPU", MB_OK ); -#endif - - return 1; - } - } - - - return 0; -} - - - //////////////////////////////////////////////////////////////////////// // WRITE REGISTERS: called by main emu //////////////////////////////////////////////////////////////////////// void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) { - const unsigned long r=reg&0xfff; + const unsigned long r=reg&0xfff; if(r>=0x0c00 && r<0x0d80) // some channel info? { @@ -101,177 +73,47 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) break; //------------------------------------------------// start case 6: - // Brain Dead 13 - align to 16 boundary - s_chan[ch].pStart= spuMemC+(unsigned long)((val<<3)&~0xf); + s_chan[ch].pStart=spuMemC+((unsigned long) val<<3); break; //------------------------------------------------// level with pre-calcs case 8: - { - const unsigned long lval=val;unsigned long lx; + { //---------------------------------------------// - s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; - s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f; - s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f; - s_chan[ch].ADSRX.SustainLevel=lval & 0x000f; + s_chan[ch].ADSRX.AttackModeExp=(val&0x8000)?1:0; + s_chan[ch].ADSRX.AttackRate = ((val>>8) & 0x007f)^0x7f; + s_chan[ch].ADSRX.DecayRate = 4*(((val>>4) & 0x000f)^0x1f); + s_chan[ch].ADSRX.SustainLevel = (val & 0x000f) << 27; //---------------------------------------------// - if(!iDebugMode) break; - //---------------------------------------------// stuff below is only for debug mode - - s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0; //0x007f - - lx=(((lval>>8) & 0x007f)>>2); // attack time to run from 0 to 100% volume - lx=min(31,lx); // no overflow on shift! - if(lx) - { - lx = (1<>4) & 0x000f; // decay: - if(lx) // our const decay value is time it takes from 100% to 0% of volume - { - lx = ((1<<(lx))*DECAY_MS)/10000L; - if(!lx) lx=1; - } - s_chan[ch].ADSR.DecayTime = // so calc how long does it take to run from 100% to the wanted sus level - (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024; - } + } break; //------------------------------------------------// adsr times with pre-calcs case 10: - { - const unsigned long lval=val;unsigned long lx; - + { //----------------------------------------------// - s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0; - s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1; - s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f; - s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0; - s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f; + s_chan[ch].ADSRX.SustainModeExp = (val&0x8000)?1:0; + s_chan[ch].ADSRX.SustainIncrease= (val&0x4000)?0:1; + s_chan[ch].ADSRX.SustainRate = ((val>>6) & 0x007f)^0x7f; + s_chan[ch].ADSRX.ReleaseModeExp = (val&0x0020)?1:0; + s_chan[ch].ADSRX.ReleaseRate = 4*((val & 0x001f)^0x1f); //----------------------------------------------// - if(!iDebugMode) break; - //----------------------------------------------// stuff below is only for debug mode - - s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0; - s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0; - - lx=((((lval>>6) & 0x007f)>>2)); // sustain time... often very high - lx=min(31,lx); // values are used to hold the volume - if(lx) // until a sound stop occurs - { // the highest value we reach (due to - lx = (1< no multithread fuckups - - s_chan[ch].pLoop=spuMemC+((unsigned long)((val<<3)&~0xf)); - - //s_chan[ch].bIgnoreLoop=1; - //ReleaseMutex(s_chan[ch].hMutex); // -> oki, on with the thread + s_chan[ch].pLoop=spuMemC+((unsigned long) val<<3); + s_chan[ch].bIgnoreLoop=1; break; //------------------------------------------------// - } - iSpuAsyncWait=0; + } + return; } switch(r) { //-------------------------------------------------// - case H_SPUaddr: - spuAddr = (unsigned long) val<<3; - break; - //-------------------------------------------------// - case H_SPUdata: - // BIOS - allow dma 00 - Check_IRQ( spuAddr, 0 ); - - spuMem[spuAddr>>1] = val; - spuAddr+=2; - if(spuAddr>0x7ffff) spuAddr=0; - break; - //-------------------------------------------------// case H_SPUctrl: spuCtrl=val; - - - // flags - if( spuCtrl & CTRL_CD_PLAY ) - spuStat |= CTRL_CD_PLAY; - else - spuStat &= ~CTRL_CD_PLAY; - - if( spuCtrl & CTRL_CD_REVERB ) - spuStat |= STAT_CD_REVERB; - else - spuStat &= ~STAT_CD_REVERB; - - - if( spuCtrl & CTRL_EXT_PLAY ) - spuStat |= STAT_EXT_PLAY; - else - spuStat &= ~STAT_EXT_PLAY; - - if( spuCtrl & CTRL_EXT_REVERB ) - spuStat |= STAT_EXT_REVERB; - else - spuStat &= ~STAT_EXT_REVERB; - - - - spuStat &= ~(STAT_DMA_NON | STAT_DMA_R | STAT_DMA_W); - - if( spuCtrl & CTRL_DMA_F ) - spuStat |= STAT_DMA_F; - - if( (spuCtrl & CTRL_DMA_F) == CTRL_DMA_R ) - spuStat |= STAT_DMA_R; - - - - // reset IRQ flag - if( (spuCtrl & CTRL_IRQ) == 0 ) { - bIrqHit = 0; - spuStat &= ~STAT_IRQ; - } - - - dwNoiseClock = (spuCtrl & CTRL_NOISE)>>8; - break; - //-------------------------------------------------// - case H_SPUstat: - spuStat=val & 0xf800; break; //-------------------------------------------------// case H_SPUReverbAddr: @@ -288,11 +130,6 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) } break; //-------------------------------------------------// - case H_SPUirqAddr: - spuIrq = val; - pSpuIrq=spuMemC+((unsigned long) val<<3); - break; - //-------------------------------------------------// case H_SPUrvolL: rvb.VolLeft=val; break; @@ -301,33 +138,6 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) rvb.VolRight=val; break; //-------------------------------------------------// - -/* - case H_ExtLeft: - //auxprintf("EL %d\n",val); - break; - //-------------------------------------------------// - case H_ExtRight: - //auxprintf("ER %d\n",val); - break; - //-------------------------------------------------// - case H_SPUmvolL: - //auxprintf("ML %d\n",val); - break; - //-------------------------------------------------// - case H_SPUmvolR: - //auxprintf("MR %d\n",val); - break; - //-------------------------------------------------// - case H_SPUMute1: - //auxprintf("M0 %04x\n",val); - break; - //-------------------------------------------------// - case H_SPUMute2: - //auxprintf("M1 %04x\n",val); - break; -*/ - //-------------------------------------------------// case H_SPUon1: SoundOn(0,16,val); break; @@ -345,13 +155,11 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) break; //-------------------------------------------------// case H_CDLeft: - iLeftXAVol = val; - if(cddavCallback) cddavCallback(0,val); - break; + iLeftXAVol=val & 0x7fff; + break; case H_CDRight: - iRightXAVol = val; - if(cddavCallback) cddavCallback(1,val); - break; + iRightXAVol=val & 0x7fff; + break; //-------------------------------------------------// case H_FMod1: FModOn(0,16,val); @@ -420,9 +228,7 @@ void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) case H_Reverb+58 : rvb.MIX_DEST_B1=(short)val; break; case H_Reverb+60 : rvb.IN_COEF_L=(short)val; break; case H_Reverb+62 : rvb.IN_COEF_R=(short)val; break; - } - - iSpuAsyncWait=0; + } } //////////////////////////////////////////////////////////////////////// @@ -437,25 +243,8 @@ void SoundOn(int start,int end,unsigned short val) // SOUND ON PSX COMAND { if((val&1) && s_chan[ch].pStart) // mmm... start has to be set before key on !?! { - s_chan[ch].bLoopJump = 0; + s_chan[ch].bIgnoreLoop=0; s_chan[ch].bNew=1; - - // do this here, not in StartSound - // - fixes fussy timing issues - s_chan[ch].iSilent=0; - s_chan[ch].bStop=0; - s_chan[ch].bOn=1; - s_chan[ch].pCurr=s_chan[ch].pStart; - -#if 0 - // ADSR init time (guess to # apu cycles) - s_chan[ch].ADSRX.StartDelay = 0; -#endif - - // Final Fantasy 7 - don't do any of these - // - sets loop address before VoiceOn - //s_chan[ch].pLoop = s_chan[ch].pStart; - dwNewChannel|=(1<0) // -> fake reverb used? - { - s_chan[ch].bRVBActive=1; // -> activate it - s_chan[ch].iRVBOffset=iReverbOff*45; - s_chan[ch].iRVBRepeat=iReverbRepeat*45; - s_chan[ch].iRVBNum =iReverbNum; - } - } - else s_chan[ch].bRVBActive=0; // else -> no reverb -} - -//////////////////////////////////////////////////////////////////////// -// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf -//////////////////////////////////////////////////////////////////////// - -static INLINE void InitREVERB(void) -{ - if(iUseReverb==2) - {memset(sRVBStart,0,NSSIZE*2*4);} -} - -//////////////////////////////////////////////////////////////////////// -// STORE REVERB -//////////////////////////////////////////////////////////////////////// - -static INLINE void StoreREVERB_CD(int left, int right,int ns) -{ - if(iUseReverb==0) return; - else - if(iUseReverb==2) // -------------------------------- // Neil's reverb - { - const int iRxl=left; - const int iRxr=right; - - ns<<=1; - - // -> we mix all active reverb channels into an extra buffer - *(sRVBStart+ns) += CLAMP16( *(sRVBStart+ns+0) + ( iRxl ) ); - *(sRVBStart+ns+1) += CLAMP16( *(sRVBStart+ns+1) + ( iRxr ) ); - } -} - - -static INLINE void StoreREVERB(int ch,int ns) -{ - if(iUseReverb==0) return; - else - if(iUseReverb==2) // -------------------------------- // Neil's reverb - { - const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000; - const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000; - - ns<<=1; - - *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer - *(sRVBStart+ns+1)+=iRxr; - } - else // --------------------------------------------- // Pete's easy fake reverb - { - int * pN;int iRn,iRr=0; - - // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on - - int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x8000; - int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x8000; - - for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2) - { - pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1); - if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd); - - (*pN)+=iRxl; - pN++; - (*pN)+=iRxr; - } - } -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps -{ - short * p=(short *)spuMem; - iOff=(iOff*4)+rvb.CurrAddr; - while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); - while(iOff0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); - while(iOff32767L) iVal=32767L; - *(p+iOff)=(short)iVal; -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping -{ - short * p=(short *)spuMem; - iOff=(iOff*4)+rvb.CurrAddr+1; - while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); - while(iOff32767L) iVal=32767L; - *(p+iOff)=(short)iVal; -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int MixREVERBLeft(int ns) -{ - if(iUseReverb==0) return 0; - else - if(iUseReverb==2) - { - static int iCnt=0; // this func will be called with 44.1 khz - - if(!rvb.StartAddr) // reverb is off - { - rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; - return 0; - } - - iCnt++; - - if(iCnt&1) // we work on every second left value: downsample to 22 khz - { - if(spuCtrl&0x80) // -> reverb on? oki - { - int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1; - - const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1)); - const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1); - - const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; - const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; - const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; - const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; - - const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L; - const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L; - const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L; - const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L; - - s_buffer1(rvb.IIR_DEST_A0, IIR_A0); - s_buffer1(rvb.IIR_DEST_A1, IIR_A1); - s_buffer1(rvb.IIR_DEST_B0, IIR_B0); - s_buffer1(rvb.IIR_DEST_B1, IIR_B1); - - ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L + - (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L + - (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L + - (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L; - ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L + - (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L + - (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L + - (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L; - - FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A); - FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A); - FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B); - FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B); - - s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L); - s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L); - - s_buffer(rvb.MIX_DEST_B0, (rvb.FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb.FB_X)/32768L); - s_buffer(rvb.MIX_DEST_B1, (rvb.FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb.FB_X)/32768L); - - rvb.iLastRVBLeft = rvb.iRVBLeft; - rvb.iLastRVBRight = rvb.iRVBRight; - - rvb.iRVBLeft = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3; - rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3; - - rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000; - rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000; - - rvb.CurrAddr++; - if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; - - return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2; - } - else // -> reverb off - { - // Vib Ribbon - grab current reverb sample (cdda data) - // - mono data - - rvb.iRVBLeft = (short) spuMem[ rvb.CurrAddr ]; - rvb.iRVBRight = rvb.iRVBLeft; - rvb.iLastRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000; - rvb.iLastRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000; - - //rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; - } - - - Check_IRQ( rvb.CurrAddr*2, 0 ); - - rvb.CurrAddr++; - if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; - } - - return rvb.iLastRVBLeft; - } - else // easy fake reverb: - { - const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value - *sRVBPlay++=0; // -> init it after - if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds - return iRV; // -> return reverb mix buf val - } -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int MixREVERBRight(void) -{ - if(iUseReverb==0) return 0; - else - if(iUseReverb==2) // Neill's reverb: - { - // Vib Ribbon - reverb always on (!), reverb write flag - if(spuCtrl & CTRL_REVERB) // -> reverb on? oki - { - int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2; - rvb.iLastRVBRight=rvb.iRVBRight; - return i; // -> just return the last right reverb val (little bit scaled by the previous right val) - } else { - // Vib Ribbon - return reverb buffer (cdda data) - return CLAMP16(rvb.iLastRVBRight); - } - } - else // easy fake reverb: - { - const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value - *sRVBPlay++=0; // -> init it after - if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds - return iRV; // -> return reverb mix buf val - } -} - -//////////////////////////////////////////////////////////////////////// - -#endif - -/* ------------------------------------------------------------------------------ -PSX reverb hardware notes -by Neill Corlett ------------------------------------------------------------------------------ - -Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway -yadda yadda. - ------------------------------------------------------------------------------ - -Basics ------- - -- The reverb buffer is 22khz 16-bit mono PCM. -- It starts at the reverb address given by 1DA2, extends to - the end of sound RAM, and wraps back to the 1DA2 address. - -Setting the address at 1DA2 resets the current reverb work address. - -This work address ALWAYS increments every 1/22050 sec., regardless of -whether reverb is enabled (bit 7 of 1DAA set). - -And the contents of the reverb buffer ALWAYS play, scaled by the -"reverberation depth left/right" volumes (1D84/1D86). -(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0) - ------------------------------------------------------------------------------ - -Register names --------------- - -These are probably not their real names. -These are probably not even correct names. -We will use them anyway, because we can. - -1DC0: FB_SRC_A (offset) -1DC2: FB_SRC_B (offset) -1DC4: IIR_ALPHA (coef.) -1DC6: ACC_COEF_A (coef.) -1DC8: ACC_COEF_B (coef.) -1DCA: ACC_COEF_C (coef.) -1DCC: ACC_COEF_D (coef.) -1DCE: IIR_COEF (coef.) -1DD0: FB_ALPHA (coef.) -1DD2: FB_X (coef.) -1DD4: IIR_DEST_A0 (offset) -1DD6: IIR_DEST_A1 (offset) -1DD8: ACC_SRC_A0 (offset) -1DDA: ACC_SRC_A1 (offset) -1DDC: ACC_SRC_B0 (offset) -1DDE: ACC_SRC_B1 (offset) -1DE0: IIR_SRC_A0 (offset) -1DE2: IIR_SRC_A1 (offset) -1DE4: IIR_DEST_B0 (offset) -1DE6: IIR_DEST_B1 (offset) -1DE8: ACC_SRC_C0 (offset) -1DEA: ACC_SRC_C1 (offset) -1DEC: ACC_SRC_D0 (offset) -1DEE: ACC_SRC_D1 (offset) -1DF0: IIR_SRC_B1 (offset) -1DF2: IIR_SRC_B0 (offset) -1DF4: MIX_DEST_A0 (offset) -1DF6: MIX_DEST_A1 (offset) -1DF8: MIX_DEST_B0 (offset) -1DFA: MIX_DEST_B1 (offset) -1DFC: IN_COEF_L (coef.) -1DFE: IN_COEF_R (coef.) - -The coefficients are signed fractional values. --32768 would be -1.0 - 32768 would be 1.0 (if it were possible... the highest is of course 32767) - -The offsets are (byte/8) offsets into the reverb buffer. -i.e. you multiply them by 8, you get byte offsets. -You can also think of them as (samples/4) offsets. -They appear to be signed. They can be negative. -None of the documented presets make them negative, though. - -Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo. - ------------------------------------------------------------------------------ - -What it does ------------- - -We take all reverb sources: -- regular channels that have the reverb bit on -- cd and external sources, if their reverb bits are on -and mix them into one stereo 44100hz signal. - -Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting -algorithm here, but I haven't figured out the hysterically exact specifics. -I use an 8-tap filter with these coefficients, which are nice but probably -not the real ones: - -0.037828187894 -0.157538631280 -0.321159685278 -0.449322115345 -0.449322115345 -0.321159685278 -0.157538631280 -0.037828187894 - -So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz. - -* IN MY EMULATION, I divide these by 2 to make it clip less. - (and of course the L/R output coefficients are adjusted to compensate) - The real thing appears to not do this. - -At every 22050hz tick: -- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb - steady-state algorithm described below -- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer - (This part may not be exactly right and I guessed at the coefs. TODO: check later.) - L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0]) - R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1]) -- Advance the current buffer position by 1 sample - -The wet out L and R are then upsampled to 44100hz and played at the -"reverberation depth left/right" (1D84/1D86) volume, independent of the main -volume. - ------------------------------------------------------------------------------ - -Reverb steady-state -------------------- - -The reverb steady-state algorithm is fairly clever, and of course by -"clever" I mean "batshit insane". - -buffer[x] is relative to the current buffer position, not the beginning of -the buffer. Note that all buffer offsets must wrap around so they're -contained within the reverb work area. - -Clipping is performed at the end... maybe also sooner, but definitely at -the end. - -IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; -IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; -IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; -IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; - -IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA); -IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA); -IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA); -IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA); - -buffer[IIR_DEST_A0 + 1sample] = IIR_A0; -buffer[IIR_DEST_A1 + 1sample] = IIR_A1; -buffer[IIR_DEST_B0 + 1sample] = IIR_B0; -buffer[IIR_DEST_B1 + 1sample] = IIR_B1; - -ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A + - buffer[ACC_SRC_B0] * ACC_COEF_B + - buffer[ACC_SRC_C0] * ACC_COEF_C + - buffer[ACC_SRC_D0] * ACC_COEF_D; -ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A + - buffer[ACC_SRC_B1] * ACC_COEF_B + - buffer[ACC_SRC_C1] * ACC_COEF_C + - buffer[ACC_SRC_D1] * ACC_COEF_D; - -FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A]; -FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A]; -FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B]; -FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B]; - -buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA; -buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA; -buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X; -buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X; - ------------------------------------------------------------------------------ -*/ - +/*************************************************************************** + reverb.c - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2003/01/19 - Pete +// - added Neill's reverb (see at the end of file) +// +// 2002/12/26 - Pete +// - adjusted reverb handling +// +// 2002/08/14 - Pete +// - added extra reverb +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_REVERB + +// will be included from spu.c +#ifdef _IN_SPU + +//////////////////////////////////////////////////////////////////////// +// globals +//////////////////////////////////////////////////////////////////////// + +// REVERB info and timing vars... + +int * sRVBPlay = 0; +int * sRVBEnd = 0; +int * sRVBStart = 0; +int iReverbOff = -1; // some delay factor for reverb +int iReverbRepeat = 0; +int iReverbNum = 1; + +//////////////////////////////////////////////////////////////////////// +// SET REVERB +//////////////////////////////////////////////////////////////////////// + +void SetREVERB(unsigned short val) +{ + switch(val) + { + case 0x0000: iReverbOff=-1; break; // off + case 0x007D: iReverbOff=32; iReverbNum=2; iReverbRepeat=128; break; // ok room + + case 0x0033: iReverbOff=32; iReverbNum=2; iReverbRepeat=64; break; // studio small + case 0x00B1: iReverbOff=48; iReverbNum=2; iReverbRepeat=96; break; // ok studio medium + case 0x00E3: iReverbOff=64; iReverbNum=2; iReverbRepeat=128; break; // ok studio large ok + + case 0x01A5: iReverbOff=128; iReverbNum=4; iReverbRepeat=32; break; // ok hall + case 0x033D: iReverbOff=256; iReverbNum=4; iReverbRepeat=64; break; // space echo + case 0x0001: iReverbOff=184; iReverbNum=3; iReverbRepeat=128; break; // echo/delay + case 0x0017: iReverbOff=128; iReverbNum=2; iReverbRepeat=128; break; // half echo + default: iReverbOff=32; iReverbNum=1; iReverbRepeat=0; break; + } +} + +//////////////////////////////////////////////////////////////////////// +// START REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StartREVERB(SPUCHAN * pChannel) +{ + if(pChannel->bReverb && (spuCtrl&0x80)) // reverb possible? + { + if(iUseReverb==2) pChannel->bRVBActive=1; + else + if(iUseReverb==1 && iReverbOff>0) // -> fake reverb used? + { + pChannel->bRVBActive=1; // -> activate it + pChannel->iRVBOffset=iReverbOff*45; + pChannel->iRVBRepeat=iReverbRepeat*45; + pChannel->iRVBNum =iReverbNum; + } + } + else pChannel->bRVBActive=0; // else -> no reverb +} + +//////////////////////////////////////////////////////////////////////// +// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf +//////////////////////////////////////////////////////////////////////// + +INLINE void InitREVERB(void) +{ + if(iUseReverb==2) + {memset(sRVBStart,0,NSSIZE*2*4);} +} + +//////////////////////////////////////////////////////////////////////// +// STORE REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StoreREVERB(SPUCHAN * pChannel,int ns) +{ + if(iUseReverb==0) return; + else + if(iUseReverb==2) // -------------------------------- // Neil's reverb + { + const int iRxl=(pChannel->sval*pChannel->iLeftVolume)/0x4000; + const int iRxr=(pChannel->sval*pChannel->iRightVolume)/0x4000; + + ns<<=1; + + *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer + *(sRVBStart+ns+1)+=iRxr; + } + else // --------------------------------------------- // Pete's easy fake reverb + { + int * pN;int iRn,iRr=0; + + // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on + + int iRxl=(pChannel->sval*pChannel->iLeftVolume)/0x8000; + int iRxr=(pChannel->sval*pChannel->iRightVolume)/0x8000; + + for(iRn=1;iRn<=pChannel->iRVBNum;iRn++,iRr+=pChannel->iRVBRepeat,iRxl/=2,iRxr/=2) + { + pN=sRVBPlay+((pChannel->iRVBOffset+iRr+ns)<<1); + if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd); + + (*pN)+=iRxl; + pN++; + (*pN)+=iRxr; + } + } +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps +{ + short * p=(short *)spuMemC; + iOff=(iOff*4)+rvb.CurrAddr; + while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping +{ + short * p=(short *)spuMemC; + iOff=(iOff*4)+rvb.CurrAddr+1; + while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixREVERBLeft(int ns) +{ + if(iUseReverb==0) return 0; + else + if(iUseReverb==2) + { + static int iCnt=0; // this func will be called with 44.1 khz + + if(!rvb.StartAddr) // reverb is off + { + rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; + return 0; + } + + iCnt++; + + if(iCnt&1) // we work on every second left value: downsample to 22 khz + { + if(spuCtrl&0x80) // -> reverb on? oki + { + int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1; + + const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1)); + const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1); + + const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; + const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; + const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; + const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; + + const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L; + + s_buffer1(rvb.IIR_DEST_A0, IIR_A0); + s_buffer1(rvb.IIR_DEST_A1, IIR_A1); + s_buffer1(rvb.IIR_DEST_B0, IIR_B0); + s_buffer1(rvb.IIR_DEST_B1, IIR_B1); + + ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L + + (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L + + (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L + + (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L; + ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L + + (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L + + (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L + + (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L; + + FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A); + FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A); + FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B); + FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B); + + s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L); + s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L); + + s_buffer(rvb.MIX_DEST_B0, (rvb.FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb.FB_X)/32768L); + s_buffer(rvb.MIX_DEST_B1, (rvb.FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb.FB_X)/32768L); + + rvb.iLastRVBLeft = rvb.iRVBLeft; + rvb.iLastRVBRight = rvb.iRVBRight; + + rvb.iRVBLeft = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3; + rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3; + + rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000; + rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000; + + rvb.CurrAddr++; + if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; + + return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2; + } + else // -> reverb off + { + rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; + } + + rvb.CurrAddr++; + if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; + } + + return rvb.iLastRVBLeft; + } + else // easy fake reverb: + { + const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value + *sRVBPlay++=0; // -> init it after + if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds + return iRV; // -> return reverb mix buf val + } +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixREVERBRight(void) +{ + if(iUseReverb==0) return 0; + else + if(iUseReverb==2) // Neill's reverb: + { + int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2; + rvb.iLastRVBRight=rvb.iRVBRight; + return i; // -> just return the last right reverb val (little bit scaled by the previous right val) + } + else // easy fake reverb: + { + const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value + *sRVBPlay++=0; // -> init it after + if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds + return iRV; // -> return reverb mix buf val + } +} + +//////////////////////////////////////////////////////////////////////// + +#endif + +/* +----------------------------------------------------------------------------- +PSX reverb hardware notes +by Neill Corlett +----------------------------------------------------------------------------- + +Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway +yadda yadda. + +----------------------------------------------------------------------------- + +Basics +------ + +- The reverb buffer is 22khz 16-bit mono PCM. +- It starts at the reverb address given by 1DA2, extends to + the end of sound RAM, and wraps back to the 1DA2 address. + +Setting the address at 1DA2 resets the current reverb work address. + +This work address ALWAYS increments every 1/22050 sec., regardless of +whether reverb is enabled (bit 7 of 1DAA set). + +And the contents of the reverb buffer ALWAYS play, scaled by the +"reverberation depth left/right" volumes (1D84/1D86). +(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0) + +----------------------------------------------------------------------------- + +Register names +-------------- + +These are probably not their real names. +These are probably not even correct names. +We will use them anyway, because we can. + +1DC0: FB_SRC_A (offset) +1DC2: FB_SRC_B (offset) +1DC4: IIR_ALPHA (coef.) +1DC6: ACC_COEF_A (coef.) +1DC8: ACC_COEF_B (coef.) +1DCA: ACC_COEF_C (coef.) +1DCC: ACC_COEF_D (coef.) +1DCE: IIR_COEF (coef.) +1DD0: FB_ALPHA (coef.) +1DD2: FB_X (coef.) +1DD4: IIR_DEST_A0 (offset) +1DD6: IIR_DEST_A1 (offset) +1DD8: ACC_SRC_A0 (offset) +1DDA: ACC_SRC_A1 (offset) +1DDC: ACC_SRC_B0 (offset) +1DDE: ACC_SRC_B1 (offset) +1DE0: IIR_SRC_A0 (offset) +1DE2: IIR_SRC_A1 (offset) +1DE4: IIR_DEST_B0 (offset) +1DE6: IIR_DEST_B1 (offset) +1DE8: ACC_SRC_C0 (offset) +1DEA: ACC_SRC_C1 (offset) +1DEC: ACC_SRC_D0 (offset) +1DEE: ACC_SRC_D1 (offset) +1DF0: IIR_SRC_B1 (offset) +1DF2: IIR_SRC_B0 (offset) +1DF4: MIX_DEST_A0 (offset) +1DF6: MIX_DEST_A1 (offset) +1DF8: MIX_DEST_B0 (offset) +1DFA: MIX_DEST_B1 (offset) +1DFC: IN_COEF_L (coef.) +1DFE: IN_COEF_R (coef.) + +The coefficients are signed fractional values. +-32768 would be -1.0 + 32768 would be 1.0 (if it were possible... the highest is of course 32767) + +The offsets are (byte/8) offsets into the reverb buffer. +i.e. you multiply them by 8, you get byte offsets. +You can also think of them as (samples/4) offsets. +They appear to be signed. They can be negative. +None of the documented presets make them negative, though. + +Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo. + +----------------------------------------------------------------------------- + +What it does +------------ + +We take all reverb sources: +- regular channels that have the reverb bit on +- cd and external sources, if their reverb bits are on +and mix them into one stereo 44100hz signal. + +Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting +algorithm here, but I haven't figured out the hysterically exact specifics. +I use an 8-tap filter with these coefficients, which are nice but probably +not the real ones: + +0.037828187894 +0.157538631280 +0.321159685278 +0.449322115345 +0.449322115345 +0.321159685278 +0.157538631280 +0.037828187894 + +So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz. + +* IN MY EMULATION, I divide these by 2 to make it clip less. + (and of course the L/R output coefficients are adjusted to compensate) + The real thing appears to not do this. + +At every 22050hz tick: +- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb + steady-state algorithm described below +- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer + (This part may not be exactly right and I guessed at the coefs. TODO: check later.) + L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0]) + R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1]) +- Advance the current buffer position by 1 sample + +The wet out L and R are then upsampled to 44100hz and played at the +"reverberation depth left/right" (1D84/1D86) volume, independent of the main +volume. + +----------------------------------------------------------------------------- + +Reverb steady-state +------------------- + +The reverb steady-state algorithm is fairly clever, and of course by +"clever" I mean "batshit insane". + +buffer[x] is relative to the current buffer position, not the beginning of +the buffer. Note that all buffer offsets must wrap around so they're +contained within the reverb work area. + +Clipping is performed at the end... maybe also sooner, but definitely at +the end. + +IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; +IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; +IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; +IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; + +IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA); +IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA); +IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA); +IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA); + +buffer[IIR_DEST_A0 + 1sample] = IIR_A0; +buffer[IIR_DEST_A1 + 1sample] = IIR_A1; +buffer[IIR_DEST_B0 + 1sample] = IIR_B0; +buffer[IIR_DEST_B1 + 1sample] = IIR_B1; + +ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A + + buffer[ACC_SRC_B0] * ACC_COEF_B + + buffer[ACC_SRC_C0] * ACC_COEF_C + + buffer[ACC_SRC_D0] * ACC_COEF_D; +ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A + + buffer[ACC_SRC_B1] * ACC_COEF_B + + buffer[ACC_SRC_C1] * ACC_COEF_C + + buffer[ACC_SRC_D1] * ACC_COEF_D; + +FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A]; +FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A]; +FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B]; +FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B]; + +buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA; +buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA; +buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X; +buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X; + +----------------------------------------------------------------------------- +*/ + diff --git a/extras/modules/peops/spu/reverb.h b/extras/modules/peops/spu/reverb.h index 28e02a71e..275e561d9 100644 --- a/extras/modules/peops/spu/reverb.h +++ b/extras/modules/peops/spu/reverb.h @@ -1,21 +1,30 @@ -/*************************************************************************** - reverb.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -void SetREVERB(unsigned short val); -static INLINE void StartREVERB(int ch); -static INLINE void StoreREVERB(int ch,int ns); - +/*************************************************************************** + reverb.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +void SetREVERB(unsigned short val); +INLINE void StartREVERB(SPUCHAN * pChannel); +INLINE void StoreREVERB(SPUCHAN * pChannel,int ns); \ No newline at end of file diff --git a/extras/modules/peops/spu/spu.c b/extras/modules/peops/spu/spu.c index 47ef182de..b4a4f9792 100644 --- a/extras/modules/peops/spu/spu.c +++ b/extras/modules/peops/spu/spu.c @@ -5,6 +5,7 @@ copyright : (C) 2002 by Pete Bernert email : BlackDove@addcom.de ***************************************************************************/ + /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * @@ -18,65 +19,26 @@ #include "stdafx.h" #define _IN_SPU - + #include "externals.h" #include "regs.h" - -#ifdef ENABLE_NLS -#include -#include -#define _(x) gettext(x) -#define N_(x) (x) -//If running under Mac OS X, use the Localizable.strings file instead. -#elif defined(_MACOSX) -#ifdef PCSXRCORE -__private_extern char* Pcsxr_locale_text(char* toloc); -#define _(String) Pcsxr_locale_text(String) -#define N_(String) String -#else -#ifndef PCSXRPLUG -#warning please define the plug being built to use Mac OS X localization! -#define _(msgid) msgid -#define N_(msgid) msgid -#else -//Kludge to get the preprocessor to accept PCSXRPLUG as a variable. -#define PLUGLOC_x(x,y) x ## y -#define PLUGLOC_y(x,y) PLUGLOC_x(x,y) -#define PLUGLOC PLUGLOC_y(PCSXRPLUG,_locale_text) -__private_extern char* PLUGLOC(char* toloc); -#define _(String) PLUGLOC(String) -#define N_(String) String -#endif -#endif -#else -#define _(x) (x) -#define N_(x) (x) -#endif - -static char * libraryInfo = N_("P.E.Op.S. Sound Driver V1.7\nCoded by Pete Bernert and the P.E.Op.S. team\n"); - + +//////////////////////////////////////////////////////////////////////// // globals +//////////////////////////////////////////////////////////////////////// // psx buffer / addresses -unsigned short* spuMem = (unsigned short *)0x49F402C0; -unsigned char * spuMemC = (unsigned char *)0x49F402C0; -unsigned char * pSpuIrq=0; +unsigned char * spuMemC = (unsigned char *)0x49F402C0;; unsigned char * pSpuBuffer; -unsigned char * pMixIrq=0; + // user settings int iVolume=1; -int iXAPitch=1; -int iUseTimer=0; -int iSPUIRQWait=1; -int iDebugMode=0; -int iRecordMode=0; +int iXAPitch=0; int iUseReverb=0; int iUseInterpolation=2; -int iDisStereo=0; -int iFreqResponse=0; // MAIN infos struct for each channel @@ -84,30 +46,17 @@ SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 i REVERBInfo rvb; unsigned long dwNoiseVal=1; // global noise generator -unsigned long dwNoiseCount; // global noise generator -unsigned long dwNoiseClock; // global noise generator -int iSpuAsyncWait=0; - -unsigned int decoded_ptr = 0; -unsigned int bIrqHit = 0; unsigned short spuCtrl=0; // some vars to store psx reg infos -unsigned short spuStat=0; -unsigned short spuIrq=0; -unsigned long spuAddr=0x200; // address into spu mem int bEndThread=0; // thread handlers -int bThreadEnded=0; int bSpuInit=0; int bSPUIsOpen=0; -uint32_t dwNewChannel=0; // flags for faster testing, if new channel starts - -void (CALLBACK *irqCallback)(void)=0; // func of main emu, called on spu irq -void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0; +unsigned long dwNewChannel=0; // flags for faster testing, if new channel starts // certain globals (were local before, but with the new timeproc I need em global) -static const int f[5][2] = { { 0, 0 }, +const int f[5][2] = { { 0, 0 }, { 60, 0 }, { 115, -52 }, { 98, -55 }, @@ -115,10 +64,11 @@ static const int f[5][2] = { { 0, 0 }, int SSumR[NSSIZE]; int SSumL[NSSIZE]; int iFMod[NSSIZE]; -int iCycle = 0; +int iCycle=0; short * pS; -int lastns=0; // last ns pos +static int lastch=-1; // last channel processed on spu irq in timer mode +static int lastns=0; // last ns pos static int iSecureStart=0; // secure start counter //////////////////////////////////////////////////////////////////////// @@ -127,115 +77,72 @@ static int iSecureStart=0; // secure start counter // dirty inline func includes -#include "reverb.c" +#include "reverb.c" #include "adsr.c" -//////////////////////////////////////////////////////////////////////// -// helpers for simple interpolation - -// -// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm -// -// instead of having n equal sample values in a row like: -// ____ -// |____ -// -// we compare the current delta change with the next delta change. -// -// if curr_delta is positive, -// -// - and next delta is smaller (or changing direction): -// \. -// -__ -// -// - and next delta significant (at least twice) bigger: -// --_ -// \. -// -// - and next delta is nearly same: -// \. -// \. -// -// -// if curr_delta is negative, -// -// - and next delta is smaller (or changing direction): -// _-- -// / -// -// - and next delta significant (at least twice) bigger: -// / -// __- -// -// - and next delta is nearly same: -// / -// / -// - - -static INLINE void InterpolateUp(int ch) +INLINE void InterpolateUp(SPUCHAN * pChannel) { - if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass + if(pChannel->SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass { - const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val - const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :) + const int id1=pChannel->SB[30]-pChannel->SB[29]; // curr delta to next val + const int id2=pChannel->SB[31]-pChannel->SB[30]; // and next delta to next-next val :) - s_chan[ch].SB[32]=0; + pChannel->SB[32]=0; if(id1>0) // curr delta positive { if(id2SB[28]=id1;pChannel->SB[32]=2;} else if(id2<(id1<<1)) - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; + pChannel->SB[28]=(id1*pChannel->sinc)/0x10000L; else - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; + pChannel->SB[28]=(id1*pChannel->sinc)/0x20000L; } else // curr delta negative { if(id2>id1) - {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} + {pChannel->SB[28]=id1;pChannel->SB[32]=2;} else if(id2>(id1<<1)) - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; + pChannel->SB[28]=(id1*pChannel->sinc)/0x10000L; else - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; + pChannel->SB[28]=(id1*pChannel->sinc)/0x20000L; } } else - if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass + if(pChannel->SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass { - s_chan[ch].SB[32]=0; + pChannel->SB[32]=0; - s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L; - if(s_chan[ch].sinc<=0x8000) - s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1)); - else s_chan[ch].SB[29]+=s_chan[ch].SB[28]; + pChannel->SB[28]=(pChannel->SB[28]*pChannel->sinc)/0x20000L; + if(pChannel->sinc<=0x8000) + pChannel->SB[29]=pChannel->SB[30]-(pChannel->SB[28]*((0x10000/pChannel->sinc)-1)); + else pChannel->SB[29]+=pChannel->SB[28]; } else // no flags? add bigger val (if possible), calc smaller step, set flag1 - s_chan[ch].SB[29]+=s_chan[ch].SB[28]; + pChannel->SB[29]+=pChannel->SB[28]; } // // even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm // -static INLINE void InterpolateDown(int ch) +INLINE void InterpolateDown(SPUCHAN * pChannel) { - if(s_chan[ch].sinc>=0x20000L) // we would skip at least one val? + if(pChannel->sinc>=0x20000L) // we would skip at least one val? { - s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight - if(s_chan[ch].sinc>=0x30000L) // we would skip even more vals? - s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight + pChannel->SB[29]+=(pChannel->SB[30]-pChannel->SB[29])/2; // add easy weight + if(pChannel->sinc>=0x30000L) // we would skip even more vals? + pChannel->SB[29]+=(pChannel->SB[31]-pChannel->SB[30])/2; // add additional next weight } } //////////////////////////////////////////////////////////////////////// // helpers for gauss interpolation -#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos]) -#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3]) +#define gval0 (((short*)(&pChannel->SB[29]))[gpos]) +#define gval(x) (((short*)(&pChannel->SB[29]))[(gpos+x)&3]) #include "gauss_i.h" @@ -247,49 +154,46 @@ static INLINE void InterpolateDown(int ch) // START SOUND... called by main thread to setup a new sound on a channel //////////////////////////////////////////////////////////////////////// -static INLINE void StartSound(int ch) +INLINE void StartSound(SPUCHAN * pChannel) { - StartADSR(ch); - StartREVERB(ch); - - // fussy timing issues - do in VoiceOn - //s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start - //s_chan[ch].bStop=0; - //s_chan[ch].bOn=1; - - s_chan[ch].s_1=0; // init mixing vars - s_chan[ch].s_2=0; - s_chan[ch].iSBPos=28; - - s_chan[ch].bNew=0; // init channel flags - - s_chan[ch].SB[29]=0; // init our interpolation helpers - s_chan[ch].SB[30]=0; + StartADSR(pChannel); + StartREVERB(pChannel); + + pChannel->pCurr=pChannel->pStart; // set sample start + + pChannel->s_1=0; // init mixing vars + pChannel->s_2=0; + pChannel->iSBPos=28; + + pChannel->bNew=0; // init channel flags + pChannel->bStop=0; + pChannel->bOn=1; + + pChannel->SB[29]=0; // init our interpolation helpers + pChannel->SB[30]=0; if(iUseInterpolation>=2) // gauss interpolation? - {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding - else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding - - dwNewChannel&=~(1<spos=0x30000L;pChannel->SB[28]=0;} // -> start with more decoding + else {pChannel->spos=0x10000L;pChannel->SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding } //////////////////////////////////////////////////////////////////////// // ALL KIND OF HELPERS //////////////////////////////////////////////////////////////////////// -static INLINE void VoiceChangeFrequency(int ch) +INLINE void VoiceChangeFrequency(SPUCHAN * pChannel) { - s_chan[ch].iUsedFreq=s_chan[ch].iActFreq; // -> take it and calc steps - s_chan[ch].sinc=s_chan[ch].iRawPitch<<4; - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag + pChannel->iUsedFreq=pChannel->iActFreq; // -> take it and calc steps + pChannel->sinc=pChannel->iRawPitch<<4; + if(!pChannel->sinc) pChannel->sinc=1; + if(iUseInterpolation==1) pChannel->SB[32]=1; // -> freq change in simle imterpolation mode: set flag } //////////////////////////////////////////////////////////////////////// -static INLINE void FModChangeFrequency(int ch,int ns) +INLINE void FModChangeFrequency(SPUCHAN * pChannel,int ns) { - int NP=s_chan[ch].iRawPitch; + int NP=pChannel->iRawPitch; NP=((32768L+iFMod[ns])*NP)/32768L; @@ -298,159 +202,95 @@ static INLINE void FModChangeFrequency(int ch,int ns) NP=(44100L*NP)/(4096L); // calc frequency - s_chan[ch].iActFreq=NP; - s_chan[ch].iUsedFreq=NP; - s_chan[ch].sinc=(((NP/10)<<16)/4410); - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - if(iUseInterpolation==1) // freq change in simple interpolation mode - s_chan[ch].SB[32]=1; + pChannel->iActFreq=NP; + pChannel->iUsedFreq=NP; + pChannel->sinc=(((NP/10)<<16)/4410); + if(!pChannel->sinc) pChannel->sinc=1; + if(iUseInterpolation==1) pChannel->SB[32]=1; // freq change in simple interpolation mode + iFMod[ns]=0; -} +} //////////////////////////////////////////////////////////////////////// -/* -Noise Algorithm -- Dr.Hell (Xebra PS1 emu) -- 100% accurate (waveform + frequency) -- http://drhell.web.fc2.com - - -Level change cycle -Freq = 0x8000 >> (NoiseClock >> 2); - -Frequency of half cycle -Half = ((NoiseClock & 3) * 2) / (4 + (NoiseClock & 3)); -- 0 = (0*2)/(4+0) = 0/4 -- 1 = (1*2)/(4+1) = 2/5 -- 2 = (2*2)/(4+2) = 4/6 -- 3 = (3*2)/(4+3) = 6/7 - -------------------------------- - -5*6*7 = 210 -4 - 0*0 = 0 -5 - 42*2 = 84 -6 - 35*4 = 140 -7 - 30*6 = 180 -*/ - -// Noise Waveform - Dr. Hell (Xebra) -char NoiseWaveAdd [64] = { - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1 -}; - -unsigned short NoiseFreqAdd[5] = { - 0, 84, 140, 180, 210 -}; - -static INLINE void NoiseClock() -{ - unsigned int level; - - level = 0x8000 >> (dwNoiseClock >> 2); - level <<= 16; - - dwNoiseCount += 0x10000; - - // Dr. Hell - fraction - dwNoiseCount += NoiseFreqAdd[ dwNoiseClock & 3 ]; - if( (dwNoiseCount&0xffff) >= NoiseFreqAdd[4] ) { - dwNoiseCount += 0x10000; - dwNoiseCount -= NoiseFreqAdd[ dwNoiseClock & 3 ]; - } - - if( dwNoiseCount >= level ) - { - while( dwNoiseCount >= level ) - dwNoiseCount -= level; +// noise handler... just produces some noise data +// surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used... +// and sometimes the noise will be used as fmod modulation... pfff - // Dr. Hell - form - dwNoiseVal = (dwNoiseVal<<1) | NoiseWaveAdd[ (dwNoiseVal>>10) & 63 ]; - } -} - -static INLINE int iGetNoiseVal(int ch) +INLINE int iGetNoiseVal(SPUCHAN * pChannel) { int fa; - fa = (short) dwNoiseVal; - - // no clip need - //if(fa>32767L) fa=32767L; - //if(fa<-32767L) fa=-32767L; + if((dwNoiseVal<<=1)&0x80000000L) + { + dwNoiseVal^=0x0040001L; + fa=((dwNoiseVal>>2)&0x7fff); + fa=-fa; + } + else fa=(dwNoiseVal>>2)&0x7fff; - // don't upset VAG decoder - //if(iUseInterpolation<2) // no gauss/cubic interpolation? - //pChannel->SB[29] = fa; // -> store noise val in "current sample" slot + // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val + fa=pChannel->iOldNoise+((fa-pChannel->iOldNoise)/((0x001f-((spuCtrl&0x3f00)>>9))+1)); + if(fa>32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + pChannel->iOldNoise=fa; - // boost volume - no more! - //return fa * 3 / 2; + if(iUseInterpolation<2) // no gauss/cubic interpolation? + pChannel->SB[29] = fa; // -> store noise val in "current sample" slot return fa; -} +} //////////////////////////////////////////////////////////////////////// -static INLINE void StoreInterpolationVal(int ch,int fa) +INLINE void StoreInterpolationVal(SPUCHAN * pChannel,int fa) { - /* - // fmod channel = sound output - if(s_chan[ch].bFMod==2) // fmod freq channel - s_chan[ch].SB[29]=fa; + if(pChannel->bFMod==2) // fmod freq channel + pChannel->SB[29]=fa; else - */ { if((spuCtrl&0x4000)==0) fa=0; // muted? else // else adjust { if(fa>32767L) fa=32767L; - if(fa<-32767L) fa=-32767L; + if(fa<-32767L) fa=-32767L; } if(iUseInterpolation>=2) // gauss/cubic interpolation - { - int gpos = s_chan[ch].SB[28]; + { + int gpos = pChannel->SB[28]; gval0 = fa; gpos = (gpos+1) & 3; - s_chan[ch].SB[28] = gpos; + pChannel->SB[28] = gpos; } else if(iUseInterpolation==1) // simple interpolation { - s_chan[ch].SB[28] = 0; - s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour' - s_chan[ch].SB[30] = s_chan[ch].SB[31]; - s_chan[ch].SB[31] = fa; - s_chan[ch].SB[32] = 1; // -> flag: calc new interolation + pChannel->SB[28] = 0; + pChannel->SB[29] = pChannel->SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour' + pChannel->SB[30] = pChannel->SB[31]; + pChannel->SB[31] = fa; + pChannel->SB[32] = 1; // -> flag: calc new interolation } - else s_chan[ch].SB[29]=fa; // no interpolation + else pChannel->SB[29]=fa; // no interpolation } } //////////////////////////////////////////////////////////////////////// -static INLINE int iGetInterpolationVal(int ch) +INLINE int iGetInterpolationVal(SPUCHAN * pChannel) { int fa; - // fmod channel = sound output - //if(s_chan[ch].bFMod==2) return s_chan[ch].SB[29]; + if(pChannel->bFMod==2) return pChannel->SB[29]; switch(iUseInterpolation) - { + { //--------------------------------------------------// case 3: // cubic interpolation { long xd;int gpos; - xd = ((s_chan[ch].spos) >> 1)+1; - gpos = s_chan[ch].SB[28]; + xd = ((pChannel->spos) >> 1)+1; + gpos = pChannel->SB[28]; fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0; fa *= (xd - (2<<15)) / 6; @@ -468,8 +308,8 @@ static INLINE int iGetInterpolationVal(int ch) case 2: // gauss interpolation { int vl, vr;int gpos; - vl = (s_chan[ch].spos >> 6) & ~3; - gpos = s_chan[ch].SB[28]; + vl = (pChannel->spos >> 6) & ~3; + gpos = pChannel->SB[28]; vr=(gauss[vl]*gval0)&~2047; vr+=(gauss[vl+1]*gval(1))&~2047; vr+=(gauss[vl+2]*gval(2))&~2047; @@ -479,15 +319,15 @@ static INLINE int iGetInterpolationVal(int ch) //--------------------------------------------------// case 1: // simple interpolation { - if(s_chan[ch].sinc<0x10000L) // -> upsampling? - InterpolateUp(ch); // --> interpolate up - else InterpolateDown(ch); // --> else down - fa=s_chan[ch].SB[29]; + if(pChannel->sinc<0x10000L) // -> upsampling? + InterpolateUp(pChannel); // --> interpolate up + else InterpolateDown(pChannel); // --> else down + fa=pChannel->SB[29]; } break; //--------------------------------------------------// default: // no interpolation { - fa=s_chan[ch].SB[29]; + fa=pChannel->SB[29]; } break; //--------------------------------------------------// } @@ -501,30 +341,17 @@ static INLINE int iGetInterpolationVal(int ch) // basically the whole sound processing is done in this fat func! //////////////////////////////////////////////////////////////////////// -// 5 ms waiting phase, if buffer is full and no new sound has to get started -// .. can be made smaller (smallest val: 1 ms), but bigger waits give -// better performance - -#define PAUSE_W 1 -#define PAUSE_L 1000 - -//////////////////////////////////////////////////////////////////////// - static void *MAINThread(void *arg) { - int s_1,s_2,fa,ns; - int voldiv = iVolume; - + int s_1,s_2,fa,ns,voldiv=iVolume; unsigned char * start;unsigned int nSample; int ch,predict_nr,shift_factor,flags,d,s; - int bIRQReturn=0; - unsigned int decoded_voice=0; - - // mute output - if( voldiv == 5 ) voldiv = 0x7fffffff; + SPUCHAN * pChannel; + while(!bEndThread) // until we are shutting down { + //--------------------------------------------------// // ok, at the beginning we are looking if there is // enuff free place in the dsound/oss buffer to // fill in new data, or if there is a new channel to start. @@ -535,7 +362,7 @@ static void *MAINThread(void *arg) if(dwNewChannel) // new channel should start immedately? { // (at least one bit 0 ... MAXCHANNEL is set?) iSecureStart++; // -> set iSecure - if(iSecureStart>1) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance) + if(iSecureStart>5) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance) } else iSecureStart=0; // 0: no new channel should start @@ -544,110 +371,71 @@ static void *MAINThread(void *arg) { iSecureStart=0; // reset secure - if(iUseTimer) return 0; // linux no-thread mode? bye - sceKernelDelayThread(PAUSE_L); // else sleep for x ms (linux) - - if(dwNewChannel) iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop + return 0; // linux no-thread mode? bye } - ns=0; + //--------------------------------------------------// continue from irq handling in timer mode? - //--------------------------------------------------// continue from irq handling in timer mode? - - if(lastns>0) // will be 0 if no continue is pending + if(lastch>=0) // will be -1 if no continue is pending { - ns=lastns; // -> setup all kind of vars to continue - lastns=0; + ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue + pChannel=&s_chan[ch]; + goto GOON; // -> directly jump to the continue point } //--------------------------------------------------// - //- main channel loop -// + //- main channel loop -// //--------------------------------------------------// { - decoded_voice = decoded_ptr; - - while(nsbNew) + { + StartSound(pChannel); // start new sound + dwNewChannel&=~(1<bOn) continue; // channel not playing? next - NoiseClock(); + if(pChannel->iActFreq!=pChannel->iUsedFreq) // new psx frequency? + VoiceChangeFrequency(pChannel); - for(ch=0;ch=0x10000L) + if(pChannel->bFMod==1 && iFMod[ns]) // fmod freq channel + FModChangeFrequency(pChannel,ns); + + while(pChannel->spos>=0x10000L) { - if(s_chan[ch].iSBPos==28) // 28 reached? + if(pChannel->iSBPos==28) // 28 reached? { - // Xenogears - Anima Relic dungeon (exp gain) - if( s_chan[ch].bLoopJump == 1 ) - s_chan[ch].pCurr = s_chan[ch].pLoop; - - s_chan[ch].bLoopJump = 0; - + start=pChannel->pCurr; // set up the current pos - start=s_chan[ch].pCurr; // set up the current pos - - if (start == spuMemC) - s_chan[ch].bOn = 0; - - if (s_chan[ch].iSilent==1 ) + if (start == (unsigned char*)-1) // special "stop" sign { - // silence = let channel keep running (IRQs) - //s_chan[ch].bOn=0; // -> turn everything off - s_chan[ch].iSilent=2; - - s_chan[ch].ADSRX.lVolume=0; - s_chan[ch].ADSRX.EnvelopeVol=0; + pChannel->bOn=0; // -> turn everything off + pChannel->ADSRX.lVolume=0; + pChannel->ADSRX.EnvelopeVol=0; + goto ENDX; // -> and done for this channel } - s_chan[ch].iSBPos=0; + pChannel->iSBPos=0; //////////////////////////////////////////// spu irq handler here? mmm... do it later - s_1=s_chan[ch].s_1; - s_2=s_chan[ch].s_2; + s_1=pChannel->s_1; + s_2=pChannel->s_2; predict_nr=(int)*start;start++; shift_factor=predict_nr&0xf; predict_nr >>= 4; flags=(int)*start;start++; - // Silhouette Mirage - Serah fight - if( predict_nr > 4 ) predict_nr = 0; - - // -------------------------------------- // + // -------------------------------------- // - for (nSample=0;nSample<28;start++) + for (nSample=0;nSample<28;start++) { d=(int)*start; s=((d&0xf)<<12); @@ -655,234 +443,88 @@ static void *MAINThread(void *arg) fa=(s >> shift_factor); fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - - // snes brr clamps - fa = CLAMP16(fa); - s_2=s_1;s_1=fa; s=((d & 0xf0) << 8); - s_chan[ch].SB[nSample++]=fa; - + pChannel->SB[nSample++]=fa; if(s&0x8000) s|=0xffff0000; - fa=(s>>shift_factor); + fa=(s>>shift_factor); fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - - // snes brr clamps - fa = CLAMP16(fa); - s_2=s_1;s_1=fa; - s_chan[ch].SB[nSample++]=fa; - } + pChannel->SB[nSample++]=fa; + } - //////////////////////////////////////////// irq check + //////////////////////////////////////////// flag handler -#if 1 - // Check channel/loop IRQs (e.g. Castlevania Chronicles) and at pos-8 for unknown reason - if( Check_IRQ( (s_chan[ch].pCurr)-spuMemC, 0 ) || - Check_IRQ( (start-spuMemC)-0, 0 ) || - Check_IRQ( (start-spuMemC)-8, 0 ) ) - { -#else - if(irqCallback && (spuCtrl&0x40)) // some callback and irq active? + if((flags&4) && (!pChannel->bIgnoreLoop)) + pChannel->pLoop=start-16; // loop adress + + if(flags&1) // 1: stop/loop { - if((pSpuIrq > start-16 && // irq address reached? - pSpuIrq <= start) || - ((flags&1) && // special: irq on looping addr, when stop/loop flag is set - (pSpuIrq > s_chan[ch].pLoop-16 && - pSpuIrq <= s_chan[ch].pLoop))) -#endif - { - s_chan[ch].iIrqDone=1; // -> debug flag - //irqCallback(); // -> call main emu (checked & called on Check_IRQ) - - if(iSPUIRQWait) // -> option: wait after irq for main emu - { - iSpuAsyncWait=1; - bIRQReturn=1; - } + // We play this block out first... + //if(!(flags&2)) // 1+2: do loop... otherwise: stop + if(flags!=3 || pChannel->pLoop==NULL) // PETE: if we don't check exactly for 3, loop hang ups will happen (DQ4, for example) + { // and checking if pLoop is set avoids crashes, yeah + start = (unsigned char*)-1; + } + else + { + start = pChannel->pLoop; } } - //////////////////////////////////////////// flag handler - - /* - SPU2-X: - $4 = set loop to current block - $2 = keep envelope on (no mute) - $1 = jump to loop address - - silence means no volume (ADSR keeps playing!!) - */ - - if(flags&4) - s_chan[ch].pLoop=start-16; - - - // Jungle Book - Rhythm 'n Groove - don't reset ignore status - // - fixes gameplay speed (IRQ hits) - //s_chan[ch].bIgnoreLoop = 0; - - - if(flags&1) - { - // ...? - //s_chan[ch].bIgnoreLoop = 0; - - // Xenogears - 7 = play missing sounds - // set jump flag - s_chan[ch].bLoopJump = 1; - - - // silence = keep playing..? - if( (flags&2) == 0 ) { - s_chan[ch].iSilent = 1; - - // silence = don't start release phase - //s_chan[ch].bStop = 1; + pChannel->pCurr=start; // store values for next cycle + pChannel->s_1=s_1; + pChannel->s_2=s_2; - //start = (unsigned char *) -1; - } - } +GOON: ; -#if 0 - // crash check - if( start == 0 ) - start = (unsigned char *) -1; - if( start >= spuMemC + 0x80000 ) - start = spuMemC - 0x80000; -#endif - - - // Silhouette Mirage - ending mini-game - - // ?? - if( start - spuMemC >= 0x80000 ) { - start -= 16; - - s_chan[ch].iSilent = 1; - s_chan[ch].bStop = 1; - } - - - s_chan[ch].pCurr=start; // store values for next cycle - s_chan[ch].s_1=s_1; - s_chan[ch].s_2=s_2; } - fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data + fa=pChannel->SB[pChannel->iSBPos++]; // get sample data - StoreInterpolationVal(ch,fa); // store val for later interpolation + StoreInterpolationVal(pChannel,fa); // store val for later interpolation - s_chan[ch].spos -= 0x10000L; + pChannel->spos -= 0x10000L; } - if(s_chan[ch].bNoise) - fa=iGetNoiseVal(ch); // get noise val - else fa=iGetInterpolationVal(ch); // get sample val - + //////////////////////////////////////////////// + + if(pChannel->bNoise) + fa=iGetNoiseVal(pChannel); // get noise val + else fa=iGetInterpolationVal(pChannel); // get sample val - // Voice 1/3 decoded buffer - if( ch == 0 ) { - spuMem[ (0x800 + decoded_voice) / 2 ] = (short) fa; - } else if( ch == 2 ) { - spuMem[ (0xc00 + decoded_voice) / 2 ] = (short) fa; - } + pChannel->sval=(MixADSR(pChannel)*fa)/1023; // mix adsr - - s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // mix adsr - - if(s_chan[ch].bFMod==2) // fmod freq channel - iFMod[ns]=s_chan[ch].sval; // -> store 1T sample data, use that to do fmod on next channel - - // mix fmod channel into output - // - Xenogears save icon (high pitch) - { + if(pChannel->bFMod==2) // fmod freq channel + iFMod[ns]=pChannel->sval; // -> store 1T sample data, use that to do fmod on next channel + else // no fmod freq channel + { ////////////////////////////////////////////// // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) - if(s_chan[ch].iMute) - s_chan[ch].sval=0; // debug mute - else - { - SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L; - SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L; - } - + SSumL[ns]+=(pChannel->sval*pChannel->iLeftVolume)/0x4000L; + SSumR[ns]+=(pChannel->sval*pChannel->iRightVolume)/0x4000L; + ////////////////////////////////////////////// - // now let us store sound data for reverb - - if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns); + // now let us store sound data for reverb + + if(pChannel->bRVBActive) StoreREVERB(pChannel,ns); } - s_chan[ch].spos += s_chan[ch].sinc; - } - - //////////////////////////////////////////////// - // ok, go on until 1 ms data of this channel is collected - - // decoded buffer - voice - decoded_voice += 2; - decoded_voice &= 0x3ff; - - - // status flag - if( decoded_voice >= 0x200 ) { - spuStat |= STAT_DECODED; - } else { - spuStat &= ~STAT_DECODED; - } - - - // IRQ work - { - unsigned char *old_irq; - unsigned int old_ptr; - - old_irq = pSpuIrq; - old_ptr = decoded_voice; - -#if 0 - // align to boundaries ($0, $200, $400, $600) - pSpuIrq = ((pSpuIrq - spuMemC) & (~0x1ff)) + spuMemC; - decoded_voice = decoded_voice & (~0x1ff); -#endif - - // check all decoded buffer IRQs - timing issue - Check_IRQ( decoded_voice + 0x000, 0 ); - Check_IRQ( decoded_voice + 0x400, 0 ); - Check_IRQ( decoded_voice + 0x800, 0 ); - Check_IRQ( decoded_voice + 0xc00, 0 ); - - pSpuIrq = old_irq; - decoded_voice = old_ptr; - } - - if(bIRQReturn) // special return for "spu irq - wait for cpu action" - { - bIRQReturn=0; - if(iUseTimer!=2) - { - DWORD dwWatchTime=timeGetTime_spu()+2500; - - while(iSpuAsyncWait && !bEndThread && - timeGetTime_spu()spos += pChannel->sinc; + + } +ENDX: ; + } + } + //---------------------------------------------------// //- here we have another 1 ms of sound data //---------------------------------------------------// @@ -890,190 +532,51 @@ static void *MAINThread(void *arg) MixXA(); - - // now safe to update decoded buffer ptr - decoded_ptr += ns * 2; - decoded_ptr &= 0x3ff; - - /////////////////////////////////////////////////////// // mix all channels (including reverb) into one buffer - if(iDisStereo) // no stereo? - { - int dl, dr; - for (ns = 0; ns < NSSIZE; ns++) - { - SSumL[ns] += MixREVERBLeft(ns); - - dl = SSumL[ns] / voldiv; SSumL[ns] = 0; - if (dl < -32767) dl = -32767; if (dl > 32767) dl = 32767; - - SSumR[ns] += MixREVERBRight(); - - dr = SSumR[ns] / voldiv; SSumR[ns] = 0; - if (dr < -32767) dr = -32767; if (dr > 32767) dr = 32767; - *pS++ = (dl + dr) / 2; - } - } - else // stereo: - for (ns = 0; ns < NSSIZE; ns++) - { - static double _interpolation_coefficient = 3.759285613; - - if(iFreqResponse) { - int sl,sr; - double ldiff, rdiff, avg, tmp; - - SSumL[ns]+=MixREVERBLeft(ns); - SSumR[ns]+=MixREVERBRight(); - - sl = SSumL[ns]; SSumL[ns]=0; - sr = SSumR[ns]; SSumR[ns]=0; - - - /* - Frequency Response - - William Pitcock (nenolod) (UPSE PSF player) - - accurate (!) - - http://nenolod.net - */ - - avg = ((sl + sr) / 2); - ldiff = sl - avg; - rdiff = sr - avg; - - tmp = avg + ldiff * _interpolation_coefficient; - if (tmp < -32768) - tmp = -32768; - if (tmp > 32767) - tmp = 32767; - sl = (int)tmp; - - tmp = avg + rdiff * _interpolation_coefficient; - if (tmp < -32768) - tmp = -32768; - if (tmp > 32767) - tmp = 32767; - sr = (int)tmp; - - - *pS++=sl/voldiv; - *pS++=sr/voldiv; - } else { - SSumL[ns]+=MixREVERBLeft(ns); - - d=SSumL[ns]/voldiv;SSumL[ns]=0; - if(d<-32767) d=-32767;if(d>32767) d=32767; - *pS++=d; - - SSumR[ns]+=MixREVERBRight(); - - d=SSumR[ns]/voldiv;SSumR[ns]=0; - if(d<-32767) d=-32767;if(d>32767) d=32767; - *pS++=d; - } + for(ns=0;ns32767) d=32767; + *pS++=d; + + SSumR[ns]+=MixREVERBRight(); + + d=SSumR[ns]/voldiv;SSumR[ns]=0; + if(d<-32767) d=-32767;if(d>32767) d=32767; + *pS++=d; } - ////////////////////////////////////////////////////// - // special irq handling in the decode buffers (0x0000-0x1000) - // we know: - // the decode buffers are located in spu memory in the following way: - // 0x0000-0x03ff CD audio left - // 0x0400-0x07ff CD audio right - // 0x0800-0x0bff Voice 1 - // 0x0c00-0x0fff Voice 3 - // and decoded data is 16 bit for one sample - // we assume: - // even if voices 1/3 are off or no cd audio is playing, the internal - // play positions will move on and wrap after 0x400 bytes. - // Therefore: we just need a pointer from spumem+0 to spumem+3ff, and - // increase this pointer on each sample by 2 bytes. If this pointer - // (or 0x400 offsets of this pointer) hits the spuirq address, we generate - // an IRQ. Only problem: the "wait for cpu" option is kinda hard to do here - // in some of Peops timer modes. So: we ignore this option here (for now). - -#if 0 - if(pMixIrq && irqCallback) - { - for(ns=0;ns=pMixIrq+(ch*0x400) && pSpuIrqspuMemC+0x3ff) pMixIrq=spuMemC; - } - } -#endif - InitREVERB(); - ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// // feed the sound - // latency = 25 ms (less pops, crackles, smoother) + // wanna have around 1/60 sec (16.666 ms) updates - //if(iCycle++>=20) - iCycle += APU_CYCLES_UPDATE; - if(iCycle > 44000/1000*LATENCY + 100*LATENCY/1000) + if(iCycle++>16) { - SoundFeedStreamData((unsigned char *)pSpuBuffer, - ((unsigned char *)pS) - ((unsigned char *)pSpuBuffer)); - pS = (short *)pSpuBuffer; - iCycle = 0; + SoundFeedStreamData((unsigned char*)pSpuBuffer, + ((unsigned char *)pS)- + ((unsigned char *)pSpuBuffer)); + pS=(short *)pSpuBuffer; + iCycle=0; } - - - if( iUseTimer == 2 ) - break; } // end of big main loop... - bThreadEnded = 1; - return 0; } -// SPU ASYNC... even newer epsxe func -// 1 time every 'cycle' cycles... harhar - -long cpu_cycles; -void CALLBACK SPUasync(unsigned long cycle) -{ - cpu_cycles += cycle; - - if(iSpuAsyncWait) - { - iSpuAsyncWait++; - if(iSpuAsyncWait<=64) return; - iSpuAsyncWait=0; - } - - if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode) - { - if(!bSpuInit) return; // -> no init, no call - - // note: usable precision difference (not using interval_time) - while( cpu_cycles >= CPU_CLOCK / 44100 * NSSIZE ) - { - MAINThread(0); // -> linux high-compat mode - - if (iSpuAsyncWait) - break; - cpu_cycles -= CPU_CLOCK / 44100 * NSSIZE; - } - } -} - +//////////////////////////////////////////////////////////////////////// // SPU UPDATE... new epsxe func // 1 time every 32 hsync lines // (312/32)x50 in pal // (262/32)x60 in ntsc +//////////////////////////////////////////////////////////////////////// // since epsxe 1.5.2 (linux) uses SPUupdate, not SPUasync, I will // leave that func in the linux port, until epsxe linux is using @@ -1081,10 +584,12 @@ void CALLBACK SPUasync(unsigned long cycle) void CALLBACK SPUupdate(void) { - MAINThread(0); + MAINThread(0); // -> linux high-compat mode } +//////////////////////////////////////////////////////////////////////// // XA AUDIO +//////////////////////////////////////////////////////////////////////// void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap) { @@ -1094,41 +599,64 @@ void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap) FeedXA(xap); // call main XA feeder } -// CDDA AUDIO -void CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes) +void CALLBACK SPUplayCDDAchannel(unsigned char *pcm, int nbytes) { if (!pcm) return; if (nbytes<=0) return; - FeedCDDA((unsigned char *)pcm, nbytes); + FeedCDDA(pcm, nbytes); } +//////////////////////////////////////////////////////////////////////// +// INIT/EXIT STUFF +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPUINIT: this func will be called first by the main emu +//////////////////////////////////////////////////////////////////////// + +long CALLBACK SPUinit(void) +{ + memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); + memset((void *)&rvb,0,sizeof(REVERBInfo)); + InitADSR(); + return 0; +} + +//////////////////////////////////////////////////////////////////////// // SETUPTIMER: init of certain buffers and threads/timers +//////////////////////////////////////////////////////////////////////// + void SetupTimer(void) { memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers memset(SSumL,0,NSSIZE*sizeof(int)); memset(iFMod,0,NSSIZE*sizeof(int)); + pS=(short *)pSpuBuffer; // setup soundbuffer pointer bEndThread=0; // init thread vars - bThreadEnded=0; bSpuInit=1; // flag: we are inited } +//////////////////////////////////////////////////////////////////////// // REMOVETIMER: kill threads/timers +//////////////////////////////////////////////////////////////////////// + void RemoveTimer(void) { bEndThread=1; // raise flag to end thread - bThreadEnded=0; // no more spu is running bSpuInit=0; } +//////////////////////////////////////////////////////////////////////// // SETUPSTREAMS: init most of the spu buffers +//////////////////////////////////////////////////////////////////////// + void SetupStreams(void) -{ +{ int i; - + pSpuBuffer=(unsigned char *)malloc(32768); // alloc mixing buffer if(iUseReverb==1) i=88200*2; @@ -1146,30 +674,26 @@ void SetupStreams(void) XAFeed = XAStart; CDDAStart = // alloc cdda buffer - (uint32_t *)malloc(44100 * sizeof(uint32_t)); - CDDAEnd = CDDAStart + 44100; + (uint32_t *)malloc(16384 * sizeof(uint32_t)); + CDDAEnd = CDDAStart + 16384; CDDAPlay = CDDAStart; CDDAFeed = CDDAStart; for(i=0;i init sustain - s_chan[i].iMute=0; - s_chan[i].iIrqDone=0; + s_chan[i].ADSRX.SustainLevel = 0xf<<27; // -> init sustain s_chan[i].pLoop=spuMemC; s_chan[i].pStart=spuMemC; s_chan[i].pCurr=spuMemC; } - - pMixIrq=spuMemC; // enable decoded buffer irqs by setting the address } +//////////////////////////////////////////////////////////////////////// // REMOVESTREAMS: free most buffer +//////////////////////////////////////////////////////////////////////// + void RemoveStreams(void) -{ +{ free(pSpuBuffer); // free mixing buffer pSpuBuffer = NULL; free(sRVBStart); // free reverb buffer @@ -1180,26 +704,10 @@ void RemoveStreams(void) CDDAStart = NULL; } -// INIT/EXIT STUFF - -// SPUINIT: this func will be called first by the main emu -long CALLBACK SPUinit(void) -{ - iReverbOff = -1; - spuIrq = 0; - spuAddr = 0x200; - bEndThread = 0; - bThreadEnded = 0; - pMixIrq = 0; - pSpuIrq = 0; - lastns = 0; - memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); - memset((void *)&rvb,0,sizeof(REVERBInfo)); - InitADSR(); - return 0; -} +//////////////////////////////////////////////////////////////////////// // SPUOPEN: called by main emu after init +//////////////////////////////////////////////////////////////////////// long SPUopen(void) { if(bSPUIsOpen) return 0; // security for some stupid main emus @@ -1218,40 +726,33 @@ long SPUopen(void) bSPUIsOpen=1; - return PSE_SPU_ERR_SUCCESS; + return PSE_SPU_ERR_SUCCESS; } +//////////////////////////////////////////////////////////////////////// // SPUCLOSE: called before shutdown +//////////////////////////////////////////////////////////////////////// + long CALLBACK SPUclose(void) { - if (!bSPUIsOpen) return 0; // some security + if(!bSPUIsOpen) return 0; // some security - bSPUIsOpen = 0; // no more open + bSPUIsOpen=0; // no more open RemoveTimer(); // no more feeding - RemoveSound(); // no more sound handling - return 0; -} + RemoveSound(); // no more sound handling -// SPUSHUTDOWN: called by main emu on final exit -long CALLBACK SPUshutdown(void) -{ - SPUclose(); RemoveStreams(); // no more streaming return 0; } -// SETUP CALLBACKS -// this functions will be called once, -// passes a callback that should be called on SPU-IRQ/cdda volume change -void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void)) -{ - irqCallback = callback; -} +//////////////////////////////////////////////////////////////////////// +// SPUSHUTDOWN: called by main emu on final exit +//////////////////////////////////////////////////////////////////////// -void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short)) +long CALLBACK SPUshutdown(void) { - cddavCallback = CDDAVcallback; -} + return 0; +} \ No newline at end of file diff --git a/extras/modules/peops/spu/spu.h b/extras/modules/peops/spu/spu.h index 0a9e6d6a0..2c302e015 100644 --- a/extras/modules/peops/spu/spu.h +++ b/extras/modules/peops/spu/spu.h @@ -1,21 +1,30 @@ -/*************************************************************************** - spu.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -void SetupTimer(void); -void RemoveTimer(void); -void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap); -void CALLBACK SPUplayCDDAchannel(short *pcm, int bytes); +/*************************************************************************** + spu.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +void SetupTimer(void); +void RemoveTimer(void); +void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap); diff --git a/extras/modules/peops/spu/stdafx.h b/extras/modules/peops/spu/stdafx.h index 0997bc1df..c04ea8108 100644 --- a/extras/modules/peops/spu/stdafx.h +++ b/extras/modules/peops/spu/stdafx.h @@ -1,49 +1,49 @@ -/*************************************************************************** - StdAfx.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -//*************************************************************************// -// History of changes: -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#include -#include -#include -#include -#include -#include -#define RRand(range) (random()%range) -#include -#include -#include - -#include "audio.h" -#include "psp.h" - -#undef CALLBACK -#define CALLBACK -#define DWORD unsigned long -#define LOWORD(l) ((unsigned short)(l)) -#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF)) - -#define INLINE inline - -#include "decode_xa.h" +/*************************************************************************** + StdAfx.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include +#include +#include +#include +#include +#include +#define RRand(range) (random()%range) +#include +#include +#include + +#include "audio.h" +#include "psp.h" + +#undef CALLBACK +#define CALLBACK +#define DWORD unsigned long +#define LOWORD(l) ((unsigned short)(l)) +#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF)) + +#define INLINE inline + +#include "decode_xa.h" diff --git a/extras/modules/peops/spu/xa.c b/extras/modules/peops/spu/xa.c index d029fc557..12b785ce7 100644 --- a/extras/modules/peops/spu/xa.c +++ b/extras/modules/peops/spu/xa.c @@ -16,7 +16,6 @@ ***************************************************************************/ #include "stdafx.h" - #define _IN_XA #include @@ -42,8 +41,8 @@ uint32_t * CDDAPlay = NULL; uint32_t * CDDAStart = NULL; uint32_t * CDDAEnd = NULL; -int iLeftXAVol = 0x8000; -int iRightXAVol = 0x8000; +int iLeftXAVol = 32767; +int iRightXAVol = 32767; static int gauss_ptr = 0; static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -53,227 +52,56 @@ static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0}; #define gvalr0 gauss_window[4+gauss_ptr] #define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)] -long cdxa_dbuf_ptr; - //////////////////////////////////////////////////////////////////////// // MIX XA & CDDA //////////////////////////////////////////////////////////////////////// -static int lastxa_lc, lastxa_rc; -static int lastcd_lc, lastcd_rc; - -static INLINE void MixXA(void) +INLINE void MixXA(void) { int ns; - int lc,rc; - unsigned long cdda_l; - int decoded_xa; - int decoded_cdda; - - decoded_xa = decoded_ptr; - - lc = 0; - rc = 0; + uint32_t l; for(ns=0;ns>16) & 0xffff); - - - // improve crackle - buffer under - // - not update fast enough - lastxa_lc = lc; - lastxa_rc = rc; - - - // Tales of Phantasia - voice meter - spuMem[ (decoded_xa + 0x000)/2 ] = (short) lc; - spuMem[ (decoded_xa + 0x400)/2 ] = (short) rc; - - decoded_xa += 2; - if( decoded_xa >= 0x400 ) - decoded_xa = 0; - - - lc = CLAMP16( (lc * iLeftXAVol) / 0x8000 ); - rc = CLAMP16( (rc * iRightXAVol) / 0x8000 ); - - - // reverb write flag - if( spuCtrl & CTRL_CD_REVERB ) { - StoreREVERB_CD( lc, rc, ns ); - } - - - // play flag - if( spuCtrl & CTRL_CD_PLAY ) { - SSumL[ns]+=lc; - SSumR[ns]+=rc; - } - } + SSumL[ns]+=(((short)(XALastVal&0xffff)) * iLeftXAVol)/32767; + SSumR[ns]+=(((short)((XALastVal>>16)&0xffff)) * iRightXAVol)/32767; + } if(XAPlay==XAFeed && XARepeat) { - //XARepeat--; + XARepeat--; for(;ns= 0x400 ) - decoded_xa = 0; - - - lc = CLAMP16( (lc * iLeftXAVol) / 0x8000 ); - rc = CLAMP16( (rc * iRightXAVol) / 0x8000 ); - - - // reverb write flags - if( spuCtrl & CTRL_CD_REVERB ) { - StoreREVERB_CD( lc, rc, ns ); - } - - - // play flag - if( spuCtrl & CTRL_CD_PLAY ) { - SSumL[ns]+=lc; - SSumR[ns]+=rc; - } + SSumL[ns]+=(((short)(XALastVal&0xffff)) * iLeftXAVol)/32767; + SSumR[ns]+=(((short)((XALastVal>>16)&0xffff)) * iRightXAVol)/32767; } } - - - decoded_cdda = decoded_ptr; - for(ns=0;ns>16) & 0xffff); - - - // improve crackle - buffer under - // - not update fast enough - lastcd_lc = lc; - lastcd_rc = rc; - - - // Vib Ribbon - playback - spuMem[ (decoded_cdda + 0x000)/2 ] = (short) lc; - spuMem[ (decoded_cdda + 0x400)/2 ] = (short) rc; - - decoded_cdda += 2; - if( decoded_cdda >= 0x400 ) - decoded_cdda = 0; - - - // Rayman - stage end fadeout - lc = CLAMP16( (lc * iLeftXAVol) / 0x8000 ); - rc = CLAMP16( (rc * iRightXAVol) / 0x8000 ); - - - // reverb write flag - if( spuCtrl & CTRL_CD_REVERB ) { - StoreREVERB_CD( lc, rc, ns ); - } - - - // play flag - if( spuCtrl & CTRL_CD_PLAY ) { - SSumL[ns]+=lc; - SSumR[ns]+=rc; - } - } - - - if(CDDAPlay==CDDAFeed && XARepeat) - { - //XARepeat--; - for(;ns= 0x400 ) - decoded_cdda = 0; - - - // Rayman - stage end fadeout - lc = CLAMP16( (lc * iLeftXAVol) / 0x8000 ); - rc = CLAMP16( (rc * iRightXAVol) / 0x8000 ); - - - // reverb write flag - if( spuCtrl & CTRL_CD_REVERB ) { - StoreREVERB_CD( lc, rc, ns ); - } - - - // play flag - if( spuCtrl & CTRL_CD_PLAY ) { - SSumL[ns]+=lc; - SSumR[ns]+=rc; - } - } + SSumL[ns]+=(((short)(l&0xffff)) * iLeftXAVol)/32767; + SSumR[ns]+=(((short)((l>>16)&0xffff)) * iRightXAVol)/32767; } } -//////////////////////////////////////////////////////////////////////// -// small linux time helper... only used for watchdog -//////////////////////////////////////////////////////////////////////// - -#ifndef _WINDOWS - -unsigned long timeGetTime_spu() -{ - struct timeval tv; - gettimeofday(&tv, 0); // well, maybe there are better ways - return tv.tv_sec * 1000 + tv.tv_usec/1000; // to do that, but at least it works -} - -#endif - //////////////////////////////////////////////////////////////////////// // FEED XA //////////////////////////////////////////////////////////////////////// -static INLINE void FeedXA(xa_decode_t *xap) +INLINE void FeedXA(xa_decode_t *xap) { - int sinc,spos,i,iSize,iPlace; + int sinc,spos,i,iSize,iPlace,vl,vr; if(!bSPUIsOpen) return; xapGlobal = xap; // store info for save states XARepeat = 100; // set up repeat -#ifdef XA_HACK - iSize=((45500*xap->nsamples)/xap->freq); // get size -#else iSize=((44100*xap->nsamples)/xap->freq); // get size -#endif if(!iSize) return; // none? bye if(XAFeed=0x10000L) + if(iUseInterpolation==2) + { + while(spos>=0x10000L) + { + l = *pS++; + gauss_window[gauss_ptr] = (short)LOWORD(l); + gauss_window[4+gauss_ptr] = (short)HIWORD(l); + gauss_ptr = (gauss_ptr+1) & 3; + spos -= 0x10000L; + } + vl = (spos >> 6) & ~3; + vr=(gauss[vl]*gvall0)&~2047; + vr+=(gauss[vl+1]*gvall(1))&~2047; + vr+=(gauss[vl+2]*gvall(2))&~2047; + vr+=(gauss[vl+3]*gvall(3))&~2047; + l= (vr >> 11) & 0xffff; + vr=(gauss[vl]*gvalr0)&~2047; + vr+=(gauss[vl+1]*gvalr(1))&~2047; + vr+=(gauss[vl+2]*gvalr(2))&~2047; + vr+=(gauss[vl+3]*gvalr(3))&~2047; + l |= vr << 5; + } + else { - l = *pS++; - spos -= 0x10000L; + while(spos>=0x10000L) + { + l = *pS++; + spos -= 0x10000L; + } } s=(short)LOWORD(l); @@ -367,10 +220,35 @@ static INLINE void FeedXA(xa_decode_t *xap) { for(i=0;i=0x10000L) + if(iUseInterpolation==2) { - l = *pS++; - spos -= 0x10000L; + while(spos>=0x10000L) + { + l = *pS++; + gauss_window[gauss_ptr] = (short)LOWORD(l); + gauss_window[4+gauss_ptr] = (short)HIWORD(l); + gauss_ptr = (gauss_ptr+1) & 3; + spos -= 0x10000L; + } + vl = (spos >> 6) & ~3; + vr=(gauss[vl]*gvall0)&~2047; + vr+=(gauss[vl+1]*gvall(1))&~2047; + vr+=(gauss[vl+2]*gvall(2))&~2047; + vr+=(gauss[vl+3]*gvall(3))&~2047; + l= (vr >> 11) & 0xffff; + vr=(gauss[vl]*gvalr0)&~2047; + vr+=(gauss[vl+1]*gvalr(1))&~2047; + vr+=(gauss[vl+2]*gvalr(2))&~2047; + vr+=(gauss[vl+3]*gvalr(3))&~2047; + l |= vr << 5; + } + else + { + while(spos>=0x10000L) + { + l = *pS++; + spos -= 0x10000L; + } } *XAFeed++=l; @@ -396,12 +274,31 @@ static INLINE void FeedXA(xa_decode_t *xap) int32_t l1; for(i=0;i=0x10000L) + if(iUseInterpolation==2) { - s = *pS++; - spos -= 0x10000L; + while(spos>=0x10000L) + { + gauss_window[gauss_ptr] = (short)*pS++; + gauss_ptr = (gauss_ptr+1) & 3; + spos -= 0x10000L; + } + vl = (spos >> 6) & ~3; + vr=(gauss[vl]*gvall0)&~2047; + vr+=(gauss[vl+1]*gvall(1))&~2047; + vr+=(gauss[vl+2]*gvall(2))&~2047; + vr+=(gauss[vl+3]*gvall(3))&~2047; + l1=s= vr >> 11; + l1 &= 0xffff; + } + else + { + while(spos>=0x10000L) + { + s = *pS++; + spos -= 0x10000L; + } + l1=s; } - l1=s; l1=(l1*iPlace)/iSize; if(l1<-32767) l1=-32767; @@ -423,14 +320,33 @@ static INLINE void FeedXA(xa_decode_t *xap) { for(i=0;i=0x10000L) + if(iUseInterpolation==2) { - s = *pS++; - spos -= 0x10000L; + while(spos>=0x10000L) + { + gauss_window[gauss_ptr] = (short)*pS++; + gauss_ptr = (gauss_ptr+1) & 3; + spos -= 0x10000L; + } + vl = (spos >> 6) & ~3; + vr=(gauss[vl]*gvall0)&~2047; + vr+=(gauss[vl+1]*gvall(1))&~2047; + vr+=(gauss[vl+2]*gvall(2))&~2047; + vr+=(gauss[vl+3]*gvall(3))&~2047; + l=s= vr >> 11; + l &= 0xffff; + } + else + { + while(spos>=0x10000L) + { + s = *pS++; + spos -= 0x10000L; + } + l=s; } - l=s; - *XAFeed++=((l&0xffff)|(l<<16)); + *XAFeed++=(l|(l<<16)); if(XAFeed==XAEnd) XAFeed=XAStart; if(XAFeed==XAPlay) @@ -449,9 +365,7 @@ static INLINE void FeedXA(xa_decode_t *xap) // FEED CDDA //////////////////////////////////////////////////////////////////////// -unsigned int cdda_ptr; - -static INLINE void FeedCDDA(unsigned char *pcm, int nBytes) +INLINE void FeedCDDA(unsigned char *pcm, int nBytes) { while(nBytes>0) { @@ -459,13 +373,12 @@ static INLINE void FeedCDDA(unsigned char *pcm, int nBytes) while(CDDAFeed==CDDAPlay-1|| (CDDAFeed==CDDAEnd-1&&CDDAPlay==CDDAStart)) { - if (!iUseTimer) sceKernelDelayThread(1000); - else return; + return; } *CDDAFeed++=(*pcm | (*(pcm+1)<<8) | (*(pcm+2)<<16) | (*(pcm+3)<<24)); nBytes-=4; pcm+=4; - } + } } #endif diff --git a/extras/modules/peops/spu/xa.h b/extras/modules/peops/spu/xa.h index ff2f0bb36..0928eba2a 100644 --- a/extras/modules/peops/spu/xa.h +++ b/extras/modules/peops/spu/xa.h @@ -1,20 +1,20 @@ -/*************************************************************************** - xa.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -static INLINE void MixXA(void); -static INLINE void FeedXA(xa_decode_t *xap); -static INLINE void FeedCDDA(unsigned char *pcm, int nBytes); +/*************************************************************************** + xa.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +INLINE void MixXA(void); +INLINE void FeedXA(xa_decode_t *xap); +INLINE void FeedCDDA(unsigned char *pcm, int nBytes);