# HG changeset patch # User nenolod # Date 1142801463 28800 # Node ID 61e7332e06522da10d9c1ac450950b1dd4bb7f87 # Parent 74576869a5060e6df46e435c24e411723c42670d [svn] - PSF2 support (everything is BORKED right now) diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/Makefile.in --- a/Plugins/Input/sexypsf/Makefile.in Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/Makefile.in Sun Mar 19 12:51:03 2006 -0800 @@ -17,6 +17,8 @@ PsxInterpreter.c \ PsxHLE.c \ spu/spu.c \ + spu/dma.c \ + spu/registers.c \ xmms.c LIBADD = -lz diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/Misc.c --- a/Plugins/Input/sexypsf/Misc.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/Misc.c Sun Mar 19 12:51:03 2006 -0800 @@ -447,8 +447,8 @@ psxInit(); psxReset(); - SPUinit(); - SPUopen(); + SPU2init(); + SPU2open(); if(!(ret=LoadPSF(path,0,0))) { diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/PsxCounters.c --- a/Plugins/Input/sexypsf/PsxCounters.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/PsxCounters.c Sun Mar 19 12:51:03 2006 -0800 @@ -103,7 +103,7 @@ if(cycles>=16) { - if(!SPUasync(cycles)) return(0); + if(!SPU2async(cycles)) return(0); last=psxRegs.cycle; } return(1); diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/PsxDma.c --- a/Plugins/Input/sexypsf/PsxDma.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/PsxDma.c Sun Mar 19 12:51:03 2006 -0800 @@ -25,13 +25,13 @@ bcr= (bcr>>16) * (bcr&0xffff) * 2; //printf("%08x, %08x\n",madr,bcr); - SPUwriteDMAMem(madr, bcr); + SPU2writeDMA4Mem(madr, bcr); } break; case 0x01000200: //spu to cpu transfer { //printf("%08x\n",madr); - SPUreadDMAMem (madr, (bcr >> 16) * (bcr & 0xffff) * 2); + SPU2readDMA4Mem (madr, (bcr >> 16) * (bcr & 0xffff) * 2); } break; } diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/PsxHw.c --- a/Plugins/Input/sexypsf/PsxHw.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/PsxHw.c Sun Mar 19 12:51:03 2006 -0800 @@ -78,7 +78,7 @@ default: if (add>=0x1f801c00 && add<0x1f801e00) { - hard = SPUreadRegister(add); + hard = SPU2read(add); } else { hard = BFLIP16(psxHu16(add)); } @@ -165,7 +165,7 @@ default: if (add>=0x1f801c00 && add<0x1f801e00) { - SPUwriteRegister(add, value); + SPU2write(add, value); return; } diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/R3000A.c --- a/Plugins/Input/sexypsf/R3000A.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/R3000A.c Sun Mar 19 12:51:03 2006 -0800 @@ -50,7 +50,7 @@ psxBiosShutdown(); psxCpu->Shutdown(); - SPUclose(); + SPU2close(); } void psxException(u32 code, u32 bd) { diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/Spu.c --- a/Plugins/Input/sexypsf/Spu.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/Spu.c Sun Mar 19 12:51:03 2006 -0800 @@ -21,3 +21,4 @@ void SPUirq(void) { psxHu32(0x1070)|=BFLIP32(0x200); } + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/Spu.h --- a/Plugins/Input/sexypsf/Spu.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/Spu.h Sun Mar 19 12:51:03 2006 -0800 @@ -31,8 +31,8 @@ void SPUirq(void); -u16 SPUreadRegister(u32 reg); -void SPUwriteRegister(u32 reg, u16 val); +unsigned short SPUreadRegister(unsigned long reg); +void SPUwriteRegister(unsigned long reg, unsigned short val); int SPUasync(u32 cycle); void SPUwriteDMAMem(u32 usPSXMem,int iSize); void SPUreadDMAMem(u32 usPSXMem,int iSize); diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/psemuxa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/psemuxa.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,28 @@ +//============================================ +//=== Audio XA decoding +//=== Kazzuya +//============================================ + +#ifndef DECODEXA_H +#define DECODEXA_H + +typedef struct +{ + long y0, y1; +} ADPCM_Decode_t; + +typedef struct +{ + int freq; + int nbits; + int stereo; + int nsamples; + ADPCM_Decode_t left, right; + short pcm[16384]; +} xa_decode_t; + +long xa_decode_sector( xa_decode_t *xdp, + unsigned char *sectorp, + int is_first_sector ); + +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu.c --- a/Plugins/Input/sexypsf/spu.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu.c Sun Mar 19 12:51:03 2006 -0800 @@ -171,6 +171,18 @@ s_chan[ch].spos=0x40000L;s_chan[ch].SB[28]=0; // -> start with more decoding } +static unsigned long long SexyTime64(void) +{ + struct timeval tv; + unsigned long long ret; + + gettimeofday(&tv,0); + ret=tv.tv_sec; + ret*=1000000; + ret+=tv.tv_usec; + return(ret); +} + //////////////////////////////////////////////////////////////////////// // MAIN SPU FUNCTION // here is the main job handler... thread, timer or direct func call diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/adsr.c --- a/Plugins/Input/sexypsf/spu/adsr.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/adsr.c Sun Mar 19 12:51:03 2006 -0800 @@ -1,618 +1,656 @@ -/*************************************************************************** - 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: -// -// 2003/01/06 - Pete -// - added Neill's ADSR timings -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#define _IN_ADSR - -// will be included from spu.c -#ifdef _IN_SPU - -//////////////////////////////////////////////////////////////////////// -// ADSR func -//////////////////////////////////////////////////////////////////////// - -static u32 RateTable[160]; - -static void InitADSR(void) // INIT ADSR -{ - u32 r,rs,rd;int i; - - memset(RateTable,0,sizeof(u32)*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; - } -} - -//////////////////////////////////////////////////////////////////////// - -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; -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int MixADSR(int ch) // MIX ADSR -{ - static const int sexytable[8]= - {0,4,6,8,9,10,11,12}; - - if(s_chan[ch].bStop) // should be stopped: - { // do release - if(s_chan[ch].ADSRX.ReleaseModeExp) - { - s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]]; - } - else - { - s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x0C + 32]; - } - - if(s_chan[ch].ADSRX.EnvelopeVol<0) - { - s_chan[ch].ADSRX.EnvelopeVol=0; - s_chan[ch].bOn=0; - s_chan[ch].bNoise=0; - } - - s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; - return s_chan[ch].ADSRX.lVolume; - } - else // not stopped yet? - { - if(s_chan[ch].ADSRX.State==0) // -> attack - { - if(s_chan[ch].ADSRX.AttackModeExp) - { - if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32]; - else - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x18 + 32]; - } - else - { - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32]; - } - - if(s_chan[ch].ADSRX.EnvelopeVol<0) - { - s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF; - s_chan[ch].ADSRX.State=1; - } - - s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; - return s_chan[ch].ADSRX.lVolume; - } - //--------------------------------------------------// - if(s_chan[ch].ADSRX.State==1) // -> decay - { - s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]]; - - if(s_chan[ch].ADSRX.EnvelopeVol<0) s_chan[ch].ADSRX.EnvelopeVol=0; - if(((s_chan[ch].ADSRX.EnvelopeVol>>27)&0xF) <= s_chan[ch].ADSRX.SustainLevel) - { - s_chan[ch].ADSRX.State=2; - } - - s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; - return s_chan[ch].ADSRX.lVolume; - } - //--------------------------------------------------// - if(s_chan[ch].ADSRX.State==2) // -> sustain - { - if(s_chan[ch].ADSRX.SustainIncrease) - { - if(s_chan[ch].ADSRX.SustainModeExp) - { - if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32]; - else - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x18 + 32]; - } - else - { - s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32]; - } - - if(s_chan[ch].ADSRX.EnvelopeVol<0) - { - s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF; - } - } - else - { - if(s_chan[ch].ADSRX.SustainModeExp) - s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]]; - else - s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x0F + 32]; - - if(s_chan[ch].ADSRX.EnvelopeVol<0) - { - s_chan[ch].ADSRX.EnvelopeVol=0; - } - } - s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; - return s_chan[ch].ADSRX.lVolume; - } - } - 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: +// +// 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 +//////////////////////////////////////////////////////////////////////// + +unsigned long 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(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; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixADSR(int ch) // MIX ADSR +{ + if(s_chan[ch].bStop) // should be stopped: + { // do release + if(s_chan[ch].ADSRX.ReleaseModeExp) + { + switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7) + { + case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +0 + 32]; break; + case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +4 + 32]; break; + case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +6 + 32]; break; + case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +8 + 32]; break; + case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +9 + 32]; break; + case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +10+ 32]; break; + case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +11+ 32]; break; + case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +12+ 32]; break; + } + } + else + { + s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x0C + 32]; + } + + if(s_chan[ch].ADSRX.EnvelopeVol<0) + { + s_chan[ch].ADSRX.EnvelopeVol=0; + s_chan[ch].bOn=0; + //s_chan[ch].bReverb=0; + //s_chan[ch].bNoise=0; + } + + s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; + return s_chan[ch].ADSRX.lVolume; + } + else // not stopped yet? + { + if(s_chan[ch].ADSRX.State==0) // -> attack + { + if(s_chan[ch].ADSRX.AttackModeExp) + { + if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32]; + else + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x18 + 32]; + } + else + { + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32]; + } + + if(s_chan[ch].ADSRX.EnvelopeVol<0) + { + s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF; + s_chan[ch].ADSRX.State=1; + } + + s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; + return s_chan[ch].ADSRX.lVolume; + } + //--------------------------------------------------// + if(s_chan[ch].ADSRX.State==1) // -> decay + { + switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7) + { + case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+0 + 32]; break; + case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+4 + 32]; break; + case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+6 + 32]; break; + case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+8 + 32]; break; + case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+9 + 32]; break; + case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+10+ 32]; break; + case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+11+ 32]; break; + case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+12+ 32]; break; + } + + if(s_chan[ch].ADSRX.EnvelopeVol<0) s_chan[ch].ADSRX.EnvelopeVol=0; + if(((s_chan[ch].ADSRX.EnvelopeVol>>27)&0xF) <= s_chan[ch].ADSRX.SustainLevel) + { + s_chan[ch].ADSRX.State=2; + } + + s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; + return s_chan[ch].ADSRX.lVolume; + } + //--------------------------------------------------// + if(s_chan[ch].ADSRX.State==2) // -> sustain + { + if(s_chan[ch].ADSRX.SustainIncrease) + { + if(s_chan[ch].ADSRX.SustainModeExp) + { + if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32]; + else + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x18 + 32]; + } + else + { + s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32]; + } + + if(s_chan[ch].ADSRX.EnvelopeVol<0) + { + s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF; + } + } + else + { + if(s_chan[ch].ADSRX.SustainModeExp) + { + switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7) + { + case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +0 + 32];break; + case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +4 + 32];break; + case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +6 + 32];break; + case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +8 + 32];break; + case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +9 + 32];break; + case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +10+ 32];break; + case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +11+ 32];break; + case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +12+ 32];break; + } + } + else + { + s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x0F + 32]; + } + + if(s_chan[ch].ADSRX.EnvelopeVol<0) + { + s_chan[ch].ADSRX.EnvelopeVol=0; + } + } + s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21; + return s_chan[ch].ADSRX.lVolume; + } + } + 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 -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/adsr.h --- a/Plugins/Input/sexypsf/spu/adsr.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/adsr.h Sun Mar 19 12:51:03 2006 -0800 @@ -1,28 +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. * - * * - ***************************************************************************/ - -//*************************************************************************// -// History of changes: -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -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(int ch); +INLINE int MixADSR(int ch); diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/alsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/alsa.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,206 @@ +/*************************************************************************** + alsa.c - description + ------------------- + begin : Sat Mar 01 2003 + 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/03/02 - linuzappz +// - fixed XRUN behavior +// +// 2003/03/01 - linuzappz +// - created +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_OSS + +#include "externals.h" + +#ifndef _WINDOWS + +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API +#include + +//////////////////////////////////////////////////////////////////////// +// small linux time helper... only used for watchdog +//////////////////////////////////////////////////////////////////////// + +unsigned long timeGetTime() +{ + 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 +} + +//////////////////////////////////////////////////////////////////////// +// oss globals +//////////////////////////////////////////////////////////////////////// + +#define ALSA_MEM_DEF +#include "alsa.h" +static snd_pcm_t *handle = NULL; +static snd_pcm_uframes_t buffer_size; + +//////////////////////////////////////////////////////////////////////// +// SETUP SOUND +//////////////////////////////////////////////////////////////////////// + +void SetupSound(void) +{ + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + snd_pcm_status_t *status; + int pspeed; + int pchannels; + int format; + int buffer_time; + int period_time; + int err; + + if(iDisStereo) pchannels=1; + else pchannels=2; + + pspeed=44100; + format=SND_PCM_FORMAT_S16_LE; + buffer_time=500000; + period_time=buffer_time/4; + + if((err=snd_pcm_open(&handle, "default", + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK))<0) + { + printf("Audio open error: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_nonblock(handle, 0))<0) + { + printf("Can't set blocking moded: %s\n", snd_strerror(err)); + return; + } + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + if((err=snd_pcm_hw_params_any(handle, hwparams))<0) + { + printf("Broken configuration for this PCM: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))<0) + { + printf("Access type not available: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_format(handle, hwparams, format))<0) + { + printf("Sample format not available: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_channels(handle, hwparams, pchannels))<0) + { + printf("Channels count not available: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0))<0) + { + printf("Rate not available: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0))<0) + { + printf("Buffer time error: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0))<0) + { + printf("Period time error: %s\n", snd_strerror(err)); + return; + } + + if((err=snd_pcm_hw_params(handle, hwparams))<0) + { + printf("Unable to install hw params: %s\n", snd_strerror(err)); + return; + } + + snd_pcm_status_alloca(&status); + if((err=snd_pcm_status(handle, status))<0) + { + printf("Unable to get status: %s\n", snd_strerror(err)); + return; + } + + buffer_size=snd_pcm_status_get_avail(status); +} + +//////////////////////////////////////////////////////////////////////// +// REMOVE SOUND +//////////////////////////////////////////////////////////////////////// + +void RemoveSound(void) +{ + if(handle != NULL) + { + snd_pcm_drop(handle); + snd_pcm_close(handle); + handle = NULL; + } +} + +//////////////////////////////////////////////////////////////////////// +// GET BYTES BUFFERED +//////////////////////////////////////////////////////////////////////// + +unsigned long SoundGetBytesBuffered(void) +{ + unsigned long l; + + if(handle == NULL) // failed to open? + return SOUNDSIZE; + l = snd_pcm_avail_update(handle); + if(l<0) return 0; + if(l no? wait + else l=0; // -> else go on + + return l; +} + +//////////////////////////////////////////////////////////////////////// +// FEED SOUND DATA +//////////////////////////////////////////////////////////////////////// + +void SoundFeedStreamData(unsigned char* pSound,long lBytes) +{ + if(handle == NULL) return; + + if(snd_pcm_state(handle) == SND_PCM_STATE_XRUN) + snd_pcm_prepare(handle); + snd_pcm_writei(handle,pSound, + iDisStereo == 1 ? lBytes/2 : lBytes/4); +} + +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/alsa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/alsa.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,30 @@ +/*************************************************************************** + alsa.h - description + ------------------- + begin : Sat Mar 01 2003 + 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. * + * * + ***************************************************************************/ + + +#ifndef _ALSA_SOUND_H +#define _ALSA_SOUND_H + +#ifdef ALSA_MEM_DEF +#define ALSA_MEM_EXTERN +#else +#define ALSA_MEM_EXTERN extern +#endif + +ALSA_MEM_EXTERN int sound_buffer_size; + +#endif // _ALSA_SOUND_H diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/cfg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/cfg.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,478 @@ +/*************************************************************************** + cfg.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/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2003/06/07 - Pete +// - added Linux NOTHREADLIB define +// +// 2003/02/28 - Pete +// - added option for kode54's interpolation and linuzappz's mono mode +// +// 2003/01/19 - Pete +// - added Neill's reverb +// +// 2002/08/04 - Pete +// - small linux bug fix: now the cfg file can be in the main emu directory as well +// +// 2002/06/08 - linuzappz +// - Added combo str for SPUasync, and MAXMODE is now defined as 2 +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_CFG + +#include "externals.h" + +//////////////////////////////////////////////////////////////////////// +// WINDOWS CONFIG/ABOUT HANDLING +//////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS + +#include "resource.h" + +//////////////////////////////////////////////////////////////////////// +// simple about dlg handler +//////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_COMMAND: + { + switch(LOWORD(wParam)) + {case IDOK: EndDialog(hW,TRUE);return TRUE;} + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////// +// READ CONFIG: from win registry +//////////////////////////////////////////////////////////////////////// + +// timer mode 2 (spuupdate sync mode) can be enabled for windows +// by setting MAXMODE to 2. +// Attention: that mode is not much tested, maybe the dsound buffers +// need to get adjusted to use that mode safely. Also please note: +// sync sound updates will _always_ cause glitches, if the system is +// busy by, for example, long lasting cdrom accesses. OK, you have +// be warned :) + +#define MAXMODE 2 +//#define MAXMODE 1 + +void ReadConfig(void) +{ + HKEY myKey; + DWORD temp; + DWORD type; + DWORD size; + + iUseXA=1; // init vars + iVolume=3; + iXAPitch=1; + iUseTimer=1; + iSPUIRQWait=0; + iDebugMode=0; + iRecordMode=0; + iUseReverb=0; + iUseInterpolation=2; + iDisStereo=0; + + if(RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\PS2Eplugin\\SPU2\\PeopsSound",0,KEY_ALL_ACCESS,&myKey)==ERROR_SUCCESS) + { + size = 4; + if(RegQueryValueEx(myKey,"UseXA",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iUseXA=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"Volume",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iVolume=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"XAPitch",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iXAPitch=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"UseTimer",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iUseTimer=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"SPUIRQWait",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iSPUIRQWait=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"DebugMode",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iDebugMode=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"RecordMode",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iRecordMode=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"UseReverb",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iUseReverb=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"UseInterpolation",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iUseInterpolation=(int)temp; + size = 4; + if(RegQueryValueEx(myKey,"DisStereo",0,&type,(LPBYTE)&temp,&size)==ERROR_SUCCESS) + iDisStereo=(int)temp; + + RegCloseKey(myKey); + } + + if(iUseTimer>MAXMODE) iUseTimer=MAXMODE; // some checks + if(iVolume<1) iVolume=1; + if(iVolume>4) iVolume=4; +} + +//////////////////////////////////////////////////////////////////////// +// WRITE CONFIG: in win registry +//////////////////////////////////////////////////////////////////////// + +void WriteConfig(void) +{ + HKEY myKey; + DWORD myDisp; + DWORD temp; + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\PS2Eplugin\\SPU2\\PeopsSound",0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&myKey,&myDisp); + temp=iUseXA; + RegSetValueEx(myKey,"UseXA",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iVolume; + RegSetValueEx(myKey,"Volume",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iXAPitch; + RegSetValueEx(myKey,"XAPitch",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iUseTimer; + RegSetValueEx(myKey,"UseTimer",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iSPUIRQWait; + RegSetValueEx(myKey,"SPUIRQWait",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iDebugMode; + RegSetValueEx(myKey,"DebugMode",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iRecordMode; + RegSetValueEx(myKey,"RecordMode",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iUseReverb; + RegSetValueEx(myKey,"UseReverb",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iUseInterpolation; + RegSetValueEx(myKey,"UseInterpolation",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + temp=iDisStereo; + RegSetValueEx(myKey,"DisStereo",0,REG_DWORD,(LPBYTE) &temp,sizeof(temp)); + + RegCloseKey(myKey); +} + +//////////////////////////////////////////////////////////////////////// +// INIT WIN CFG DIALOG +//////////////////////////////////////////////////////////////////////// + +BOOL OnInitDSoundDialog(HWND hW) +{ + HWND hWC; + + ReadConfig(); + + if(iUseXA) CheckDlgButton(hW,IDC_ENABXA,TRUE); + + if(iXAPitch) CheckDlgButton(hW,IDC_XAPITCH,TRUE); + + hWC=GetDlgItem(hW,IDC_VOLUME); + ComboBox_AddString(hWC, "0: low"); + ComboBox_AddString(hWC, "1: medium"); + ComboBox_AddString(hWC, "2: loud"); + ComboBox_AddString(hWC, "3: loudest"); + ComboBox_SetCurSel(hWC,4-iVolume); + + if(iSPUIRQWait) CheckDlgButton(hW,IDC_IRQWAIT,TRUE); + if(iDebugMode) CheckDlgButton(hW,IDC_DEBUGMODE,TRUE); + if(iRecordMode) CheckDlgButton(hW,IDC_RECORDMODE,TRUE); + if(iDisStereo) CheckDlgButton(hW,IDC_DISSTEREO,TRUE); + + hWC=GetDlgItem(hW,IDC_USETIMER); + ComboBox_AddString(hWC, "0: Fast mode (thread, less compatible spu timing)"); + ComboBox_AddString(hWC, "1: High compatibility mode (timer event, slower)"); +#if MAXMODE == 2 + //ComboBox_AddString(hWC, "2: Use spu update calls (TESTMODE!)"); + ComboBox_AddString(hWC, "2: Use SPUasync (must be supported by the emu)"); +#endif + ComboBox_SetCurSel(hWC,iUseTimer); + + hWC=GetDlgItem(hW,IDC_USEREVERB); + ComboBox_AddString(hWC, "0: No reverb (fastest)"); + ComboBox_AddString(hWC, "1: SPU2 reverb (may be buggy, not tested yet)"); + ComboBox_SetCurSel(hWC,iUseReverb); + + hWC=GetDlgItem(hW,IDC_INTERPOL); + ComboBox_AddString(hWC, "0: None (fastest)"); + ComboBox_AddString(hWC, "1: Simple interpolation"); + ComboBox_AddString(hWC, "2: Gaussian interpolation (good quality)"); + ComboBox_AddString(hWC, "3: Cubic interpolation (better treble)"); + ComboBox_SetCurSel(hWC,iUseInterpolation); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////// +// WIN CFG DLG OK +//////////////////////////////////////////////////////////////////////// + +void OnDSoundOK(HWND hW) +{ + HWND hWC; + + if(IsDlgButtonChecked(hW,IDC_ENABXA)) + iUseXA=1; else iUseXA=0; + + if(IsDlgButtonChecked(hW,IDC_XAPITCH)) + iXAPitch=1; else iXAPitch=0; + + hWC=GetDlgItem(hW,IDC_VOLUME); + iVolume=4-ComboBox_GetCurSel(hWC); + + hWC=GetDlgItem(hW,IDC_USETIMER); + iUseTimer=ComboBox_GetCurSel(hWC); + + hWC=GetDlgItem(hW,IDC_USEREVERB); + iUseReverb=ComboBox_GetCurSel(hWC); + + hWC=GetDlgItem(hW,IDC_INTERPOL); + iUseInterpolation=ComboBox_GetCurSel(hWC); + + if(IsDlgButtonChecked(hW,IDC_IRQWAIT)) + iSPUIRQWait=1; else iSPUIRQWait=0; + + if(IsDlgButtonChecked(hW,IDC_DEBUGMODE)) + iDebugMode=1; else iDebugMode=0; + + if(IsDlgButtonChecked(hW,IDC_RECORDMODE)) + iRecordMode=1; else iRecordMode=0; + + if(IsDlgButtonChecked(hW,IDC_DISSTEREO)) + iDisStereo=1; else iDisStereo=0; + + WriteConfig(); // write registry + + EndDialog(hW,TRUE); +} + +//////////////////////////////////////////////////////////////////////// +// WIN CFG DLG CANCEL +//////////////////////////////////////////////////////////////////////// + +void OnDSoundCancel(HWND hW) +{ + EndDialog(hW,FALSE); +} + +//////////////////////////////////////////////////////////////////////// +// WIN CFG PROC +//////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK DSoundDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + return OnInitDSoundDialog(hW); + + case WM_COMMAND: + { + switch(LOWORD(wParam)) + { + case IDCANCEL: OnDSoundCancel(hW);return TRUE; + case IDOK: OnDSoundOK(hW); return TRUE; + } + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// LINUX CONFIG/ABOUT HANDLING +//////////////////////////////////////////////////////////////////////// + +#else + +char * pConfigFile=NULL; + +#include + +//////////////////////////////////////////////////////////////////////// +// START EXTERNAL CFG TOOL +//////////////////////////////////////////////////////////////////////// + +void StartCfgTool(char * pCmdLine) +{ + FILE * cf;char filename[255],t[255]; + + strcpy(filename,"cfg/cfgPeopsOSS2"); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + getcwd(t,255); + chdir("cfg"); + sprintf(filename,"./cfgPeopsOSS2 %s",pCmdLine); + system(filename); + chdir(t); + } + else + { + strcpy(filename,"cfgPeopsOSS2"); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + sprintf(filename,"./cfgPeopsOSS2 %s",pCmdLine); + system(filename); + } + else + { + sprintf(filename,"%s/cfgPeopsOSS2",getenv("HOME")); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + getcwd(t,255); + chdir(getenv("HOME")); + sprintf(filename,"./cfgPeopsOSS2 %s",pCmdLine); + system(filename); + chdir(t); + } + else printf("cfgPeopsOSS2 not found!\n"); + } + } +} + +///////////////////////////////////////////////////////// +// READ LINUX CONFIG FILE +///////////////////////////////////////////////////////// + +void ReadConfigFile(void) +{ + FILE *in;char t[256];int len; + char * pB, * p; + + if(pConfigFile) + { + strcpy(t,pConfigFile); + in = fopen(t,"rb"); + if(!in) return; + } + else + { + strcpy(t,"cfg/spuPeopsOSS2.cfg"); + in = fopen(t,"rb"); + if(!in) + { + strcpy(t,"spuPeopsOSS2.cfg"); + in = fopen(t,"rb"); + if(!in) + { + sprintf(t,"%s/spuPeopsOSS2.cfg",getenv("HOME")); + in = fopen(t,"rb"); + if(!in) return; + } + } + } + + pB=(char *)malloc(32767); + memset(pB,0,32767); + + len = fread(pB, 1, 32767, in); + fclose(in); + + strcpy(t,"\nVolume");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iVolume=atoi(p+len); + if(iVolume<1) iVolume=1; + if(iVolume>4) iVolume=4; + + strcpy(t,"\nUseXA");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iUseXA=atoi(p+len); + if(iUseXA<0) iUseXA=0; + if(iUseXA>1) iUseXA=1; + + strcpy(t,"\nXAPitch");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iXAPitch=atoi(p+len); + if(iXAPitch<0) iXAPitch=0; + if(iXAPitch>1) iXAPitch=1; + + strcpy(t,"\nHighCompMode");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iUseTimer=atoi(p+len); + if(iUseTimer<0) iUseTimer=0; + // note: timer mode 1 (win time events) is not supported + // in linux. But timer mode 2 (spuupdate) is safe to use. + if(iUseTimer) iUseTimer=2; + +#ifdef NOTHREADLIB + iUseTimer=2; +#endif + + strcpy(t,"\nSPUIRQWait");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iSPUIRQWait=atoi(p+len); + if(iSPUIRQWait<0) iSPUIRQWait=0; + if(iSPUIRQWait>1) iSPUIRQWait=1; + + strcpy(t,"\nUseReverb");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iUseReverb=atoi(p+len); + if(iUseReverb<0) iUseReverb=0; + if(iUseReverb>1) iUseReverb=1; + + strcpy(t,"\nUseInterpolation");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iUseInterpolation=atoi(p+len); + if(iUseInterpolation<0) iUseInterpolation=0; + if(iUseInterpolation>3) iUseInterpolation=3; + + strcpy(t,"\nDisStereo");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iDisStereo=atoi(p+len); + if(iDisStereo<0) iDisStereo=0; + if(iDisStereo>1) iDisStereo=1; + + free(pB); +} + +///////////////////////////////////////////////////////// +// READ CONFIG called by spu funcs +///////////////////////////////////////////////////////// + +void ReadConfig(void) +{ + iVolume=3; + iUseXA=1; + iXAPitch=0; + iSPUIRQWait=1; + iUseTimer=2; + iUseReverb=0; + iUseInterpolation=2; + iDisStereo=0; + + ReadConfigFile(); +} + +#endif + + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/cfg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/cfg.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,36 @@ +/*************************************************************************** + cfg.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 ReadConfig(void); + + +#ifdef _WINDOWS +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK DSoundDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +#else +void StartCfgTool(char * pCmdLine); +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/debug.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/debug.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,521 @@ +/*************************************************************************** + debug.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/12/25 - Pete +// - update mute checkboxes if core selection changes +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2003/01/06 - Pete +// - added Neil's ADSR timings +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_DEBUG + +#include "externals.h" + +//////////////////////////////////////////////////////////////////////// +// WINDOWS DEBUG DIALOG HANDLING +//////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS + +#include "resource.h" + +//#define SMALLDEBUG +//#include + +//////////////////////////////////////////////////////////////////////// +// display debug infos + +const COLORREF crStreamCol[]={ + RGB( 0, 0, 0), + RGB(255,255,255), + RGB(128, 0,128), + RGB( 0,128, 0), + RGB( 0, 0,255), + RGB(255, 0, 0) + }; + +const COLORREF crAdsrCol[] ={ + RGB( 0, 0, 0), + RGB(255, 0, 0), + RGB( 0,255, 0), + RGB(255, 0,255), + RGB( 0, 0,255), + RGB( 0, 0, 0), + }; + +HBRUSH hBStream[6]; // brushes for stream lines +HPEN hPAdsr[6]; // pens for adsr lines +int iSelChannel=0; // user selected channel +int iCoreOffset=0; + +//////////////////////////////////////////////////////////////////////// +// display the sound data waves: no subclassing used, so the +// area will not be redrawn... but faster that way, and good enuff +// for debugging purposes + +void DisplayStreamInfos(HWND hW) +{ + HWND hWS=GetDlgItem(hW,IDC_SAREA); + HDC hdc;RECT r;HBRUSH hBO;int ch,dy,i,j,id; + + //----------------------------------------------------// + + GetClientRect(hWS,&r); // get size of stream display + hdc=GetDC(hWS); // device context + r.right--; // leave the right border intact + ScrollDC(hdc,-1,0,&r,&r,NULL,NULL); // scroll one pixel to the left + + //----------------------------------------------------// + + hBO=SelectObject(hdc,hBStream[0]); // clean the right border + PatBlt(hdc,r.right-1,0,1,r.bottom,PATCOPY); + + //----------------------------------------------------// + + dy=r.bottom/HLFCHAN; // size of one channel area + + for(ch=0;ch get one channel data (-32k ... 32k) + j=(dy*j)/32768; if(j==0) j=1; // -> adjust to display coords + i=(dy/2)+(ch*r.bottom/HLFCHAN)-j/2; // -> position where to paint it + + + + if (s_chan[ch+iCoreOffset].iMute) id=1; // -> get color id + else if(s_chan[ch+iCoreOffset].bNoise) id=2; + else if(s_chan[ch+iCoreOffset].bFMod==2) id=3; + else if(s_chan[ch+iCoreOffset].bFMod==1) id=4; + else id=5; + + SelectObject(hdc,hBStream[id]); // -> select the brush + PatBlt(hdc,r.right-1,i,1,j,PATCOPY); // -> paint the value line + } + + if(ch) SetPixel(hdc,r.right-1, // -> not first line? + ch*r.bottom/HLFCHAN,RGB(0,0,0)); // --> draw the line (one dot scrolled to the left) + } + + //----------------------------------------------------// + + SelectObject(hdc,hBO); // repair brush + + ReleaseDC(hWS,hdc); // release context +} + +//////////////////////////////////////////////////////////////////////// +// display adsr lines: also no subclassing for repainting used + +void DisplayADSRInfos(HWND hW) +{ + HWND hWS=GetDlgItem(hW,IDC_ADSR); + HDC hdc;RECT r;HBRUSH hBO;char szB[16]; + int ch=iSelChannel+iCoreOffset,dx,dy,dm,dn,ia,id,is,ir; + + //----------------------------------------------------// get display size + + GetClientRect(hWS,&r); + hdc=GetDC(hWS); + + //----------------------------------------------------// clean the area + + hBO=SelectObject(hdc,hBStream[0]); + PatBlt(hdc,0,0,r.right,r.bottom,PATCOPY); + r.left++;r.right-=2;r.top++;r.bottom-=2; // shrink the display rect for better optics + + //----------------------------------------------------// + + ia=min(s_chan[ch].ADSR.AttackTime,10000); // get adsr, but limit it for drawing + id=min(s_chan[ch].ADSR.DecayTime,10000); + is=min(s_chan[ch].ADSR.SustainTime,10000); + ir=min(s_chan[ch].ADSR.ReleaseTime,10000); + + dx=ia+id+is+ir; // get the dx in (limited) adsr units + + // set the real values to the info statics + SetDlgItemInt(hW,IDC_SADSR1,s_chan[ch].ADSRX.AttackRate,FALSE); + SetDlgItemInt(hW,IDC_SADSR2,s_chan[ch].ADSRX.DecayRate,FALSE); + SetDlgItemInt(hW,IDC_SADSR3,s_chan[ch].ADSRX.SustainRate,FALSE); + SetDlgItemInt(hW,IDC_SADSR4,s_chan[ch].ADSRX.ReleaseRate,FALSE); + SetDlgItemInt(hW,IDC_SADSR5,s_chan[ch].ADSRX.SustainLevel,FALSE); + SetDlgItemInt(hW,IDC_SADSR6,s_chan[ch].ADSRX.SustainIncrease,TRUE); + SetDlgItemInt(hW,IDC_SADSR7,s_chan[ch].ADSRX.lVolume,TRUE); + wsprintf(szB,"%08lX",s_chan[ch].ADSRX.EnvelopeVol); + SetDlgItemText(hW,IDC_SADSR8,szB); + + if(dx) // something to draw? + { + HPEN hPO=SelectObject(hdc,hPAdsr[1]); // sel A pen + dn=r.left; + MoveToEx(hdc,dn,r.bottom,NULL); // move to bottom left corner + + dn+=(ia*r.right)/dx; // calc A x line pos + LineTo(hdc,dn,r.top); // line to AxPos,top + + SelectObject(hdc,hPAdsr[2]); // sel D pen + dn+=(id*r.right)/dx; // calc D x line pos + dy=r.top+((1024-s_chan[ch].ADSR.SustainLevel)* // calc the D y pos + r.bottom)/1024; // (our S level is ranged from 0 to 1024) + LineTo(hdc,dn,dy); // line to DxPos,SLevel + + SelectObject(hdc,hPAdsr[3]); // sel S pen + if(s_chan[ch].ADSR.SustainTime>10000) + dm=1; // we have to fake the S values... S will + else // inc/decrease until channel stop... + if(s_chan[ch].ADSR.SustainTime==0) + dm=0; // we dunno here when this will happen, + else + dm=21-(((s_chan[ch].ADSR.SustainTime/500))); // so we do some more or less angled line, + dy=dy-(s_chan[ch].ADSR.SustainModeDec*dm); // roughly depending on the S time + if(dy>r.bottom) dy=r.bottom; + if(dy>1); + SetDlgItemText(hW,IDC_CI10,szB); + if(s_chan[ch].pCurr==(unsigned char *)-1) + SetDlgItemText(hW,IDC_CI11,"FFFFFFFF"); + else + { + wsprintf(szB,"%08lX",((unsigned long)s_chan[ch].pCurr-(unsigned long)spuMemC)>>1); + SetDlgItemText(hW,IDC_CI11,szB); + } + + wsprintf(szB,"%08lX",((unsigned long)s_chan[ch].pLoop-(unsigned long)spuMemC)>>1); + SetDlgItemText(hW,IDC_CI12,szB); + SetDlgItemInt(hW,IDC_CI13,s_chan[ch].iRightVolume,TRUE); + SetDlgItemInt(hW,IDC_CI14,s_chan[ch].iLeftVolume,TRUE); + SetDlgItemInt(hW,IDC_CI15,s_chan[ch].iActFreq,TRUE); + SetDlgItemInt(hW,IDC_CI16,s_chan[ch].iUsedFreq,TRUE); + +// wsprintf(szB,"%04x",s_chan[ch].iRightVolRaw); + wsprintf(szB,"R%d",s_chan[ch].bVolumeR); + SetDlgItemText(hW,IDC_CI17,szB); +// wsprintf(szB,"%04x",s_chan[ch].iLeftVolRaw); + wsprintf(szB,"L%d",s_chan[ch].bVolumeL); + SetDlgItemText(hW,IDC_CI18,szB); + + wsprintf(szB,"%08lX",s_chan[ch].iNextAdr); + SetDlgItemText(hW,IDC_CI19,szB); + + // generic infos + if(pSpuIrq[ch/24]==0) + SetDlgItemText(hW,IDC_STA1,"FFFFFFFF"); + else + { + wsprintf(szB,"%08lX",((unsigned long)pSpuIrq[ch/24]-(unsigned long)spuMemC)>>1); + SetDlgItemText(hW,IDC_STA1,szB); + } + + wsprintf(szB,"%04X",spuCtrl2[ch/24]); + SetDlgItemText(hW,IDC_STA2,szB); + wsprintf(szB,"%04X",spuStat2[ch/24]); + SetDlgItemText(hW,IDC_STA3,szB); + + wsprintf(szB,"%08lX",spuAddr2[ch/24]); + SetDlgItemText(hW,IDC_STA4,szB); + + // xa infos + if(XAPlay<=XAFeed) ch=XAFeed-XAPlay; + else ch=(XAFeed-XAStart)+(XAEnd-XAPlay); + SetDlgItemInt(hW,IDC_XA4,ch,FALSE); + SetDlgItemInt(hW,IDC_XA5,iLeftXAVol,TRUE); + SetDlgItemInt(hW,IDC_XA6,iRightXAVol,TRUE); + if(!xapGlobal) return; + SetDlgItemInt(hW,IDC_XA1,xapGlobal->freq,TRUE); + SetDlgItemInt(hW,IDC_XA2,xapGlobal->stereo,TRUE); + SetDlgItemInt(hW,IDC_XA3,xapGlobal->nsamples,TRUE); +} + +//////////////////////////////////////////////////////////////////////// +// display everything (called in dialog timer for value refreshing) + +void DisplayDebugInfos(HWND hW) +{ + DisplayStreamInfos(hW); + DisplayADSRInfos(hW); + DisplayChannelInfos(hW); +} + +EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val); +EXPORT_GCC unsigned short CALLBACK SPU2read(unsigned long reg); + +//////////////////////////////////////////////////////////////////////// +// main debug dlg handler + +char old_buffer[128]; +int iBuffRepeats=0; + +BOOL CALLBACK DebugDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + //--------------------------------------------------// init + case WM_INITDIALOG: + { + int i; + ShowCursor(TRUE); // mmm... who is hiding it? main emu? tsts + iSelChannel=0; // sel first channel + iCoreOffset=0; + CheckRadioButton(hW,IDC_CHAN1,IDC_CHAN24,IDC_CHAN1); + CheckRadioButton(hW,IDC_CORE1,IDC_CORE2,IDC_CORE1); + if(iUseXA) CheckDlgButton(hW,IDC_XA,TRUE); + + memset(old_buffer,0,128); + // create brushes/pens + hBStream[0]=CreateSolidBrush(GetSysColor(COLOR_3DFACE)); + hPAdsr[0]=CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DFACE)); + for(i=1;i<6;i++) + { + hBStream[i]=CreateSolidBrush(crStreamCol[i]); + hPAdsr[i]=CreatePen(PS_SOLID,0,crAdsrCol[i]); + } + SetTimer(hW,999,50,NULL); // now create update timer + return TRUE; + } + //--------------------------------------------------// destroy + case WM_DESTROY: + { + int i; + KillTimer(hW,999); // first kill timer + for(i=0;i<6;i++) // then kill brushes/pens + { + DeleteObject(hBStream[i]); + DeleteObject(hPAdsr[i]); + } + }break; + //--------------------------------------------------// timer + case WM_TIMER: + { + if(wParam==999) DisplayDebugInfos(hW); // update all values + }break; + //--------------------------------------------------// command + case WM_COMMAND: + { + if(wParam==IDCANCEL) iDebugMode=2; // cancel? raise flag for destroying the dialog + + if(wParam==IDC_REGWRITE) + { + char szB[8];unsigned long l; + GetDlgItemText(hW,IDC_REGEDIT,szB,8); + l=strtoul(szB,NULL,16); + GetDlgItemText(hW,IDC_VALEDIT,szB,8); + SPU2write(l,(unsigned short)strtoul(szB,NULL,16)); + } + + if(wParam==IDC_CLEAR) + { + SetDlgItemText(hW,IDC_LOG,""); + } + + if(wParam==IDC_COPY) + { + SendMessage(GetDlgItem(hW,IDC_LOG),EM_SETSEL,0,-1); + SendMessage(GetDlgItem(hW,IDC_LOG),WM_COPY,0,0); + MessageBeep(0xFFFFFFFF); + } + + if(wParam==IDC_REGREAD) + { + char szB[8];unsigned long l; + GetDlgItemText(hW,IDC_REGEDIT,szB,8); + l=strtoul(szB,NULL,16); + wsprintf(szB,"%04x",SPU2read(l)); + SetDlgItemText(hW,IDC_VALEDIT,szB); + } + + if(wParam==IDC_XA) + { + if(IsDlgButtonChecked(hW,wParam)) // -> mute/unmute it + iUseXA=1; + else iUseXA=0; + } + + if(wParam>=IDC_CORE1 && wParam<=IDC_CORE2) // core clicked? + { + int i; + + if(IsDlgButtonChecked(hW,IDC_CORE1)) // -> sel correct half of channels + iCoreOffset=0; + else iCoreOffset=24; + + for(i=IDC_MUTE1;i<=IDC_MUTE24;i++) + { + if(s_chan[i-IDC_MUTE1+iCoreOffset].iMute) + CheckDlgButton(hW,i,TRUE); + else CheckDlgButton(hW,i,FALSE); + } + + InvalidateRect(hW,NULL,TRUE); + UpdateWindow(hW); + } + + if(wParam>=IDC_MUTE1 && wParam<=IDC_MUTE24) // mute clicked? + { + if(IsDlgButtonChecked(hW,wParam)) // -> mute/unmute it + s_chan[wParam-IDC_MUTE1+iCoreOffset].iMute=1; + else + s_chan[wParam-IDC_MUTE1+iCoreOffset].iMute=0; + } + // all mute/unmute + if(wParam==IDC_MUTEOFF) SendMessage(hW,WM_MUTE,0,0); + if(wParam==IDC_MUTEON) SendMessage(hW,WM_MUTE,1,0); + + if(wParam>=IDC_CHAN1 && wParam<=IDC_CHAN24) // sel channel + { + if(IsDlgButtonChecked(hW,wParam)) + { + iSelChannel=wParam-IDC_CHAN1; + SetDlgItemInt(hW,IDC_CHANNUM,iSelChannel+1,FALSE); + } + } + }break; + //--------------------------------------------------// mute + case WM_MUTE: + { // will be called by the mute/unmute all button and on savestate load + int i; + for(i=IDC_MUTE1;i<=IDC_MUTE24;i++) + { + CheckDlgButton(hW,i,wParam); + if(wParam) + s_chan[i-IDC_MUTE1+iCoreOffset].iMute=1; + else + s_chan[i-IDC_MUTE1+iCoreOffset].iMute=0; + } + }break; + //--------------------------------------------------// size + case WM_SIZE: + if(wParam==SIZE_MINIMIZED) SetFocus(hWMain); // if we get minimized, set the foxus to the main window + break; + //--------------------------------------------------// setcursor + case WM_SETCURSOR: + { + SetCursor(LoadCursor(NULL,IDC_ARROW)); // force the arrow + return TRUE; + } + //--------------------------------------------------// + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////// + +void logprintf(LPCTSTR pFormat, ...) +{ + if(iDebugMode!=1) return; + if(!IsWindow(hWDebug)) return; + else + if(IsDlgButtonChecked(hWDebug,IDC_NOLOG)) return; + else + { + char buffer [128]; + va_list args; + + va_start(args, pFormat); + wvsprintf(buffer, pFormat, args); + va_end(args); + + if(strcmp(old_buffer,buffer)==0) + { + iBuffRepeats++; + if(iBuffRepeats>=100) + { + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_SETSEL,0,0); + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_REPLACESEL,0,(long)old_buffer); + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_REPLACESEL,0,(long)"->[100]\r\n"); + iBuffRepeats=0; + return; + } + return; + } + + if(iBuffRepeats) + { + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_SETSEL,0,0); + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_REPLACESEL,0,(long)old_buffer); + wsprintf(old_buffer,"->[%d]\r\n",iBuffRepeats); + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_REPLACESEL,0,(long)old_buffer); + iBuffRepeats=0; + } + + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_SETSEL,0,0); + SendMessage(GetDlgItem(hWDebug,IDC_LOG),EM_REPLACESEL,0,(long)buffer); + + lstrcpy(old_buffer,buffer); + + } +} + +#endif + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/debug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/debug.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,30 @@ +/*************************************************************************** + debug.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 +// +//*************************************************************************// + +#ifdef _WINDOWS +BOOL CALLBACK DebugDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +void logprintf(LPCTSTR pFormat, ...); +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/dma.c --- a/Plugins/Input/sexypsf/spu/dma.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/dma.c Sun Mar 19 12:51:03 2006 -0800 @@ -1,75 +1,214 @@ -/*************************************************************************** - dma.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: -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#include "stdafx.h" - -#define _IN_DMA - -//#include "externals.h" -//////////////////////////////////////////////////////////////////////// -// READ DMA (many values) -//////////////////////////////////////////////////////////////////////// - -void SPUreadDMAMem(u32 usPSXMem,int iSize) -{ - int i; - - for(i=0;i>1]; // spu addr got by writeregister - usPSXMem+=2; - spuAddr+=2; // inc spu addr - if(spuAddr>0x7ffff) spuAddr=0; // wrap - } -} - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -// to investigate: do sound data updates by writedma affect spu -// irqs? Will an irq be triggered, if new data is written to -// the memory irq address? - -//////////////////////////////////////////////////////////////////////// -// WRITE DMA (many values) -//////////////////////////////////////////////////////////////////////// - -void SPUwriteDMAMem(u32 usPSXMem,int iSize) -{ - int i; - - for(i=0;i>1] = *(u16 *)PSXM(usPSXMem); - usPSXMem+=2; // spu addr got by writeregister - spuAddr+=2; // inc spu addr - if(spuAddr>0x7ffff) spuAddr=0; // wrap - } -} - -//////////////////////////////////////////////////////////////////////// - +/*************************************************************************** + dma.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/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_DMA + +#include "externals.h" +#include "registers.h" +#include "debug.h" + +//////////////////////////////////////////////////////////////////////// +// READ DMA (many values) +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2readDMA4Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("READDMA4 %X - %X\r\n",spuAddr2[0],iSize); + + if(spuAddr2[0]<=0x1fff) + logprintf("# OUTPUT AREA ACCESS #############\r\n"); + } + +#endif + + for(i=0;i0xfffff) spuAddr2[0]=0; // wrap + } + + spuAddr2[0]+=0x20; //????? + + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + regArea[(PS2_C0_ADMAS)>>1]=0; // Auto DMA complete + spuStat2[0]=0x80; // DMA complete +} + +EXPORT_GCC void CALLBACK SPU2readDMA7Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("READDMA7 %X - %X\r\n",spuAddr2[1],iSize); + + if(spuAddr2[1]<=0x1fff) + logprintf("# OUTPUT AREA ACCESS #############\r\n"); + } +#endif + + for(i=0;i0xfffff) spuAddr2[1]=0; // wrap + } + + spuAddr2[1]+=0x20; //????? + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + regArea[(PS2_C1_ADMAS)>>1]=0; // Auto DMA complete + spuStat2[1]=0x80; // DMA complete +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +// to investigate: do sound data updates by writedma affect spu +// irqs? Will an irq be triggered, if new data is written to +// the memory irq address? + +//////////////////////////////////////////////////////////////////////// +// WRITE DMA (many values) +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2writeDMA4Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("WRITEDMA4 %X - %X\r\n",spuAddr2[0],iSize); + + if(spuAddr2[0]>=0x2000 && spuAddr2[0]<=0x27ff) + logprintf("# RAW INPUT ###############\r\n"); + } +#endif + + for(i=0;i0xfffff) spuAddr2[0]=0; // wrap + } + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + spuStat2[0]=0x80; // DMA complete +} + +EXPORT_GCC void CALLBACK SPU2writeDMA7Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("WRITEDMA7 %X - %X\r\n",spuAddr2[1],iSize); + if(spuAddr2[1]>=0x2000 && spuAddr2[1]<=0x27ff) + logprintf("# RAW INPUT ###############\r\n"); + } +#endif + + for(i=0;i0xfffff) spuAddr2[1]=0; // wrap + } + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + spuStat2[1]=0x80; // DMA complete +} + +//////////////////////////////////////////////////////////////////////// +// INTERRUPTS +//////////////////////////////////////////////////////////////////////// + +void InterruptDMA4(void) +{ +// taken from linuzappz NULL spu2 +// spu2Rs16(CORE0_ATTR)&= ~0x30; +// spu2Rs16(REG__1B0) = 0; +// spu2Rs16(SPU2_STATX_WRDY_M)|= 0x80; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("IRQDMA4\r\n"); +#endif + + spuCtrl2[0]&=~0x30; + regArea[(PS2_C0_ADMAS)>>1]=0; + spuStat2[0]|=0x80; +} + +EXPORT_GCC void CALLBACK SPU2interruptDMA4(void) +{ + InterruptDMA4(); +} + +void InterruptDMA7(void) +{ +// taken from linuzappz NULL spu2 +// spu2Rs16(CORE1_ATTR)&= ~0x30; +// spu2Rs16(REG__5B0) = 0; +// spu2Rs16(SPU2_STATX_DREQ)|= 0x80; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("IRQDMA7\r\n"); +#endif + + spuCtrl2[1]&=~0x30; + regArea[(PS2_C1_ADMAS)>>1]=0; + spuStat2[1]|=0x80; +} + +EXPORT_GCC void CALLBACK SPU2interruptDMA7(void) +{ + InterruptDMA7(); +} + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/dma.h --- a/Plugins/Input/sexypsf/spu/dma.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/dma.h Sun Mar 19 12:51:03 2006 -0800 @@ -24,8 +24,6 @@ // //*************************************************************************// - -u16 CALLBACK SPUreadDMA(void); -void CALLBACK SPUreadDMAMem(u16 * pusPSXMem,int iSize); -void CALLBACK SPUwriteDMA(u16 val); -void CALLBACK SPUwriteDMAMem(u16 * pusPSXMem,int iSize); +void InterruptDMA4(void); +void InterruptDMA7(void); + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/dsound.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/dsound.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,280 @@ +/*************************************************************************** + dsound.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: +// +// 2005/08/29 - Pete +// - changed to 48Khz output +// +// 2003/01/12 - Pete +// - added recording funcs +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_DSOUND + +#include "externals.h" + +#ifdef _WINDOWS +#define _LPCWAVEFORMATEX_DEFINED +#include + +#include "record.h" + +//////////////////////////////////////////////////////////////////////// +// dsound globals +//////////////////////////////////////////////////////////////////////// + +LPDIRECTSOUND lpDS; +LPDIRECTSOUNDBUFFER lpDSBP = NULL; +LPDIRECTSOUNDBUFFER lpDSB = NULL; +DSBUFFERDESC dsbd; +DSBUFFERDESC dsbdesc; +DSCAPS dscaps; +DSBCAPS dsbcaps; + +unsigned long LastWrite=0xffffffff; +unsigned long LastPlay=0; + +//////////////////////////////////////////////////////////////////////// +// SETUP SOUND +//////////////////////////////////////////////////////////////////////// + +void SetupSound(void) +{ + HRESULT dsval;WAVEFORMATEX pcmwf; + + dsval = DirectSoundCreate(NULL,&lpDS,NULL); + if(dsval!=DS_OK) + { + MessageBox(hWMain,"DirectSoundCreate!","Error",MB_OK); + return; + } + + if(DS_OK!=IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_PRIORITY)) + { + if(DS_OK!=IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_NORMAL)) + { + MessageBox(hWMain,"SetCooperativeLevel!","Error",MB_OK); + return; + } + } + + memset(&dsbd,0,sizeof(DSBUFFERDESC)); + dsbd.dwSize = 20; // NT4 hack! sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + dsval=IDirectSound_CreateSoundBuffer(lpDS,&dsbd,&lpDSBP,NULL); + if(dsval!=DS_OK) + { + MessageBox(hWMain, "CreateSoundBuffer (Primary)", "Error",MB_OK); + return; + } + + memset(&pcmwf, 0, sizeof(WAVEFORMATEX)); + pcmwf.wFormatTag = WAVE_FORMAT_PCM; + + if(iDisStereo) {pcmwf.nChannels = 1; pcmwf.nBlockAlign = 2;} + else {pcmwf.nChannels = 2; pcmwf.nBlockAlign = 4;} + + pcmwf.nSamplesPerSec = 48000; + + pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; + pcmwf.wBitsPerSample = 16; + + dsval=IDirectSoundBuffer_SetFormat(lpDSBP,&pcmwf); + if(dsval!=DS_OK) + { + MessageBox(hWMain, "SetFormat!", "Error",MB_OK); + return; + } + + dscaps.dwSize = sizeof(DSCAPS); + dsbcaps.dwSize = sizeof(DSBCAPS); + IDirectSound_GetCaps(lpDS,&dscaps); + IDirectSoundBuffer_GetCaps(lpDSBP,&dsbcaps); + + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = 20; // NT4 hack! sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + dsbdesc.dwBufferBytes = SOUNDSIZE; + dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + dsval=IDirectSound_CreateSoundBuffer(lpDS,&dsbdesc,&lpDSB,NULL); + if(dsval!=DS_OK) + { + MessageBox(hWMain,"CreateSoundBuffer (Secondary)", "Error",MB_OK); + return; + } + + dsval=IDirectSoundBuffer_Play(lpDSBP,0,0,DSBPLAY_LOOPING); + if(dsval!=DS_OK) + { + MessageBox(hWMain,"Play (Primary)","Error",MB_OK); + return; + } + + dsval=IDirectSoundBuffer_Play(lpDSB,0,0,DSBPLAY_LOOPING); + if(dsval!=DS_OK) + { + MessageBox(hWMain,"Play (Secondary)","Error",MB_OK); + return; + } +} + +//////////////////////////////////////////////////////////////////////// +// REMOVE SOUND +//////////////////////////////////////////////////////////////////////// + +void RemoveSound(void) +{ + int iRes; + + if(iDoRecord) RecordStop(); + + if(lpDSB!=NULL) + { + IDirectSoundBuffer_Stop(lpDSB); + iRes=IDirectSoundBuffer_Release(lpDSB); + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes=IDirectSoundBuffer_Release(lpDSB); + lpDSB=NULL; + } + + if(lpDSBP!=NULL) + { + IDirectSoundBuffer_Stop(lpDSBP); + iRes=IDirectSoundBuffer_Release(lpDSBP); + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes=IDirectSoundBuffer_Release(lpDSBP); + lpDSBP=NULL; + } + + if(lpDS!=NULL) + { + iRes=IDirectSound_Release(lpDS); + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes=IDirectSound_Release(lpDS); + lpDS=NULL; + } + +} + +//////////////////////////////////////////////////////////////////////// +// GET BYTES BUFFERED +//////////////////////////////////////////////////////////////////////// + +unsigned long SoundGetBytesBuffered(void) +{ + unsigned long cplay,cwrite; + + if(LastWrite==0xffffffff) return 0; + + IDirectSoundBuffer_GetCurrentPosition(lpDSB,&cplay,&cwrite); + + if(cplay>SOUNDSIZE) return SOUNDSIZE; + + if(cplay>2; + + lpSS=(unsigned long *)pSound; + while(dw) {*lpSD++=*lpSS++;dw--;} + + if(lpvPtr2) + { + lpSD=(unsigned long *)lpvPtr2; + dw=dwBytes2>>2; + while(dw) {*lpSD++=*lpSS++;dw--;} + } + + IDirectSoundBuffer_Unlock(lpDSB,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2); + + LastWrite+=lBytes; + if(LastWrite>=SOUNDSIZE) LastWrite-=SOUNDSIZE; + LastPlay=cplay; +} + +#endif + + + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/dsoundoss.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/dsoundoss.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,36 @@ +/*************************************************************************** + dsoundoss.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 SetupSound(void); +void RemoveSound(void); +unsigned long SoundGetBytesBuffered(void); +void SoundFeedStreamData(unsigned char* pSound,long lBytes); + +#ifndef _WINDOWS +unsigned long timeGetTime(); +#endif + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/externals.h --- a/Plugins/Input/sexypsf/spu/externals.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/externals.h Sun Mar 19 12:51:03 2006 -0800 @@ -1,177 +1,357 @@ -/*************************************************************************** - 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 "types.h" -#include "PsxMem.h" - -//*************************************************************************// -// History of changes: -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define min(a,b) (((a) < (b)) ? (a) : (b)) - -//////////////////////////////////////////////////////////////////////// -// spu defines -//////////////////////////////////////////////////////////////////////// - -// num of channels -#define MAXCHAN 24 - -/////////////////////////////////////////////////////////// -// struct defines -/////////////////////////////////////////////////////////// - -// ADSR INFOS PER CHANNEL -typedef struct -{ - int AttackModeExp; - s32 AttackTime; - s32 DecayTime; - s32 SustainLevel; - int SustainModeExp; - s32 SustainModeDec; - s32 SustainTime; - int ReleaseModeExp; - u32 ReleaseVal; - s32 ReleaseTime; - s32 ReleaseStartTime; - s32 ReleaseVol; - s32 lTime; - s32 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; - s32 lVolume; - s32 lDummy1; - s32 lDummy2; -} ADSRInfoEx; - -/////////////////////////////////////////////////////////// - -// Tmp Flags - -// used for debug channel muting -#define FLAG_MUTE 1 - -/////////////////////////////////////////////////////////// - -// MAIN CHANNEL STRUCT -typedef struct -{ - int bNew; // start flag - - int iSBPos; // mixing stuff - int spos; - int sinc; - int SB[32+1]; - int sval; - - u8 * pStart; // start ptr into sound mem - u8 * pCurr; // current pos in sound mem - u8 * 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 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 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 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 Enabled; - 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; +/*************************************************************************** + 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: +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 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 76800 + +// 137 ms test buffer... if less than that is buffered, a new upload will happen +#define TESTSIZE 26304 + +// num of channels +#define MAXCHAN 48 +#define HLFCHAN 24 + +// ~ 1 ms of data +#define NSSIZE 48 + +/////////////////////////////////////////////////////////// +// 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 iStartAdr; + int iLoopAdr; + int iNextAdr; + + int bOn; // is channel active (sample playing?) + int bStop; // is channel stopped (sample _can_ still be playing, ADSR Release phase) + int bEndPoint; // end point reached + int bReverbL; // can we do reverb on this channel? must have ctrl register bit, to get active + int bReverbR; + + int bVolumeL; // Volume on/off + int bVolumeR; + + 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 iMute; // mute mode + 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 bNoise; // noise active flag + int bFMod; // freq mod (0=off, 1=sound channel, 2=freq channel) + 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 EndAddr; // reverb area end addr in samples + int CurrAddr; // reverb area curr addr in samples + + int VolLeft; + int VolRight; + int iLastRVBLeft; + int iLastRVBRight; + int iRVBLeft; + int iRVBRight; + int iCnt; + + 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 regArea[]; +extern unsigned short spuMem[]; +extern unsigned char * spuMemC; +extern unsigned char * pSpuIrq[]; +extern unsigned char * pSpuBuffer; + +// user settings + +extern int iUseXA; +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; +// MISC + +extern SPUCHAN s_chan[]; +extern REVERBInfo rvb[]; + +extern unsigned long dwNoiseVal; +extern unsigned short spuCtrl2[]; +extern unsigned short spuStat2[]; +extern unsigned long spuIrq2[]; +extern unsigned long spuAddr2[]; +extern unsigned long spuRvbAddr2[]; +extern unsigned long spuRvbAEnd2[]; + +extern int bEndThread; +extern int bThreadEnded; +extern int bSpuInit; + +extern int SSumR[]; +extern int SSumL[]; +extern int iCycle; +extern short * pS; +extern unsigned long dwNewChannel2[]; +extern unsigned long dwEndChannel2[]; + +extern int iSpuAsyncWait; + +#ifdef _WINDOWS +extern HWND hWMain; // window handle +extern HWND hWDebug; +#endif + +extern void (CALLBACK *cddavCallback)(unsigned short,unsigned short); + +#endif + +/////////////////////////////////////////////////////////// +// CFG.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_CFG + +#ifndef _WINDOWS +extern char * pConfigFile; +#endif + +#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 unsigned long * XAFeed; +extern unsigned long * XAPlay; +extern unsigned long * XAStart; +extern unsigned long * XAEnd; + +extern unsigned long XARepeat; +extern unsigned long XALastVal; + +extern int iLeftXAVol; +extern int iRightXAVol; + +#endif + +/////////////////////////////////////////////////////////// +// REVERB.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_REVERB + +extern int * sRVBPlay[]; +extern int * sRVBEnd[]; +extern int * sRVBStart[]; + +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/freeze.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/freeze.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,260 @@ +/*************************************************************************** + freeze.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/12/24 - Pete +// - freeze functions adapted to pcsx2-0.7 +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2003/03/20 - Pete +// - fix to prevent the new interpolations from crashing when loading a save state +// +// 2003/01/06 - Pete +// - small changes for version 1.3 adsr save state loading +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_FREEZE + +#include "externals.h" +#include "registers.h" +#include "spu.h" +#include "regs.h" +#include "debug.h" +#include "resource.h" + +//////////////////////////////////////////////////////////////////////// +// freeze structs +//////////////////////////////////////////////////////////////////////// + +typedef struct { + int size; + char *data; +} SPUFreeze_t; + +typedef struct +{ + char szSPUName[8]; + unsigned long ulFreezeVersion; + unsigned long ulFreezeSize; + unsigned char cSPUPort[64*1024]; + unsigned char cSPURam[2*1024*1024]; + xa_decode_t xaS; +} SPUFreeze_Ex_t; + +typedef struct +{ + unsigned long spuIrq0; + unsigned long spuIrq1; + unsigned long pSpuIrq0; + unsigned long pSpuIrq1; + unsigned long dummy0; + unsigned long dummy1; + unsigned long dummy2; + unsigned long dummy3; + + SPUCHAN s_chan[MAXCHAN]; + +} SPUOSSFreeze_t; + +//////////////////////////////////////////////////////////////////////// + +void LoadStateV1(SPUFreeze_Ex_t * pF); // newest version +void LoadStateUnknown(SPUFreeze_Ex_t * pF); // unknown format + +//////////////////////////////////////////////////////////////////////// +// SPUFREEZE: called by main emu on savestate load/save +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC long CALLBACK SPU2freeze(unsigned long ulFreezeMode,SPUFreeze_t * pFt) +{ + int i;SPUOSSFreeze_t * pFO;SPUFreeze_Ex_t * pF; + + if(!pFt) return 0; // first check + + if(ulFreezeMode) // save? + {//--------------------------------------------------// + pFt->size=sizeof(SPUFreeze_Ex_t)+sizeof(SPUOSSFreeze_t); + + if(ulFreezeMode==2) return 0; // emu just asking for size? bye + + if(!pFt->data) return 0; + + pF=(SPUFreeze_Ex_t *)pFt->data; + + memset(pF,0,pFt->size); + + strcpy(pF->szSPUName,"PBOSS2"); + pF->ulFreezeVersion=1; + pF->ulFreezeSize=pFt->size; + // save mode: + RemoveTimer(); // stop timer + + memcpy(pF->cSPURam,spuMem,2*1024*1024); // copy common infos + memcpy(pF->cSPUPort,regArea,64*1024); + + if(xapGlobal && XAPlay!=XAFeed) // some xa + { + pF->xaS=*xapGlobal; + } + else + memset(&pF->xaS,0,sizeof(xa_decode_t)); // or clean xa + + pFO=(SPUOSSFreeze_t *)(pF+1); // store special stuff + + pFO->spuIrq0=spuIrq2[0]; + if(pSpuIrq[0]) pFO->pSpuIrq0 = (unsigned long)pSpuIrq[0]-(unsigned long)spuMemC; + pFO->spuIrq1=spuIrq2[1]; + if(pSpuIrq[1]) pFO->pSpuIrq1 = (unsigned long)pSpuIrq[1]-(unsigned long)spuMemC; + + for(i=0;is_chan[i],(void *)&s_chan[i],sizeof(SPUCHAN)); + if(pFO->s_chan[i].pStart) + pFO->s_chan[i].pStart-=(unsigned long)spuMemC; + if(pFO->s_chan[i].pCurr) + pFO->s_chan[i].pCurr-=(unsigned long)spuMemC; + if(pFO->s_chan[i].pLoop) + pFO->s_chan[i].pLoop-=(unsigned long)spuMemC; + } + + SetupTimer(); // sound processing on again + + return 1; + //--------------------------------------------------// + } + + // load state: +#ifdef _WINDOWS + if(iDebugMode==1 && IsWindow(hWDebug)) // we have to disbale the debug window, if active + DestroyWindow(hWDebug); + hWDebug=0; + + if(IsBadReadPtr(pFt,sizeof(SPUFreeze_t))) // check bad emu stuff + return 0; +#endif + + if(pFt->size!=sizeof(SPUFreeze_Ex_t)+ // not our stuff? bye + sizeof(SPUOSSFreeze_t)) return 0; + if(!pFt->data) return 0; + + pF=(SPUFreeze_Ex_t *)pFt->data; + + RemoveTimer(); // we stop processing while doing the save! + + memcpy(spuMem,pF->cSPURam,2*1024*1024); // get ram + memcpy(regArea,pF->cSPUPort,64*1024); + + if(pF->xaS.nsamples<=4032) // start xa again + SPU2playADPCMchannel(&pF->xaS); + + xapGlobal=0; + + if(!strcmp(pF->szSPUName,"PBOSS2") && + pF->ulFreezeVersion==1) + LoadStateV1(pF); + else LoadStateUnknown(pF); + + // repair some globals + for(i=0xFFFE;i>=0x0000;i-=2) + { + SPU2write(i,regArea[i>>1]); + } + + // fix to prevent new interpolations from crashing + for(i=0;ispuIrq0; + if(pFO->pSpuIrq0) pSpuIrq[0] = pFO->pSpuIrq0+spuMemC; else pSpuIrq[0]=0; + spuIrq2[1] = pFO->spuIrq1; + if(pFO->pSpuIrq1) pSpuIrq[1] = pFO->pSpuIrq1+spuMemC; else pSpuIrq[1]=0; + + for(i=0;is_chan[i],sizeof(SPUCHAN)); + + s_chan[i].pStart+=(unsigned long)spuMemC; + s_chan[i].pCurr+=(unsigned long)spuMemC; + s_chan[i].pLoop+=(unsigned long)spuMemC; + s_chan[i].iMute=0; + s_chan[i].iIrqDone=0; + } +} + +//////////////////////////////////////////////////////////////////////// + +void LoadStateUnknown(SPUFreeze_Ex_t * pF) +{ + int i; + + for(i=0;i>1)) // can we write in at least the half of fragments? + l=SOUNDSIZE; // -> no? wait + else l=0; // -> else go on + } + + return l; +} + +//////////////////////////////////////////////////////////////////////// +// FEED SOUND DATA +//////////////////////////////////////////////////////////////////////// + +void SoundFeedStreamData(unsigned char* pSound,long lBytes) +{ + if(oss_audio_fd == -1) return; + write(oss_audio_fd,pSound,lBytes); +} + +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/oss.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/oss.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,35 @@ +/*************************************************************************** + oss_sound.h - description + ------------------- + begin : Wed Dec 8 1999 + copyright : (C) 1999 by Marcin "Duddie" Dudar + email : duddie@psemu.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + + +#ifndef _OSS_SOUND_H +#define _OSS_SOUND_H + +#ifdef OSS_MEM_DEF +#define OSS_MEM_EXTERN +#else +#define OSS_MEM_EXTERN extern +#endif + +OSS_MEM_EXTERN int sound_buffer_size; + +#define OSS_MODE_STEREO 1 +#define OSS_MODE_MONO 0 + +#define OSS_SPEED_44100 44100 + +#endif // _OSS_SOUND_H diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/psemu.c diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/psemuxa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/psemuxa.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,28 @@ +//============================================ +//=== Audio XA decoding +//=== Kazzuya +//============================================ + +#ifndef DECODEXA_H +#define DECODEXA_H + +typedef struct +{ + long y0, y1; +} ADPCM_Decode_t; + +typedef struct +{ + int freq; + int nbits; + int stereo; + int nsamples; + ADPCM_Decode_t left, right; + short pcm[16384]; +} xa_decode_t; + +long xa_decode_sector( xa_decode_t *xdp, + unsigned char *sectorp, + int is_first_sector ); + +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/record.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/record.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,191 @@ +/*************************************************************************** + spu.c - description + ------------------- + begin : Sun Jan 12 2003 + copyright : (C) 2003 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: +// +// 2005/08/29 - Pete +// - changed to 48Khz output +// +// 2003/03/01 - Pete +// - added mono mode +// +// 2003/01/12 - Pete +// - added recording funcs (win version only) +// +//*************************************************************************// + +#include "stdafx.h" + +#ifdef _WINDOWS + +#include +#include "resource.h" +#include "externals.h" + +#define _IN_RECORD + +#include "record.h" + +//////////////////////////////////////////////////////////////////////// + +int iDoRecord=0; +HMMIO hWaveFile=NULL; +MMCKINFO mmckMain; +MMCKINFO mmckData; +char szFileName[256]; + +//////////////////////////////////////////////////////////////////////// + +void RecordStart() +{ + WAVEFORMATEX pcmwf; + + // setup header in the same format as our directsound stream + memset(&pcmwf,0,sizeof(WAVEFORMATEX)); + pcmwf.wFormatTag = WAVE_FORMAT_PCM; + + if(iDisStereo) + { + pcmwf.nChannels = 1; + pcmwf.nBlockAlign = 2; + } + else + { + pcmwf.nChannels = 2; + pcmwf.nBlockAlign = 4; + } + + pcmwf.nSamplesPerSec = 48000; + pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; + pcmwf.wBitsPerSample = 16; + + // create file + hWaveFile=mmioOpen(szFileName,NULL,MMIO_CREATE|MMIO_WRITE|MMIO_EXCLUSIVE | MMIO_ALLOCBUF); + if(!hWaveFile) return; + + // setup WAVE, fmt and data chunks + memset(&mmckMain,0,sizeof(MMCKINFO)); + mmckMain.fccType = mmioFOURCC('W','A','V','E'); + + mmioCreateChunk(hWaveFile,&mmckMain,MMIO_CREATERIFF); + + memset(&mmckData,0,sizeof(MMCKINFO)); + mmckData.ckid = mmioFOURCC('f','m','t',' '); + mmckData.cksize = sizeof(WAVEFORMATEX); + + mmioCreateChunk(hWaveFile,&mmckData,0); + mmioWrite(hWaveFile,(char*)&pcmwf,sizeof(WAVEFORMATEX)); + mmioAscend(hWaveFile,&mmckData,0); + + mmckData.ckid = mmioFOURCC('d','a','t','a'); + mmioCreateChunk(hWaveFile,&mmckData,0); +} + +//////////////////////////////////////////////////////////////////////// + +void RecordStop() +{ + // first some check, if recording is running + iDoRecord=0; + if(!hWaveFile) return; + + // now finish writing & close the wave file + mmioAscend(hWaveFile,&mmckData,0); + mmioAscend(hWaveFile,&mmckMain,0); + mmioClose(hWaveFile,0); + + // init var + hWaveFile=NULL; +} + +//////////////////////////////////////////////////////////////////////// + +void RecordBuffer(unsigned char* pSound,long lBytes) +{ + // write the samples + if(hWaveFile) mmioWrite(hWaveFile,pSound,lBytes); +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK RecordDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + //--------------------------------------------------// init + case WM_INITDIALOG: + { + SetDlgItemText(hW,IDC_WAVFILE,"C:\\PEOPS.WAV"); // init filename edit + ShowCursor(TRUE); // mmm... who is hiding it? main emu? tsts + return TRUE; + } + //--------------------------------------------------// destroy + case WM_DESTROY: + { + RecordStop(); + }break; + //--------------------------------------------------// command + case WM_COMMAND: + { + if(wParam==IDCANCEL) iRecordMode=2; // cancel? raise flag for destroying the dialog + + if(wParam==IDC_RECORD) // record start/stop? + { + if(IsWindowEnabled(GetDlgItem(hW,IDC_WAVFILE))) // not started yet (edit is not disabled): + { + GetDlgItemText(hW,IDC_WAVFILE,szFileName,255);// get filename + + RecordStart(); // start recording + + if(hWaveFile) // start was ok? + { // -> disable filename edit, change text, raise flag + EnableWindow(GetDlgItem(hW,IDC_WAVFILE),FALSE); + SetDlgItemText(hW,IDC_RECORD,"Stop recording"); + iDoRecord=1; + } + else MessageBeep(0xFFFFFFFF); // error starting recording? BEEP + } + else // stop recording? + { + RecordStop(); // -> just do it + EnableWindow(GetDlgItem(hW,IDC_WAVFILE),TRUE);// -> enable filename edit again + SetDlgItemText(hW,IDC_RECORD,"Start recording"); + } + SetFocus(hWMain); + } + }break; + //--------------------------------------------------// size + case WM_SIZE: + if(wParam==SIZE_MINIMIZED) SetFocus(hWMain); // if we get minimized, set the foxus to the main window + break; + //--------------------------------------------------// setcursor + case WM_SETCURSOR: + { + SetCursor(LoadCursor(NULL,IDC_ARROW)); // force the arrow + return TRUE; + } + //--------------------------------------------------// + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////// +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/record.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/record.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,12 @@ +#ifndef _RECORD_H_ +#define _RECORD_H_ + +#ifdef _WINDOWS +void RecordStart(); +void RecordBuffer(unsigned char* pSound,long lBytes); +void RecordStop(); +BOOL CALLBACK RecordDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +#endif + +#endif + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/registers.c --- a/Plugins/Input/sexypsf/spu/registers.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/registers.c Sun Mar 19 12:51:03 2006 -0800 @@ -1,490 +1,1130 @@ -/*************************************************************************** - registers.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. * - * * - ***************************************************************************/ - -/* ChangeLog - - February 8, 2004 - xodnizel - - Fixed setting of reverb volume. Just typecast val("u16") to s16. - Also adjusted the normal channel volume to be one less than what it was before when the - "phase invert" bit is set. I'm assuming it's just in two's complement. - - 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 - -//#include "externals.h" -//#include "registers.h" -//#include "regs.h" - -//////////////////////////////////////////////////////////////////////// -// WRITE REGISTERS: called by main emu -//////////////////////////////////////////////////////////////////////// - -void SPUwriteRegister(u32 reg, u16 val) -{ - const u32 r=reg&0xfff; - regArea[(r-0xc00)>>1] = val; - - if(r>=0x0c00 && r<0x0d80) // some channel info? - { - int ch=(r>>4)-0xc0; // calc channel - - //if(ch==20) printf("%08x: %04x\n",reg,val); - - switch(r&0x0f) - { - //------------------------------------------------// r volume - case 0: - SetVolumeLR(0,(u8)ch,val); - break; - //------------------------------------------------// l volume - case 2: - SetVolumeLR(1,(u8)ch,val); - break; - //------------------------------------------------// pitch - case 4: - SetPitch(ch,val); - break; - //------------------------------------------------// start - case 6: - s_chan[ch].pStart=spuMemC+((u32) val<<3); - break; - //------------------------------------------------// level with pre-calcs - case 8: - { - const u32 lval=val; // DEBUG CHECK - //---------------------------------------------// - 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; - //---------------------------------------------// - } - break; - //------------------------------------------------// adsr times with pre-calcs - case 10: - { - const u32 lval=val; // DEBUG CHECK - - //----------------------------------------------// - 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; - //----------------------------------------------// - } - break; - //------------------------------------------------// adsr volume... mmm have to investigate this - //case 0xC: - // break; - //------------------------------------------------// - case 0xE: // loop? - s_chan[ch].pLoop=spuMemC+((u32) val<<3); - s_chan[ch].bIgnoreLoop=1; - break; - //------------------------------------------------// - } - return; - } - - switch(r) - { - //-------------------------------------------------// - case H_SPUaddr: - spuAddr = (u32) val<<3; - break; - //-------------------------------------------------// - case H_SPUdata: - spuMem[spuAddr>>1] = BFLIP16(val); - spuAddr+=2; - if(spuAddr>0x7ffff) spuAddr=0; - break; - //-------------------------------------------------// - case H_SPUctrl: - spuCtrl=val; - break; - //-------------------------------------------------// - case H_SPUstat: - spuStat=val & 0xf800; - break; - //-------------------------------------------------// - case H_SPUReverbAddr: - if(val==0xFFFF || val<=0x200) - {rvb.StartAddr=rvb.CurrAddr=0;} - else - { - const s32 iv=(u32)val<<2; - if(rvb.StartAddr!=iv) - { - rvb.StartAddr=(u32)val<<2; - rvb.CurrAddr=rvb.StartAddr; - } - } - break; - //-------------------------------------------------// - case H_SPUirqAddr: - spuIrq = val; - pSpuIrq=spuMemC+((u32) val<<3); - break; - //-------------------------------------------------// - /* Volume settings appear to be at least 15-bit unsigned in this case. - Definitely NOT 15-bit signed. Probably 16-bit signed, so s16 type cast. - Check out "Chrono Cross: Shadow's End Forest" - */ - case H_SPUrvolL: - rvb.VolLeft=(s16)val; - //printf("%d\n",val); - break; - //-------------------------------------------------// - case H_SPUrvolR: - rvb.VolRight=(s16)val; - //printf("%d\n",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: - //printf("M0 %04x\n",val); - break; - //-------------------------------------------------// - case H_SPUMute2: - // printf("M1 %04x\n",val); - break; -*/ - //-------------------------------------------------// - case H_SPUon1: - SoundOn(0,16,val); - break; - //-------------------------------------------------// - case H_SPUon2: - // printf("Boop: %08x: %04x\n",reg,val); - SoundOn(16,24,val); - break; - //-------------------------------------------------// - case H_SPUoff1: - SoundOff(0,16,val); - break; - //-------------------------------------------------// - case H_SPUoff2: - SoundOff(16,24,val); - // printf("Boop: %08x: %04x\n",reg,val); - break; - //-------------------------------------------------// - case H_FMod1: - FModOn(0,16,val); - break; - //-------------------------------------------------// - case H_FMod2: - FModOn(16,24,val); - break; - //-------------------------------------------------// - case H_Noise1: - NoiseOn(0,16,val); - break; - //-------------------------------------------------// - case H_Noise2: - NoiseOn(16,24,val); - break; - //-------------------------------------------------// - case H_RVBon1: - rvb.Enabled&=~0xFFFF; - rvb.Enabled|=val; - break; - - //-------------------------------------------------// - case H_RVBon2: - rvb.Enabled&=0xFFFF; - rvb.Enabled|=val<<16; - break; - - //-------------------------------------------------// - case H_Reverb+0: - rvb.FB_SRC_A=val; - break; - - case H_Reverb+2 : rvb.FB_SRC_B=(s16)val; break; - case H_Reverb+4 : rvb.IIR_ALPHA=(s16)val; break; - case H_Reverb+6 : rvb.ACC_COEF_A=(s16)val; break; - case H_Reverb+8 : rvb.ACC_COEF_B=(s16)val; break; - case H_Reverb+10 : rvb.ACC_COEF_C=(s16)val; break; - case H_Reverb+12 : rvb.ACC_COEF_D=(s16)val; break; - case H_Reverb+14 : rvb.IIR_COEF=(s16)val; break; - case H_Reverb+16 : rvb.FB_ALPHA=(s16)val; break; - case H_Reverb+18 : rvb.FB_X=(s16)val; break; - case H_Reverb+20 : rvb.IIR_DEST_A0=(s16)val; break; - case H_Reverb+22 : rvb.IIR_DEST_A1=(s16)val; break; - case H_Reverb+24 : rvb.ACC_SRC_A0=(s16)val; break; - case H_Reverb+26 : rvb.ACC_SRC_A1=(s16)val; break; - case H_Reverb+28 : rvb.ACC_SRC_B0=(s16)val; break; - case H_Reverb+30 : rvb.ACC_SRC_B1=(s16)val; break; - case H_Reverb+32 : rvb.IIR_SRC_A0=(s16)val; break; - case H_Reverb+34 : rvb.IIR_SRC_A1=(s16)val; break; - case H_Reverb+36 : rvb.IIR_DEST_B0=(s16)val; break; - case H_Reverb+38 : rvb.IIR_DEST_B1=(s16)val; break; - case H_Reverb+40 : rvb.ACC_SRC_C0=(s16)val; break; - case H_Reverb+42 : rvb.ACC_SRC_C1=(s16)val; break; - case H_Reverb+44 : rvb.ACC_SRC_D0=(s16)val; break; - case H_Reverb+46 : rvb.ACC_SRC_D1=(s16)val; break; - case H_Reverb+48 : rvb.IIR_SRC_B1=(s16)val; break; - case H_Reverb+50 : rvb.IIR_SRC_B0=(s16)val; break; - case H_Reverb+52 : rvb.MIX_DEST_A0=(s16)val; break; - case H_Reverb+54 : rvb.MIX_DEST_A1=(s16)val; break; - case H_Reverb+56 : rvb.MIX_DEST_B0=(s16)val; break; - case H_Reverb+58 : rvb.MIX_DEST_B1=(s16)val; break; - case H_Reverb+60 : rvb.IN_COEF_L=(s16)val; break; - case H_Reverb+62 : rvb.IN_COEF_R=(s16)val; break; - } - -} - -//////////////////////////////////////////////////////////////////////// -// READ REGISTER: called by main emu -//////////////////////////////////////////////////////////////////////// - -u16 SPUreadRegister(u32 reg) -{ - const u32 r=reg&0xfff; - - if(r>=0x0c00 && r<0x0d80) - { - switch(r&0x0f) - { - case 0xC: // get adsr vol - { - const int ch=(r>>4)-0xc0; - if(s_chan[ch].bNew) return 1; // we are started, but not processed? return 1 - if(s_chan[ch].ADSRX.lVolume && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well - !s_chan[ch].ADSRX.EnvelopeVol) - return 1; - return (u16)(s_chan[ch].ADSRX.EnvelopeVol>>16); - } - - case 0xE: // get loop address - { - const int ch=(r>>4)-0xc0; - if(s_chan[ch].pLoop==NULL) return 0; - return (u16)((s_chan[ch].pLoop-spuMemC)>>3); - } - } - } - - switch(r) - { - case H_SPUctrl: - return spuCtrl; - - case H_SPUstat: - return spuStat; - - case H_SPUaddr: - return (u16)(spuAddr>>3); - - case H_SPUdata: - { - u16 s=BFLIP16(spuMem[spuAddr>>1]); - spuAddr+=2; - if(spuAddr>0x7ffff) spuAddr=0; - return s; - } - - case H_SPUirqAddr: - return spuIrq; - - //case H_SPUIsOn1: - // return IsSoundOn(0,16); - - //case H_SPUIsOn2: - // return IsSoundOn(16,24); - - } - - return regArea[(r-0xc00)>>1]; -} - -//////////////////////////////////////////////////////////////////////// -// SOUND ON register write -//////////////////////////////////////////////////////////////////////// - -static void SoundOn(int start,int end,u16 val) // SOUND ON PSX COMAND -{ - int ch; - - for(ch=start;ch>=1) // loop channels - { - if((val&1) && s_chan[ch].pStart) // mmm... start has to be set before key on !?! - { - s_chan[ch].bIgnoreLoop=0; - s_chan[ch].bNew=1; - } - } -} - -//////////////////////////////////////////////////////////////////////// -// SOUND OFF register write -//////////////////////////////////////////////////////////////////////// - -static void SoundOff(int start,int end,u16 val) // SOUND OFF PSX COMMAND -{ - int ch; - for(ch=start;ch>=1) // loop channels - { - if(val&1) // && s_chan[i].bOn) mmm... - { - s_chan[ch].bStop=1; - } - } -} - -//////////////////////////////////////////////////////////////////////// -// FMOD register write -//////////////////////////////////////////////////////////////////////// - -static void FModOn(int start,int end,u16 val) // FMOD ON PSX COMMAND -{ - int ch; - - for(ch=start;ch>=1) // loop channels - { - if(val&1) // -> fmod on/off - { - if(ch>0) - { - s_chan[ch].bFMod=1; // --> sound channel - s_chan[ch-1].bFMod=2; // --> freq channel - } - } - else - { - s_chan[ch].bFMod=0; // --> turn off fmod - } - } -} - -//////////////////////////////////////////////////////////////////////// -// NOISE register write -//////////////////////////////////////////////////////////////////////// - -static void NoiseOn(int start,int end,u16 val) // NOISE ON PSX COMMAND -{ - int ch; - - for(ch=start;ch>=1) // loop channels - { - if(val&1) // -> noise on/off - { - s_chan[ch].bNoise=1; - } - else - { - s_chan[ch].bNoise=0; - } - } -} - -//////////////////////////////////////////////////////////////////////// -// LEFT VOLUME register write -//////////////////////////////////////////////////////////////////////// - -// please note: sweep is wrong. - -static void SetVolumeLR(int right, u8 ch,s16 vol) // LEFT VOLUME -{ - //if(vol&0xc000) - //printf("%d %08x\n",right,vol); - if(right) - s_chan[ch].iRightVolRaw=vol; - else - s_chan[ch].iLeftVolRaw=vol; - - if(vol&0x8000) // sweep? - { - s16 sInc=1; // -> sweep up? - if(vol&0x2000) sInc=-1; // -> or down? - if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this - vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64 - vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half! - vol*=128; - vol&=0x3fff; - //puts("Sweep"); - } - else // no sweep: - { - if(vol&0x4000) - vol=(vol&0x3FFF)-0x4000; - else - vol&=0x3FFF; - - //if(vol&0x4000) // -> mmm... phase inverted? have to investigate this - // vol=0-(0x3fff-(vol&0x3fff)); - //else - // vol&=0x3fff; - } - if(right) - s_chan[ch].iRightVolume=vol; - else - s_chan[ch].iLeftVolume=vol; // store volume -} - -//////////////////////////////////////////////////////////////////////// -// PITCH register write -//////////////////////////////////////////////////////////////////////// - -static void SetPitch(int ch,u16 val) // SET PITCH -{ - int NP; - if(val>0x3fff) NP=0x3fff; // get pitch val - else NP=val; - - s_chan[ch].iRawPitch=NP; - - NP=(44100L*NP)/4096L; // calc frequency - if(NP<1) NP=1; // some security - s_chan[ch].iActFreq=NP; // store frequency -} +/*************************************************************************** + registers.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/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 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 + +#include "externals.h" +#include "registers.h" +#include "regs.h" +#include "reverb.h" +#include "debug.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 + +//////////////////////////////////////////////////////////////////////// +// WRITE REGISTERS: called by main emu +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val) +{ + long r=reg&0xffff; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("W_REG %X - %X\r\n",reg&0xFFFF,val); +#endif + + regArea[r>>1] = val; + + if((r>=0x0000 && r<0x0180)||(r>=0x0400 && r<0x0580)) // some channel info? + { + int ch=(r>>4)&0x1f; + if(r>=0x400) ch+=24; + + switch(r&0x0f) + { + //------------------------------------------------// r volume + case 0: + SetVolumeL((unsigned char)ch,val); + break; + //------------------------------------------------// l volume + case 2: + SetVolumeR((unsigned char)ch,val); + break; + //------------------------------------------------// pitch + case 4: + SetPitch(ch,val); + break; + //------------------------------------------------// level with pre-calcs + case 6: + { + 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; + //---------------------------------------------// + 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 8: + { + 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; + //----------------------------------------------// + 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<=0x01c0 && r<0x02E0)||(r>=0x05c0 && r<0x06E0)) // some channel info? + { + int ch=0; + if(r>=0x400) {ch=24;r-=0x400;} + + ch+=(r-0x1c0)/12; + r-=(ch%24)*12; + switch(r) + { + //------------------------------------------------// + case 0x1C0: + s_chan[ch].iStartAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iStartAdr&0xFFFF); + s_chan[ch].pStart=spuMemC+(s_chan[ch].iStartAdr<<1); + break; + case 0x1C2: + s_chan[ch].iStartAdr=(s_chan[ch].iStartAdr & 0xF0000) | (val & 0xFFFF); + s_chan[ch].pStart=spuMemC+(s_chan[ch].iStartAdr<<1); + break; + //------------------------------------------------// + case 0x1C4: + s_chan[ch].iLoopAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iLoopAdr&0xFFFF); + s_chan[ch].pLoop=spuMemC+(s_chan[ch].iLoopAdr<<1); + s_chan[ch].bIgnoreLoop=1; + break; + case 0x1C6: + s_chan[ch].iLoopAdr=(s_chan[ch].iLoopAdr & 0xF0000) | (val & 0xFFFF); + s_chan[ch].pLoop=spuMemC+(s_chan[ch].iLoopAdr<<1); + s_chan[ch].bIgnoreLoop=1; + break; + //------------------------------------------------// + case 0x1C8: + // unused... check if it gets written as well + s_chan[ch].iNextAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iNextAdr&0xFFFF); + break; + case 0x1CA: + // unused... check if it gets written as well + s_chan[ch].iNextAdr=(s_chan[ch].iNextAdr & 0xF0000) | (val & 0xFFFF); + break; + //------------------------------------------------// + } + + iSpuAsyncWait=0; + + return; + } + + switch(r) + { + //-------------------------------------------------// + case PS2_C0_SPUaddr_Hi: + spuAddr2[0] = (((unsigned long)val&0xf)<<16)|(spuAddr2[0]&0xFFFF); + break; + //-------------------------------------------------// + case PS2_C0_SPUaddr_Lo: + spuAddr2[0] = (spuAddr2[0] & 0xF0000) | (val & 0xFFFF); + break; + //-------------------------------------------------// + case PS2_C1_SPUaddr_Hi: + spuAddr2[1] = (((unsigned long)val&0xf)<<16)|(spuAddr2[1]&0xFFFF); + break; + //-------------------------------------------------// + case PS2_C1_SPUaddr_Lo: + spuAddr2[1] = (spuAddr2[1] & 0xF0000) | (val & 0xFFFF); + break; + //-------------------------------------------------// + case PS2_C0_SPUdata: + spuMem[spuAddr2[0]] = val; + spuAddr2[0]++; + if(spuAddr2[0]>0xfffff) spuAddr2[0]=0; + break; + //-------------------------------------------------// + case PS2_C1_SPUdata: + spuMem[spuAddr2[1]] = val; + spuAddr2[1]++; + if(spuAddr2[1]>0xfffff) spuAddr2[1]=0; + break; + //-------------------------------------------------// + case PS2_C0_ATTR: + spuCtrl2[0]=val; + break; + //-------------------------------------------------// + case PS2_C1_ATTR: + spuCtrl2[1]=val; + break; + //-------------------------------------------------// + case PS2_C0_SPUstat: + spuStat2[0]=val; + break; + //-------------------------------------------------// + case PS2_C1_SPUstat: + spuStat2[1]=val; + break; + //-------------------------------------------------// + case PS2_C0_ReverbAddr_Hi: + spuRvbAddr2[0] = (((unsigned long)val&0xf)<<16)|(spuRvbAddr2[0]&0xFFFF); + SetReverbAddr(0); + break; + //-------------------------------------------------// + case PS2_C0_ReverbAddr_Lo: + spuRvbAddr2[0] = (spuRvbAddr2[0] & 0xF0000) | (val & 0xFFFF); + SetReverbAddr(0); + break; + //-------------------------------------------------// + case PS2_C0_ReverbAEnd_Hi: + spuRvbAEnd2[0] = (((unsigned long)val&0xf)<<16)|(/*spuRvbAEnd2[0]&*/0xFFFF); + rvb[0].EndAddr=spuRvbAEnd2[0]; + break; + //-------------------------------------------------// + case PS2_C1_ReverbAEnd_Hi: + spuRvbAEnd2[1] = (((unsigned long)val&0xf)<<16)|(/*spuRvbAEnd2[1]&*/0xFFFF); + rvb[1].EndAddr=spuRvbAEnd2[1]; + break; + //-------------------------------------------------// + case PS2_C1_ReverbAddr_Hi: + spuRvbAddr2[1] = (((unsigned long)val&0xf)<<16)|(spuRvbAddr2[1]&0xFFFF); + SetReverbAddr(1); + break; + //-------------------------------------------------// + case PS2_C1_ReverbAddr_Lo: + spuRvbAddr2[1] = (spuRvbAddr2[1] & 0xF0000) | (val & 0xFFFF); + SetReverbAddr(1); + break; + //-------------------------------------------------// + case PS2_C0_SPUirqAddr_Hi: + spuIrq2[0] = (((unsigned long)val&0xf)<<16)|(spuIrq2[0]&0xFFFF); + pSpuIrq[0]=spuMemC+(spuIrq2[0]<<1); + break; + //-------------------------------------------------// + case PS2_C0_SPUirqAddr_Lo: + spuIrq2[0] = (spuIrq2[0] & 0xF0000) | (val & 0xFFFF); + pSpuIrq[0]=spuMemC+(spuIrq2[0]<<1); + break; + //-------------------------------------------------// + case PS2_C1_SPUirqAddr_Hi: + spuIrq2[1] = (((unsigned long)val&0xf)<<16)|(spuIrq2[1]&0xFFFF); + pSpuIrq[1]=spuMemC+(spuIrq2[1]<<1); + break; + //-------------------------------------------------// + case PS2_C1_SPUirqAddr_Lo: + spuIrq2[1] = (spuIrq2[1] & 0xF0000) | (val & 0xFFFF); + pSpuIrq[1]=spuMemC+(spuIrq2[1]<<1); + break; + //-------------------------------------------------// + case PS2_C0_SPUrvolL: + rvb[0].VolLeft=val; + break; + //-------------------------------------------------// + case PS2_C0_SPUrvolR: + rvb[0].VolRight=val; + break; + //-------------------------------------------------// + case PS2_C1_SPUrvolL: + rvb[1].VolLeft=val; + break; + //-------------------------------------------------// + case PS2_C1_SPUrvolR: + rvb[1].VolRight=val; + break; + //-------------------------------------------------// + case PS2_C0_SPUon1: + SoundOn(0,16,val); + break; + //-------------------------------------------------// + case PS2_C0_SPUon2: + SoundOn(16,24,val); + break; + //-------------------------------------------------// + case PS2_C1_SPUon1: + SoundOn(24,40,val); + break; + //-------------------------------------------------// + case PS2_C1_SPUon2: + SoundOn(40,48,val); + break; + //-------------------------------------------------// + case PS2_C0_SPUoff1: + SoundOff(0,16,val); + break; + //-------------------------------------------------// + case PS2_C0_SPUoff2: + SoundOff(16,24,val); + break; + //-------------------------------------------------// + case PS2_C1_SPUoff1: + SoundOff(24,40,val); + break; + //-------------------------------------------------// + case PS2_C1_SPUoff2: + SoundOff(40,48,val); + break; + //-------------------------------------------------// + case PS2_C0_SPUend1: + case PS2_C0_SPUend2: + if(val) dwEndChannel2[0]=0; + break; + //-------------------------------------------------// + case PS2_C1_SPUend1: + case PS2_C1_SPUend2: + if(val) dwEndChannel2[1]=0; + break; + //-------------------------------------------------// + case PS2_C0_FMod1: + FModOn(0,16,val); + break; + //-------------------------------------------------// + case PS2_C0_FMod2: + FModOn(16,24,val); + break; + //-------------------------------------------------// + case PS2_C1_FMod1: + FModOn(24,40,val); + break; + //-------------------------------------------------// + case PS2_C1_FMod2: + FModOn(40,48,val); + break; + //-------------------------------------------------// + case PS2_C0_Noise1: + NoiseOn(0,16,val); + break; + //-------------------------------------------------// + case PS2_C0_Noise2: + NoiseOn(16,24,val); + break; + //-------------------------------------------------// + case PS2_C1_Noise1: + NoiseOn(24,40,val); + break; + //-------------------------------------------------// + case PS2_C1_Noise2: + NoiseOn(40,48,val); + break; + //-------------------------------------------------// + case PS2_C0_DryL1: + VolumeOn(0,16,val,0); + break; + //-------------------------------------------------// + case PS2_C0_DryL2: + VolumeOn(16,24,val,0); + break; + //-------------------------------------------------// + case PS2_C1_DryL1: + VolumeOn(24,40,val,0); + break; + //-------------------------------------------------// + case PS2_C1_DryL2: + VolumeOn(40,48,val,0); + break; + //-------------------------------------------------// + case PS2_C0_DryR1: + VolumeOn(0,16,val,1); + break; + //-------------------------------------------------// + case PS2_C0_DryR2: + VolumeOn(16,24,val,1); + break; + //-------------------------------------------------// + case PS2_C1_DryR1: + VolumeOn(24,40,val,1); + break; + //-------------------------------------------------// + case PS2_C1_DryR2: + VolumeOn(40,48,val,1); + break; + //-------------------------------------------------// + case PS2_C0_RVBon1_L: + ReverbOn(0,16,val,0); + break; + //-------------------------------------------------// + case PS2_C0_RVBon2_L: + ReverbOn(16,24,val,0); + break; + //-------------------------------------------------// + case PS2_C1_RVBon1_L: + ReverbOn(24,40,val,0); + break; + //-------------------------------------------------// + case PS2_C1_RVBon2_L: + ReverbOn(40,48,val,0); + break; + //-------------------------------------------------// + case PS2_C0_RVBon1_R: + ReverbOn(0,16,val,1); + break; + //-------------------------------------------------// + case PS2_C0_RVBon2_R: + ReverbOn(16,24,val,1); + break; + //-------------------------------------------------// + case PS2_C1_RVBon1_R: + ReverbOn(24,40,val,1); + break; + //-------------------------------------------------// + case PS2_C1_RVBon2_R: + ReverbOn(40,48,val,1); + break; + //-------------------------------------------------// + case PS2_C0_Reverb+0: + rvb[0].FB_SRC_A=(((unsigned long)val&0xf)<<16)|(rvb[0].FB_SRC_A&0xFFFF); + break; + case PS2_C0_Reverb+2: + rvb[0].FB_SRC_A=(rvb[0].FB_SRC_A & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+4: + rvb[0].FB_SRC_B=(((unsigned long)val&0xf)<<16)|(rvb[0].FB_SRC_B&0xFFFF); + break; + case PS2_C0_Reverb+6: + rvb[0].FB_SRC_B=(rvb[0].FB_SRC_B & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+8: + rvb[0].IIR_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_A0&0xFFFF); + break; + case PS2_C0_Reverb+10: + rvb[0].IIR_DEST_A0=(rvb[0].IIR_DEST_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+12: + rvb[0].IIR_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_A1&0xFFFF); + break; + case PS2_C0_Reverb+14: + rvb[0].IIR_DEST_A1=(rvb[0].IIR_DEST_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+16: + rvb[0].ACC_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_A0&0xFFFF); + break; + case PS2_C0_Reverb+18: + rvb[0].ACC_SRC_A0=(rvb[0].ACC_SRC_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+20: + rvb[0].ACC_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_A1&0xFFFF); + break; + case PS2_C0_Reverb+22: + rvb[0].ACC_SRC_A1=(rvb[0].ACC_SRC_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+24: + rvb[0].ACC_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_B0&0xFFFF); + break; + case PS2_C0_Reverb+26: + rvb[0].ACC_SRC_B0=(rvb[0].ACC_SRC_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+28: + rvb[0].ACC_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_B1&0xFFFF); + break; + case PS2_C0_Reverb+30: + rvb[0].ACC_SRC_B1=(rvb[0].ACC_SRC_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+32: + rvb[0].IIR_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_A0&0xFFFF); + break; + case PS2_C0_Reverb+34: + rvb[0].IIR_SRC_A0=(rvb[0].IIR_SRC_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+36: + rvb[0].IIR_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_A1&0xFFFF); + break; + case PS2_C0_Reverb+38: + rvb[0].IIR_SRC_A1=(rvb[0].IIR_SRC_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+40: + rvb[0].IIR_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_B0&0xFFFF); + break; + case PS2_C0_Reverb+42: + rvb[0].IIR_DEST_B0=(rvb[0].IIR_DEST_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+44: + rvb[0].IIR_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_B1&0xFFFF); + break; + case PS2_C0_Reverb+46: + rvb[0].IIR_DEST_B1=(rvb[0].IIR_DEST_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+48: + rvb[0].ACC_SRC_C0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_C0&0xFFFF); + break; + case PS2_C0_Reverb+50: + rvb[0].ACC_SRC_C0=(rvb[0].ACC_SRC_C0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+52: + rvb[0].ACC_SRC_C1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_C1&0xFFFF); + break; + case PS2_C0_Reverb+54: + rvb[0].ACC_SRC_C1=(rvb[0].ACC_SRC_C1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+56: + rvb[0].ACC_SRC_D0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_D0&0xFFFF); + break; + case PS2_C0_Reverb+58: + rvb[0].ACC_SRC_D0=(rvb[0].ACC_SRC_D0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+60: + rvb[0].ACC_SRC_D1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_D1&0xFFFF); + break; + case PS2_C0_Reverb+62: + rvb[0].ACC_SRC_D1=(rvb[0].ACC_SRC_D1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+64: + rvb[0].IIR_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_B1&0xFFFF); + break; + case PS2_C0_Reverb+66: + rvb[0].IIR_SRC_B1=(rvb[0].IIR_SRC_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+68: + rvb[0].IIR_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_B0&0xFFFF); + break; + case PS2_C0_Reverb+70: + rvb[0].IIR_SRC_B0=(rvb[0].IIR_SRC_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+72: + rvb[0].MIX_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_A0&0xFFFF); + break; + case PS2_C0_Reverb+74: + rvb[0].MIX_DEST_A0=(rvb[0].MIX_DEST_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+76: + rvb[0].MIX_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_A1&0xFFFF); + break; + case PS2_C0_Reverb+78: + rvb[0].MIX_DEST_A1=(rvb[0].MIX_DEST_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+80: + rvb[0].MIX_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_B0&0xFFFF); + break; + case PS2_C0_Reverb+82: + rvb[0].MIX_DEST_B0=(rvb[0].MIX_DEST_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_Reverb+84: + rvb[0].MIX_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_B1&0xFFFF); + break; + case PS2_C0_Reverb+86: + rvb[0].MIX_DEST_B1=(rvb[0].MIX_DEST_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C0_ReverbX+0: rvb[0].IIR_ALPHA=(short)val; break; + case PS2_C0_ReverbX+2: rvb[0].ACC_COEF_A=(short)val; break; + case PS2_C0_ReverbX+4: rvb[0].ACC_COEF_B=(short)val; break; + case PS2_C0_ReverbX+6: rvb[0].ACC_COEF_C=(short)val; break; + case PS2_C0_ReverbX+8: rvb[0].ACC_COEF_D=(short)val; break; + case PS2_C0_ReverbX+10: rvb[0].IIR_COEF=(short)val; break; + case PS2_C0_ReverbX+12: rvb[0].FB_ALPHA=(short)val; break; + case PS2_C0_ReverbX+14: rvb[0].FB_X=(short)val; break; + case PS2_C0_ReverbX+16: rvb[0].IN_COEF_L=(short)val; break; + case PS2_C0_ReverbX+18: rvb[0].IN_COEF_R=(short)val; break; + //-------------------------------------------------// + case PS2_C1_Reverb+0: + rvb[1].FB_SRC_A=(((unsigned long)val&0xf)<<16)|(rvb[1].FB_SRC_A&0xFFFF); + break; + case PS2_C1_Reverb+2: + rvb[1].FB_SRC_A=(rvb[1].FB_SRC_A & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+4: + rvb[1].FB_SRC_B=(((unsigned long)val&0xf)<<16)|(rvb[1].FB_SRC_B&0xFFFF); + break; + case PS2_C1_Reverb+6: + rvb[1].FB_SRC_B=(rvb[1].FB_SRC_B & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+8: + rvb[1].IIR_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_A0&0xFFFF); + break; + case PS2_C1_Reverb+10: + rvb[1].IIR_DEST_A0=(rvb[1].IIR_DEST_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+12: + rvb[1].IIR_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_A1&0xFFFF); + break; + case PS2_C1_Reverb+14: + rvb[1].IIR_DEST_A1=(rvb[1].IIR_DEST_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+16: + rvb[1].ACC_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_A0&0xFFFF); + break; + case PS2_C1_Reverb+18: + rvb[1].ACC_SRC_A0=(rvb[1].ACC_SRC_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+20: + rvb[1].ACC_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_A1&0xFFFF); + break; + case PS2_C1_Reverb+22: + rvb[1].ACC_SRC_A1=(rvb[1].ACC_SRC_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+24: + rvb[1].ACC_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_B0&0xFFFF); + break; + case PS2_C1_Reverb+26: + rvb[1].ACC_SRC_B0=(rvb[1].ACC_SRC_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+28: + rvb[1].ACC_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_B1&0xFFFF); + break; + case PS2_C1_Reverb+30: + rvb[1].ACC_SRC_B1=(rvb[1].ACC_SRC_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+32: + rvb[1].IIR_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_A0&0xFFFF); + break; + case PS2_C1_Reverb+34: + rvb[1].IIR_SRC_A0=(rvb[1].IIR_SRC_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+36: + rvb[1].IIR_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_A1&0xFFFF); + break; + case PS2_C1_Reverb+38: + rvb[1].IIR_SRC_A1=(rvb[1].IIR_SRC_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+40: + rvb[1].IIR_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_B0&0xFFFF); + break; + case PS2_C1_Reverb+42: + rvb[1].IIR_DEST_B0=(rvb[1].IIR_DEST_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+44: + rvb[1].IIR_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_B1&0xFFFF); + break; + case PS2_C1_Reverb+46: + rvb[1].IIR_DEST_B1=(rvb[1].IIR_DEST_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+48: + rvb[1].ACC_SRC_C0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_C0&0xFFFF); + break; + case PS2_C1_Reverb+50: + rvb[1].ACC_SRC_C0=(rvb[1].ACC_SRC_C0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+52: + rvb[1].ACC_SRC_C1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_C1&0xFFFF); + break; + case PS2_C1_Reverb+54: + rvb[1].ACC_SRC_C1=(rvb[1].ACC_SRC_C1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+56: + rvb[1].ACC_SRC_D0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_D0&0xFFFF); + break; + case PS2_C1_Reverb+58: + rvb[1].ACC_SRC_D0=(rvb[1].ACC_SRC_D0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+60: + rvb[1].ACC_SRC_D1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_D1&0xFFFF); + break; + case PS2_C1_Reverb+62: + rvb[1].ACC_SRC_D1=(rvb[1].ACC_SRC_D1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+64: + rvb[1].IIR_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_B1&0xFFFF); + break; + case PS2_C1_Reverb+66: + rvb[1].IIR_SRC_B1=(rvb[1].IIR_SRC_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+68: + rvb[1].IIR_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_B0&0xFFFF); + break; + case PS2_C1_Reverb+70: + rvb[1].IIR_SRC_B0=(rvb[1].IIR_SRC_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+72: + rvb[1].MIX_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_A0&0xFFFF); + break; + case PS2_C1_Reverb+74: + rvb[1].MIX_DEST_A0=(rvb[1].MIX_DEST_A0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+76: + rvb[1].MIX_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_A1&0xFFFF); + break; + case PS2_C1_Reverb+78: + rvb[1].MIX_DEST_A1=(rvb[1].MIX_DEST_A1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+80: + rvb[1].MIX_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_B0&0xFFFF); + break; + case PS2_C1_Reverb+82: + rvb[1].MIX_DEST_B0=(rvb[1].MIX_DEST_B0 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_Reverb+84: + rvb[1].MIX_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_B1&0xFFFF); + break; + case PS2_C1_Reverb+86: + rvb[1].MIX_DEST_B1=(rvb[1].MIX_DEST_B1 & 0xF0000) | ((val) & 0xFFFF); + break; + case PS2_C1_ReverbX+0: rvb[1].IIR_ALPHA=(short)val; break; + case PS2_C1_ReverbX+2: rvb[1].ACC_COEF_A=(short)val; break; + case PS2_C1_ReverbX+4: rvb[1].ACC_COEF_B=(short)val; break; + case PS2_C1_ReverbX+6: rvb[1].ACC_COEF_C=(short)val; break; + case PS2_C1_ReverbX+8: rvb[1].ACC_COEF_D=(short)val; break; + case PS2_C1_ReverbX+10: rvb[1].IIR_COEF=(short)val; break; + case PS2_C1_ReverbX+12: rvb[1].FB_ALPHA=(short)val; break; + case PS2_C1_ReverbX+14: rvb[1].FB_X=(short)val; break; + case PS2_C1_ReverbX+16: rvb[1].IN_COEF_L=(short)val; break; + case PS2_C1_ReverbX+18: rvb[1].IN_COEF_R=(short)val; break; + } + + iSpuAsyncWait=0; + +} + +//////////////////////////////////////////////////////////////////////// +// READ REGISTER: called by main emu +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC unsigned short CALLBACK SPU2read(unsigned long reg) +{ + long r=reg&0xffff; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("R_REG %X\r\n",reg&0xFFFF); +#endif + + iSpuAsyncWait=0; + + if((r>=0x0000 && r<0x0180)||(r>=0x0400 && r<0x0580)) // some channel info? + { + switch(r&0x0f) + { + //------------------------------------------------// env value + case 10: + { + int ch=(r>>4)&0x1f; + if(r>=0x400) ch+=24; + if(s_chan[ch].bNew) return 1; // we are started, but not processed? return 1 + if(s_chan[ch].ADSRX.lVolume && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well + !s_chan[ch].ADSRX.EnvelopeVol) + return 1; + return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16); + }break; + } + } + + if((r>=0x01c0 && r<0x02E0)||(r>=0x05c0 && r<0x06E0)) // some channel info? + { + int ch=0;unsigned long rx=r; + if(rx>=0x400) {ch=24;rx-=0x400;} + + ch+=(rx-0x1c0)/12; + rx-=(ch%24)*12; + + switch(rx) + { + //------------------------------------------------// + case 0x1C4: + return (((s_chan[ch].pLoop-spuMemC)>>17)&0xF); + break; + case 0x1C6: + return (((s_chan[ch].pLoop-spuMemC)>>1)&0xFFFF); + break; + //------------------------------------------------// + case 0x1C8: + return (((s_chan[ch].pCurr-spuMemC)>>17)&0xF); + break; + case 0x1CA: + return (((s_chan[ch].pCurr-spuMemC)>>1)&0xFFFF); + break; + //------------------------------------------------// + } + } + + switch(r) + { + //--------------------------------------------------// + case PS2_C0_SPUend1: + return (unsigned short)((dwEndChannel2[0]&0xFFFF)); + case PS2_C0_SPUend2: + return (unsigned short)((dwEndChannel2[0]>>16)); + //--------------------------------------------------// + case PS2_C1_SPUend1: + return (unsigned short)((dwEndChannel2[1]&0xFFFF)); + case PS2_C1_SPUend2: + return (unsigned short)((dwEndChannel2[1]>>16)); + //--------------------------------------------------// + case PS2_C0_ATTR: + return spuCtrl2[0]; + break; + //--------------------------------------------------// + case PS2_C1_ATTR: + return spuCtrl2[1]; + break; + //--------------------------------------------------// + case PS2_C0_SPUstat: + return spuStat2[0]; + break; + //--------------------------------------------------// + case PS2_C1_SPUstat: + return spuStat2[1]; + break; + //--------------------------------------------------// + case PS2_C0_SPUdata: + { + unsigned short s=spuMem[spuAddr2[0]]; + spuAddr2[0]++; + if(spuAddr2[0]>0xfffff) spuAddr2[0]=0; + return s; + } + //--------------------------------------------------// + case PS2_C1_SPUdata: + { + unsigned short s=spuMem[spuAddr2[1]]; + spuAddr2[1]++; + if(spuAddr2[1]>0xfffff) spuAddr2[1]=0; + return s; + } + //--------------------------------------------------// + case PS2_C0_SPUaddr_Hi: + return (unsigned short)((spuAddr2[0]>>16)&0xF); + break; + case PS2_C0_SPUaddr_Lo: + return (unsigned short)((spuAddr2[0]&0xFFFF)); + break; + //--------------------------------------------------// + case PS2_C1_SPUaddr_Hi: + return (unsigned short)((spuAddr2[1]>>16)&0xF); + break; + case PS2_C1_SPUaddr_Lo: + return (unsigned short)((spuAddr2[1]&0xFFFF)); + break; + //--------------------------------------------------// + } + + return regArea[r>>1]; +} + +//////////////////////////////////////////////////////////////////////// +// SOUND ON register write +//////////////////////////////////////////////////////////////////////// + +void SoundOn(int start,int end,unsigned short val) // SOUND ON PSX COMAND +{ + int ch; + + for(ch=start;ch>=1) // loop channels + { + if((val&1) && s_chan[ch].pStart) // mmm... start has to be set before key on !?! + { + s_chan[ch].bIgnoreLoop=0; + s_chan[ch].bNew=1; + dwNewChannel2[ch/24]|=(1<<(ch%24)); // bitfield for faster testing + } + } +} + +//////////////////////////////////////////////////////////////////////// +// SOUND OFF register write +//////////////////////////////////////////////////////////////////////// + +void SoundOff(int start,int end,unsigned short val) // SOUND OFF PSX COMMAND +{ + int ch; + for(ch=start;ch>=1) // loop channels + { + if(val&1) // && s_chan[i].bOn) mmm... + { + s_chan[ch].bStop=1; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// FMOD register write +//////////////////////////////////////////////////////////////////////// + +void FModOn(int start,int end,unsigned short val) // FMOD ON PSX COMMAND +{ + int ch; + + for(ch=start;ch>=1) // loop channels + { + if(val&1) // -> fmod on/off + { + if(ch>0) + { + s_chan[ch].bFMod=1; // --> sound channel + s_chan[ch-1].bFMod=2; // --> freq channel + } + } + else + { + s_chan[ch].bFMod=0; // --> turn off fmod + } + } +} + +//////////////////////////////////////////////////////////////////////// +// NOISE register write +//////////////////////////////////////////////////////////////////////// + +void NoiseOn(int start,int end,unsigned short val) // NOISE ON PSX COMMAND +{ + int ch; + + for(ch=start;ch>=1) // loop channels + { + if(val&1) // -> noise on/off + { + s_chan[ch].bNoise=1; + } + else + { + s_chan[ch].bNoise=0; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// LEFT VOLUME register write +//////////////////////////////////////////////////////////////////////// + +// please note: sweep and phase invert are wrong... but I've never seen +// them used + +void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME +{ + s_chan[ch].iLeftVolRaw=vol; + + if(vol&0x8000) // sweep? + { + short sInc=1; // -> sweep up? + if(vol&0x2000) sInc=-1; // -> or down? + if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this + vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64 + vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half! + vol*=128; + } + else // no sweep: + { + if(vol&0x4000) // -> mmm... phase inverted? have to investigate this + //vol^=0xffff; + vol=0x3fff-(vol&0x3fff); + } + + vol&=0x3fff; + s_chan[ch].iLeftVolume=vol; // store volume +} + +//////////////////////////////////////////////////////////////////////// +// RIGHT VOLUME register write +//////////////////////////////////////////////////////////////////////// + +void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME +{ + s_chan[ch].iRightVolRaw=vol; + + if(vol&0x8000) // comments... see above :) + { + short sInc=1; + if(vol&0x2000) sInc=-1; + if(vol&0x1000) vol^=0xffff; + vol=((vol&0x7f)+1)/2; + vol+=vol/(2*sInc); + vol*=128; + } + else + { + if(vol&0x4000) //vol=vol^=0xffff; + vol=0x3fff-(vol&0x3fff); + } + + vol&=0x3fff; + + s_chan[ch].iRightVolume=vol; +} + +//////////////////////////////////////////////////////////////////////// +// PITCH register write +//////////////////////////////////////////////////////////////////////// + +void SetPitch(int ch,unsigned short val) // SET PITCH +{ + int NP; + if(val>0x3fff) NP=0x3fff; // get pitch val + else NP=val; + + s_chan[ch].iRawPitch=NP; + + NP=(44100L*NP)/4096L; // calc frequency + if(NP<1) NP=1; // some security + s_chan[ch].iActFreq=NP; // store frequency +} + +//////////////////////////////////////////////////////////////////////// +// REVERB register write +//////////////////////////////////////////////////////////////////////// + +void ReverbOn(int start,int end,unsigned short val,int iRight) // REVERB ON PSX COMMAND +{ + int ch; + + for(ch=start;ch>=1) // loop channels + { + if(val&1) // -> reverb on/off + { + if(iRight) s_chan[ch].bReverbR=1; + else s_chan[ch].bReverbL=1; + } + else + { + if(iRight) s_chan[ch].bReverbR=0; + else s_chan[ch].bReverbL=0; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// REVERB START register write +//////////////////////////////////////////////////////////////////////// + +void SetReverbAddr(int core) +{ + long val=spuRvbAddr2[core]; + + if(rvb[core].StartAddr!=val) + { + if(val<=0x27ff) + { + rvb[core].StartAddr=rvb[core].CurrAddr=0; + } + else + { + rvb[core].StartAddr=val; + rvb[core].CurrAddr=rvb[core].StartAddr; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// DRY LEFT/RIGHT per voice switches +//////////////////////////////////////////////////////////////////////// + +void VolumeOn(int start,int end,unsigned short val,int iRight) // VOLUME ON PSX COMMAND +{ + int ch; + + for(ch=start;ch>=1) // loop channels + { + if(val&1) // -> reverb on/off + { + if(iRight) s_chan[ch].bVolumeR=1; + else s_chan[ch].bVolumeL=1; + } + else + { + if(iRight) s_chan[ch].bVolumeR=0; + else s_chan[ch].bVolumeL=0; + } + } +} + + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/registers.h --- a/Plugins/Input/sexypsf/spu/registers.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/registers.h Sun Mar 19 12:51:03 2006 -0800 @@ -1,9 +1,5 @@ /*************************************************************************** registers.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de ***************************************************************************/ /*************************************************************************** @@ -19,11 +15,661 @@ //*************************************************************************// // History of changes: // -// 2002/05/15 - Pete -// - generic cleanup for the Peops release +// 2004/04/04 - Pete +// - generic cleanup for the Peops release... register values by Kanodin & +// his team // //*************************************************************************// +//########################################################################### + +#define PS2_C0_SPUaddr_Hi (0x000 + 0x1A8) +#define PS2_C0_SPUaddr_Lo (0x000 + 0x1AA) +#define PS2_C1_SPUaddr_Hi (0x400 + 0x1A8) +#define PS2_C1_SPUaddr_Lo (0x400 + 0x1AA) +#define PS2_C0_SPUdata (0x000 + 0x1AC) +#define PS2_C1_SPUdata (0x400 + 0x1AC) + +#define PS2_C0_SPUDMActrl (0x000 + 0x1AE) +#define PS2_C1_SPUDMActrl (0x400 + 0x1AE) + +#define PS2_C0_SPUstat (0x000 + 0x344) +#define PS2_C1_SPUstat (0x400 + 0x344) +#define PS2_C0_ReverbAddr_Hi (0x000 + 0x2E0) +#define PS2_C0_ReverbAddr_Lo (0x000 + 0x2E2) +#define PS2_C1_ReverbAddr_Hi (0x400 + 0x2E0) +#define PS2_C1_ReverbAddr_Lo (0x400 + 0x2E2) + +#define PS2_C0_ReverbAEnd_Hi (0x000 + 0x33C) +#define PS2_C0_ReverbAEnd_Lo (0x000 + 0x33E) +#define PS2_C1_ReverbAEnd_Hi (0x400 + 0x33C) +#define PS2_C1_ReverbAEnd_Lo (0x400 + 0x33E) + +#define PS2_C0_DryL1 (0x000 + 0x188) +#define PS2_C1_DryL1 (0x400 + 0x188) +#define PS2_C0_DryL2 (0x000 + 0x18A) +#define PS2_C1_DryL2 (0x400 + 0x18A) + +#define PS2_C0_DryR1 (0x000 + 0x190) +#define PS2_C1_DryR1 (0x400 + 0x190) +#define PS2_C0_DryR2 (0x000 + 0x192) +#define PS2_C1_DryR2 (0x400 + 0x192) + +#define PS2_C0_ATTR (0x000 + 0x19A) +#define PS2_C1_ATTR (0x400 + 0x19A) +#define PS2_C0_ADMAS (0x000 + 0x1B0) +#define PS2_C1_ADMAS (0x400 + 0x1B0) + +#define PS2_C0_SPUirqAddr_Hi (0x000 + 0x19C) +#define PS2_C0_SPUirqAddr_Lo (0x000 + 0x19D) +#define PS2_C1_SPUirqAddr_Hi (0x400 + 0x19C) +#define PS2_C1_SPUirqAddr_Lo (0x400 + 0x19D) +#define PS2_C0_SPUrvolL (0x000 + 0x764) +#define PS2_C0_SPUrvolR (0x000 + 0x766) +#define PS2_C1_SPUrvolL (0x028 + 0x764) +#define PS2_C1_SPUrvolR (0x028 + 0x766) +#define PS2_C0_SPUon1 (0x000 + 0x1A0) +#define PS2_C0_SPUon2 (0x000 + 0x1A2) +#define PS2_C1_SPUon1 (0x400 + 0x1A0) +#define PS2_C1_SPUon2 (0x400 + 0x1A2) +#define PS2_C0_SPUoff1 (0x000 + 0x1A4) +#define PS2_C0_SPUoff2 (0x000 + 0x1A6) +#define PS2_C1_SPUoff1 (0x400 + 0x1A4) +#define PS2_C1_SPUoff2 (0x400 + 0x1A6) +#define PS2_C0_FMod1 (0x000 + 0x180) +#define PS2_C0_FMod2 (0x000 + 0x182) +#define PS2_C1_FMod1 (0x400 + 0x180) +#define PS2_C1_FMod2 (0x400 + 0x182) +#define PS2_C0_Noise1 (0x000 + 0x184) +#define PS2_C0_Noise2 (0x000 + 0x186) +#define PS2_C1_Noise1 (0x400 + 0x184) +#define PS2_C1_Noise2 (0x400 + 0x186) + +#define PS2_C0_RVBon1_L (0x000 + 0x18C) +#define PS2_C0_RVBon2_L (0x000 + 0x18E) +#define PS2_C0_RVBon1_R (0x000 + 0x194) +#define PS2_C0_RVBon2_R (0x000 + 0x196) + +#define PS2_C1_RVBon1_L (0x400 + 0x18C) +#define PS2_C1_RVBon2_L (0x400 + 0x18E) +#define PS2_C1_RVBon1_R (0x400 + 0x194) +#define PS2_C1_RVBon2_R (0x400 + 0x196) +#define PS2_C0_Reverb (0x000 + 0x2E4) +#define PS2_C1_Reverb (0x400 + 0x2E4) +#define PS2_C0_ReverbX (0x000 + 0x774) +#define PS2_C1_ReverbX (0x028 + 0x774) +#define PS2_C0_SPUend1 (0x000 + 0x340) +#define PS2_C0_SPUend2 (0x000 + 0x342) +#define PS2_C1_SPUend1 (0x400 + 0x340) +#define PS2_C1_SPUend2 (0x400 + 0x342) + + + +//########################################################################### + +/* + Included the info received in Regs.txt list by Neill Corlett - Kanodin + + Voice parameters: + SD_VP_VOLL, SD_VP_VOLR - Volume left/right per voice. Assuming identical to PS1. + SD_VP_PITCH - Pitch scaler 0000-3FFF. Assuming identical to PS1. + SD_VP_ADSR1, SD_VP_ADSR1 - Envelope data. Bitfields are documented as identical to PS1. + SD_VP_ENVX - Current envelope value. Assuming identical to PS1. + SD_VP_VOLXL, SD_VP_VOLXR - Current voice volume left/right. Does not exist on the PS1. + Guessing that this is handy for the increase/decrease modes. + + Voice addresses: + + SD_VA_SSA - Sample start address; assuming identical to PS1 + SD_VA_LSAX - Loop start address; assuming identical to PS1 + SD_VA_NAX - Seems to be documented as the current playing address. + Does not exist on PS1. + + Switches: + + SD_S_PMON - Pitch mod; assuming identical to PS1 + SD_S_NON - Noise; assuming identical to PS1 + SD_S_VMIXL, SD_S_VMIXR - Voice mix L/R. Guessing this is just a separate L/R version + of the "voice enable" bits on the PS1. + SD_S_VMIXEL, SD_S_VMIXER - Voice effect mix L/R. Guessing this is just a separate L/R + version of the "voice reverb enable" bits on the PS1. + SD_S_KON, SD_S_KOFF - Key on/off; assuming identical to PS1 + + + Addresses: + + SD_A_TSA - Transfer start address; assuming identical to PS1 + SD_A_ESA - Effect start address - this is probably analogous to the + PS1's reverb work area start address + SD_A_EEA - Effect end address - this would've been fixed to 0x7FFFF on + the PS1; settable in 128K increments on the PS2. + SD_A_IRQA - IRQ address; assuming identical to PS1 + + Volume parameters: + + SD_P_MVOLL, SD_P_MVOLR - Master volume L/R; assuming identical to PS1 + SD_P_EVOLL, SD_P_EVOLR - Effect volume L/R; assuming analogous to RVOL on the PS1 + SD_P_AVOLL, SD_P_AVOLR - External input volume L/R + This is probably where CORE0 connects to CORE1 + SD_P_BVOLL, SD_P_BVOLR - Sound data input volume - perhaps this is the volume of + the raw PCM auto-DMA input? analogous to CD input volume? + SD_P_MVOLXL, SD_P_MVOLXR - Current master volume L/R; seems self-explanatory + + SD_P_MMIX - Mixer / effect enable bits. + bit 11 = MSNDL = voice output dry L + 10 = MSNDR = voice output dry R + 9 = MSNDEL = voice output wet L + 8 = MSNDER = voice output wet R + 7 = MINL = sound data input dry L + 6 = MINR = sound data input dry R + 5 = MINEL = sound data input wet L + 4 = MINER = sound data input wet R + 3 = SINL = core external input dry L + 2 = SINR = core external input dry R + 1 = SINEL = core external input wet L + 0 = SINER = core external input wet R + +Core attributes (SD_C) + + bit 4..5 - DMA related + bit 6 - IRQ enable + bit 7 - effect enable (reverb enable) + bit 13..8 - noise clock + bit 14 - mute + + - if you READ the two DMA related bits, if either are set, the channel is + considered "busy" by sceSdVoiceTrans + + + +Reverb parameters: + + Same as PS1 reverb (I used the names from my reverb doc). + + +Other PS2 IOP notes + + There's two DMA controllers: + The original one at 1F801080-1F8010FF (channels 0-6) + A new one at 1F801500-1F80157F (channels 7-13) + + They appear to function the same way - 7 channels each. + + SPU CORE0's DMA channel is 4 as per usual + SPU CORE1's DMA channel is 7 + +DMA channel 10 is SIF + + Original INTR controller at 1F801000-1F80107F + + All interrupt handling seems to be done using the old INTR, but + with some new bits defined: + + + + Reading from 1F801078 masks interrupts and returns 1 if they weren't + masked before. Writing 1 to 1F801078 re-enables interrupts. + Writing 0 doesn't. Maybe it was like that on the original PS1 too. + +Six root counters: + + RTC# address sources size prescale interrupt# +0 0x1F801100 sysclock,pixel 16 bit 1 only 4 +1 0x1F801110 sysclock,hline 16 bit 1 only 5 +2 0x1F801120 sysclock 16 bit 1,8 6 +3 0x1F801480 sysclock,hline 32 bit 1 only 14 +4 0x1F801490 sysclock 32 bit 1,8,16,256 15 +5 0x1F8014A0 sysclock 32 bit 1,8,16,256 16 + +Count (0x0) and Compare (0x8) registers work as before, only with more bits +in the new counters. + +Mode (0x4) works like this when written: + + bits 0..2 gate + bit 3 reset on target + bit 4 target interrupt enable + bit 5 overflow interrupt enable + bit 6 master enable (?) + bit 7 ? + bit 8 clock select + bit 9 prescale (OLD) + bit 10..12 ? + bit 13..14 prescale (NEW) + bit 15 ? always set to 1 + +Gate: + TM_NO_GATE 000 + TM_GATE_ON_Count 001 + TM_GATE_ON_ClearStart 011 + TM_GATE_ON_Clear_OFF_Start 101 + TM_GATE_ON_Start 111 + + V-blank ----+ +----------------------------+ +------ + | | | | + | | | | + +----+ +----+ + TM_NO_GATE: + + 0================================>============ + + TM_GATE_ON_Count: + + <---->0==========================><---->0===== + + TM_GATE_ON_ClearStart: + + 0====>0================================>0===== + + TM_GATE_ON_Clear_OFF_Start: + + 0====><-------------------------->0====><----- + + TM_GATE_ON_Start: + + <---->0==========================>============ + + reset on target: if set, counter resets to 0 when Compare value is reached + + target interrupt enable: if set, interrupt when Compare value is reached + overflow interrupt enable: if set, interrupt when counter overflows + + master enable: if this bit is clear, the timer should do nothing. + + clock select: for counters 0, 1, and 3, setting this will select the alternate + counter (pixel or hline) + + prescale (OLD): for counter 2 only. set this to prescale (divide) by 8. + + prescale (NEW): for counters 4 and 5 only: + + 00 = prescale by 1 + 01 = prescale by 8 + 10 = prescale by 16 + 11 = prescale by 256 + +Writing 0x4 also clears the counter. (I think.) + +When 0x4 is read, it becomes Status: + + bit 0..10 ? + bit 11 compare value was reached + bit 12 count overflowed + bit 13..15 ? + +Reading probably clears these bits. + + + + 1F8014B0 (word) - timer-related but otherwise unknown + 1F8014C0 (word) - timer-related but otherwise unknown + + + don't currently know how the interrupts work for DMA ch7 yet + + 1F801060 (word) - address of some kind. + + 1F801450 (word) - + if bit 3 is SET, we're in PS1 mode. + if bit 3 is CLEAR, we're in PS2 IOP mode. + + 1F802070 (byte) - unknown. status byte of some kind? visible to EE? + + 1D000000-1D00007F (?) - SIF related + + 1D000020 (word) - read counter of some sort? + sceSifInit waits for bit 0x10000 of this to be set. + 1D000030 (word) - read counter of some sort? + 1D000040 (word) - read bits 0x20, 0x40 mean something + 1D000060 (word) - used to detect whether the SIF interface exists + read must be 0x1D000060, or the top 20 bits must be zero +*/ + +/* + +// DirectX Audio SPU2 Driver for PCSX2 +// audio.c by J.F. and Kanodin (hooper1@cox.net) +// +// Copyright 2003 J.F. and Kanodin, and distributed under the +// terms of the GNU General Public License, v2 or later. +// http://www.gnu.org/copyleft/gpl.html. + +Included these just in case you need them J.F. - Kanodin + +// Core Start Addresses +#define CORE0 0x1f900000 +#define CORE1 0x1f900400 + + + #define IOP_INT_VBLANK (1<<0) + #define IOP_INT_GM (1<<1) + #define IOP_INT_CDROM (1<<2) + #define IOP_INT_DMA (1<<3) + #define IOP_INT_RTC0 (1<<4) + #define IOP_INT_RTC1 (1<<5) + #define IOP_INT_RTC2 (1<<6) + #define IOP_INT_SIO0 (1<<7) + #define IOP_INT_SIO1 (1<<8) + #define IOP_INT_SPU (1<<9) + #define IOP_INT_PIO (1<<10) + #define IOP_INT_EVBLANK (1<<11) + #define IOP_INT_DVD (1<<12) + #define IOP_INT_PCMCIA (1<<13) + #define IOP_INT_RTC3 (1<<14) + #define IOP_INT_RTC4 (1<<15) + #define IOP_INT_RTC5 (1<<16) + #define IOP_INT_SIO2 (1<<17) + #define IOP_INT_HTR0 (1<<18) + #define IOP_INT_HTR1 (1<<19) + #define IOP_INT_HTR2 (1<<20) + #define IOP_INT_HTR3 (1<<21) + #define IOP_INT_USB (1<<22) + #define IOP_INT_EXTR (1<<23) + #define IOP_INT_FWRE (1<<24) + #define IOP_INT_FDMA (1<<25) + +// CORE0 => +0x000, CORE1 => +0x400 + +// individual voice parameter regs + +#define VP_VOLL(cr, vc) (0x400 * cr + 0x000 + (vc << 4)) // voice volume (left) +#define VP_VOLR(cr, vc) (0x400 * cr + 0x002 + (vc << 4)) // voice volume (right) +#define VP_PITCH(cr, vc) (0x400 * cr + 0x004 + (vc << 4)) // voice pitch +#define VP_ADSR1(cr, vc) (0x400 * cr + 0x006 + (vc << 4)) // voice envelope (AR, DR, SL) +#define VP_ADSR2(cr, vc) (0x400 * cr + 0x008 + (vc << 4)) // voice envelope (SR, RR) +#define VP_ENVX(cr, vc) (0x400 * cr + 0x00A + (vc << 4)) // voice envelope (current value) +#define VP_VOLXL(cr, vc) (0x400 * cr + 0x00C + (vc << 4)) // voice volume (current value left) +#define VP_VOLXR(cr, vc) (0x400 * cr + 0x00E + (vc << 4)) // voice volume (current value right) + +#define VA_SSA(cr, vc) (0x400 * cr + 0x1C0 + (vc * 12)) // voice waveform data start address +#define VA_LSAX(cr, vc) (0x400 * cr + 0x1C4 + (vc * 12)) // voice waveform data loop address +#define VA_NAX(cr, vc) (0x400 * cr + 0x1C8 + (vc * 12)) // voice waveform data next address + +// common settings + +#define S_PMON(cr) (0x400 * cr + 0x180) // pitch modulation on +#define S_NON(cr) (0x400 * cr + 0x184) // noise generator on +#define S_VMIXL(cr) (0x400 * cr + 0x188) // voice output mixing (dry left) +#define S_VMIXEL(cr) (0x400 * cr + 0x18C) // voice output mixing (wet left) +#define S_VMIXR(cr) (0x400 * cr + 0x190) // voice output mixing (dry right) +#define S_VMIXER(cr) (0x400 * cr + 0x194) // voice output mixing (wet right) +#define P_MMIX(cr) (0x400 * cr + 0x198) // output type after voice mixing (See paragraph below) +#define P_ATTR(cr) (0x400 * cr + 0x19A) // core attributes (See paragraph below) +#define A_IRQA(cr) (0x400 * cr + 0x19C) // IRQ address +#define S_KON(cr) (0x400 * cr + 0x1A0) // key on (start voice sound generation) +#define S_KOFF(cr) (0x400 * cr + 0x1A4) // key off (end voice sound generation) +#define A_TSA(cr) (0x400 * cr + 0x1A8) // DMA transfer start address +#define P_DATA(cr) (0x400 * cr + 0x1AC) // DMA data register +#define P_CTRL(cr) (0x400 * cr + 0x1AE) // DMA control register +#define P_ADMAS(cr) (0x400 * cr + 0x1B0) // AutoDMA status + +#define A_ESA(cr) (0x400 * cr + 0x2E0) // effects work area start address + +#define FB_SRC_A(cr) (0x400 * cr + 0x2E4) +#define FB_SRC_B(cr) (0x400 * cr + 0x2E8) +#define IIR_DEST_A0(cr) (0x400 * cr + 0x2EC) +#define IIR_DEST_A1(cr) (0x400 * cr + 0x2F0) +#define ACC_SRC_A0(cr) (0x400 * cr + 0x2F4) +#define ACC_SRC_A1(cr) (0x400 * cr + 0x2F8) +#define ACC_SRC_B0(cr) (0x400 * cr + 0x2FC) + +#define ACC_SRC_B1(cr) (0x400 * cr + 0x300) +#define IIR_SRC_A0(cr) (0x400 * cr + 0x304) +#define IIR_SRC_A1(cr) (0x400 * cr + 0x308) +#define IIR_DEST_B0(cr) (0x400 * cr + 0x30C) +#define IIR_DEST_B1(cr) (0x400 * cr + 0x310) +#define ACC_SRC_C0(cr) (0x400 * cr + 0x314) +#define ACC_SRC_C1(cr) (0x400 * cr + 0x318) + +#define ACC_SRC_D0(cr) (0x400 * cr + 0x31C) +#define ACC_SRC_D1(cr) (0x400 * cr + 0x320) +#define IIR_SRC_B1(cr) (0x400 * cr + 0x324) +#define IIR_SRC_B0(cr) (0x400 * cr + 0x328) +#define MIX_DEST_A0(cr) (0x400 * cr + 0x32C) +#define MIX_DEST_A1(cr) (0x400 * cr + 0x330) +#define MIX_DEST_B0(cr) (0x400 * cr + 0x334) +#define MIX_DEST_B1(cr) (0x400 * cr + 0x338) + +#define A_EEA(cr) (0x400 * cr + 0x33C) // effects work area end address + +#define P_ENDX(cr) (0x400 * cr + 0x340) // voice loop end status +#define P_STAT(cr) (0x400 * cr + 0x344) // DMA status register +#define P_ENDS(cr) (0x400 * cr + 0x346) // ? + +// CORE0 => +0x400, CORE1 => +0x428 + +#define P_MVOLL(cr) (0x28 * cr + 0x760) // master volume (left) +#define P_MVOLR(cr) (0x28 * cr + 0x762) // master volume (right) +#define P_EVOLL(cr) (0x28 * cr + 0x764) // effect return volume (left) +#define P_EVOLR(cr) (0x28 * cr + 0x766) // effect return volume (right) +#define P_AVOLL(cr) (0x28 * cr + 0x768) // core external input volume (left) +#define P_AVOLR(cr) (0x28 * cr + 0x76A) // core external input volume (right) +#define P_BVOLL(cr) (0x28 * cr + 0x76C) // sound data input volume (left) +#define P_BVOLR(cr) (0x28 * cr + 0x76E) // sound data input volume (right) +#define P_MVOLXL(cr) (0x28 * cr + 0x770) // current master volume (left) +#define P_MVOLXR(cr) (0x28 * cr + 0x772) // current master volume (right) + +#define IIR_ALPHA(cr) (0x28 * cr + 0x774) +#define ACC_COEF_A(cr) (0x28 * cr + 0x776) +#define ACC_COEF_B(cr) (0x28 * cr + 0x778) +#define ACC_COEF_C(cr) (0x28 * cr + 0x77A) +#define ACC_COEF_D(cr) (0x28 * cr + 0x77C) +#define IIR_COEF(cr) (0x28 * cr + 0x77E) +#define FB_ALPHA(cr) (0x28 * cr + 0x780) +#define FB_X(cr) (0x28 * cr + 0x782) +#define IN_COEF_L(cr) (0x28 * cr + 0x784) +#define IN_COEF_R(cr) (0x28 * cr + 0x786) + +// CORE1 only => +0x400 + +#define SPDIF_OUT 0x7C0 // SPDIF Out: OFF/'PCM'/Bitstream/Bypass +#define SPDIF_MODE 0x7C6 +#define SPDIF_MEDIA 0x7C8 // SPDIF Media: 'CD'/DVD +#define SPDIF_COPY 0x7CA // SPDIF Copy Protection + +// PS1 SPU CORE + +// individual voice settings + +#define SPU_VP_PITCH(vc) (0xC04 + (vc << 4)) // voice pitch +#define SPU_VA_SSA(vc) (0xC06 + (vc << 4)) // voice waveform data start address +#define SPU_VP_ADSR(vc) (0xC08 + (vc << 4)) // voice envelope +#define SPU_VA_SSA(vc) (0xC0E + (vc << 4)) // voice waveform data loop address + +// common settings + +#define SPU_P_MVOLL 0xD80 // master volume (left) +#define SPU_P_MVOLR 0xD82 // master volume (right) +#define SPU_P_RVOLL 0xD84 // effect return volume (left) +#define SPU_P_RVOLR 0xD86 // effect return volume (right) +#define SPU_S_KON1 0xD88 // key on +#define SPU_S_KON2 0xD8A // +#define SPU_S_KOFF1 0xD8C // key off +#define SPU_S_KOFF2 0xD8E // +#define SPU_S_PMON1 0xD90 // pitch modulation on +#define SPU_S_PMON2 0xD92 // +#define SPU_S_NON1 0xD94 // noise generator on +#define SPU_S_NON2 0xD96 // +#define SPU_S_RVBON1 0xD98 // effects on +#define SPU_S_RVBON2 0xD9A // +#define SPU_S_MUTE1 0xD9C // voice mute +#define SPU_S_MUTE2 0xD9E // + +#define SPU_A_ESA 0xDA2 // effects work area start +#define SPU_A_IRQA 0xDA4 // IRQ address +#define SPU_A_TSA 0xDA6 // DMA transfer start address +#define SPU_P_DATA 0xDA8 // DMA data register +#define SPU_P_CTRL 0xDAA // DMA control register +#define SPU_P_STAT 0xDAE // DMA status register + +#define SPU_P_CDL 0xDB0 // sound data input volume (left) +#define SPU_P_CDR 0xDB2 // sound data input volume (right) +#define SPU_P_EXTL 0xDB4 // external input volume (left) +#define SPU_P_EXTR 0xDB6 // external input volume (right) + +#define SPU_P_REVERB 0xDC0 // effects control + + +// Individual voice parameter regs CORE 0 +// Only + + +#define VP_VOLL(cr, vc) (0x400 * cr + 0x000 + (vc << 4)) // voice volume (left) +#define VP_VOLR(cr, vc) (0x400 * cr + 0x002 + (vc << 4)) // voice volume (right) +#define VP_PITCH(cr, vc) (0x400 * cr + 0x004 + (vc << 4)) // voice pitch +#define VP_ADSR1(cr, vc) (0x400 * cr + 0x006 + (vc << 4)) // voice envelope (AR, DR, SL) +#define VP_ADSR2(cr, vc) (0x400 * cr + 0x008 + (vc << 4)) // voice envelope (SR, RR) +#define VP_ENVX(cr, vc) (0x400 * cr + 0x00A + (vc << 4)) // voice envelope (current value) +#define VP_VOLXL(cr, vc) (0x400 * cr + 0x00C + (vc << 4)) // voice volume (current value left) +#define VP_VOLXR(cr, vc) (0x400 * cr + 0x00E + (vc << 4)) // voice volume (current value right) + +#define VA_SSA(cr, vc) (0x400 * cr + 0x1C0 + (vc * 12)) // voice waveform data start address +#define VA_LSAX(cr, vc) (0x400 * cr + 0x1C4 + (vc * 12)) // voice waveform data loop address +#define VA_NAX(cr, vc) (0x400 * cr + 0x1C8 + (vc * 12)) // voice waveform data next address + + +// CORE 0 Common Settings + + +#define S_PMON(cr) (0x400 * cr + 0x180) // pitch modulation on +#define S_NON(cr) (0x400 * cr + 0x184) // noise generator on +#define S_VMIXL(cr) (0x400 * cr + 0x188) // voice output mixing (dry left) +#define S_VMIXEL(cr) (0x400 * cr + 0x18C) // voice output mixing (wet left) +#define S_VMIXR(cr) (0x400 * cr + 0x190) // voice output mixing (dry right) +#define S_VMIXER(cr) (0x400 * cr + 0x194) // voice output mixing (wet right) +#define P_MMIX(cr) (0x400 * cr + 0x198) // output type after voice mixing (See paragraph below) +#define P_ATTR(cr) (0x400 * cr + 0x19A) // core attributes (See paragraph below) +#define A_IRQA(cr) (0x400 * cr + 0x19C) // IRQ address +#define S_KON(cr) (0x400 * cr + 0x1A0) // key on (start voice sound generation) +#define S_KOFF(cr) (0x400 * cr + 0x1A4) // key off (end voice sound generation) +#define A_TSA(cr) (0x400 * cr + 0x1A8) // DMA transfer start address +#define P_DATA(cr) (0x400 * cr + 0x1AC) // DMA data register +#define P_CTRL(cr) (0x400 * cr + 0x1AE) // DMA control register +#define P_ADMAS(cr) (0x400 * cr + 0x1B0) // AutoDMA status + +#define A_ESA(cr) (0x400 * cr + 0x2E0) // effects work area start address + + +// Core 0 Reverb Addresses + + +#define FB_SRC_A(cr) (0x400 * cr + 0x2E4) +#define FB_SRC_B(cr) (0x400 * cr + 0x2E8) +#define IIR_DEST_A0(cr) (0x400 * cr + 0x2EC) +#define IIR_DEST_A1(cr) (0x400 * cr + 0x2F0) +#define ACC_SRC_A0(cr) (0x400 * cr + 0x2F4) +#define ACC_SRC_A1(cr) (0x400 * cr + 0x2F8) +#define ACC_SRC_B0(cr) (0x400 * cr + 0x2FC) + +#define ACC_SRC_B1(cr) (0x400 * cr + 0x300) +#define IIR_SRC_A0(cr) (0x400 * cr + 0x304) +#define IIR_SRC_A1(cr) (0x400 * cr + 0x308) +#define IIR_DEST_B0(cr) (0x400 * cr + 0x30C) +#define IIR_DEST_B1(cr) (0x400 * cr + 0x310) +#define ACC_SRC_C0(cr) (0x400 * cr + 0x314) +#define ACC_SRC_C1(cr) (0x400 * cr + 0x318) + +#define ACC_SRC_D0(cr) (0x400 * cr + 0x31C) +#define ACC_SRC_D1(cr) (0x400 * cr + 0x320) +#define IIR_SRC_B1(cr) (0x400 * cr + 0x324) +#define IIR_SRC_B0(cr) (0x400 * cr + 0x328) +#define MIX_DEST_A0(cr) (0x400 * cr + 0x32C) +#define MIX_DEST_A1(cr) (0x400 * cr + 0x330) +#define MIX_DEST_B0(cr) (0x400 * cr + 0x334) +#define MIX_DEST_B1(cr) (0x400 * cr + 0x338) + +#define A_EEA(cr) (0x400 * cr + 0x33C) // effects work area end address + +#define P_ENDX(cr) (0x400 * cr + 0x340) // voice loop end status +#define P_STAT(cr) (0x400 * cr + 0x344) // DMA status register +#define P_ENDS(cr) (0x400 * cr + 0x346) // ? + + +// CORE 0 Specific + + +#define P_MVOLL(cr) (0x28 * cr + 0x760) // master volume (left) +#define P_MVOLR(cr) (0x28 * cr + 0x762) // master volume (right) +#define P_EVOLL(cr) (0x28 * cr + 0x764) // effect return volume (left) +#define P_EVOLR(cr) (0x28 * cr + 0x766) // effect return volume (right) +#define P_AVOLL(cr) (0x28 * cr + 0x768) // core external input volume (left) +#define P_AVOLR(cr) (0x28 * cr + 0x76A) // core external input volume (right) +#define P_BVOLL(cr) (0x28 * cr + 0x76C) // sound data input volume (left) +#define P_BVOLR(cr) (0x28 * cr + 0x76E) // sound data input volume (right) +#define P_MVOLXL(cr) (0x28 * cr + 0x770) // current master volume (left) +#define P_MVOLXR(cr) (0x28 * cr + 0x772) // current master volume (right) + + +// More CORE 0 Reverb + + +#define IIR_ALPHA(cr) (0x28 * cr + 0x774) +#define ACC_COEF_A(cr) (0x28 * cr + 0x776) +#define ACC_COEF_B(cr) (0x28 * cr + 0x778) +#define ACC_COEF_C(cr) (0x28 * cr + 0x77A) +#define ACC_COEF_D(cr) (0x28 * cr + 0x77C) +#define IIR_COEF(cr) (0x28 * cr + 0x77E) +#define FB_ALPHA(cr) (0x28 * cr + 0x780) +#define FB_X(cr) (0x28 * cr + 0x782) +#define IN_COEF_L(cr) (0x28 * cr + 0x784) +#define IN_COEF_R(cr) (0x28 * cr + 0x786) + + +// CORE 1 only + +#define SPDIF_OUT 0x7C0 // SPDIF Out: OFF/'PCM'/Bitstream/Bypass +#define SPDIF_MODE 0x7C6 +#define SPDIF_MEDIA 0x7C8 // SPDIF Media: 'CD'/DVD +#define SPDIF_COPY 0x7CA // SPDIF Copy Protection +*/ + +/* PS1 SPU CORE + +*** The below really isn't needed, only if you *** +*** want to add SPU support to the plugin *** +*** which I see no need to add at this time. *** +*** individual voice settings *** + +#define SPU_VP_PITCH(vc) (0xC04 + (vc << 4)) // voice pitch +#define SPU_VA_SSA(vc) (0xC06 + (vc << 4)) // voice waveform data start address +#define SPU_VP_ADSR(vc) (0xC08 + (vc << 4)) // voice envelope +#define SPU_VA_SSA(vc) (0xC0E + (vc << 4)) // voice waveform data loop address + +// common settings + +#define SPU_P_MVOLL 0xD80 // master volume (left) +#define SPU_P_MVOLR 0xD82 // master volume (right) +#define SPU_P_RVOLL 0xD84 // effect return volume (left) +#define SPU_P_RVOLR 0xD86 // effect return volume (right) +#define SPU_S_KON1 0xD88 // key on +#define SPU_S_KON2 0xD8A // +#define SPU_S_KOFF1 0xD8C // key off +#define SPU_S_KOFF2 0xD8E // +#define SPU_S_PMON1 0xD90 // pitch modulation on +#define SPU_S_PMON2 0xD92 // +#define SPU_S_NON1 0xD94 // noise generator on +#define SPU_S_NON2 0xD96 // +#define SPU_S_RVBON1 0xD98 // effects on +#define SPU_S_RVBON2 0xD9A // +#define SPU_S_MUTE1 0xD9C // voice mute +#define SPU_S_MUTE2 0xD9E // + +#define SPU_A_ESA 0xDA2 // effects work area start +#define SPU_A_IRQA 0xDA4 // IRQ address +#define SPU_A_TSA 0xDA6 // DMA transfer start address +#define SPU_P_DATA 0xDA8 // DMA data register +#define SPU_P_CTRL 0xDAA // DMA control register +#define SPU_P_STAT 0xDAE // DMA status register + +#define SPU_P_CDL 0xDB0 // sound data input volume (left) +#define SPU_P_CDR 0xDB2 // sound data input volume (right) +#define SPU_P_EXTL 0xDB4 // external input volume (left) +#define SPU_P_EXTR 0xDB6 // external input volume (right) + +#define SPU_P_REVERB 0xDC0 // effects control +*/ + +/* #define H_SPUReverbAddr 0x0da2 #define H_SPUirqAddr 0x0da4 #define H_SPUaddr 0x0da6 @@ -150,4 +796,4 @@ #define H_SPU_ADSRLevel21 0x0d58 #define H_SPU_ADSRLevel22 0x0d68 #define H_SPU_ADSRLevel23 0x0d78 - +*/ diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/regs.h --- a/Plugins/Input/sexypsf/spu/regs.h Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/regs.h Sun Mar 19 12:51:03 2006 -0800 @@ -1,34 +1,43 @@ -/*************************************************************************** - regs.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 -// -//*************************************************************************// - - -static void SoundOn(int start,int end,u16 val); -static void SoundOff(int start,int end,u16 val); -static void FModOn(int start,int end,u16 val); -static void NoiseOn(int start,int end,u16 val); -static void SetVolumeLR(int right, u8 ch,s16 vol); -static void SetPitch(int ch,u16 val); -void SPUwriteRegister(u32 reg, u16 val); +/*************************************************************************** + regs.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: +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +void SoundOn(int start,int end,unsigned short val); +void SoundOff(int start,int end,unsigned short val); +void VolumeOn(int start,int end,unsigned short val,int iRight); +void FModOn(int start,int end,unsigned short val); +void NoiseOn(int start,int end,unsigned short val); +void SetVolumeL(unsigned char ch,short vol); +void SetVolumeR(unsigned char ch,short vol); +void SetPitch(int ch,unsigned short val); +void ReverbOn(int start,int end,unsigned short val,int iRight); +void SetReverbAddr(int core); + +EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val); + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/resource.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/resource.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,158 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by spu2PeopsSound.rc +// +#define IDC_SETS1 3 +#define IDC_SETS2 4 +#define IDOK2 5 +#define IDD_DIALOG1 130 +#define IDD_ABOUT 130 +#define IDD_CFGDLG 131 +#define IDD_DEBUG 135 +#define IDB_BITMAP1 136 +#define IDB_BITMAP2 137 +#define IDB_BITMAP3 138 +#define IDB_BITMAP4 139 +#define IDB_BITMAP5 140 +#define IDD_RECORD 141 +#define IDC_XAVOLUME 1004 +#define IDC_ENABXA 1005 +#define IDC_XAPITCH 1006 +#define IDC_XABLOCK 1007 +#define IDC_USETIMER 1007 +#define IDC_CMIXRATE 1008 +#define IDC_USEIRQ 1008 +#define IDC_USEREVERB 1008 +#define IDC_CMODE 1009 +#define IDC_VOLUME 1009 +#define IDC_CFILTER 1010 +#define IDC_IRQWAIT 1010 +#define IDC_CQUALITY 1011 +#define IDC_DEBUGMODE 1011 +#define IDC_CDSOUND 1012 +#define IDC_INTERPOL 1012 +#define IDC_PLAYALWAYS 1013 +#define IDC_RECORDMODE 1013 +#define IDC_IGNOREPITCH 1014 +#define IDC_DISSTEREO 1014 +#define IDC_AMPLIF 1015 +#define IDC_VENVELOPE 1016 +#define IDC_VOL1 1016 +#define IDC_REVERB 1017 +#define IDC_VOL2 1017 +#define IDC_VOL3 1018 +#define IDC_VOL4 1019 +#define IDC_SAREA 1022 +#define IDC_ADSR 1023 +#define IDC_MUTE1 1047 +#define IDC_MUTE2 1048 +#define IDC_MUTE3 1049 +#define IDC_MUTE4 1050 +#define IDC_MUTE5 1051 +#define IDC_MUTE6 1052 +#define IDC_MUTE7 1053 +#define IDC_MUTE8 1054 +#define IDC_MUTE9 1055 +#define IDC_MUTE10 1056 +#define IDC_MUTE11 1057 +#define IDC_MUTE12 1058 +#define IDC_MUTE13 1059 +#define IDC_MUTE14 1060 +#define IDC_MUTE15 1061 +#define IDC_MUTE16 1062 +#define IDC_MUTE17 1063 +#define IDC_MUTE18 1064 +#define IDC_MUTE19 1065 +#define IDC_MUTE20 1066 +#define IDC_MUTE21 1067 +#define IDC_MUTE22 1068 +#define IDC_MUTE23 1069 +#define IDC_MUTE24 1070 +#define IDC_CHAN1 1071 +#define IDC_CHAN2 1072 +#define IDC_CHAN3 1073 +#define IDC_CHAN4 1074 +#define IDC_CHAN5 1075 +#define IDC_CHAN6 1076 +#define IDC_CHAN7 1077 +#define IDC_CHAN8 1078 +#define IDC_CHAN9 1079 +#define IDC_CHAN10 1080 +#define IDC_CHAN11 1081 +#define IDC_CHAN12 1082 +#define IDC_CHAN13 1083 +#define IDC_CHAN14 1084 +#define IDC_CHAN15 1085 +#define IDC_CHAN16 1086 +#define IDC_CHAN17 1087 +#define IDC_CHAN18 1088 +#define IDC_CHAN19 1089 +#define IDC_CHAN20 1090 +#define IDC_CHAN21 1091 +#define IDC_CHAN22 1092 +#define IDC_CHAN23 1093 +#define IDC_CHAN24 1094 +#define IDC_SADSR1 1096 +#define IDC_SADSR2 1097 +#define IDC_SADSR3 1098 +#define IDC_SADSR4 1099 +#define IDC_SADSR5 1100 +#define IDC_SADSR6 1101 +#define IDC_CHANNUM 1102 +#define IDC_MUTEOFF 1103 +#define IDC_MUTEON 1104 +#define IDC_SADSR7 1105 +#define IDC_CI1 1106 +#define IDC_CI2 1107 +#define IDC_CI3 1108 +#define IDC_CI4 1109 +#define IDC_CI5 1110 +#define IDC_CI6 1111 +#define IDC_CI7 1112 +#define IDC_CI8 1113 +#define IDC_CI9 1114 +#define IDC_CI10 1115 +#define IDC_CI11 1116 +#define IDC_CI12 1117 +#define IDC_CI13 1118 +#define IDC_CI14 1119 +#define IDC_CI15 1120 +#define IDC_CI16 1121 +#define IDC_STA1 1122 +#define IDC_SADSR8 1123 +#define IDC_STA2 1124 +#define IDC_STA3 1125 +#define IDC_STA4 1126 +#define IDC_XA1 1127 +#define IDC_XA2 1128 +#define IDC_XA3 1129 +#define IDC_XA4 1130 +#define IDC_CI17 1131 +#define IDC_CI18 1132 +#define IDC_XA 1133 +#define IDC_WAVFILE 1134 +#define IDC_CI19 1134 +#define IDC_XA5 1135 +#define IDC_RECORD 1135 +#define IDC_XA6 1136 +#define IDC_CORE1 1137 +#define IDC_CORE2 1138 +#define IDC_REGWRITE 1139 +#define IDC_REGREAD 1140 +#define IDC_CLEAR 1141 +#define IDC_LOG 1142 +#define IDC_NOLOG 1143 +#define IDC_REGEDIT 1144 +#define IDC_VALEDIT 1145 +#define IDC_COPY 1146 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 142 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1145 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/reverb.c --- a/Plugins/Input/sexypsf/spu/reverb.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/reverb.c Sun Mar 19 12:51:03 2006 -0800 @@ -1,383 +1,420 @@ -/*************************************************************************** - 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/03/17 - xodnizel -// - Implemented Neill's 44.1Khz-22050Hz downsampling data -// I also need to check if the ~4 sample delay doesn't screw any sounds -// up by making things too out of phase. It could be fixed easily(elsewhere). -// -// 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 -// -//*************************************************************************// - -#define _IN_REVERB - -// will be included from spu.c -#ifdef _IN_SPU - -//////////////////////////////////////////////////////////////////////// -// globals -//////////////////////////////////////////////////////////////////////// - -// REVERB info and timing vars... - -//////////////////////////////////////////////////////////////////////// - -static INLINE s64 g_buffer(int iOff) // get_buffer content helper: takes care about wraps -{ - s16 * p=(s16 *)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)=(s16)BFLIP16((s16)iVal); -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping -{ - s16 * p=(s16 *)spuMem; - iOff=(iOff*4)+rvb.CurrAddr+1; - while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); - while(iOff32767L) iVal=32767L; - *(p+iOff)=(s16)BFLIP16((s16)iVal); -} - -static INLINE void MixREVERBLeftRight(s32 *oleft, s32 *oright, s32 inleft, s32 inright) -{ - static s32 downbuf[2][8]; - static s32 upbuf[2][8]; - static int dbpos=0,ubpos=0; - static s32 downcoeffs[8]={ /* Symmetry is sexy. */ - 1283,5344,10895,15243, - 15243,10895,5344,1283 - }; - int x; - - if(!rvb.StartAddr) // reverb is off - { - rvb.iRVBLeft=rvb.iRVBRight=0; - return; - } - - //if(inleft<-32767 || inleft>32767) printf("%d\n",inleft); - //if(inright<-32767 || inright>32767) printf("%d\n",inright); - downbuf[0][dbpos]=inleft; - downbuf[1][dbpos]=inright; - dbpos=(dbpos+1)&7; - - if(dbpos&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; - s32 INPUT_SAMPLE_L=0; - s32 INPUT_SAMPLE_R=0; - - for(x=0;x<8;x++) - { - INPUT_SAMPLE_L+=(downbuf[0][(dbpos+x)&7]*downcoeffs[x])>>8; /* Lose insignificant - digits to prevent - overflow(check this) */ - INPUT_SAMPLE_R+=(downbuf[1][(dbpos+x)&7]*downcoeffs[x])>>8; - } - - INPUT_SAMPLE_L>>=(16-8); - INPUT_SAMPLE_R>>=(16-8); - { - const s64 IIR_INPUT_A0 = ((g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_L * rvb.IN_COEF_L)>>15); - const s64 IIR_INPUT_A1 = ((g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_R * rvb.IN_COEF_R)>>15); - const s64 IIR_INPUT_B0 = ((g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_L * rvb.IN_COEF_L)>>15); - const s64 IIR_INPUT_B1 = ((g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_R * rvb.IN_COEF_R)>>15); - const s64 IIR_A0 = ((IIR_INPUT_A0 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))>>15); - const s64 IIR_A1 = ((IIR_INPUT_A1 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))>>15); - const s64 IIR_B0 = ((IIR_INPUT_B0 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))>>15); - const s64 IIR_B1 = ((IIR_INPUT_B1 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))>>15); - - 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)>>15) + - ((g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)>>15) + - ((g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)>>15) + - ((g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)>>15); - ACC1 = ((g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)>>15) + - ((g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)>>15) + - ((g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)>>15) + - ((g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)>>15); - - 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)>>15)); - s_buffer(rvb.MIX_DEST_A1, ACC1 - ((FB_A1 * rvb.FB_ALPHA)>>15)); - - s_buffer(rvb.MIX_DEST_B0, ((rvb.FB_ALPHA * ACC0)>>15) - ((FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))>>15) - ((FB_B0 * rvb.FB_X)>>15)); - s_buffer(rvb.MIX_DEST_B1, ((rvb.FB_ALPHA * ACC1)>>15) - ((FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))>>15) - ((FB_B1 * rvb.FB_X)>>15)); - - 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 = ((s64)rvb.iRVBLeft * rvb.VolLeft) >> 14; - rvb.iRVBRight = ((s64)rvb.iRVBRight * rvb.VolRight) >> 14; - - upbuf[0][ubpos]=rvb.iRVBLeft; - upbuf[1][ubpos]=rvb.iRVBRight; - ubpos=(ubpos+1)&7; - } // Bracket hack(et). - } - else // -> reverb off - { - rvb.iRVBLeft=rvb.iRVBRight=0; - return; - } - rvb.CurrAddr++; - if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; - } - else - { - upbuf[0][ubpos]=0; - upbuf[1][ubpos]=0; - ubpos=(ubpos+1)&7; - } - { - s32 retl=0,retr=0; - for(x=0;x<8;x++) - { - retl+=(upbuf[0][(ubpos+x)&7]*downcoeffs[x])>>8; - retr+=(upbuf[1][(ubpos+x)&7]*downcoeffs[x])>>8; - } - retl>>=(16-8-1); /* -1 To adjust for the null padding. */ - retr>>=(16-8-1); - - *oleft+=retl; - *oright+=retr; - } -} - -//////////////////////////////////////////////////////////////////////// - -#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: +// +// 2004/04/04 - Pete +// - changed to SPU2 functionality +// +// 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[2]; +int * sRVBEnd[2]; +int * sRVBStart[2]; + +//////////////////////////////////////////////////////////////////////// +// START REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StartREVERB(int ch) +{ + int core=ch/24; + + if((s_chan[ch].bReverbL || s_chan[ch].bReverbR) && (spuCtrl2[core]&0x80)) // reverb possible? + { + if(iUseReverb==1) s_chan[ch].bRVBActive=1; + } + else s_chan[ch].bRVBActive=0; // else -> no reverb +} + +//////////////////////////////////////////////////////////////////////// +// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf +//////////////////////////////////////////////////////////////////////// + +INLINE void InitREVERB(void) +{ + if(iUseReverb==1) + { + memset(sRVBStart[0],0,NSSIZE*2*4); + memset(sRVBStart[1],0,NSSIZE*2*4); + } +} + +//////////////////////////////////////////////////////////////////////// +// STORE REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StoreREVERB(int ch,int ns) +{ + int core=ch/24; + + if(iUseReverb==0) return; + else + if(iUseReverb==1) // -------------------------------- // Neil's reverb + { + const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume*s_chan[ch].bReverbL)/0x4000; + const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume*s_chan[ch].bReverbR)/0x4000; + + ns<<=1; + + *(sRVBStart[core]+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer + *(sRVBStart[core]+ns+1)+=iRxr; + } +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int g_buffer(int iOff,int core) // get_buffer content helper: takes care about wraps +{ + short * p=(short *)spuMem; + iOff=(iOff)+rvb[core].CurrAddr; + while(iOff>rvb[core].EndAddr) iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1)); + while(iOffrvb[core].EndAddr) iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1)); + while(iOff32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE void s_buffer1(int iOff,int iVal,int core) // set_buffer (+1 sample) content helper: takes care about wraps and clipping +{ + short * p=(short *)spuMem; + iOff=(iOff)+rvb[core].CurrAddr+1; + while(iOff>rvb[core].EndAddr) iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1)); + while(iOff32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixREVERBLeft(int ns,int core) +{ + if(iUseReverb==1) + { + if(!rvb[core].StartAddr || !rvb[core].EndAddr || + rvb[core].StartAddr>=rvb[core].EndAddr) // reverb is off + { + rvb[core].iLastRVBLeft=rvb[core].iLastRVBRight=rvb[core].iRVBLeft=rvb[core].iRVBRight=0; + return 0; + } + + rvb[core].iCnt++; + + if(rvb[core].iCnt&1) // we work on every second left value: downsample to 22 khz + { + if((spuCtrl2[core]&0x80)) // -> reverb on? oki + { + int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1; + + const int INPUT_SAMPLE_L=*(sRVBStart[core]+(ns<<1)); + const int INPUT_SAMPLE_R=*(sRVBStart[core]+(ns<<1)+1); + + const int IIR_INPUT_A0 = (g_buffer(rvb[core].IIR_SRC_A0,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb[core].IN_COEF_L)/32768L; + const int IIR_INPUT_A1 = (g_buffer(rvb[core].IIR_SRC_A1,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb[core].IN_COEF_R)/32768L; + const int IIR_INPUT_B0 = (g_buffer(rvb[core].IIR_SRC_B0,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb[core].IN_COEF_L)/32768L; + const int IIR_INPUT_B1 = (g_buffer(rvb[core].IIR_SRC_B1,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb[core].IN_COEF_R)/32768L; + + const int IIR_A0 = (IIR_INPUT_A0 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_A0,core) * (32768L - rvb[core].IIR_ALPHA))/32768L; + const int IIR_A1 = (IIR_INPUT_A1 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_A1,core) * (32768L - rvb[core].IIR_ALPHA))/32768L; + const int IIR_B0 = (IIR_INPUT_B0 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_B0,core) * (32768L - rvb[core].IIR_ALPHA))/32768L; + const int IIR_B1 = (IIR_INPUT_B1 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_B1,core) * (32768L - rvb[core].IIR_ALPHA))/32768L; + + s_buffer1(rvb[core].IIR_DEST_A0, IIR_A0,core); + s_buffer1(rvb[core].IIR_DEST_A1, IIR_A1,core); + s_buffer1(rvb[core].IIR_DEST_B0, IIR_B0,core); + s_buffer1(rvb[core].IIR_DEST_B1, IIR_B1,core); + + ACC0 = (g_buffer(rvb[core].ACC_SRC_A0,core) * rvb[core].ACC_COEF_A)/32768L + + (g_buffer(rvb[core].ACC_SRC_B0,core) * rvb[core].ACC_COEF_B)/32768L + + (g_buffer(rvb[core].ACC_SRC_C0,core) * rvb[core].ACC_COEF_C)/32768L + + (g_buffer(rvb[core].ACC_SRC_D0,core) * rvb[core].ACC_COEF_D)/32768L; + ACC1 = (g_buffer(rvb[core].ACC_SRC_A1,core) * rvb[core].ACC_COEF_A)/32768L + + (g_buffer(rvb[core].ACC_SRC_B1,core) * rvb[core].ACC_COEF_B)/32768L + + (g_buffer(rvb[core].ACC_SRC_C1,core) * rvb[core].ACC_COEF_C)/32768L + + (g_buffer(rvb[core].ACC_SRC_D1,core) * rvb[core].ACC_COEF_D)/32768L; + + FB_A0 = g_buffer(rvb[core].MIX_DEST_A0 - rvb[core].FB_SRC_A,core); + FB_A1 = g_buffer(rvb[core].MIX_DEST_A1 - rvb[core].FB_SRC_A,core); + FB_B0 = g_buffer(rvb[core].MIX_DEST_B0 - rvb[core].FB_SRC_B,core); + FB_B1 = g_buffer(rvb[core].MIX_DEST_B1 - rvb[core].FB_SRC_B,core); + + s_buffer(rvb[core].MIX_DEST_A0, ACC0 - (FB_A0 * rvb[core].FB_ALPHA)/32768L,core); + s_buffer(rvb[core].MIX_DEST_A1, ACC1 - (FB_A1 * rvb[core].FB_ALPHA)/32768L,core); + + s_buffer(rvb[core].MIX_DEST_B0, (rvb[core].FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb[core].FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb[core].FB_X)/32768L,core); + s_buffer(rvb[core].MIX_DEST_B1, (rvb[core].FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb[core].FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb[core].FB_X)/32768L,core); + + rvb[core].iLastRVBLeft = rvb[core].iRVBLeft; + rvb[core].iLastRVBRight = rvb[core].iRVBRight; + + rvb[core].iRVBLeft = (g_buffer(rvb[core].MIX_DEST_A0,core)+g_buffer(rvb[core].MIX_DEST_B0,core))/3; + rvb[core].iRVBRight = (g_buffer(rvb[core].MIX_DEST_A1,core)+g_buffer(rvb[core].MIX_DEST_B1,core))/3; + + rvb[core].iRVBLeft = (rvb[core].iRVBLeft * rvb[core].VolLeft) / 0x4000; + rvb[core].iRVBRight = (rvb[core].iRVBRight * rvb[core].VolRight) / 0x4000; + + rvb[core].CurrAddr++; + if(rvb[core].CurrAddr>rvb[core].EndAddr) rvb[core].CurrAddr=rvb[core].StartAddr; + + return rvb[core].iLastRVBLeft+(rvb[core].iRVBLeft-rvb[core].iLastRVBLeft)/2; + } + else // -> reverb off + { + rvb[core].iLastRVBLeft=rvb[core].iLastRVBRight=rvb[core].iRVBLeft=rvb[core].iRVBRight=0; + } + + rvb[core].CurrAddr++; + if(rvb[core].CurrAddr>rvb[core].EndAddr) rvb[core].CurrAddr=rvb[core].StartAddr; + } + + return rvb[core].iLastRVBLeft; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixREVERBRight(int core) +{ + if(iUseReverb==1) // Neill's reverb: + { + int i=rvb[core].iLastRVBRight+(rvb[core].iRVBRight-rvb[core].iLastRVBRight)/2; + rvb[core].iLastRVBRight=rvb[core].iRVBRight; + return i; // -> just return the last right reverb val (little bit scaled by the previous right val) + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// + +#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 -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/reverb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/reverb.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,33 @@ +/*************************************************************************** + 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: +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +INLINE void StartREVERB(int ch); +INLINE void StoreREVERB(int ch,int ns); + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/spu.c --- a/Plugins/Input/sexypsf/spu/spu.c Sat Mar 18 14:48:04 2006 -0800 +++ b/Plugins/Input/sexypsf/spu/spu.c Sun Mar 19 12:51:03 2006 -0800 @@ -1,637 +1,1331 @@ -/*************************************************************************** - spu.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/03/01 - linuzappz -// - libraryName changes using ALSA -// -// 2003/02/28 - Pete -// - added option for type of interpolation -// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) -// - added MONO support for MSWindows DirectSound -// -// 2003/02/20 - kode54 -// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault -// -// 2003/02/19 - kode54 -// - moved SPU IRQ handler and changed sample flag processing -// -// 2003/02/18 - kode54 -// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that -// ADSR timing is relative to the frequency at which a sample is played... I guess -// this remains to be seen, and I don't know whether ADSR is applied to noise channels... -// -// 2003/02/09 - kode54 -// - one-shot samples now process the end block before stopping -// - in light of removing fmod hack, now processing ADSR on frequency channel as well -// -// 2003/02/08 - kode54 -// - replaced easy interpolation with gaussian -// - removed fmod averaging hack -// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) -// -// 2003/02/08 - linuzappz -// - small bugfix for one usleep that was 1 instead of 1000 -// - added iDisStereo for no stereo (Linux) -// -// 2003/01/22 - Pete -// - added easy interpolation & small noise adjustments -// -// 2003/01/19 - Pete -// - added Neill's reverb -// -// 2003/01/12 - Pete -// - added recording window handlers -// -// 2003/01/06 - Pete -// - added Neill's ADSR timings -// -// 2002/12/28 - Pete -// - adjusted spu irq handling, fmod handling and loop handling -// -// 2002/08/14 - Pete -// - added extra reverb -// -// 2002/06/08 - linuzappz -// - SPUupdate changed for SPUasync -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#define _IN_SPU - -#include "stdafx.h" -#include "externals.h" -#include "spu.h" -#include "regs.h" -#include "registers.h" - -#include "PsxMem.h" -#include "driver.h" - -//////////////////////////////////////////////////////////////////////// -// globals -//////////////////////////////////////////////////////////////////////// - -// psx buffer / addresses - -static u16 regArea[0x200]; -static u16 spuMem[256*1024]; -static u8 * spuMemC; -static u8 * pSpuIrq=0; -static u8 * pSpuBuffer; - -// user settings -static int iVolume; - -// MAIN infos struct for each channel - -static SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) -static REVERBInfo rvb; - -static u32 dwNoiseVal=1; // global noise generator - -static u16 spuCtrl=0; // some vars to store psx reg infos -static u16 spuStat=0; -static u16 spuIrq=0; -static u32 spuAddr=0xffffffff; // address into spu mem -static int bSPUIsOpen=0; - -static const int f[5][2] = { - { 0, 0 }, - { 60, 0 }, - { 115, -52 }, - { 98, -55 }, - { 122, -60 } }; -s16 * pS; - -//////////////////////////////////////////////////////////////////////// -// CODE AREA -//////////////////////////////////////////////////////////////////////// - -// dirty inline func includes - -#include "reverb.c" -#include "adsr.c" - -// Try this to increase speed. -#include "registers.c" -#include "dma.c" - -//////////////////////////////////////////////////////////////////////// -// helpers for so-called "gauss interpolation" - -#define gval0 (((int *)(&s_chan[ch].SB[29]))[gpos]) -#define gval(x) (((int *)(&s_chan[ch].SB[29]))[(gpos+x)&3]) - -#include "gauss_i.h" - -//////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////// -// START SOUND... called by main thread to setup a new sound on a channel -//////////////////////////////////////////////////////////////////////// - -static INLINE void StartSound(int ch) -{ - StartADSR(ch); - - s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start - - 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].bStop=0; - s_chan[ch].bOn=1; - - s_chan[ch].SB[29]=0; // init our interpolation helpers - s_chan[ch].SB[30]=0; - - s_chan[ch].spos=0x40000L;s_chan[ch].SB[28]=0; // -> start with more decoding -} - -//////////////////////////////////////////////////////////////////////// -// MAIN SPU FUNCTION -// here is the main job handler... thread, timer or direct func call -// basically the whole sound processing is done in this fat func! -//////////////////////////////////////////////////////////////////////// - -static u32 sampcount; -static u32 decaybegin; -static u32 decayend; - -// Counting to 65536 results in full volume offage. -void SPUsetlength(s32 stop, s32 fade) -{ - if(stop==~0) - { - decaybegin=~0; - } - else - { - stop=(stop*441)/10; - fade=(fade*441)/10; - - decaybegin=stop; - decayend=stop+fade; - } -} - -static u32 seektime; -static s32 poo; -int sexypsf_seek(u32 t) -{ - seektime=t*441/10; - if(seektime>sampcount) return(1); - return(0); -} - -#define CLIP(_x) {if(_x>32767) _x=32767; if(_x<-32767) _x=-32767;} -int SPUasync(u32 cycles) -{ - int volmul=iVolume; - static s32 dosampies; - s32 temp; - - poo+=cycles; - dosampies=poo/384; - if(!dosampies) return(1); - poo-=dosampies*384; - temp=dosampies; - - while(temp) - { - s32 revLeft=0, revRight=0; - s32 sl=0, sr=0; - int ch,fa; - - temp--; - //--------------------------------------------------// - //- main channel loop -// - //--------------------------------------------------// - { - for(ch=0;ch take it and calc steps - s_chan[ch].sinc=s_chan[ch].iRawPitch<<4; - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - } - - while(s_chan[ch].spos>=0x10000L) - { - if(s_chan[ch].iSBPos==28) // 28 reached? - { - int predict_nr,shift_factor,flags,d,s; - u8* start;unsigned int nSample; - int s_1,s_2; - - start=s_chan[ch].pCurr; // set up the current pos - - if (start == (u8*)-1) // special "stop" sign - { - s_chan[ch].bOn=0; // -> turn everything off - s_chan[ch].ADSRX.lVolume=0; - s_chan[ch].ADSRX.EnvelopeVol=0; - goto ENDX; // -> and done for this channel - } - - s_chan[ch].iSBPos=0; // Reset buffer play index. - - //////////////////////////////////////////// spu irq handler here? mmm... do it later - - s_1=s_chan[ch].s_1; - s_2=s_chan[ch].s_2; - - predict_nr=(int)*start;start++; - shift_factor=predict_nr&0xf; - predict_nr >>= 4; - flags=(int)*start;start++; - - // -------------------------------------- // - // Decode new samples into s_chan[ch].SB[0 through 27] - for (nSample=0;nSample<28;start++) - { - d=(int)*start; - s=((d&0xf)<<12); - if(s&0x8000) s|=0xffff0000; - - fa=(s >> shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1;s_1=fa; - s=((d & 0xf0) << 8); - - s_chan[ch].SB[nSample++]=fa; - - if(s&0x8000) s|=0xffff0000; - fa=(s>>shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1;s_1=fa; - - s_chan[ch].SB[nSample++]=fa; - } - - //////////////////////////////////////////// irq check - - if(spuCtrl&0x40) // irq active? - { - 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))) - { - //extern s32 spuirqvoodoo; - s_chan[ch].iIrqDone=1; // -> debug flag - SPUirq(); - //puts("IRQ"); - //if(spuirqvoodoo!=-1) - //{ - // spuirqvoodoo=temp*384; - // temp=0; - //} - } - } - - //////////////////////////////////////////// flag handler - - if((flags&4) && (!s_chan[ch].bIgnoreLoop)) - s_chan[ch].pLoop=start-16; // loop adress - - if(flags&1) // 1: stop/loop - { - // We play this block out first... - //if(!(flags&2)) // 1+2: do loop... otherwise: stop - if(flags!=3 || s_chan[ch].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 = (u8*)-1; - } - else - { - start = s_chan[ch].pLoop; - } - } - - 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 - - if((spuCtrl&0x4000)==0) fa=0; // muted? - else CLIP(fa); - - { - int gpos; - gpos = s_chan[ch].SB[28]; - gval0 = fa; - gpos = (gpos+1) & 3; - s_chan[ch].SB[28] = gpos; - } - s_chan[ch].spos -= 0x10000L; - } - - //////////////////////////////////////////////// - // 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 - - if(s_chan[ch].bNoise) - { - //puts("Noise"); - if((dwNoiseVal<<=1)&0x80000000L) - { - dwNoiseVal^=0x0040001L; - fa=((dwNoiseVal>>2)&0x7fff); - fa=-fa; - } - else fa=(dwNoiseVal>>2)&0x7fff; - - // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val - fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl&0x3f00)>>9))+1)); - if(fa>32767L) fa=32767L; - if(fa<-32767L) fa=-32767L; - s_chan[ch].iOldNoise=fa; - - } //---------------------------------------- - else // NO NOISE (NORMAL SAMPLE DATA) HERE - { - int vl, vr, gpos; - vl = (s_chan[ch].spos >> 6) & ~3; - gpos = s_chan[ch].SB[28]; - vr=(gauss[vl]*gval0)>>9; - vr+=(gauss[vl+1]*gval(1))>>9; - vr+=(gauss[vl+2]*gval(2))>>9; - vr+=(gauss[vl+3]*gval(3))>>9; - fa = vr>>2; - } - - s_chan[ch].sval = (MixADSR(ch) * fa)>>10; // / 1023; // add adsr - if(s_chan[ch].bFMod==2) // fmod freq channel - { - int NP=s_chan[ch+1].iRawPitch; - NP=((32768L+s_chan[ch].sval)*NP)>>15; ///32768L; - - if(NP>0x3fff) NP=0x3fff; - if(NP<0x1) NP=0x1; - - // mmmm... if I do this, all is screwed - // s_chan[ch+1].iRawPitch=NP; - - NP=(44100L*NP)/(4096L); // calc frequency - - s_chan[ch+1].iActFreq=NP; - s_chan[ch+1].iUsedFreq=NP; - s_chan[ch+1].sinc=(((NP/10)<<16)/4410); - if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; - - // mmmm... set up freq decoding positions? - // s_chan[ch+1].iSBPos=28; - // s_chan[ch+1].spos=0x10000L; - } - else - { - ////////////////////////////////////////////// - // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) - int tmpl,tmpr; - - tmpl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14; - tmpr=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14; - - sl+=tmpl; - sr+=tmpr; - - if(((rvb.Enabled>>ch)&1) && (spuCtrl&0x80)) - { - revLeft+=tmpl; - revRight+=tmpr; - } - } - - s_chan[ch].spos += s_chan[ch].sinc; - ENDX: ; - } - } - - /////////////////////////////////////////////////////// - // mix all channels (including reverb) into one buffer - MixREVERBLeftRight(&sl,&sr,revLeft,revRight); - if(sampcount>=decaybegin) - { - s32 dmul; - if(decaybegin!=(u32)~0) // Is anyone REALLY going to be playing a song - // for 13 hours? - { - if(sampcount>=decayend) return(0); - dmul=256-(256*(sampcount-decaybegin)/(decayend-decaybegin)); - sl=(sl*dmul)>>8; - sr=(sr*dmul)>>8; - } - } - sampcount++; - sl=(sl*volmul)>>8; - sr=(sr*volmul)>>8; - - //{ - // static double asl=0; - // static double asr=0; - - // asl+=(sl-asl)/5; - // asr+=(sl-asr)/5; - - //sl-=asl; - //sr-=asr; - - // if(sl>32767 || sl < -32767) printf("Left: %d, %f\n",sl,asl); - // if(sr>32767 || sr < -32767) printf("Right: %d, %f\n",sl,asl); - //} - - if(sl>32767) sl=32767; if(sl<-32767) sl=-32767; - if(sr>32767) sr=32767; if(sr<-32767) sr=-32767; - *pS++=sl; - *pS++=sr; - } - - return(1); -} - -void sexypsf_stop(void) -{ - decaybegin=decayend=0; -} - -void SPUendflush(void) -{ - if((seektime!=(u32)~0) && seektime>sampcount) - { - pS=(s16 *)pSpuBuffer; - sexypsf_update(0,0); - } - else if((u8*)pS>((u8*)pSpuBuffer+1024)) - { - sexypsf_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer); - pS=(s16 *)pSpuBuffer; - } -} - -#ifdef TIMEO -static u64 begintime; -static u64 SexyTime64(void) -{ - struct timeval tv; - u64 ret; - - gettimeofday(&tv,0); - ret=tv.tv_sec; - ret*=1000000; - ret+=tv.tv_usec; - return(ret); -} -#endif -//////////////////////////////////////////////////////////////////////// -// INIT/EXIT STUFF -//////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////// -// SPUINIT: this func will be called first by the main emu -//////////////////////////////////////////////////////////////////////// - -int SPUinit(void) -{ - spuMemC=(u8*)spuMem; // just small setup - memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); - memset((void *)&rvb,0,sizeof(REVERBInfo)); - memset(regArea,0,sizeof(regArea)); - memset(spuMem,0,sizeof(spuMem)); - InitADSR(); - sampcount=poo=0; - seektime=(u32)~0; - #ifdef TIMEO - begintime=SexyTime64(); - #endif - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// SETUPSTREAMS: init most of the spu buffers -//////////////////////////////////////////////////////////////////////// - -void SetupStreams(void) -{ - int i; - - pSpuBuffer=(u8*)malloc(32768); // alloc mixing buffer - pS=(s16 *)pSpuBuffer; - - for(i=0;i init sustain - s_chan[i].iIrqDone=0; - s_chan[i].pLoop=spuMemC; - s_chan[i].pStart=spuMemC; - s_chan[i].pCurr=spuMemC; - } -} - -//////////////////////////////////////////////////////////////////////// -// REMOVESTREAMS: free most buffer -//////////////////////////////////////////////////////////////////////// - -void RemoveStreams(void) -{ - free(pSpuBuffer); // free mixing buffer - pSpuBuffer=NULL; - - #ifdef TIMEO - { - u64 tmp; - tmp=SexyTime64(); - tmp-=begintime; - if(tmp) - tmp=(u64)sampcount*1000000/tmp; - printf("%lld samples per second\n",tmp); - } - #endif -} - - -//////////////////////////////////////////////////////////////////////// -// SPUOPEN: called by main emu after init -//////////////////////////////////////////////////////////////////////// - -int SPUopen(void) -{ - if(bSPUIsOpen) return 0; // security for some stupid main emus - spuIrq=0; - - spuStat=spuCtrl=0; - spuAddr=0xffffffff; - dwNoiseVal=1; - - spuMemC=(u8*)spuMem; - memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN)); - pSpuIrq=0; - - iVolume=128; //85; - SetupStreams(); // prepare streaming - - bSPUIsOpen=1; - - return 1; -} - -//////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////// -// SPUCLOSE: called before shutdown -//////////////////////////////////////////////////////////////////////// - -int SPUclose(void) -{ - if(!bSPUIsOpen) return 0; // some security - - bSPUIsOpen=0; // no more open - - RemoveStreams(); // no more streaming - - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// SPUSHUTDOWN: called by main emu on final exit -//////////////////////////////////////////////////////////////////////// - -int SPUshutdown(void) -{ - return 0; -} - +/*************************************************************************** + spu.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: +// +// 2005/08/29 - Pete +// - changed to 48Khz output +// +// 2004/12/25 - Pete +// - inc'd version for pcsx2-0.7 +// +// 2004/04/18 - Pete +// - changed all kind of things in the plugin +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2003/04/07 - Eric +// - adjusted cubic interpolation algorithm +// +// 2003/03/16 - Eric +// - added cubic interpolation +// +// 2003/03/01 - linuzappz +// - libraryName changes using ALSA +// +// 2003/02/28 - Pete +// - added option for type of interpolation +// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) +// - added MONO support for MSWindows DirectSound +// +// 2003/02/20 - kode54 +// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault +// +// 2003/02/19 - kode54 +// - moved SPU IRQ handler and changed sample flag processing +// +// 2003/02/18 - kode54 +// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that +// ADSR timing is relative to the frequency at which a sample is played... I guess +// this remains to be seen, and I don't know whether ADSR is applied to noise channels... +// +// 2003/02/09 - kode54 +// - one-shot samples now process the end block before stopping +// - in light of removing fmod hack, now processing ADSR on frequency channel as well +// +// 2003/02/08 - kode54 +// - replaced easy interpolation with gaussian +// - removed fmod averaging hack +// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) +// +// 2003/02/08 - linuzappz +// - small bugfix for one usleep that was 1 instead of 1000 +// - added iDisStereo for no stereo (Linux) +// +// 2003/01/22 - Pete +// - added easy interpolation & small noise adjustments +// +// 2003/01/19 - Pete +// - added Neill's reverb +// +// 2003/01/12 - Pete +// - added recording window handlers +// +// 2003/01/06 - Pete +// - added Neill's ADSR timings +// +// 2002/12/28 - Pete +// - adjusted spu irq handling, fmod handling and loop handling +// +// 2002/08/14 - Pete +// - added extra reverb +// +// 2002/06/08 - linuzappz +// - SPUupdate changed for SPUasync +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_SPU + +#include "externals.h" +#include "cfg.h" +#include "dsoundoss.h" +#include "regs.h" +#include "debug.h" +#include "record.h" +#include "resource.h" +#include "dma.h" + +//////////////////////////////////////////////////////////////////////// +// spu version infos/name +//////////////////////////////////////////////////////////////////////// + +const unsigned char version = 4; +const unsigned char revision = 1; +const unsigned char build = 3; +#ifdef _WINDOWS +static char * libraryName = "P.E.Op.S. SPU2 DSound Driver"; +#else +#ifndef USEALSA +static char * libraryName = "P.E.Op.S. SPU2 OSS Driver"; +#else +static char * libraryName = "P.E.Op.S. SPU2 ALSA Driver"; +#endif +#endif +static char * libraryInfo = "P.E.Op.S. SPU2 Driver V1.3\nCoded by Pete Bernert and the P.E.Op.S. team\n"; + +//////////////////////////////////////////////////////////////////////// +// globals +//////////////////////////////////////////////////////////////////////// + +// psx buffer / addresses + +unsigned short regArea[32*1024]; +unsigned short spuMem[1*1024*1024]; +unsigned char * spuMemC; +unsigned char * pSpuIrq[2]; +unsigned char * pSpuBuffer; + +// sexypsf tracking stuff + +static unsigned long seektime; +static unsigned long sampcount; +static unsigned long decaybegin; +static unsigned long decayend; + +void SPUsetlength(long stop, long fade) +{ + if(stop==~0) + { + decaybegin=~0; + } + else + { + stop=(stop*441)/10; + fade=(fade*441)/10; + + decaybegin=stop; + decayend=stop+fade; + } +} + +// user settings + +int iUseXA=1; +int iVolume=3; +int iXAPitch=1; +int iUseTimer=2; +int iSPUIRQWait=1; +int iDebugMode=0; +int iRecordMode=0; +int iUseReverb=0; +int iUseInterpolation=2; +int iDisStereo=0; + +// MAIN infos struct for each channel + +SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) +REVERBInfo rvb[2]; + +unsigned long dwNoiseVal=1; // global noise generator + +unsigned short spuCtrl2[2]; // some vars to store psx reg infos +unsigned short spuStat2[2]; +unsigned long spuIrq2[2]; +unsigned long spuAddr2[2]; // address into spu mem +unsigned long spuRvbAddr2[2]; +unsigned long spuRvbAEnd2[2]; +int bEndThread=0; // thread handlers +int bThreadEnded=0; +int bSpuInit=0; +int bSPUIsOpen=0; + +#ifdef _WINDOWS +HWND hWMain=0; // window handle +HWND hWDebug=0; +HWND hWRecord=0; +static HANDLE hMainThread; +#else +// 2003/06/07 - Pete +#ifndef NOTHREADLIB +static pthread_t thread = -1; // thread id (linux) +#endif +#endif + +unsigned long dwNewChannel2[2]; // flags for faster testing, if new channel starts +unsigned long dwEndChannel2[2]; + +// UNUSED IN PS2 YET +void (CALLBACK *irqCallback)(void)=0; // func of main emu, called on spu irq +void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0; + +// certain globals (were local before, but with the new timeproc I need em global) + +const int f[5][2] = { { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 } }; +int SSumR[NSSIZE]; +int SSumL[NSSIZE]; +int iCycle=0; +short * pS; + +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 + +//////////////////////////////////////////////////////////////////////// +// CODE AREA +//////////////////////////////////////////////////////////////////////// + +// dirty inline func includes + +#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 unsigned long long SexyTime64(void) +{ + struct timeval tv; + unsigned long long ret; + + gettimeofday(&tv,0); + ret=tv.tv_sec; + ret*=1000000; + ret+=tv.tv_usec; + return(ret); +} + +INLINE void InterpolateUp(int ch) +{ + if(s_chan[ch].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 :) + + s_chan[ch].SB[32]=0; + + if(id1>0) // curr delta positive + { + if(id2id1) + {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} + else + if(id2>(id1<<1)) + s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; + else + s_chan[ch].SB[28]=(id1*s_chan[ch].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 + { + s_chan[ch].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]; + } + else // no flags? add bigger val (if possible), calc smaller step, set flag1 + s_chan[ch].SB[29]+=s_chan[ch].SB[28]; +} + +// +// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm +// + +INLINE void InterpolateDown(int ch) +{ + if(s_chan[ch].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 + } +} + +//////////////////////////////////////////////////////////////////////// +// 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]) + +#include "gauss_i.h" + +//////////////////////////////////////////////////////////////////////// + +#include "xa.c" + +//////////////////////////////////////////////////////////////////////// +// START SOUND... called by main thread to setup a new sound on a channel +//////////////////////////////////////////////////////////////////////// + +INLINE void StartSound(int ch) +{ + dwNewChannel2[ch/24]&=~(1<<(ch%24)); // clear new channel bit + dwEndChannel2[ch/24]&=~(1<<(ch%24)); // clear end channel bit + + StartADSR(ch); + StartREVERB(ch); + + s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start + + 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].bStop=0; + s_chan[ch].bOn=1; + + s_chan[ch].SB[29]=0; // init our interpolation helpers + s_chan[ch].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 +} + +//////////////////////////////////////////////////////////////////////// +// MAIN SPU FUNCTION +// here is the main job handler... thread, timer or direct func call +// 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 5 +#define PAUSE_L 5000 + +//////////////////////////////////////////////////////////////////////// + +int iSpuAsyncWait=0; + +#ifdef _WINDOWS +static VOID CALLBACK MAINProc(UINT nTimerId,UINT msg,DWORD dwUser,DWORD dwParam1, DWORD dwParam2) +#else +static void *MAINThread(void *arg) +#endif +{ + 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 gpos,bIRQReturn=0; + + 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. + // if not, we wait (thread) or return (timer/spuasync) + // until enuff free place is available/a new channel gets + // started + + if(dwNewChannel2[0] || dwNewChannel2[1]) // new channel should start immedately? + { // (at least one bit 0 ... MAXCHANNEL is set?) + iSecureStart++; // -> set iSecure + 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 + + while(!iSecureStart && !bEndThread // no new start? no thread end? +#if 0 + && (SoundGetBytesBuffered()>TESTSIZE) +#endif + ) // and still enuff data in sound buffer? + { + iSecureStart=0; // reset secure + +#ifdef _WINDOWS + if(iUseTimer) // no-thread mode? + { + if(iUseTimer==1) // -> ok, timer mode 1: setup a oneshot timer of x ms to wait + timeSetEvent(PAUSE_W,1,MAINProc,0,TIME_ONESHOT); + return; // -> and done this time (timer mode 1 or 2) + } + // win thread mode: + Sleep(PAUSE_W); // sleep for x ms (win) +#else + if(iUseTimer) return 0; // linux no-thread mode? bye + usleep(PAUSE_L); // else sleep for x ms (linux) +#endif + + if(dwNewChannel2[0] || dwNewChannel2[1]) + iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop + } + + //--------------------------------------------------// continue from irq handling in timer mode? + + if(lastch>=0) // will be -1 if no continue is pending + { + ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue + goto GOON; // -> directly jump to the continue point + } + + //--------------------------------------------------// + //- main channel loop -// + //--------------------------------------------------// + { + for(ch=0;ch 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 + } + + ns=0; + while(ns=0x10000L) + { + if(s_chan[ch].iSBPos==28) // 28 reached? + { + start=s_chan[ch].pCurr; // set up the current pos + + if (start == (unsigned char*)-1) // special "stop" sign + { + s_chan[ch].bOn=0; // -> turn everything off + s_chan[ch].ADSRX.lVolume=0; + s_chan[ch].ADSRX.EnvelopeVol=0; + goto ENDX; // -> and done for this channel + } + + s_chan[ch].iSBPos=0; + + //////////////////////////////////////////// spu irq handler here? mmm... do it later + + s_1=s_chan[ch].s_1; + s_2=s_chan[ch].s_2; + + predict_nr=(int)*start;start++; + shift_factor=predict_nr&0xf; + predict_nr >>= 4; + flags=(int)*start;start++; + + // -------------------------------------- // + + for (nSample=0;nSample<28;start++) + { + d=(int)*start; + s=((d&0xf)<<12); + if(s&0x8000) s|=0xffff0000; + + fa=(s >> shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1;s_1=fa; + s=((d & 0xf0) << 8); + + s_chan[ch].SB[nSample++]=fa; + + if(s&0x8000) s|=0xffff0000; + fa=(s>>shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1;s_1=fa; + + s_chan[ch].SB[nSample++]=fa; + } + + //////////////////////////////////////////// irq check + + if(spuCtrl2[ch/24]&0x40) // some irq active? + { + if((pSpuIrq[ch/24] > start-16 && // irq address reached? + pSpuIrq[ch/24] <= start) || + ((flags&1) && // special: irq on looping addr, when stop/loop flag is set + (pSpuIrq[ch/24] > s_chan[ch].pLoop-16 && + pSpuIrq[ch/24] <= s_chan[ch].pLoop))) + { + s_chan[ch].iIrqDone=1; // -> debug flag + + if(irqCallback) irqCallback(); // -> call main emu (not supported in SPU2 right now) + else + { + if(ch<24) InterruptDMA4(); // -> let's see what is happening if we call our irqs instead ;) + else InterruptDMA7(); + } + + if(iSPUIRQWait) // -> option: wait after irq for main emu + { + iSpuAsyncWait=1; + bIRQReturn=1; + } + } + } + + //////////////////////////////////////////// flag handler + + if((flags&4) && (!s_chan[ch].bIgnoreLoop)) + s_chan[ch].pLoop=start-16; // loop adress + + if(flags&1) // 1: stop/loop + { + dwEndChannel2[ch/24]|=(1<<(ch%24)); + + // We play this block out first... + //if(!(flags&2)|| s_chan[ch].pLoop==NULL) + // 1+2: do loop... otherwise: stop + if(flags!=3 || s_chan[ch].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 = s_chan[ch].pLoop; + } + } + + s_chan[ch].pCurr=start; // store values for next cycle + s_chan[ch].s_1=s_1; + s_chan[ch].s_2=s_2; + + //////////////////////////////////////////// + + if(bIRQReturn) // special return for "spu irq - wait for cpu action" + { + bIRQReturn=0; + if(iUseTimer!=2) + { + DWORD dwWatchTime=SexyTime64()+2500; + + while(iSpuAsyncWait && !bEndThread && + SexyTime64()32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + } + + if(iUseInterpolation>=2) // gauss/cubic interpolation + { + gpos = s_chan[ch].SB[28]; + gval0 = fa; + gpos = (gpos+1) & 3; + s_chan[ch].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 + } + else s_chan[ch].SB[29]=fa; // no interpolation + + s_chan[ch].spos -= 0x10000L; + } + + //////////////////////////////////////////////// + // 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 + + if(s_chan[ch].bNoise) + { + if((dwNoiseVal<<=1)&0x80000000L) + { + dwNoiseVal^=0x0040001L; + fa=((dwNoiseVal>>2)&0x7fff); + fa=-fa; + } + else fa=(dwNoiseVal>>2)&0x7fff; + + // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val + fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl2[ch/24]&0x3f00)>>9))+1)); + if(fa>32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + s_chan[ch].iOldNoise=fa; + + if(iUseInterpolation<2) // no gauss/cubic interpolation? + s_chan[ch].SB[29] = fa; // -> store noise val in "current sample" slot + } //---------------------------------------- + else // NO NOISE (NORMAL SAMPLE DATA) HERE + {//------------------------------------------// + if(iUseInterpolation==3) // cubic interpolation + { + long xd; + xd = ((s_chan[ch].spos) >> 1)+1; + gpos = s_chan[ch].SB[28]; + + fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0; + fa *= (xd - (2<<15)) / 6; + fa >>= 15; + fa += gval(2) - gval(1) - gval(1) + gval0; + fa *= (xd - (1<<15)) >> 1; + fa >>= 15; + fa += gval(1) - gval0; + fa *= xd; + fa >>= 15; + fa = fa + gval0; + } + //------------------------------------------// + else + if(iUseInterpolation==2) // gauss interpolation + { + int vl, vr; + vl = (s_chan[ch].spos >> 6) & ~3; + gpos = s_chan[ch].SB[28]; + vr=(gauss[vl]*gval0)&~2047; + vr+=(gauss[vl+1]*gval(1))&~2047; + vr+=(gauss[vl+2]*gval(2))&~2047; + vr+=(gauss[vl+3]*gval(3))&~2047; + fa = vr>>11; +/* + vr=(gauss[vl]*gval0)>>9; + vr+=(gauss[vl+1]*gval(1))>>9; + vr+=(gauss[vl+2]*gval(2))>>9; + vr+=(gauss[vl+3]*gval(3))>>9; + fa = vr>>2; +*/ + } + //------------------------------------------// + else + if(iUseInterpolation==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]; + } + //------------------------------------------// + else fa=s_chan[ch].SB[29]; // no interpolation + } + + s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // add adsr + + if(s_chan[ch].bFMod==2) // fmod freq channel + { + int NP=s_chan[ch+1].iRawPitch; + + NP=((32768L+s_chan[ch].sval)*NP)/32768L; // mmm... I still need to adjust that to 1/48 khz... we will wait for the first game/demo using it to decide how to do it :) + + if(NP>0x3fff) NP=0x3fff; + if(NP<0x1) NP=0x1; + + NP=(44100L*NP)/(4096L); // calc frequency + + s_chan[ch+1].iActFreq=NP; + s_chan[ch+1].iUsedFreq=NP; + s_chan[ch+1].sinc=(((NP/10)<<16)/4410); + if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; + if(iUseInterpolation==1) // freq change in sipmle interpolation mode + s_chan[ch+1].SB[32]=1; + +// mmmm... set up freq decoding positions? +// s_chan[ch+1].iSBPos=28; +// s_chan[ch+1].spos=0x10000L; + } + else + { + ////////////////////////////////////////////// + // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) + + if(s_chan[ch].iMute) + s_chan[ch].sval=0; // debug mute + else + { + if(s_chan[ch].bVolumeL) + SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L; + if(s_chan[ch].bVolumeR) + SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L; + } + + ////////////////////////////////////////////// + // now let us store sound data for reverb + + if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns); + } + + //////////////////////////////////////////////// + // ok, go on until 1 ms data of this channel is collected + + ns++; + s_chan[ch].spos += s_chan[ch].sinc; + + } +ENDX: ; + } + } + + //---------------------------------------------------// + //- here we have another 1 ms of sound data + //---------------------------------------------------// + // mix XA infos (if any) + sampcount++; + + + if(XAPlay!=XAFeed || XARepeat) MixXA(); + + /////////////////////////////////////////////////////// + // mix all channels (including reverb) into one buffer + + if(iDisStereo) // no stereo? + { + int dl,dr; + for(ns=0;ns32767) dl=32767; + + SSumR[ns]+=MixREVERBRight(0); + SSumR[ns]+=MixREVERBRight(1); + + 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;ns32767) d=32767; + *pS++=d; + + SSumR[ns]+=MixREVERBRight(0); + SSumR[ns]+=MixREVERBRight(1); + + d=SSumR[ns]/voldiv;SSumR[ns]=0; + if(d<-32767) d=-32767;if(d>32767) d=32767; + *pS++=d; + } + + InitREVERB(); + + } + + // end of big main loop... + + bThreadEnded=1; + +#ifndef _WINDOWS + return 0; +#endif +} + +void sexypsf_stop(void) +{ + decaybegin=decayend=0; +} + +void SPUendflush(void) +{ + if((seektime!=~0) && seektime>sampcount) + { + pS=(short *)pSpuBuffer; + sexypsf_update(0,0); + } + else if((unsigned short *)pS>((unsigned short *)pSpuBuffer+1024)) + { + sexypsf_update((unsigned short *)pSpuBuffer,(unsigned short *)pS-(unsigned short*)pSpuBuffer); + pS=(short *)pSpuBuffer; + } +} + +//////////////////////////////////////////////////////////////////////// +// WINDOWS THREAD... simply calls the timer func and stays forever :) +//////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS + +DWORD WINAPI MAINThreadEx(LPVOID lpParameter) +{ + MAINProc(0,0,0,0,0); + return 0; +} + +#endif + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPU ASYNC... even newer epsxe func +// 1 time every 'cycle' cycles... harhar +//////////////////////////////////////////////////////////////////////// + +int sexypsf_seek(unsigned long t) +{ + seektime=t*441/10; + if(seektime>sampcount) return(1); + return(0); +} + +EXPORT_GCC void CALLBACK SPU2async(unsigned long cycle) +{ + if(iSpuAsyncWait) + { + iSpuAsyncWait++; + if(iSpuAsyncWait<=64) return; + iSpuAsyncWait=0; + } + +#ifdef _WINDOWS + if(iDebugMode==2) + { + if(IsWindow(hWDebug)) DestroyWindow(hWDebug); + hWDebug=0;iDebugMode=0; + } + if(iRecordMode==2) + { + if(IsWindow(hWRecord)) DestroyWindow(hWRecord); + hWRecord=0;iRecordMode=0; + } +#endif + + 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 + +#ifdef _WINDOWS + MAINProc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks +#else + MAINThread(0); // -> linux high-compat mode +#endif + } +} + +//////////////////////////////////////////////////////////////////////// +// XA AUDIO +//////////////////////////////////////////////////////////////////////// + +// dummy func... not used yet +EXPORT_GCC void CALLBACK SPU2playADPCMchannel(xa_decode_t *xap) +{ + if(!iUseXA) return; // no XA? bye + if(!xap) return; + if(!xap->freq) return; // no xa freq ? bye + + FeedXA(xap); // call main XA feeder +} + +//////////////////////////////////////////////////////////////////////// +// INIT/EXIT STUFF +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPUINIT: this func will be called first by the main emu +//////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS +static HINSTANCE hIRE = NULL; +#endif + +EXPORT_GCC long CALLBACK SPU2init(void) +{ + spuMemC=(unsigned char *)spuMem; // just small setup + memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); + memset(rvb,0,2*sizeof(REVERBInfo)); + + InitADSR(); + +#ifdef _WINDOWS + if(hIRE==NULL) hIRE=LoadLibrary("Riched32.dll "); // needed for debug output +#endif + + 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)); + pS=(short *)pSpuBuffer; // setup soundbuffer pointer + + bEndThread=0; // init thread vars + bThreadEnded=0; + bSpuInit=1; // flag: we are inited + +#ifdef _WINDOWS + + if(iUseTimer==1) // windows: use timer + { + timeBeginPeriod(1); + timeSetEvent(1,1,MAINProc,0,TIME_ONESHOT); + } + else + if(iUseTimer==0) // windows: use thread + { + //_beginthread(MAINThread,0,NULL); + DWORD dw; + hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw); + SetThreadPriority(hMainThread, + //THREAD_PRIORITY_TIME_CRITICAL); + THREAD_PRIORITY_HIGHEST); + } + +#else + +#ifndef NOTHREADLIB + if(!iUseTimer) // linux: use thread + { + pthread_create(&thread, NULL, MAINThread, NULL); + } +#endif + +#endif +} + +//////////////////////////////////////////////////////////////////////// +// REMOVETIMER: kill threads/timers +//////////////////////////////////////////////////////////////////////// + +void RemoveTimer(void) +{ + bEndThread=1; // raise flag to end thread + +#ifdef _WINDOWS + + if(iUseTimer!=2) // windows thread? + { + while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended + Sleep(5L); + } + if(iUseTimer==1) timeEndPeriod(1); // windows timer? stop it + +#else + +#ifndef NOTHREADLIB + if(!iUseTimer) // linux tread? + { + int i=0; + while(!bThreadEnded && i<2000) {usleep(1000L);i++;} // -> wait until thread has ended + if(thread!=-1) {pthread_cancel(thread);thread=-1;} // -> cancel thread anyway + } +#endif + +#endif + + 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 + + i=NSSIZE*2; + + sRVBStart[0] = (int *)malloc(i*4); // alloc reverb buffer + memset(sRVBStart[0],0,i*4); + sRVBEnd[0] = sRVBStart[0] + i; + sRVBPlay[0] = sRVBStart[0]; + sRVBStart[1] = (int *)malloc(i*4); // alloc reverb buffer + memset(sRVBStart[1],0,i*4); + sRVBEnd[1] = sRVBStart[1] + i; + sRVBPlay[1] = sRVBStart[1]; + + XAStart = // alloc xa buffer (not used here anyway) + (unsigned long *)malloc(48000*4); + XAPlay = XAStart; + XAFeed = XAStart; + XAEnd = XAStart + 48000; + + for(i=0;i init sustain + s_chan[i].iMute=0; + s_chan[i].iIrqDone=0; + s_chan[i].pLoop=spuMemC; + s_chan[i].pStart=spuMemC; + s_chan[i].pCurr=spuMemC; + } +} + +//////////////////////////////////////////////////////////////////////// +// REMOVESTREAMS: free most buffer +//////////////////////////////////////////////////////////////////////// + +void RemoveStreams(void) +{ + free(pSpuBuffer); // free mixing buffer + pSpuBuffer=NULL; + free(sRVBStart[0]); // free reverb buffer + sRVBStart[0]=0; + free(sRVBStart[1]); // free reverb buffer + sRVBStart[1]=0; + free(XAStart); // free XA buffer + XAStart=0; + +/* + int i; + for(i=0;i +#include +#include "mmsystem.h" +#include #include -#include -#include + +// enable that for auxprintf(); +//#define SMALLDEBUG +//#include +//void auxprintf (LPCTSTR pFormat, ...); + +#define INLINE __inline + +////////////////////////////////////////////////////////// +// LINUX +////////////////////////////////////////////////////////// +#else + +#define EXPORT_GCC + +#include +#include +#include +#include +#include +#include +#include +#ifndef NOTHREADLIB +#include +#endif +#define RRand(range) (random()%range) +#include +#include +#include + +#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 + +#endif + +#include "psemuxa.h" diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/xa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/xa.c Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,363 @@ +/*************************************************************************** + xa.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/02/18 - kode54 +// - added gaussian interpolation +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_XA + +// will be included from spu.c +#ifdef _IN_SPU + +//////////////////////////////////////////////////////////////////////// +// XA GLOBALS +//////////////////////////////////////////////////////////////////////// + +xa_decode_t * xapGlobal=0; + +unsigned long * XAFeed = NULL; +unsigned long * XAPlay = NULL; +unsigned long * XAStart = NULL; +unsigned long * XAEnd = NULL; + +unsigned long XARepeat = 0; +unsigned long XALastVal = 0; + +int iLeftXAVol = 32767; +int iRightXAVol = 32767; + +static int gauss_ptr = 0; +static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + +#define gvall0 gauss_window[gauss_ptr] +#define gvall(x) gauss_window[(gauss_ptr+x)&3] +#define gvalr0 gauss_window[4+gauss_ptr] +#define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)] + +//////////////////////////////////////////////////////////////////////// +// MIX XA +//////////////////////////////////////////////////////////////////////// + +INLINE void MixXA(void) +{ + int ns; + + for(ns=0;ns>16)&0xffff)) * iRightXAVol)/32767; + } + + if(XAPlay==XAFeed && XARepeat) + { + XARepeat--; + for(;ns>16)&0xffff)) * iRightXAVol)/32767; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// FEED XA +//////////////////////////////////////////////////////////////////////// + +INLINE void FeedXA(xa_decode_t *xap) +{ + int sinc,spos,i,iSize,iPlace,vl,vr; + + if(!bSPUIsOpen) return; + + xapGlobal = xap; // store info for save states + XARepeat = 100; // set up repeat + + iSize=((44100*xap->nsamples)/xap->freq); // get size + if(!iSize) return; // none? bye + + if(XAFeed=10) + { + if(!dwFPS) dwFPS=1; + dw1=1000000/dwFPS; + if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1; + else dwL1=dw1; + dw2=(xap->freq*100/xap->nsamples); + if((!dw1)||((dw2+100)>=dw1)) iLastSize=0; + else + { + iLastSize=iSize*dw2/dw1; + if(iLastSize>iPlace) iLastSize=iPlace; + iSize=iLastSize; + } + iFPSCnt=0;dwFPS=0; + } + else + { + if(iLastSize) iSize=iLastSize; + } + } + //----------------------------------------------------// + + spos=0x10000L; + sinc = (xap->nsamples << 16) / iSize; // calc freq by num / size + + if(xap->stereo) + { + unsigned long * pS=(unsigned long *)xap->pcm; + unsigned long l=0; + + if(iXAPitch) + { + long l1,l2;short s; + for(i=0;i=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; + } + } + + s=(short)LOWORD(l); + l1=s; + l1=(l1*iPlace)/iSize; + if(l1<-32767) l1=-32767; + if(l1> 32767) l1=32767; + s=(short)HIWORD(l); + l2=s; + l2=(l2*iPlace)/iSize; + if(l2<-32767) l2=-32767; + if(l2> 32767) l2=32767; + l=(l1&0xffff)|(l2<<16); + + *XAFeed++=l; + + if(XAFeed==XAEnd) XAFeed=XAStart; + if(XAFeed==XAPlay) + { + if(XAPlay!=XAStart) XAFeed=XAPlay-1; + break; + } + + spos += sinc; + } + } + else + { + for(i=0;i=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; + + if(XAFeed==XAEnd) XAFeed=XAStart; + if(XAFeed==XAPlay) + { + if(XAPlay!=XAStart) XAFeed=XAPlay-1; + break; + } + + spos += sinc; + } + } + } + else + { + unsigned short * pS=(unsigned short *)xap->pcm; + unsigned long l;short s=0; + + if(iXAPitch) + { + long l1; + for(i=0;i=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=(l1*iPlace)/iSize; + if(l1<-32767) l1=-32767; + if(l1> 32767) l1=32767; + l=(l1&0xffff)|(l1<<16); + *XAFeed++=l; + + if(XAFeed==XAEnd) XAFeed=XAStart; + if(XAFeed==XAPlay) + { + if(XAPlay!=XAStart) XAFeed=XAPlay-1; + break; + } + + spos += sinc; + } + } + else + { + for(i=0;i=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; + } + + *XAFeed++=(l|(l<<16)); + + if(XAFeed==XAEnd) XAFeed=XAStart; + if(XAFeed==XAPlay) + { + if(XAPlay!=XAStart) XAFeed=XAPlay-1; + break; + } + + spos += sinc; + } + } + } +} + +#endif + diff -r 74576869a506 -r 61e7332e0652 Plugins/Input/sexypsf/spu/xa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/sexypsf/spu/xa.h Sun Mar 19 12:51:03 2006 -0800 @@ -0,0 +1,29 @@ +/*************************************************************************** + 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. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + + +INLINE void MixXA(void); +INLINE void FeedXA(xa_decode_t *xap);