diff options
| author | SND\dario86_cp <SND\dario86_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2011-03-12 18:54:28 +0000 |
|---|---|---|
| committer | SND\dario86_cp <SND\dario86_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2011-03-12 18:54:28 +0000 |
| commit | a58cfdac407bc1d8fedc11acd924b275ba28cc51 (patch) | |
| tree | b4c2e08c34ef1bfe0ba947ef8eed931c9a43fc0e /plugins/dfsound | |
| parent | 9bdd06684bcc627c06ddcf4c406f6b48f0dfe389 (diff) | |
| download | pcsxr-a58cfdac407bc1d8fedc11acd924b275ba28cc51.tar.gz | |
Commited patch in issue #8171 (by darktjm).
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@64524 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'plugins/dfsound')
| -rw-r--r-- | plugins/dfsound/Makefile.am | 2 | ||||
| -rw-r--r-- | plugins/dfsound/adsr.c | 1527 | ||||
| -rw-r--r-- | plugins/dfsound/adsr.h | 39 | ||||
| -rw-r--r-- | plugins/dfsound/cfg.c | 350 | ||||
| -rw-r--r-- | plugins/dfsound/externals.h | 720 | ||||
| -rw-r--r-- | plugins/dfsound/freeze.c | 458 | ||||
| -rw-r--r-- | plugins/dfsound/nullsnd.c | 2 | ||||
| -rw-r--r-- | plugins/dfsound/oss.c | 3 | ||||
| -rw-r--r-- | plugins/dfsound/pulseaudio.c | 2 | ||||
| -rw-r--r-- | plugins/dfsound/reverb.c | 925 | ||||
| -rw-r--r-- | plugins/dfsound/sdl.c | 2 | ||||
| -rw-r--r-- | plugins/dfsound/spu.c | 44 | ||||
| -rw-r--r-- | plugins/dfsound/spu.h | 43 | ||||
| -rw-r--r-- | plugins/dfsound/spucfg-0.1df/main.c | 6 | ||||
| -rw-r--r-- | plugins/dfsound/stdafx.h | 44 | ||||
| -rw-r--r-- | plugins/dfsound/xa.c | 246 |
16 files changed, 2209 insertions, 2204 deletions
diff --git a/plugins/dfsound/Makefile.am b/plugins/dfsound/Makefile.am index 094d5d6a..ca62b32e 100644 --- a/plugins/dfsound/Makefile.am +++ b/plugins/dfsound/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I../../include +AM_CPPFLAGS = -I../../include -I../../libpcsxcore bindir = @libdir@/games/psemu/ libdir = @libdir@/games/psemu/ diff --git a/plugins/dfsound/adsr.c b/plugins/dfsound/adsr.c index f75a5127..d0f7808e 100644 --- a/plugins/dfsound/adsr.c +++ b/plugins/dfsound/adsr.c @@ -1,763 +1,764 @@ -/***************************************************************************
- adsr.c - description
- -------------------
- begin : Wed May 15 2002
- copyright : (C) 2002 by Pete Bernert
- email : BlackDove@addcom.de
- ***************************************************************************/
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. See also the license.txt file for *
- * additional informations. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#define _IN_ADSR
-
-// will be included from spu.c
-#ifdef _IN_SPU
-
-////////////////////////////////////////////////////////////////////////
-// ADSR func
-////////////////////////////////////////////////////////////////////////
-
-/*
-ADSR
-- Dr. Hell (Xebra PS1 emu)
-- Accurate (!)
-- http://drhell.web.fc2.com
-
-
-Envelope increase
-0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2))
-48+: 7 - (RATE & 3) / (1 <<((RATE>> 2) - 11))
-
-Envelope decrease
-0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2))
-48+: -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11))
-
-
-Exponential increase
-0000-5FFF = (rate + 0)
-6000+ = (rate + 8)
-
-Exponential decrease
-(molecules (decrease) * level)>> 15
-
------------------------------------
-
-Fraction (release rate)
-1<<((4*32>>2)-11) = 1<<21
-
-
-Increase
-40 = (7-0)<<(11-10) = 7<<1 = 14
-41 = (7-1)<<(11-10) = 6<<1 = 12
-42 = (7-2)<<(11-10) = 5<<1 = 10
-43 = (7-3)<<(11-10) = 4<<1 = 8
-
-44 = (7-0)<<(11-11) = 7<<0 = 7
-45 = (7-1)<<(11-11) = 6<<0 = 6
-46 = (7-2)<<(11-11) = 5<<0 = 5
-47 = (7-3)<<(11-11) = 4<<0 = 4
---
-48 = (7-0) / 1<<(12-11) = 7 / 2
-49 = (7-1) / 1<<(12-11) = 6 / 2
-50 = (7-2) / 1<<(12-11) = 5 / 2
-51 = (7-3) / 1<<(12-11) = 4 / 2
-
-52 = (7-0) / 1<<(13-11) = 7 / 4
-56 = (7-0) / 1<<(14-11) = 7 / 8
-60 = (7-0) / 1<<(15-11) = 7 / 16
-
-
-Decrease
-40 = (-8+0)<<(11-10) = -8<<1 = -16
-41 = (-8+1)<<(11-10) = -7<<1 = -14
-42 = (-8+2)<<(11-10) = -6<<1 = -12
-43 = (-8+3)<<(11-10) = -5<<1 = -10
-
-44 = (-8+0)<<(11-11) = -8<<0 = -8
-45 = (-8+1)<<(11-11) = -7<<0 = -7
-46 = (-8+2)<<(11-11) = -6<<0 = -6
-47 = (-8+3)<<(11-11) = -5<<0 = -5
---
-48 = (-8+0) / 1<<(12-11) = -8 / 2
-49 = (-8+1) / 1<<(12-11) = -7 / 2
-50 = (-8+2) / 1<<(12-11) = -6 / 2
-51 = (-8+3) / 1<<(12-11) = -5 / 2
-*/
-
-
-static int RateTableAdd[128];
-static int RateTableAdd_f[128];
-static int RateTableSub[128];
-static int RateTableSub_f[128];
-static const int RateTable_denom = 1 << (( (4*32)>>2) - 11);
-
-void InitADSR(void) // INIT ADSR
-{
- int lcv;
-
- memset(RateTableAdd,0,sizeof(int)*128);
- memset(RateTableAdd_f,0,sizeof(int)*128);
- memset(RateTableSub,0,sizeof(int)*128);
- memset(RateTableSub_f,0,sizeof(int)*128);
-
-
- // Optimize table - Dr. Hell ADSR math
- for( lcv=0; lcv<48; lcv++ ) {
- RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2));
- RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2));
-
- RateTableAdd_f[lcv] = 0;
- RateTableSub_f[lcv] = 0;
- }
-
- for( lcv=48; lcv<128; lcv++ ) {
- int denom;
-
- denom = 1 << ((lcv>>2) - 11);
-
- // whole
- RateTableAdd[lcv] = (7 - (lcv&3)) / denom;
- RateTableSub[lcv] = (-8 + (lcv&3)) / denom;
-
- // fraction
- RateTableAdd_f[lcv] = (7 - (lcv&3)) % denom;
- RateTableSub_f[lcv] = (-8 + (lcv&3)) % denom;
-
- RateTableAdd_f[lcv] *= RateTable_denom / denom;
- RateTableSub_f[lcv] *= RateTable_denom / denom;
-
- // goofy compiler - mod
- if( RateTableSub_f[lcv] > 0 ) RateTableSub_f[lcv] = -RateTableSub_f[lcv];
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE void StartADSR(int ch) // MIX ADSR
-{
- s_chan[ch].ADSRX.lVolume=1; // and init some adsr vars
- s_chan[ch].ADSRX.State=0;
- s_chan[ch].ADSRX.EnvelopeVol=0;
- s_chan[ch].ADSRX.EnvelopeVol_f=0;
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE int MixADSR(int ch) // MIX ADSR
-{
- int EnvelopeVol = s_chan[ch].ADSRX.EnvelopeVol;
- int EnvelopeVol_f = s_chan[ch].ADSRX.EnvelopeVol_f;
-
-
- // dead volume - voice on
- if( s_chan[ch].iSilent == 2 ) {
- if( s_chan[ch].bStop ) s_chan[ch].bOn = 0;
- return 0;
- }
-
-
- if(s_chan[ch].bStop) // should be stopped:
- { // do release
- if(s_chan[ch].ADSRX.ReleaseModeExp)
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ] * EnvelopeVol ) >> 15;
- else
- EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.ReleaseRate * 4 ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
- if(EnvelopeVol<0)
- {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- // don't stop if this chan can still cause irqs
- if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq))
- s_chan[ch].bOn=0;
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- else // not stopped yet?
- {
- if(s_chan[ch].ADSRX.State==0) // -> attack
- {
- if(s_chan[ch].ADSRX.AttackModeExp)
- {
- if(EnvelopeVol>=0x6000) {
- EnvelopeVol+=RateTableAdd[s_chan[ch].ADSRX.AttackRate + 8];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 8];
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
- }
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0];
- }
-
- if( EnvelopeVol_f >= RateTable_denom ) {
- EnvelopeVol_f -= RateTable_denom;
- EnvelopeVol++;
- }
-
- if(EnvelopeVol>=0x8000)
- {
- EnvelopeVol=0x7FFF;
- EnvelopeVol_f=RateTable_denom;
- s_chan[ch].ADSRX.State=1;
- }
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- //--------------------------------------------------//
- if(s_chan[ch].ADSRX.State==1) // -> decay
- {
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.DecayRate * 4 ] * EnvelopeVol ) >> 15;
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.DecayRate * 4 ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
- if(EnvelopeVol<0) {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- }
-
- // FF7 cursor - use Neill's 4-bit accuracy
- if( ((EnvelopeVol>>11)&0xf) <= s_chan[ch].ADSRX.SustainLevel)
- {
- s_chan[ch].ADSRX.State=2;
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- //--------------------------------------------------//
- if(s_chan[ch].ADSRX.State==2) // -> sustain
- {
- if(s_chan[ch].ADSRX.SustainIncrease)
- {
- if(s_chan[ch].ADSRX.SustainModeExp)
- {
- if(EnvelopeVol>=0x6000) {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 8];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 8];
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
- }
- }
- else {
- EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0];
- EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0];
- }
-
- if( EnvelopeVol_f >= RateTable_denom ) {
- EnvelopeVol_f -= RateTable_denom;
- EnvelopeVol++;
- }
-
- if(EnvelopeVol >= 0x8000)
- {
- EnvelopeVol=0x7FFF;
- EnvelopeVol_f=RateTable_denom;
- }
- }
- else
- {
- if(s_chan[ch].ADSRX.SustainModeExp)
- EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.SustainRate ] * EnvelopeVol ) >> 15;
- else
- EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.SustainRate ];
-
- EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.SustainRate ];
- if( EnvelopeVol_f < 0 ) {
- EnvelopeVol_f += RateTable_denom;
- EnvelopeVol--;
- }
-
-
- if(EnvelopeVol<0) {
- EnvelopeVol=0;
- EnvelopeVol_f=0;
- }
- }
-
-
- s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol;
- s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f;
- s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5;
- return EnvelopeVol>>5;
- }
- }
- return 0;
-}
-
-#endif
-
-/*
-James Higgs ADSR investigations:
-
-PSX SPU Envelope Timings
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-First, here is an extract from doomed's SPU doc, which explains the basics
-of the SPU "volume envelope":
-
-*** doomed doc extract start ***
-
---------------------------------------------------------------------------
-Voices.
---------------------------------------------------------------------------
-The SPU has 24 hardware voices. These voices can be used to reproduce sample
-data, noise or can be used as frequency modulator on the next voice.
-Each voice has it's own programmable ADSR envelope filter. The main volume
-can be programmed independently for left and right output.
-
-The ADSR envelope filter works as follows:
-Ar = Attack rate, which specifies the speed at which the volume increases
- from zero to it's maximum value, as soon as the note on is given. The
- slope can be set to lineair or exponential.
-Dr = Decay rate specifies the speed at which the volume decreases to the
- sustain level. Decay is always decreasing exponentially.
-Sl = Sustain level, base level from which sustain starts.
-Sr = Sustain rate is the rate at which the volume of the sustained note
- increases or decreases. This can be either lineair or exponential.
-Rr = Release rate is the rate at which the volume of the note decreases
- as soon as the note off is given.
-
- lvl |
- ^ | /\Dr __
- Sl _| _ / _ \__--- \
- | / ---__ \ Rr
- | /Ar Sr \ \
- | / \\
- |/___________________\________
- ->time
-
-The overal volume can also be set to sweep up or down lineairly or
-exponentially from it's current value. This can be done seperately
-for left and right.
-
-Relevant SPU registers:
--------------------------------------------------------------
-$1f801xx8 Attack/Decay/Sustain level
-bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
-desc.|Am| Ar |Dr |Sl |
-
-Am 0 Attack mode Linear
- 1 Exponential
-
-Ar 0-7f attack rate
-Dr 0-f decay rate
-Sl 0-f sustain level
--------------------------------------------------------------
-$1f801xxa Sustain rate, Release Rate.
-bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
-desc.|Sm|Sd| 0| Sr |Rm|Rr |
-
-Sm 0 sustain rate mode linear
- 1 exponential
-Sd 0 sustain rate mode increase
- 1 decrease
-Sr 0-7f Sustain Rate
-Rm 0 Linear decrease
- 1 Exponential decrease
-Rr 0-1f Release Rate
-
-Note: decay mode is always Expontial decrease, and thus cannot
-be set.
--------------------------------------------------------------
-$1f801xxc Current ADSR volume
-bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
-desc.|ADSRvol |
-
-ADSRvol Returns the current envelope volume when
- read.
--- James' Note: return range: 0 -> 32767
-
-*** doomed doc extract end ***
-
-By using a small PSX proggie to visualise the envelope as it was played,
-the following results for envelope timing were obtained:
-
-1. Attack rate value (linear mode)
-
- Attack value range: 0 -> 127
-
- Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 |
- -----------------------------------------------------------------
- Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890|
-
- Note: frames is no. of PAL frames to reach full volume (100%
- amplitude)
-
- Hmm, noticing that the time taken to reach full volume doubles
- every time we add 4 to our attack value, we know the equation is
- of form:
- frames = k * 2 ^ (value / 4)
-
- (You may ponder about envelope generator hardware at this point,
- or maybe not... :)
-
- By substituting some stuff and running some checks, we get:
-
- k = 0.00257 (close enuf)
-
- therefore,
- frames = 0.00257 * 2 ^ (value / 4)
- If you just happen to be writing an emulator, then you can probably
- use an equation like:
-
- %volume_increase_per_tick = 1 / frames
-
-
- ------------------------------------
- Pete:
- ms=((1<<(value>>2))*514)/10000
- ------------------------------------
-
-2. Decay rate value (only has log mode)
-
- Decay value range: 0 -> 15
-
- Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
- ------------------------------------------------
- frames | | | | | 6 | 12 | 24 | 47 |
-
- Note: frames here is no. of PAL frames to decay to 50% volume.
-
- formula: frames = k * 2 ^ (value)
-
- Substituting, we get: k = 0.00146
-
- Further info on logarithmic nature:
- frames to decay to sustain level 3 = 3 * frames to decay to
- sustain level 9
-
- Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
- 50% volume.
-
- Frag it - just use linear approx.
-
- ------------------------------------
- Pete:
- ms=((1<<value)*292)/10000
- ------------------------------------
-
-
-3. Sustain rate value (linear mode)
-
- Sustain rate range: 0 -> 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<<value)*446)/10000
- ------------------------------------
-
-
-Other notes:
-
-Log stuff not figured out. You may get some clues from the "Decay rate"
-stuff above. For emu purposes it may not be important - use linear
-approx.
-
-To get timings in millisecs, multiply frames by 20.
-
-
-
-- James Higgs 17/6/2000
-james7780@yahoo.com
-
-//---------------------------------------------------------------
-
-OLD adsr mixing according to james' rules... has to be called
-every one millisecond
-
-
- long v,v2,lT,l1,l2,l3;
-
- if(s_chan[ch].bStop) // psx wants to stop? -> 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<l1) // -> 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(lT<l1) // attack
- { // no exp mode used (yet)
-// if(s_chan[ch].ADSR.AttackModeExp)
-// {
-// v=(v*lT)/l1;
-// }
-// else
- {
- v=(v*lT)/l1;
- }
- if(v==0) v=1;
- }
- else // decay
- { // should be exp, but who cares? ;)
- l2=s_chan[ch].ADSR.DecayTime;
- v2=s_chan[ch].ADSR.SustainLevel;
-
- lT-=l1;
- if(lT<l2)
- {
- v-=(((v-v2)*lT)/l2);
- }
- else // sustain
- { // no exp mode used (yet)
- l3=s_chan[ch].ADSR.SustainTime;
- lT-=l2;
- if(s_chan[ch].ADSR.SustainModeDec>0)
- {
- 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. * + * * + ***************************************************************************/ + +#include "stdafx.h" +#include "adsr.h" + +#define _IN_ADSR + +// will be included from spu.c +#ifdef _IN_SPU + +//////////////////////////////////////////////////////////////////////// +// ADSR func +//////////////////////////////////////////////////////////////////////// + +/* +ADSR +- Dr. Hell (Xebra PS1 emu) +- Accurate (!) +- http://drhell.web.fc2.com + + +Envelope increase +0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2)) +48+: 7 - (RATE & 3) / (1 <<((RATE>> 2) - 11)) + +Envelope decrease +0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2)) +48+: -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11)) + + +Exponential increase +0000-5FFF = (rate + 0) +6000+ = (rate + 8) + +Exponential decrease +(molecules (decrease) * level)>> 15 + +----------------------------------- + +Fraction (release rate) +1<<((4*32>>2)-11) = 1<<21 + + +Increase +40 = (7-0)<<(11-10) = 7<<1 = 14 +41 = (7-1)<<(11-10) = 6<<1 = 12 +42 = (7-2)<<(11-10) = 5<<1 = 10 +43 = (7-3)<<(11-10) = 4<<1 = 8 + +44 = (7-0)<<(11-11) = 7<<0 = 7 +45 = (7-1)<<(11-11) = 6<<0 = 6 +46 = (7-2)<<(11-11) = 5<<0 = 5 +47 = (7-3)<<(11-11) = 4<<0 = 4 +-- +48 = (7-0) / 1<<(12-11) = 7 / 2 +49 = (7-1) / 1<<(12-11) = 6 / 2 +50 = (7-2) / 1<<(12-11) = 5 / 2 +51 = (7-3) / 1<<(12-11) = 4 / 2 + +52 = (7-0) / 1<<(13-11) = 7 / 4 +56 = (7-0) / 1<<(14-11) = 7 / 8 +60 = (7-0) / 1<<(15-11) = 7 / 16 + + +Decrease +40 = (-8+0)<<(11-10) = -8<<1 = -16 +41 = (-8+1)<<(11-10) = -7<<1 = -14 +42 = (-8+2)<<(11-10) = -6<<1 = -12 +43 = (-8+3)<<(11-10) = -5<<1 = -10 + +44 = (-8+0)<<(11-11) = -8<<0 = -8 +45 = (-8+1)<<(11-11) = -7<<0 = -7 +46 = (-8+2)<<(11-11) = -6<<0 = -6 +47 = (-8+3)<<(11-11) = -5<<0 = -5 +-- +48 = (-8+0) / 1<<(12-11) = -8 / 2 +49 = (-8+1) / 1<<(12-11) = -7 / 2 +50 = (-8+2) / 1<<(12-11) = -6 / 2 +51 = (-8+3) / 1<<(12-11) = -5 / 2 +*/ + + +static int RateTableAdd[128]; +static int RateTableAdd_f[128]; +static int RateTableSub[128]; +static int RateTableSub_f[128]; +static const int RateTable_denom = 1 << (( (4*32)>>2) - 11); + +void InitADSR(void) // INIT ADSR +{ + int lcv; + + memset(RateTableAdd,0,sizeof(int)*128); + memset(RateTableAdd_f,0,sizeof(int)*128); + memset(RateTableSub,0,sizeof(int)*128); + memset(RateTableSub_f,0,sizeof(int)*128); + + + // Optimize table - Dr. Hell ADSR math + for( lcv=0; lcv<48; lcv++ ) { + RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2)); + RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2)); + + RateTableAdd_f[lcv] = 0; + RateTableSub_f[lcv] = 0; + } + + for( lcv=48; lcv<128; lcv++ ) { + int denom; + + denom = 1 << ((lcv>>2) - 11); + + // whole + RateTableAdd[lcv] = (7 - (lcv&3)) / denom; + RateTableSub[lcv] = (-8 + (lcv&3)) / denom; + + // fraction + RateTableAdd_f[lcv] = (7 - (lcv&3)) % denom; + RateTableSub_f[lcv] = (-8 + (lcv&3)) % denom; + + RateTableAdd_f[lcv] *= RateTable_denom / denom; + RateTableSub_f[lcv] *= RateTable_denom / denom; + + // goofy compiler - mod + if( RateTableSub_f[lcv] > 0 ) RateTableSub_f[lcv] = -RateTableSub_f[lcv]; + } +} + +//////////////////////////////////////////////////////////////////////// + +INLINE void StartADSR(int ch) // MIX ADSR +{ + s_chan[ch].ADSRX.lVolume=1; // and init some adsr vars + s_chan[ch].ADSRX.State=0; + s_chan[ch].ADSRX.EnvelopeVol=0; + s_chan[ch].ADSRX.EnvelopeVol_f=0; +} + +//////////////////////////////////////////////////////////////////////// + +INLINE int MixADSR(int ch) // MIX ADSR +{ + int EnvelopeVol = s_chan[ch].ADSRX.EnvelopeVol; + int EnvelopeVol_f = s_chan[ch].ADSRX.EnvelopeVol_f; + + + // dead volume - voice on + if( s_chan[ch].iSilent == 2 ) { + if( s_chan[ch].bStop ) s_chan[ch].bOn = 0; + return 0; + } + + + if(s_chan[ch].bStop) // should be stopped: + { // do release + if(s_chan[ch].ADSRX.ReleaseModeExp) + EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ] * EnvelopeVol ) >> 15; + else + EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.ReleaseRate * 4 ]; + + EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.ReleaseRate * 4 ]; + if( EnvelopeVol_f < 0 ) { + EnvelopeVol_f += RateTable_denom; + EnvelopeVol--; + } + + if(EnvelopeVol<0) + { + EnvelopeVol=0; + EnvelopeVol_f=0; + // don't stop if this chan can still cause irqs + if(!(spuCtrl&0x40) || (s_chan[ch].pCurr > pSpuIrq && s_chan[ch].pLoop > pSpuIrq)) + s_chan[ch].bOn=0; + } + + + s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; + s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; + s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; + return EnvelopeVol>>5; + } + else // not stopped yet? + { + if(s_chan[ch].ADSRX.State==0) // -> attack + { + if(s_chan[ch].ADSRX.AttackModeExp) + { + if(EnvelopeVol>=0x6000) { + EnvelopeVol+=RateTableAdd[s_chan[ch].ADSRX.AttackRate + 8]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 8]; + } + else { + EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0]; + } + } + else { + EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.AttackRate + 0]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.AttackRate + 0]; + } + + if( EnvelopeVol_f >= RateTable_denom ) { + EnvelopeVol_f -= RateTable_denom; + EnvelopeVol++; + } + + if(EnvelopeVol>=0x8000) + { + EnvelopeVol=0x7FFF; + EnvelopeVol_f=RateTable_denom; + s_chan[ch].ADSRX.State=1; + } + + s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; + s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; + s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; + return EnvelopeVol>>5; + } + //--------------------------------------------------// + if(s_chan[ch].ADSRX.State==1) // -> decay + { + EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.DecayRate * 4 ] * EnvelopeVol ) >> 15; + + EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.DecayRate * 4 ]; + if( EnvelopeVol_f < 0 ) { + EnvelopeVol_f += RateTable_denom; + EnvelopeVol--; + } + + if(EnvelopeVol<0) { + EnvelopeVol=0; + EnvelopeVol_f=0; + } + + // FF7 cursor - use Neill's 4-bit accuracy + if( ((EnvelopeVol>>11)&0xf) <= s_chan[ch].ADSRX.SustainLevel) + { + s_chan[ch].ADSRX.State=2; + } + + + s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; + s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; + s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; + return EnvelopeVol>>5; + } + //--------------------------------------------------// + if(s_chan[ch].ADSRX.State==2) // -> sustain + { + if(s_chan[ch].ADSRX.SustainIncrease) + { + if(s_chan[ch].ADSRX.SustainModeExp) + { + if(EnvelopeVol>=0x6000) { + EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 8]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 8]; + } + else { + EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0]; + } + } + else { + EnvelopeVol+=RateTableAdd[ s_chan[ch].ADSRX.SustainRate + 0]; + EnvelopeVol_f += RateTableAdd_f[ s_chan[ch].ADSRX.SustainRate + 0]; + } + + if( EnvelopeVol_f >= RateTable_denom ) { + EnvelopeVol_f -= RateTable_denom; + EnvelopeVol++; + } + + if(EnvelopeVol >= 0x8000) + { + EnvelopeVol=0x7FFF; + EnvelopeVol_f=RateTable_denom; + } + } + else + { + if(s_chan[ch].ADSRX.SustainModeExp) + EnvelopeVol += ( RateTableSub[ s_chan[ch].ADSRX.SustainRate ] * EnvelopeVol ) >> 15; + else + EnvelopeVol += RateTableSub[ s_chan[ch].ADSRX.SustainRate ]; + + EnvelopeVol_f += RateTableSub_f[ s_chan[ch].ADSRX.SustainRate ]; + if( EnvelopeVol_f < 0 ) { + EnvelopeVol_f += RateTable_denom; + EnvelopeVol--; + } + + + if(EnvelopeVol<0) { + EnvelopeVol=0; + EnvelopeVol_f=0; + } + } + + + s_chan[ch].ADSRX.EnvelopeVol=EnvelopeVol; + s_chan[ch].ADSRX.EnvelopeVol_f=EnvelopeVol_f; + s_chan[ch].ADSRX.lVolume=EnvelopeVol>>5; + return EnvelopeVol>>5; + } + } + return 0; +} + +#endif + +/* +James Higgs ADSR investigations: + +PSX SPU Envelope Timings +~~~~~~~~~~~~~~~~~~~~~~~~ + +First, here is an extract from doomed's SPU doc, which explains the basics +of the SPU "volume envelope": + +*** doomed doc extract start *** + +-------------------------------------------------------------------------- +Voices. +-------------------------------------------------------------------------- +The SPU has 24 hardware voices. These voices can be used to reproduce sample +data, noise or can be used as frequency modulator on the next voice. +Each voice has it's own programmable ADSR envelope filter. The main volume +can be programmed independently for left and right output. + +The ADSR envelope filter works as follows: +Ar = Attack rate, which specifies the speed at which the volume increases + from zero to it's maximum value, as soon as the note on is given. The + slope can be set to lineair or exponential. +Dr = Decay rate specifies the speed at which the volume decreases to the + sustain level. Decay is always decreasing exponentially. +Sl = Sustain level, base level from which sustain starts. +Sr = Sustain rate is the rate at which the volume of the sustained note + increases or decreases. This can be either lineair or exponential. +Rr = Release rate is the rate at which the volume of the note decreases + as soon as the note off is given. + + lvl | + ^ | /\Dr __ + Sl _| _ / _ \__--- \ + | / ---__ \ Rr + | /Ar Sr \ \ + | / \\ + |/___________________\________ + ->time + +The overal volume can also be set to sweep up or down lineairly or +exponentially from it's current value. This can be done seperately +for left and right. + +Relevant SPU registers: +------------------------------------------------------------- +$1f801xx8 Attack/Decay/Sustain level +bit |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00| +desc.|Am| Ar |Dr |Sl | + +Am 0 Attack mode Linear + 1 Exponential + +Ar 0-7f attack rate +Dr 0-f decay rate +Sl 0-f sustain level +------------------------------------------------------------- +$1f801xxa Sustain rate, Release Rate. +bit |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00| +desc.|Sm|Sd| 0| Sr |Rm|Rr | + +Sm 0 sustain rate mode linear + 1 exponential +Sd 0 sustain rate mode increase + 1 decrease +Sr 0-7f Sustain Rate +Rm 0 Linear decrease + 1 Exponential decrease +Rr 0-1f Release Rate + +Note: decay mode is always Expontial decrease, and thus cannot +be set. +------------------------------------------------------------- +$1f801xxc Current ADSR volume +bit |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00| +desc.|ADSRvol | + +ADSRvol Returns the current envelope volume when + read. +-- James' Note: return range: 0 -> 32767 + +*** doomed doc extract end *** + +By using a small PSX proggie to visualise the envelope as it was played, +the following results for envelope timing were obtained: + +1. Attack rate value (linear mode) + + Attack value range: 0 -> 127 + + Value | 48 | 52 | 56 | 60 | 64 | 68 | 72 | | 80 | + ----------------------------------------------------------------- + Frames | 11 | 21 | 42 | 84 | 169| 338| 676| |2890| + + Note: frames is no. of PAL frames to reach full volume (100% + amplitude) + + Hmm, noticing that the time taken to reach full volume doubles + every time we add 4 to our attack value, we know the equation is + of form: + frames = k * 2 ^ (value / 4) + + (You may ponder about envelope generator hardware at this point, + or maybe not... :) + + By substituting some stuff and running some checks, we get: + + k = 0.00257 (close enuf) + + therefore, + frames = 0.00257 * 2 ^ (value / 4) + If you just happen to be writing an emulator, then you can probably + use an equation like: + + %volume_increase_per_tick = 1 / frames + + + ------------------------------------ + Pete: + ms=((1<<(value>>2))*514)/10000 + ------------------------------------ + +2. Decay rate value (only has log mode) + + Decay value range: 0 -> 15 + + Value | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + ------------------------------------------------ + frames | | | | | 6 | 12 | 24 | 47 | + + Note: frames here is no. of PAL frames to decay to 50% volume. + + formula: frames = k * 2 ^ (value) + + Substituting, we get: k = 0.00146 + + Further info on logarithmic nature: + frames to decay to sustain level 3 = 3 * frames to decay to + sustain level 9 + + Also no. of frames to 25% volume = roughly 1.85 * no. of frames to + 50% volume. + + Frag it - just use linear approx. + + ------------------------------------ + Pete: + ms=((1<<value)*292)/10000 + ------------------------------------ + + +3. Sustain rate value (linear mode) + + Sustain rate range: 0 -> 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<<value)*446)/10000 + ------------------------------------ + + +Other notes: + +Log stuff not figured out. You may get some clues from the "Decay rate" +stuff above. For emu purposes it may not be important - use linear +approx. + +To get timings in millisecs, multiply frames by 20. + + + +- James Higgs 17/6/2000 +james7780@yahoo.com + +//--------------------------------------------------------------- + +OLD adsr mixing according to james' rules... has to be called +every one millisecond + + + long v,v2,lT,l1,l2,l3; + + if(s_chan[ch].bStop) // psx wants to stop? -> 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<l1) // -> 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(lT<l1) // attack + { // no exp mode used (yet) +// if(s_chan[ch].ADSR.AttackModeExp) +// { +// v=(v*lT)/l1; +// } +// else + { + v=(v*lT)/l1; + } + if(v==0) v=1; + } + else // decay + { // should be exp, but who cares? ;) + l2=s_chan[ch].ADSR.DecayTime; + v2=s_chan[ch].ADSR.SustainLevel; + + lT-=l1; + if(lT<l2) + { + v-=(((v-v2)*lT)/l2); + } + else // sustain + { // no exp mode used (yet) + l3=s_chan[ch].ADSR.SustainTime; + lT-=l2; + if(s_chan[ch].ADSR.SustainModeDec>0) + { + if(l3!=0) v2+=((v-v2)*lT)/l3; + else v2=v; + } + else + { + if(l3!=0) v2-=(v2*lT)/l3; + else v2=v; + } + + if(v2>v) v2=v; + if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;} + + v=v2; + } + } + } + + //----------------------------------------------------// + // ok, done for this channel, so increase time + + s_chan[ch].ADSR.lTime+=1; // 1 = 1.020408f ms; + + if(v>1024) v=1024; // adjust volume + if(v<0) v=0; + s_chan[ch].ADSR.lVolume=v; // store act volume + + return v; // return the volume factor +*/ + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +/* +----------------------------------------------------------------------------- +Neill Corlett +Playstation SPU envelope timing notes +----------------------------------------------------------------------------- + +This is preliminary. This may be wrong. But the model described herein fits +all of my experimental data, and it's just simple enough to sound right. + +ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally. +The value returned by channel reg 0xC is (envelope_level>>16). + +Each sample, an increment or decrement value will be added to or +subtracted from this envelope level. + +Create the rate log table. The values double every 4 entries. + entry #0 = 4 + + 4, 5, 6, 7, + 8,10,12,14, + 16,20,24,28, ... + + entry #40 = 4096... + entry #44 = 8192... + entry #48 = 16384... + entry #52 = 32768... + entry #56 = 65536... + +increments and decrements are in terms of ratelogtable[n] +n may exceed the table bounds (plan on n being between -32 and 127). +table values are all clipped between 0x00000000 and 0x3FFFFFFF + +when you "voice on", the envelope is always fully reset. +(yes, it may click. the real thing does this too.) + +envelope level begins at zero. + +each state happens for at least 1 cycle +(transitions are not instantaneous) +this may result in some oddness: if the decay rate is uberfast, it will cut +the envelope from full down to half in one sample, potentially skipping over +the sustain level + +ATTACK +------ +- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and + proceed to DECAY. + +Linear attack mode: +- line extends upward to 0x7FFFFFFF +- increment per sample is ratelogtable[(Ar^0x7F)-0x10] + +Logarithmic attack mode: +if envelope_level < 0x60000000: + - line extends upward to 0x60000000 + - increment per sample is ratelogtable[(Ar^0x7F)-0x10] +else: + - line extends upward to 0x7FFFFFFF + - increment per sample is ratelogtable[(Ar^0x7F)-0x18] + +DECAY +----- +- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN. + Do not clip to the sustain level. +- current line ends at (envelope_level & 0x07FFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(4*(Dr^0x1F))-0x18+0] + 1: ratelogtable[(4*(Dr^0x1F))-0x18+4] + 2: ratelogtable[(4*(Dr^0x1F))-0x18+6] + 3: ratelogtable[(4*(Dr^0x1F))-0x18+8] + 4: ratelogtable[(4*(Dr^0x1F))-0x18+9] + 5: ratelogtable[(4*(Dr^0x1F))-0x18+10] + 6: ratelogtable[(4*(Dr^0x1F))-0x18+11] + 7: ratelogtable[(4*(Dr^0x1F))-0x18+12] + (note that this is the same as the release rate formula, except that + decay rates 10-1F aren't possible... those would be slower in theory) + +SUSTAIN +------- +- no terminating condition except for voice off +- Sd=0 (increase) behavior is identical to ATTACK for both log and linear. +- Sd=1 (decrease) behavior: +Linear sustain decrease: +- line extends to 0x00000000 +- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F] +Logarithmic sustain decrease: +- current line ends at (envelope_level & 0x07FFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(Sr^0x7F)-0x1B+0] + 1: ratelogtable[(Sr^0x7F)-0x1B+4] + 2: ratelogtable[(Sr^0x7F)-0x1B+6] + 3: ratelogtable[(Sr^0x7F)-0x1B+8] + 4: ratelogtable[(Sr^0x7F)-0x1B+9] + 5: ratelogtable[(Sr^0x7F)-0x1B+10] + 6: ratelogtable[(Sr^0x7F)-0x1B+11] + 7: ratelogtable[(Sr^0x7F)-0x1B+12] + +RELEASE +------- +- if the envelope level has overflowed to negative, clip to 0 and QUIT. + +Linear release mode: +- line extends to 0x00000000 +- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C] + +Logarithmic release mode: +- line extends to (envelope_level & 0x0FFFFFFF) +- decrement per sample depends on (envelope_level>>28)&0x7 + 0: ratelogtable[(4*(Rr^0x1F))-0x18+0] + 1: ratelogtable[(4*(Rr^0x1F))-0x18+4] + 2: ratelogtable[(4*(Rr^0x1F))-0x18+6] + 3: ratelogtable[(4*(Rr^0x1F))-0x18+8] + 4: ratelogtable[(4*(Rr^0x1F))-0x18+9] + 5: ratelogtable[(4*(Rr^0x1F))-0x18+10] + 6: ratelogtable[(4*(Rr^0x1F))-0x18+11] + 7: ratelogtable[(4*(Rr^0x1F))-0x18+12] + +----------------------------------------------------------------------------- +*/ + diff --git a/plugins/dfsound/adsr.h b/plugins/dfsound/adsr.h index ff2af1ff..e15031dc 100644 --- a/plugins/dfsound/adsr.h +++ b/plugins/dfsound/adsr.h @@ -1,19 +1,20 @@ -/***************************************************************************
- 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. *
- * *
- ***************************************************************************/
-
-INLINE void StartADSR(int ch);
-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. * + * * + ***************************************************************************/ + +INLINE void StartADSR(int ch); +INLINE int MixADSR(int ch); +void InitADSR(void); diff --git a/plugins/dfsound/cfg.c b/plugins/dfsound/cfg.c index 9414eee5..2bf42c9c 100644 --- a/plugins/dfsound/cfg.c +++ b/plugins/dfsound/cfg.c @@ -1,173 +1,177 @@ -/***************************************************************************
- 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. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#define _IN_CFG
-
-#include "externals.h"
-
-////////////////////////////////////////////////////////////////////////
-// LINUX CONFIG/ABOUT HANDLING
-////////////////////////////////////////////////////////////////////////
-
-#include <unistd.h>
-
-////////////////////////////////////////////////////////////////////////
-// START EXTERNAL CFG TOOL
-////////////////////////////////////////////////////////////////////////
-
-void StartCfgTool(char * pCmdLine)
-{
- FILE * cf;
- char filename[255];
-
- strcpy(filename,"cfgDFSound");
- cf=fopen(filename,"rb");
- if(cf!=NULL)
- {
- fclose(cf);
- if(fork()==0)
- {
- execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL);
- exit(0);
- }
- }
- else
- {
- strcpy(filename,"cfg/cfgDFSound");
- cf=fopen(filename,"rb");
- if(cf!=NULL)
- {
- fclose(cf);
- if(fork()==0)
- {
- chdir("cfg");
- execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL);
- exit(0);
- }
- }
- else
- {
- sprintf(filename,"%s/cfgDFSound",getenv("HOME"));
- cf=fopen(filename,"rb");
- if(cf!=NULL)
- {
- fclose(cf);
- if(fork()==0)
- {
- chdir(getenv("HOME"));
- execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL);
- exit(0);
- }
- }
- else printf("Sound error: cfgDFSound not found!\n");
- }
- }
-}
-
-/////////////////////////////////////////////////////////
-// READ LINUX CONFIG FILE
-/////////////////////////////////////////////////////////
-
-void ReadConfigFile(void)
-{
- FILE *in;char t[256];int len;
- char * pB, * p;
-
- strcpy(t,"dfsound.cfg");
- in = fopen(t,"rb");
- if(!in)
- {
- strcpy(t,"cfg/dfsound.cfg");
- in = fopen(t,"rb");
- if(!in)
- {
- sprintf(t,"%s/dfsound.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=4-atoi(p+len);
- if(iVolume<1) iVolume=1;
- if(iVolume>5) iVolume=5;
-
- 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;
-
- 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>2) iUseReverb=2;
-
- 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;
- - strcpy(t,"\nFreqResponse");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;}
- if(p) iFreqResponse=atoi(p+len);
- if(iFreqResponse<0) iFreqResponse=0;
- if(iFreqResponse>1) iFreqResponse=1;
-
- free(pB);
-}
-
-/////////////////////////////////////////////////////////
-// READ CONFIG called by spu funcs
-/////////////////////////////////////////////////////////
-
-void ReadConfig(void)
-{
- iVolume=2;
- iXAPitch=0;
- iSPUIRQWait=1;
- iUseTimer=2;
- iUseReverb=2;
- iUseInterpolation=2;
- iDisStereo=0;
- iFreqResponse=0;
-
- ReadConfigFile();
-}
+/*************************************************************************** + 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. * + * * + ***************************************************************************/ + +#include "stdafx.h" + +#define _IN_CFG + +#include "cfg.h" + +#include "externals.h" + +//////////////////////////////////////////////////////////////////////// +// LINUX CONFIG/ABOUT HANDLING +//////////////////////////////////////////////////////////////////////// + +#include <unistd.h> + +//////////////////////////////////////////////////////////////////////// +// START EXTERNAL CFG TOOL +//////////////////////////////////////////////////////////////////////// + +void StartCfgTool(char * pCmdLine) +{ + FILE * cf; + char filename[255]; + + strcpy(filename,"cfgDFSound"); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + if(fork()==0) + { + execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL); + exit(0); + } + } + else + { + strcpy(filename,"cfg/cfgDFSound"); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + if(fork()==0) + { + if(chdir("cfg") != 0) + perror("cfg"); + execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL); + exit(0); + } + } + else + { + sprintf(filename,"%s/cfgDFSound",getenv("HOME")); + cf=fopen(filename,"rb"); + if(cf!=NULL) + { + fclose(cf); + if(fork()==0) + { + if(chdir(getenv("HOME")) != 0) + perror("HOME"); + execl("./cfgDFSound","cfgDFSound",pCmdLine,NULL); + exit(0); + } + } + else printf("Sound error: cfgDFSound not found!\n"); + } + } +} + +///////////////////////////////////////////////////////// +// READ LINUX CONFIG FILE +///////////////////////////////////////////////////////// + +static void ReadConfigFile(void) +{ + FILE *in;char t[256];int len; + char * pB, * p; + + strcpy(t,"dfsound.cfg"); + in = fopen(t,"rb"); + if(!in) + { + strcpy(t,"cfg/dfsound.cfg"); + in = fopen(t,"rb"); + if(!in) + { + sprintf(t,"%s/dfsound.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=4-atoi(p+len); + if(iVolume<1) iVolume=1; + if(iVolume>5) iVolume=5; + + 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; + + 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>2) iUseReverb=2; + + 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; + + strcpy(t,"\nFreqResponse");p=strstr(pB,t);if(p) {p=strstr(p,"=");len=1;} + if(p) iFreqResponse=atoi(p+len); + if(iFreqResponse<0) iFreqResponse=0; + if(iFreqResponse>1) iFreqResponse=1; + + free(pB); +} + +///////////////////////////////////////////////////////// +// READ CONFIG called by spu funcs +///////////////////////////////////////////////////////// + +void ReadConfig(void) +{ + iVolume=2; + iXAPitch=0; + iSPUIRQWait=1; + iUseTimer=2; + iUseReverb=2; + iUseInterpolation=2; + iDisStereo=0; + iFreqResponse=0; + + ReadConfigFile(); +} diff --git a/plugins/dfsound/externals.h b/plugins/dfsound/externals.h index b8a5c43d..011205b8 100644 --- a/plugins/dfsound/externals.h +++ b/plugins/dfsound/externals.h @@ -1,359 +1,361 @@ -/***************************************************************************
- 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 <stdint.h>
-
-/////////////////////////////////////////////////////////
-// generic defines
-/////////////////////////////////////////////////////////
-
-#define PSE_LT_SPU 4
-#define PSE_SPU_ERR_SUCCESS 0
-#define PSE_SPU_ERR -60
-#define PSE_SPU_ERR_NOTCONFIGURED PSE_SPU_ERR - 1
-#define PSE_SPU_ERR_INIT PSE_SPU_ERR - 2
-#ifndef max
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-#define min(a,b) (((a) < (b)) ? (a) : (b))
-#endif
-
-////////////////////////////////////////////////////////////////////////
-// spu defines
-////////////////////////////////////////////////////////////////////////
-
-// sound buffer sizes
-// 400 ms complete sound buffer
-#define SOUNDSIZE 70560
-// 137 ms test buffer... if less than that is buffered, a new upload will happen
-#define TESTSIZE 24192
-
-// num of channels
-#define MAXCHAN 24
-
-
-// ~ 1 ms of data - somewhat slower than Eternal
-//#define NSSIZE 45
-//#define INTERVAL_TIME 1000
-
-// ~ 0.5 ms of data - roughly Eternal maybe
-//#define NSSIZE 23
-//#define INTERVAL_TIME 2000
-
-// ~ 0.25 ms of data - seems a little bad..?
-//#define NSSIZE 12
-//#define INTERVAL_TIME 4000
-
-#define NSSIZE 10
-#define APU_CYCLES_UPDATE NSSIZE
-
-
-// update times
-#if 0
-// PEOPS DSound 1.09a - good sound cards
-#define LATENCY 10
-#elif defined (_WINDOWS)
-// work on most cards
-#define LATENCY 25
-#else
-// work on most cards
-#define LATENCY 25
-#endif
-
-
-// make sure this is bigger than cpu action - no glitchy
-#define INTERVAL_TIME 4500
-
-
-#define CPU_CLOCK 33868800
-
-///////////////////////////////////////////////////////////
-// struct defines
-///////////////////////////////////////////////////////////
-
-// ADSR INFOS PER CHANNEL
-typedef struct
-{
- int AttackModeExp;
- long AttackTime;
- long DecayTime;
- long SustainLevel;
- int SustainModeExp;
- long SustainModeDec;
- long SustainTime;
- int ReleaseModeExp;
- unsigned long ReleaseVal;
- long ReleaseTime;
- long ReleaseStartTime;
- long ReleaseVol;
- long lTime;
- long lVolume;
-} ADSRInfo;
-
-typedef struct
-{
- int State;
- int AttackModeExp;
- int AttackRate;
- int DecayRate;
- int SustainLevel;
- int SustainModeExp;
- int SustainIncrease;
- int SustainRate;
- int ReleaseModeExp;
- int ReleaseRate;
- int EnvelopeVol;
- int EnvelopeVol_f; // fraction
- long lVolume;
- long lDummy1;
- long lDummy2;
-} ADSRInfoEx;
-
-///////////////////////////////////////////////////////////
-
-// Tmp Flags
-
-// used for debug channel muting
-#define FLAG_MUTE 1
-
-// used for simple interpolation
-#define FLAG_IPOL0 2
-#define FLAG_IPOL1 4
-
-///////////////////////////////////////////////////////////
-
-// MAIN CHANNEL STRUCT
-typedef struct
-{
- // no mutexes used anymore... don't need them to sync access
- //HANDLE hMutex;
-
- int bNew; // start flag
-
- int iSBPos; // mixing stuff
- int spos;
- int sinc;
- int SB[32+32]; // Pete added another 32 dwords in 1.6 ... prevents overflow issues with gaussian/cubic interpolation (thanx xodnizel!), and can be used for even better interpolations, eh? :)
- int sval;
-
- unsigned char * pStart; // start ptr into sound mem
- unsigned char * pCurr; // current pos in sound mem
- unsigned char * pLoop; // loop ptr in sound mem
-
- int bOn; // is channel active (sample playing?)
- int bStop; // is channel stopped (sample _can_ still be playing, ADSR Release phase)
- int bReverb; // can we do reverb on this channel? must have ctrl register bit, to get active
- int iActFreq; // current psx pitch
- int iUsedFreq; // current pc pitch
- int iLeftVolume; // left volume
- int iLeftVolRaw; // left psx volume value
- int bIgnoreLoop; // ignore loop bit, if an external loop address is used
- int iMute; // mute mode (debug)
- int iSilent; // voice on - sound on/off
- int iRightVolume; // right volume
- int iRightVolRaw; // right psx volume value
- int iRawPitch; // raw pitch (0...3fff)
- int iIrqDone; // debug irq done flag
- int s_1; // last decoding infos
- int s_2;
- int bRVBActive; // reverb active flag
- int iRVBOffset; // reverb offset
- int iRVBRepeat; // reverb repeat
- int bNoise; // noise active flag
- int bFMod; // freq mod (0=off, 1=sound channel, 2=freq channel)
- int iRVBNum; // another reverb helper
- int iOldNoise; // old noise val for this channel
- ADSRInfo ADSR; // active ADSR settings
- ADSRInfoEx ADSRX; // next ADSR settings (will be moved to active on sample start)
-} SPUCHAN;
-
-///////////////////////////////////////////////////////////
-
-typedef struct
-{
- int StartAddr; // reverb area start addr in samples
- int CurrAddr; // reverb area curr addr in samples
-
- int VolLeft;
- int VolRight;
- int iLastRVBLeft;
- int iLastRVBRight;
- int iRVBLeft;
- int iRVBRight;
-
- int FB_SRC_A; // (offset)
- int FB_SRC_B; // (offset)
- int IIR_ALPHA; // (coef.)
- int ACC_COEF_A; // (coef.)
- int ACC_COEF_B; // (coef.)
- int ACC_COEF_C; // (coef.)
- int ACC_COEF_D; // (coef.)
- int IIR_COEF; // (coef.)
- int FB_ALPHA; // (coef.)
- int FB_X; // (coef.)
- int IIR_DEST_A0; // (offset)
- int IIR_DEST_A1; // (offset)
- int ACC_SRC_A0; // (offset)
- int ACC_SRC_A1; // (offset)
- int ACC_SRC_B0; // (offset)
- int ACC_SRC_B1; // (offset)
- int IIR_SRC_A0; // (offset)
- int IIR_SRC_A1; // (offset)
- int IIR_DEST_B0; // (offset)
- int IIR_DEST_B1; // (offset)
- int ACC_SRC_C0; // (offset)
- int ACC_SRC_C1; // (offset)
- int ACC_SRC_D0; // (offset)
- int ACC_SRC_D1; // (offset)
- int IIR_SRC_B1; // (offset)
- int IIR_SRC_B0; // (offset)
- int MIX_DEST_A0; // (offset)
- int MIX_DEST_A1; // (offset)
- int MIX_DEST_B0; // (offset)
- int MIX_DEST_B1; // (offset)
- int IN_COEF_L; // (coef.)
- int IN_COEF_R; // (coef.)
-} REVERBInfo;
-
-#ifdef _WINDOWS
-extern HINSTANCE hInst;
-#define WM_MUTE (WM_USER+543)
-#endif
-
-///////////////////////////////////////////////////////////
-// SPU.C globals
-///////////////////////////////////////////////////////////
-
-#ifndef _IN_SPU
-
-// psx buffers / addresses
-
-extern unsigned short regArea[];
-extern unsigned short spuMem[];
-extern unsigned char * spuMemC;
-extern unsigned char * pSpuIrq;
-extern unsigned char * pSpuBuffer;
-
-// user settings
-
-extern int iVolume;
-extern int iXAPitch;
-extern int iUseTimer;
-extern int iSPUIRQWait;
-extern int iDebugMode;
-extern int iRecordMode;
-extern int iUseReverb;
-extern int iUseInterpolation;
-extern int iDisStereo;
-extern int iFreqResponse;
-// MISC
-
-extern int iSpuAsyncWait;
-
-extern SPUCHAN s_chan[];
-extern REVERBInfo rvb;
-
-extern unsigned long dwNoiseVal;
-extern unsigned long dwNoiseClock;
-extern unsigned long dwNoiseCount;
-extern unsigned short spuCtrl;
-extern unsigned short spuStat;
-extern unsigned short spuIrq;
-extern unsigned long spuAddr;
-extern int bEndThread;
-extern int bThreadEnded;
-extern int bSpuInit;
-extern uint32_t dwNewChannel;
-
-extern int SSumR[];
-extern int SSumL[];
-extern int iCycle;
-extern short * pS;
-
-#ifdef _WINDOWS
-extern HWND hWMain; // window handle
-extern HWND hWDebug;
-#endif
-
-extern void (CALLBACK *cddavCallback)(unsigned short,unsigned short);
-
-#endif
-
-///////////////////////////////////////////////////////////
-// DSOUND.C globals
-///////////////////////////////////////////////////////////
-
-#ifndef _IN_DSOUND
-
-#ifdef _WINDOWS
-extern unsigned long LastWrite;
-extern unsigned long LastPlay;
-#endif
-
-#endif
-
-///////////////////////////////////////////////////////////
-// RECORD.C globals
-///////////////////////////////////////////////////////////
-
-#ifndef _IN_RECORD
-
-#ifdef _WINDOWS
-extern int iDoRecord;
-#endif
-
-#endif
-
-///////////////////////////////////////////////////////////
-// XA.C globals
-///////////////////////////////////////////////////////////
-
-#ifndef _IN_XA
-
-extern xa_decode_t * xapGlobal;
-
-extern uint32_t * XAFeed;
-extern uint32_t * XAPlay;
-extern uint32_t * XAStart;
-extern uint32_t * XAEnd;
-
-extern uint32_t XARepeat;
-extern uint32_t XALastVal;
-
-extern uint32_t * CDDAFeed;
-extern uint32_t * CDDAPlay;
-extern uint32_t * CDDAStart;
-extern uint32_t * CDDAEnd;
-
-extern int iLeftXAVol;
-extern int iRightXAVol;
-
-#endif
-
-///////////////////////////////////////////////////////////
-// REVERB.C globals
-///////////////////////////////////////////////////////////
-
-#ifndef _IN_REVERB
-
-extern int * sRVBPlay;
-extern int * sRVBEnd;
-extern int * sRVBStart;
-extern int iReverbOff;
-extern int iReverbRepeat;
-extern int iReverbNum;
-
-#endif
+/*************************************************************************** + externals.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +#include <stdint.h> + +#include "psemu_plugin_defs.h" + +///////////////////////////////////////////////////////// +// generic defines +///////////////////////////////////////////////////////// + +#define PSE_LT_SPU 4 +#define PSE_SPU_ERR_SUCCESS 0 +#define PSE_SPU_ERR -60 +#define PSE_SPU_ERR_NOTCONFIGURED PSE_SPU_ERR - 1 +#define PSE_SPU_ERR_INIT PSE_SPU_ERR - 2 +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +//////////////////////////////////////////////////////////////////////// +// spu defines +//////////////////////////////////////////////////////////////////////// + +// sound buffer sizes +// 400 ms complete sound buffer +#define SOUNDSIZE 70560 +// 137 ms test buffer... if less than that is buffered, a new upload will happen +#define TESTSIZE 24192 + +// num of channels +#define MAXCHAN 24 + + +// ~ 1 ms of data - somewhat slower than Eternal +//#define NSSIZE 45 +//#define INTERVAL_TIME 1000 + +// ~ 0.5 ms of data - roughly Eternal maybe +//#define NSSIZE 23 +//#define INTERVAL_TIME 2000 + +// ~ 0.25 ms of data - seems a little bad..? +//#define NSSIZE 12 +//#define INTERVAL_TIME 4000 + +#define NSSIZE 10 +#define APU_CYCLES_UPDATE NSSIZE + + +// update times +#if 0 +// PEOPS DSound 1.09a - good sound cards +#define LATENCY 10 +#elif defined (_WINDOWS) +// work on most cards +#define LATENCY 25 +#else +// work on most cards +#define LATENCY 25 +#endif + + +// make sure this is bigger than cpu action - no glitchy +#define INTERVAL_TIME 4500 + + +#define CPU_CLOCK 33868800 + +/////////////////////////////////////////////////////////// +// struct defines +/////////////////////////////////////////////////////////// + +// ADSR INFOS PER CHANNEL +typedef struct +{ + int AttackModeExp; + long AttackTime; + long DecayTime; + long SustainLevel; + int SustainModeExp; + long SustainModeDec; + long SustainTime; + int ReleaseModeExp; + unsigned long ReleaseVal; + long ReleaseTime; + long ReleaseStartTime; + long ReleaseVol; + long lTime; + long lVolume; +} ADSRInfo; + +typedef struct +{ + int State; + int AttackModeExp; + int AttackRate; + int DecayRate; + int SustainLevel; + int SustainModeExp; + int SustainIncrease; + int SustainRate; + int ReleaseModeExp; + int ReleaseRate; + int EnvelopeVol; + int EnvelopeVol_f; // fraction + long lVolume; + long lDummy1; + long lDummy2; +} ADSRInfoEx; + +/////////////////////////////////////////////////////////// + +// Tmp Flags + +// used for debug channel muting +#define FLAG_MUTE 1 + +// used for simple interpolation +#define FLAG_IPOL0 2 +#define FLAG_IPOL1 4 + +/////////////////////////////////////////////////////////// + +// MAIN CHANNEL STRUCT +typedef struct +{ + // no mutexes used anymore... don't need them to sync access + //HANDLE hMutex; + + int bNew; // start flag + + int iSBPos; // mixing stuff + int spos; + int sinc; + int SB[32+32]; // Pete added another 32 dwords in 1.6 ... prevents overflow issues with gaussian/cubic interpolation (thanx xodnizel!), and can be used for even better interpolations, eh? :) + int sval; + + unsigned char * pStart; // start ptr into sound mem + unsigned char * pCurr; // current pos in sound mem + unsigned char * pLoop; // loop ptr in sound mem + + int bOn; // is channel active (sample playing?) + int bStop; // is channel stopped (sample _can_ still be playing, ADSR Release phase) + int bReverb; // can we do reverb on this channel? must have ctrl register bit, to get active + int iActFreq; // current psx pitch + int iUsedFreq; // current pc pitch + int iLeftVolume; // left volume + int iLeftVolRaw; // left psx volume value + int bIgnoreLoop; // ignore loop bit, if an external loop address is used + int iMute; // mute mode (debug) + int iSilent; // voice on - sound on/off + int iRightVolume; // right volume + int iRightVolRaw; // right psx volume value + int iRawPitch; // raw pitch (0...3fff) + int iIrqDone; // debug irq done flag + int s_1; // last decoding infos + int s_2; + int bRVBActive; // reverb active flag + int iRVBOffset; // reverb offset + int iRVBRepeat; // reverb repeat + int bNoise; // noise active flag + int bFMod; // freq mod (0=off, 1=sound channel, 2=freq channel) + int iRVBNum; // another reverb helper + int iOldNoise; // old noise val for this channel + ADSRInfo ADSR; // active ADSR settings + ADSRInfoEx ADSRX; // next ADSR settings (will be moved to active on sample start) +} SPUCHAN; + +/////////////////////////////////////////////////////////// + +typedef struct +{ + int StartAddr; // reverb area start addr in samples + int CurrAddr; // reverb area curr addr in samples + + int VolLeft; + int VolRight; + int iLastRVBLeft; + int iLastRVBRight; + int iRVBLeft; + int iRVBRight; + + int FB_SRC_A; // (offset) + int FB_SRC_B; // (offset) + int IIR_ALPHA; // (coef.) + int ACC_COEF_A; // (coef.) + int ACC_COEF_B; // (coef.) + int ACC_COEF_C; // (coef.) + int ACC_COEF_D; // (coef.) + int IIR_COEF; // (coef.) + int FB_ALPHA; // (coef.) + int FB_X; // (coef.) + int IIR_DEST_A0; // (offset) + int IIR_DEST_A1; // (offset) + int ACC_SRC_A0; // (offset) + int ACC_SRC_A1; // (offset) + int ACC_SRC_B0; // (offset) + int ACC_SRC_B1; // (offset) + int IIR_SRC_A0; // (offset) + int IIR_SRC_A1; // (offset) + int IIR_DEST_B0; // (offset) + int IIR_DEST_B1; // (offset) + int ACC_SRC_C0; // (offset) + int ACC_SRC_C1; // (offset) + int ACC_SRC_D0; // (offset) + int ACC_SRC_D1; // (offset) + int IIR_SRC_B1; // (offset) + int IIR_SRC_B0; // (offset) + int MIX_DEST_A0; // (offset) + int MIX_DEST_A1; // (offset) + int MIX_DEST_B0; // (offset) + int MIX_DEST_B1; // (offset) + int IN_COEF_L; // (coef.) + int IN_COEF_R; // (coef.) +} REVERBInfo; + +#ifdef _WINDOWS +extern HINSTANCE hInst; +#define WM_MUTE (WM_USER+543) +#endif + +/////////////////////////////////////////////////////////// +// SPU.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_SPU + +// psx buffers / addresses + +extern unsigned short regArea[]; +extern unsigned short spuMem[]; +extern unsigned char * spuMemC; +extern unsigned char * pSpuIrq; +extern unsigned char * pSpuBuffer; + +// user settings + +extern int iVolume; +extern int iXAPitch; +extern int iUseTimer; +extern int iSPUIRQWait; +extern int iDebugMode; +extern int iRecordMode; +extern int iUseReverb; +extern int iUseInterpolation; +extern int iDisStereo; +extern int iFreqResponse; +// MISC + +extern int iSpuAsyncWait; + +extern SPUCHAN s_chan[]; +extern REVERBInfo rvb; + +extern unsigned long dwNoiseVal; +extern unsigned long dwNoiseClock; +extern unsigned long dwNoiseCount; +extern unsigned short spuCtrl; +extern unsigned short spuStat; +extern unsigned short spuIrq; +extern unsigned long spuAddr; +extern int bEndThread; +extern int bThreadEnded; +extern int bSpuInit; +extern uint32_t dwNewChannel; + +extern int SSumR[]; +extern int SSumL[]; +extern int iCycle; +extern short * pS; + +#ifdef _WINDOWS +extern HWND hWMain; // window handle +extern HWND hWDebug; +#endif + +extern void (CALLBACK *cddavCallback)(unsigned short,unsigned short); + +#endif + +/////////////////////////////////////////////////////////// +// DSOUND.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_DSOUND + +#ifdef _WINDOWS +extern unsigned long LastWrite; +extern unsigned long LastPlay; +#endif + +#endif + +/////////////////////////////////////////////////////////// +// RECORD.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_RECORD + +#ifdef _WINDOWS +extern int iDoRecord; +#endif + +#endif + +/////////////////////////////////////////////////////////// +// XA.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_XA + +extern xa_decode_t * xapGlobal; + +extern uint32_t * XAFeed; +extern uint32_t * XAPlay; +extern uint32_t * XAStart; +extern uint32_t * XAEnd; + +extern uint32_t XARepeat; +extern uint32_t XALastVal; + +extern uint32_t * CDDAFeed; +extern uint32_t * CDDAPlay; +extern uint32_t * CDDAStart; +extern uint32_t * CDDAEnd; + +extern int iLeftXAVol; +extern int iRightXAVol; + +#endif + +/////////////////////////////////////////////////////////// +// REVERB.C globals +/////////////////////////////////////////////////////////// + +#ifndef _IN_REVERB + +extern int * sRVBPlay; +extern int * sRVBEnd; +extern int * sRVBStart; +extern int iReverbOff; +extern int iReverbRepeat; +extern int iReverbNum; + +#endif diff --git a/plugins/dfsound/freeze.c b/plugins/dfsound/freeze.c index 156fd693..f724568f 100644 --- a/plugins/dfsound/freeze.c +++ b/plugins/dfsound/freeze.c @@ -1,235 +1,223 @@ -/***************************************************************************
- 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. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#define _IN_FREEZE
-
-#include "externals.h"
-#include "registers.h"
-#include "spu.h"
-#include "regs.h"
-
-////////////////////////////////////////////////////////////////////////
-// freeze structs
-////////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- char szSPUName[8];
- uint32_t ulFreezeVersion;
- uint32_t ulFreezeSize;
- unsigned char cSPUPort[0x200];
- unsigned char cSPURam[0x80000];
- xa_decode_t xaS;
-} SPUFreeze_t;
-
-typedef struct
-{
- unsigned short spuIrq;
- uint32_t pSpuIrq;
- uint32_t spuAddr;
- uint32_t dummy1;
- uint32_t dummy2;
- uint32_t dummy3;
-
- SPUCHAN s_chan[MAXCHAN];
-
-} SPUOSSFreeze_t;
-
-////////////////////////////////////////////////////////////////////////
-
-void LoadStateV5(SPUFreeze_t * pF); // newest version
-void LoadStateUnknown(SPUFreeze_t * pF); // unknown format
-
-extern int lastch;
-
-////////////////////////////////////////////////////////////////////////
-// SPUFREEZE: called by main emu on savestate load/save
-////////////////////////////////////////////////////////////////////////
-
-long CALLBACK SPUfreeze(uint32_t ulFreezeMode,SPUFreeze_t * pF)
-{
- int i;SPUOSSFreeze_t * pFO;
-
- if(!pF) return 0; // first check
-
- if(!bSpuInit) return 0;
-
- if(ulFreezeMode) // info or save?
- {//--------------------------------------------------//
- if(ulFreezeMode==1)
- memset(pF,0,sizeof(SPUFreeze_t)+sizeof(SPUOSSFreeze_t));
-
- strcpy(pF->szSPUName,"PBOSS");
- pF->ulFreezeVersion=5;
- pF->ulFreezeSize=sizeof(SPUFreeze_t)+sizeof(SPUOSSFreeze_t);
-
- if(ulFreezeMode==2) return 1; // info mode? ok, bye
- // save mode:
- RemoveTimer(); // stop timer
-
- memcpy(pF->cSPURam,spuMem,0x80000); // copy common infos
- memcpy(pF->cSPUPort,regArea,0x200);
-
- 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->spuIrq=spuIrq;
- if(pSpuIrq) pFO->pSpuIrq = (unsigned long)pSpuIrq-(unsigned long)spuMemC;
-
- pFO->spuAddr=spuAddr;
- if(pFO->spuAddr==0) pFO->spuAddr=0xbaadf00d;
-
- for(i=0;i<MAXCHAN;i++)
- {
- memcpy((void *)&pFO->s_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;
- //--------------------------------------------------//
- }
-
- if(ulFreezeMode!=0) return 0; // bad mode? bye
-
-#ifdef _WINDOWS
- if(iDebugMode && IsWindow(hWDebug)) // clean debug mute infos
- SendMessage(hWDebug,WM_MUTE,0,0);
- if(IsBadReadPtr(pF,sizeof(SPUFreeze_t))) // check bad emu stuff
- return 0;
-#endif
-
- RemoveTimer(); // we stop processing while doing the save!
-
- memcpy(spuMem,pF->cSPURam,0x80000); // get ram
- memcpy(regArea,pF->cSPUPort,0x200);
-
- if(pF->xaS.nsamples<=4032) // start xa again
- SPUplayADPCMchannel(&pF->xaS);
-
- xapGlobal=0;
-
- if(!strcmp(pF->szSPUName,"PBOSS") && pF->ulFreezeVersion==5)
- LoadStateV5(pF);
- else LoadStateUnknown(pF);
-
- lastch = -1;
-
- // repair some globals
- for(i=0;i<=62;i+=2)
- SPUwriteRegister(H_Reverb+i,regArea[(H_Reverb+i-0xc00)>>1]);
- SPUwriteRegister(H_SPUReverbAddr,regArea[(H_SPUReverbAddr-0xc00)>>1]);
- SPUwriteRegister(H_SPUrvolL,regArea[(H_SPUrvolL-0xc00)>>1]);
- SPUwriteRegister(H_SPUrvolR,regArea[(H_SPUrvolR-0xc00)>>1]);
-
- SPUwriteRegister(H_SPUctrl,(unsigned short)(regArea[(H_SPUctrl-0xc00)>>1]|0x4000));
- SPUwriteRegister(H_SPUstat,regArea[(H_SPUstat-0xc00)>>1]);
- SPUwriteRegister(H_CDLeft,regArea[(H_CDLeft-0xc00)>>1]);
- SPUwriteRegister(H_CDRight,regArea[(H_CDRight-0xc00)>>1]);
-
- // fix to prevent new interpolations from crashing
- for(i=0;i<MAXCHAN;i++) s_chan[i].SB[28]=0;
-
- SetupTimer(); // start sound processing again
-
- // stop load crackling
- //cpu_cycles = 0;
- //iCycle = 0;
-
- // fix movie lag
- CDDAEnd = CDDAStart + 44100;
- CDDAPlay = CDDAStart;
- CDDAFeed = CDDAStart;
-
- XAPlay = XAStart;
- XAFeed = XAStart;
- XAEnd = XAStart + 44100;
-
- return 1;
-}
-
-////////////////////////////////////////////////////////////////////////
-
-void LoadStateV5(SPUFreeze_t * pF)
-{
- int i;SPUOSSFreeze_t * pFO;
-
- pFO=(SPUOSSFreeze_t *)(pF+1);
-
- spuIrq = pFO->spuIrq;
- if(pFO->pSpuIrq) pSpuIrq = pFO->pSpuIrq+spuMemC; else pSpuIrq=NULL;
-
- if(pFO->spuAddr)
- {
- spuAddr = pFO->spuAddr;
- if (spuAddr == 0xbaadf00d) spuAddr = 0;
- }
-
- for(i=0;i<MAXCHAN;i++)
- {
- memcpy((void *)&s_chan[i],(void *)&pFO->s_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_t * pF)
-{
- int i;
-
- for(i=0;i<MAXCHAN;i++)
- {
- s_chan[i].bOn=0;
- s_chan[i].bNew=0;
- s_chan[i].bStop=0;
- s_chan[i].ADSR.lVolume=0;
- s_chan[i].pLoop=(unsigned char *)((int)spuMemC+4096);
- s_chan[i].pStart=(unsigned char *)((int)spuMemC+4096);
- s_chan[i].iMute=0;
- s_chan[i].iIrqDone=0;
- }
-
- dwNewChannel=0;
- pSpuIrq=0;
-
- for(i=0;i<0xc0;i++)
- {
- SPUwriteRegister(0x1f801c00+i*2,regArea[i]);
- }
-}
-
-////////////////////////////////////////////////////////////////////////
+/*************************************************************************** + 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. * + * * + ***************************************************************************/ + +#include "stdafx.h" + +#define _IN_FREEZE + +#include "externals.h" +#include "registers.h" +#include "spu.h" +#include "regs.h" + +//////////////////////////////////////////////////////////////////////// +// freeze structs +//////////////////////////////////////////////////////////////////////// + +typedef struct +{ + unsigned short spuIrq; + uint32_t pSpuIrq; + uint32_t spuAddr; + uint32_t dummy1; + uint32_t dummy2; + uint32_t dummy3; + + SPUCHAN s_chan[MAXCHAN]; + +} SPUOSSFreeze_t; + +//////////////////////////////////////////////////////////////////////// + +void LoadStateV5(SPUFreeze_t * pF); // newest version +void LoadStateUnknown(SPUFreeze_t * pF); // unknown format + +//////////////////////////////////////////////////////////////////////// +// SPUFREEZE: called by main emu on savestate load/save +//////////////////////////////////////////////////////////////////////// + +long CALLBACK SPUfreeze(uint32_t ulFreezeMode,SPUFreeze_t * pF) +{ + int i;SPUOSSFreeze_t * pFO; + + if(!pF) return 0; // first check + + if(!bSpuInit) return 0; + + if(ulFreezeMode) // info or save? + {//--------------------------------------------------// + if(ulFreezeMode==1) + memset(pF,0,sizeof(SPUFreeze_t)+sizeof(SPUOSSFreeze_t)); + + strcpy(pF->PluginName,"PBOSS"); + pF->PluginVersion=5; + pF->Size=sizeof(SPUFreeze_t)+sizeof(SPUOSSFreeze_t); + + if(ulFreezeMode==2) return 1; // info mode? ok, bye + // save mode: + RemoveTimer(); // stop timer + + memcpy(pF->SPURam,spuMem,0x80000); // copy common infos + memcpy(pF->SPUPorts,regArea,0x200); + + if(xapGlobal && XAPlay!=XAFeed) // some xa + { + pF->xa=*xapGlobal; + } + else + memset(&pF->xa,0,sizeof(xa_decode_t)); // or clean xa + + pFO=(SPUOSSFreeze_t *)(pF+1); // store special stuff + + pFO->spuIrq=spuIrq; + if(pSpuIrq) pFO->pSpuIrq = (unsigned long)pSpuIrq-(unsigned long)spuMemC; + + pFO->spuAddr=spuAddr; + if(pFO->spuAddr==0) pFO->spuAddr=0xbaadf00d; + + for(i=0;i<MAXCHAN;i++) + { + memcpy((void *)&pFO->s_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; + //--------------------------------------------------// + } + + if(ulFreezeMode!=0) return 0; // bad mode? bye + +#ifdef _WINDOWS + if(iDebugMode && IsWindow(hWDebug)) // clean debug mute infos + SendMessage(hWDebug,WM_MUTE,0,0); + if(IsBadReadPtr(pF,sizeof(SPUFreeze_t))) // check bad emu stuff + return 0; +#endif + + RemoveTimer(); // we stop processing while doing the save! + + memcpy(spuMem,pF->SPURam,0x80000); // get ram + memcpy(regArea,pF->SPUPorts,0x200); + + if(pF->xa.nsamples<=4032) // start xa again + SPUplayADPCMchannel(&pF->xa); + + xapGlobal=0; + + if(!strcmp(pF->PluginName,"PBOSS") && pF->PluginVersion==5) + LoadStateV5(pF); + else LoadStateUnknown(pF); + + lastch = -1; + + // repair some globals + for(i=0;i<=62;i+=2) + SPUwriteRegister(H_Reverb+i,regArea[(H_Reverb+i-0xc00)>>1]); + SPUwriteRegister(H_SPUReverbAddr,regArea[(H_SPUReverbAddr-0xc00)>>1]); + SPUwriteRegister(H_SPUrvolL,regArea[(H_SPUrvolL-0xc00)>>1]); + SPUwriteRegister(H_SPUrvolR,regArea[(H_SPUrvolR-0xc00)>>1]); + + SPUwriteRegister(H_SPUctrl,(unsigned short)(regArea[(H_SPUctrl-0xc00)>>1]|0x4000)); + SPUwriteRegister(H_SPUstat,regArea[(H_SPUstat-0xc00)>>1]); + SPUwriteRegister(H_CDLeft,regArea[(H_CDLeft-0xc00)>>1]); + SPUwriteRegister(H_CDRight,regArea[(H_CDRight-0xc00)>>1]); + + // fix to prevent new interpolations from crashing + for(i=0;i<MAXCHAN;i++) s_chan[i].SB[28]=0; + + SetupTimer(); // start sound processing again + + // stop load crackling + //cpu_cycles = 0; + //iCycle = 0; + + // fix movie lag + CDDAEnd = CDDAStart + 44100; + CDDAPlay = CDDAStart; + CDDAFeed = CDDAStart; + + XAPlay = XAStart; + XAFeed = XAStart; + XAEnd = XAStart + 44100; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// + +void LoadStateV5(SPUFreeze_t * pF) +{ + int i;SPUOSSFreeze_t * pFO; + + pFO=(SPUOSSFreeze_t *)(pF+1); + + spuIrq = pFO->spuIrq; + if(pFO->pSpuIrq) pSpuIrq = pFO->pSpuIrq+spuMemC; else pSpuIrq=NULL; + + if(pFO->spuAddr) + { + spuAddr = pFO->spuAddr; + if (spuAddr == 0xbaadf00d) spuAddr = 0; + } + + for(i=0;i<MAXCHAN;i++) + { + memcpy((void *)&s_chan[i],(void *)&pFO->s_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_t * pF) +{ + int i; + + for(i=0;i<MAXCHAN;i++) + { + s_chan[i].bOn=0; + s_chan[i].bNew=0; + s_chan[i].bStop=0; + s_chan[i].ADSR.lVolume=0; + s_chan[i].pLoop=(unsigned char *)((unsigned long)spuMemC+4096); + s_chan[i].pStart=(unsigned char *)((unsigned long)spuMemC+4096); + s_chan[i].iMute=0; + s_chan[i].iIrqDone=0; + } + + dwNewChannel=0; + pSpuIrq=0; + + for(i=0;i<0xc0;i++) + { + SPUwriteRegister(0x1f801c00+i*2,regArea[i]); + } +} + +//////////////////////////////////////////////////////////////////////// diff --git a/plugins/dfsound/nullsnd.c b/plugins/dfsound/nullsnd.c index bf079094..87046642 100644 --- a/plugins/dfsound/nullsnd.c +++ b/plugins/dfsound/nullsnd.c @@ -2,6 +2,8 @@ #define _IN_OSS #include "externals.h" +#include "dsoundoss.h" + // SETUP SOUND void SetupSound(void) { diff --git a/plugins/dfsound/oss.c b/plugins/dfsound/oss.c index f4dd215d..d39412d9 100644 --- a/plugins/dfsound/oss.c +++ b/plugins/dfsound/oss.c @@ -21,6 +21,8 @@ #include "externals.h" +#include <errno.h> + //////////////////////////////////////////////////////////////////////// // oss globals //////////////////////////////////////////////////////////////////////// @@ -31,7 +33,6 @@ #define OSS_SPEED_44100 44100 static int oss_audio_fd = -1; -extern int errno; //////////////////////////////////////////////////////////////////////// // SETUP SOUND diff --git a/plugins/dfsound/pulseaudio.c b/plugins/dfsound/pulseaudio.c index 60051557..2185d149 100644 --- a/plugins/dfsound/pulseaudio.c +++ b/plugins/dfsound/pulseaudio.c @@ -26,6 +26,8 @@ comment : Much of this was taken from simple.c, in the pulseaudio #include "externals.h" #include <pulse/pulseaudio.h> +#include "dsoundoss.h" + //////////////////////////////////////////////////////////////////////// // pulseaudio structs //////////////////////////////////////////////////////////////////////// diff --git a/plugins/dfsound/reverb.c b/plugins/dfsound/reverb.c index 92e31fcb..dfab03a4 100644 --- a/plugins/dfsound/reverb.c +++ b/plugins/dfsound/reverb.c @@ -1,462 +1,463 @@ -/***************************************************************************
- 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. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#define _IN_REVERB
-
-// will be included from spu.c
-#ifdef _IN_SPU
-
-////////////////////////////////////////////////////////////////////////
-// globals
-////////////////////////////////////////////////////////////////////////
-
-// REVERB info and timing vars...
-
-int * sRVBPlay = 0;
-int * sRVBEnd = 0;
-int * sRVBStart = 0;
-int iReverbOff = -1; // some delay factor for reverb
-int iReverbRepeat = 0;
-int iReverbNum = 1;
-
-////////////////////////////////////////////////////////////////////////
-// SET REVERB
-////////////////////////////////////////////////////////////////////////
-
-void SetREVERB(unsigned short val)
-{
- switch(val)
- {
- case 0x0000: iReverbOff=-1; break; // off
- case 0x007D: iReverbOff=32; iReverbNum=2; iReverbRepeat=128; break; // ok room
-
- case 0x0033: iReverbOff=32; iReverbNum=2; iReverbRepeat=64; break; // studio small
- case 0x00B1: iReverbOff=48; iReverbNum=2; iReverbRepeat=96; break; // ok studio medium
- case 0x00E3: iReverbOff=64; iReverbNum=2; iReverbRepeat=128; break; // ok studio large ok
-
- case 0x01A5: iReverbOff=128; iReverbNum=4; iReverbRepeat=32; break; // ok hall
- case 0x033D: iReverbOff=256; iReverbNum=4; iReverbRepeat=64; break; // space echo
- case 0x0001: iReverbOff=184; iReverbNum=3; iReverbRepeat=128; break; // echo/delay
- case 0x0017: iReverbOff=128; iReverbNum=2; iReverbRepeat=128; break; // half echo
- default: iReverbOff=32; iReverbNum=1; iReverbRepeat=0; break;
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-// START REVERB
-////////////////////////////////////////////////////////////////////////
-
-INLINE void StartREVERB(int ch)
-{
- if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible?
- {
- if(iUseReverb==2) s_chan[ch].bRVBActive=1;
- else
- if(iUseReverb==1 && iReverbOff>0) // -> fake reverb used?
- {
- s_chan[ch].bRVBActive=1; // -> activate it
- s_chan[ch].iRVBOffset=iReverbOff*45;
- s_chan[ch].iRVBRepeat=iReverbRepeat*45;
- s_chan[ch].iRVBNum =iReverbNum;
- }
- }
- else s_chan[ch].bRVBActive=0; // else -> no reverb
-}
-
-////////////////////////////////////////////////////////////////////////
-// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf
-////////////////////////////////////////////////////////////////////////
-
-INLINE void InitREVERB(void)
-{
- if(iUseReverb==2)
- {memset(sRVBStart,0,NSSIZE*2*4);}
-}
-
-////////////////////////////////////////////////////////////////////////
-// STORE REVERB
-////////////////////////////////////////////////////////////////////////
-
-INLINE void StoreREVERB(int ch,int ns)
-{
- if(iUseReverb==0) return;
- else
- if(iUseReverb==2) // -------------------------------- // Neil's reverb
- {
- const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000;
- const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000;
-
- ns<<=1;
-
- *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer
- *(sRVBStart+ns+1)+=iRxr;
- }
- else // --------------------------------------------- // Pete's easy fake reverb
- {
- int * pN;int iRn,iRr=0;
-
- // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on
-
- int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x8000;
- int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x8000;
-
- for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2)
- {
- pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1);
- if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd);
-
- (*pN)+=iRxl;
- pN++;
- (*pN)+=iRxr;
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps
-{
- short * p=(short *)spuMem;
- iOff=(iOff*4)+rvb.CurrAddr;
- while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
- while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
- return (int)*(p+iOff);
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE void s_buffer(int iOff,int iVal) // set_buffer content helper: takes care about wraps and clipping
-{
- short * p=(short *)spuMem;
- iOff=(iOff*4)+rvb.CurrAddr;
- while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
- while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
- if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
- *(p+iOff)=(short)iVal;
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping
-{
- short * p=(short *)spuMem;
- iOff=(iOff*4)+rvb.CurrAddr+1;
- while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
- while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
- if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
- *(p+iOff)=(short)iVal;
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE int MixREVERBLeft(int ns)
-{
- if(iUseReverb==0) return 0;
- else
- if(iUseReverb==2)
- {
- static int iCnt=0; // this func will be called with 44.1 khz
-
- if(!rvb.StartAddr) // reverb is off
- {
- rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
- return 0;
- }
-
- iCnt++;
-
- if(iCnt&1) // we work on every second left value: downsample to 22 khz
- {
- if(spuCtrl&0x80) // -> reverb on? oki
- {
- int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
-
- const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1));
- const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1);
-
- const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
- const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
- const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
- const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
-
- const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L;
- const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L;
- const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L;
- const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L;
-
- s_buffer1(rvb.IIR_DEST_A0, IIR_A0);
- s_buffer1(rvb.IIR_DEST_A1, IIR_A1);
- s_buffer1(rvb.IIR_DEST_B0, IIR_B0);
- s_buffer1(rvb.IIR_DEST_B1, IIR_B1);
-
- ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L +
- (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L +
- (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L +
- (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L;
- ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L +
- (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L +
- (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L +
- (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L;
-
- FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A);
- FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A);
- FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B);
- FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B);
-
- s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L);
- s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L);
-
- s_buffer(rvb.MIX_DEST_B0, (rvb.FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb.FB_X)/32768L);
- s_buffer(rvb.MIX_DEST_B1, (rvb.FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb.FB_X)/32768L);
-
- rvb.iLastRVBLeft = rvb.iRVBLeft;
- rvb.iLastRVBRight = rvb.iRVBRight;
-
- rvb.iRVBLeft = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3;
- rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3;
-
- rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000;
- rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000;
-
- rvb.CurrAddr++;
- if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
-
- return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2;
- }
- else // -> reverb off
- {
- rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
- }
-
- rvb.CurrAddr++;
- if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
- }
-
- return rvb.iLastRVBLeft;
- }
- else // easy fake reverb:
- {
- const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value
- *sRVBPlay++=0; // -> init it after
- if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds
- return iRV; // -> return reverb mix buf val
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-
-INLINE int MixREVERBRight(void)
-{
- if(iUseReverb==0) return 0;
- else
- if(iUseReverb==2) // Neill's reverb:
- {
- int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2;
- rvb.iLastRVBRight=rvb.iRVBRight;
- return i; // -> just return the last right reverb val (little bit scaled by the previous right val)
- }
- else // easy fake reverb:
- {
- const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value
- *sRVBPlay++=0; // -> init it after
- if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds
- return iRV; // -> return reverb mix buf val
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-
-#endif
-
-/*
------------------------------------------------------------------------------
-PSX reverb hardware notes
-by Neill Corlett
------------------------------------------------------------------------------
-
-Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
-yadda yadda.
-
------------------------------------------------------------------------------
-
-Basics
-------
-
-- The reverb buffer is 22khz 16-bit mono PCM.
-- It starts at the reverb address given by 1DA2, extends to
- the end of sound RAM, and wraps back to the 1DA2 address.
-
-Setting the address at 1DA2 resets the current reverb work address.
-
-This work address ALWAYS increments every 1/22050 sec., regardless of
-whether reverb is enabled (bit 7 of 1DAA set).
-
-And the contents of the reverb buffer ALWAYS play, scaled by the
-"reverberation depth left/right" volumes (1D84/1D86).
-(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
-
------------------------------------------------------------------------------
-
-Register names
---------------
-
-These are probably not their real names.
-These are probably not even correct names.
-We will use them anyway, because we can.
-
-1DC0: FB_SRC_A (offset)
-1DC2: FB_SRC_B (offset)
-1DC4: IIR_ALPHA (coef.)
-1DC6: ACC_COEF_A (coef.)
-1DC8: ACC_COEF_B (coef.)
-1DCA: ACC_COEF_C (coef.)
-1DCC: ACC_COEF_D (coef.)
-1DCE: IIR_COEF (coef.)
-1DD0: FB_ALPHA (coef.)
-1DD2: FB_X (coef.)
-1DD4: IIR_DEST_A0 (offset)
-1DD6: IIR_DEST_A1 (offset)
-1DD8: ACC_SRC_A0 (offset)
-1DDA: ACC_SRC_A1 (offset)
-1DDC: ACC_SRC_B0 (offset)
-1DDE: ACC_SRC_B1 (offset)
-1DE0: IIR_SRC_A0 (offset)
-1DE2: IIR_SRC_A1 (offset)
-1DE4: IIR_DEST_B0 (offset)
-1DE6: IIR_DEST_B1 (offset)
-1DE8: ACC_SRC_C0 (offset)
-1DEA: ACC_SRC_C1 (offset)
-1DEC: ACC_SRC_D0 (offset)
-1DEE: ACC_SRC_D1 (offset)
-1DF0: IIR_SRC_B1 (offset)
-1DF2: IIR_SRC_B0 (offset)
-1DF4: MIX_DEST_A0 (offset)
-1DF6: MIX_DEST_A1 (offset)
-1DF8: MIX_DEST_B0 (offset)
-1DFA: MIX_DEST_B1 (offset)
-1DFC: IN_COEF_L (coef.)
-1DFE: IN_COEF_R (coef.)
-
-The coefficients are signed fractional values.
--32768 would be -1.0
- 32768 would be 1.0 (if it were possible... the highest is of course 32767)
-
-The offsets are (byte/8) offsets into the reverb buffer.
-i.e. you multiply them by 8, you get byte offsets.
-You can also think of them as (samples/4) offsets.
-They appear to be signed. They can be negative.
-None of the documented presets make them negative, though.
-
-Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.
-
------------------------------------------------------------------------------
-
-What it does
-------------
-
-We take all reverb sources:
-- regular channels that have the reverb bit on
-- cd and external sources, if their reverb bits are on
-and mix them into one stereo 44100hz signal.
-
-Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting
-algorithm here, but I haven't figured out the hysterically exact specifics.
-I use an 8-tap filter with these coefficients, which are nice but probably
-not the real ones:
-
-0.037828187894
-0.157538631280
-0.321159685278
-0.449322115345
-0.449322115345
-0.321159685278
-0.157538631280
-0.037828187894
-
-So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
-
-* IN MY EMULATION, I divide these by 2 to make it clip less.
- (and of course the L/R output coefficients are adjusted to compensate)
- The real thing appears to not do this.
-
-At every 22050hz tick:
-- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
- steady-state algorithm described below
-- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
- (This part may not be exactly right and I guessed at the coefs. TODO: check later.)
- L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
- R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
-- Advance the current buffer position by 1 sample
-
-The wet out L and R are then upsampled to 44100hz and played at the
-"reverberation depth left/right" (1D84/1D86) volume, independent of the main
-volume.
-
------------------------------------------------------------------------------
-
-Reverb steady-state
--------------------
-
-The reverb steady-state algorithm is fairly clever, and of course by
-"clever" I mean "batshit insane".
-
-buffer[x] is relative to the current buffer position, not the beginning of
-the buffer. Note that all buffer offsets must wrap around so they're
-contained within the reverb work area.
-
-Clipping is performed at the end... maybe also sooner, but definitely at
-the end.
-
-IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
-IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
-IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
-IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
-
-IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
-IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
-IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
-IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
-
-buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
-buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
-buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
-buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
-
-ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
- buffer[ACC_SRC_B0] * ACC_COEF_B +
- buffer[ACC_SRC_C0] * ACC_COEF_C +
- buffer[ACC_SRC_D0] * ACC_COEF_D;
-ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
- buffer[ACC_SRC_B1] * ACC_COEF_B +
- buffer[ACC_SRC_C1] * ACC_COEF_C +
- buffer[ACC_SRC_D1] * ACC_COEF_D;
-
-FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
-FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
-FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
-FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
-
-buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
-buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
-buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
-buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
-
------------------------------------------------------------------------------
-*/
-
+/*************************************************************************** + 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. * + * * + ***************************************************************************/ + +#include "stdafx.h" +#include "reverb.h" + +#define _IN_REVERB + +// will be included from spu.c +#ifdef _IN_SPU + +//////////////////////////////////////////////////////////////////////// +// globals +//////////////////////////////////////////////////////////////////////// + +// REVERB info and timing vars... + +int * sRVBPlay = 0; +int * sRVBEnd = 0; +int * sRVBStart = 0; +int iReverbOff = -1; // some delay factor for reverb +int iReverbRepeat = 0; +int iReverbNum = 1; + +//////////////////////////////////////////////////////////////////////// +// SET REVERB +//////////////////////////////////////////////////////////////////////// + +void SetREVERB(unsigned short val) +{ + switch(val) + { + case 0x0000: iReverbOff=-1; break; // off + case 0x007D: iReverbOff=32; iReverbNum=2; iReverbRepeat=128; break; // ok room + + case 0x0033: iReverbOff=32; iReverbNum=2; iReverbRepeat=64; break; // studio small + case 0x00B1: iReverbOff=48; iReverbNum=2; iReverbRepeat=96; break; // ok studio medium + case 0x00E3: iReverbOff=64; iReverbNum=2; iReverbRepeat=128; break; // ok studio large ok + + case 0x01A5: iReverbOff=128; iReverbNum=4; iReverbRepeat=32; break; // ok hall + case 0x033D: iReverbOff=256; iReverbNum=4; iReverbRepeat=64; break; // space echo + case 0x0001: iReverbOff=184; iReverbNum=3; iReverbRepeat=128; break; // echo/delay + case 0x0017: iReverbOff=128; iReverbNum=2; iReverbRepeat=128; break; // half echo + default: iReverbOff=32; iReverbNum=1; iReverbRepeat=0; break; + } +} + +//////////////////////////////////////////////////////////////////////// +// START REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StartREVERB(int ch) +{ + if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible? + { + if(iUseReverb==2) s_chan[ch].bRVBActive=1; + else + if(iUseReverb==1 && iReverbOff>0) // -> fake reverb used? + { + s_chan[ch].bRVBActive=1; // -> activate it + s_chan[ch].iRVBOffset=iReverbOff*45; + s_chan[ch].iRVBRepeat=iReverbRepeat*45; + s_chan[ch].iRVBNum =iReverbNum; + } + } + else s_chan[ch].bRVBActive=0; // else -> no reverb +} + +//////////////////////////////////////////////////////////////////////// +// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf +//////////////////////////////////////////////////////////////////////// + +static INLINE void InitREVERB(void) +{ + if(iUseReverb==2) + {memset(sRVBStart,0,NSSIZE*2*4);} +} + +//////////////////////////////////////////////////////////////////////// +// STORE REVERB +//////////////////////////////////////////////////////////////////////// + +INLINE void StoreREVERB(int ch,int ns) +{ + if(iUseReverb==0) return; + else + if(iUseReverb==2) // -------------------------------- // Neil's reverb + { + const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000; + const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000; + + ns<<=1; + + *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer + *(sRVBStart+ns+1)+=iRxr; + } + else // --------------------------------------------- // Pete's easy fake reverb + { + int * pN;int iRn,iRr=0; + + // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on + + int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x8000; + int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x8000; + + for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2) + { + pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1); + if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd); + + (*pN)+=iRxl; + pN++; + (*pN)+=iRxr; + } + } +} + +//////////////////////////////////////////////////////////////////////// + +static INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps +{ + short * p=(short *)spuMem; + iOff=(iOff*4)+rvb.CurrAddr; + while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff); + return (int)*(p+iOff); +} + +//////////////////////////////////////////////////////////////////////// + +static INLINE void s_buffer(int iOff,int iVal) // set_buffer content helper: takes care about wraps and clipping +{ + short * p=(short *)spuMem; + iOff=(iOff*4)+rvb.CurrAddr; + while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff); + if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +static INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping +{ + short * p=(short *)spuMem; + iOff=(iOff*4)+rvb.CurrAddr+1; + while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000); + while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff); + if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L; + *(p+iOff)=(short)iVal; +} + +//////////////////////////////////////////////////////////////////////// + +static INLINE int MixREVERBLeft(int ns) +{ + if(iUseReverb==0) return 0; + else + if(iUseReverb==2) + { + static int iCnt=0; // this func will be called with 44.1 khz + + if(!rvb.StartAddr) // reverb is off + { + rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; + return 0; + } + + iCnt++; + + if(iCnt&1) // we work on every second left value: downsample to 22 khz + { + if(spuCtrl&0x80) // -> reverb on? oki + { + int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1; + + const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1)); + const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1); + + const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; + const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; + const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L; + const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L; + + const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L; + const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L; + + s_buffer1(rvb.IIR_DEST_A0, IIR_A0); + s_buffer1(rvb.IIR_DEST_A1, IIR_A1); + s_buffer1(rvb.IIR_DEST_B0, IIR_B0); + s_buffer1(rvb.IIR_DEST_B1, IIR_B1); + + ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L + + (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L + + (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L + + (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L; + ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L + + (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L + + (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L + + (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L; + + FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A); + FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A); + FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B); + FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B); + + s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L); + s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L); + + s_buffer(rvb.MIX_DEST_B0, (rvb.FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb.FB_X)/32768L); + s_buffer(rvb.MIX_DEST_B1, (rvb.FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb.FB_X)/32768L); + + rvb.iLastRVBLeft = rvb.iRVBLeft; + rvb.iLastRVBRight = rvb.iRVBRight; + + rvb.iRVBLeft = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3; + rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3; + + rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000; + rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000; + + rvb.CurrAddr++; + if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; + + return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2; + } + else // -> reverb off + { + rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0; + } + + rvb.CurrAddr++; + if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr; + } + + return rvb.iLastRVBLeft; + } + else // easy fake reverb: + { + const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value + *sRVBPlay++=0; // -> init it after + if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds + return iRV; // -> return reverb mix buf val + } +} + +//////////////////////////////////////////////////////////////////////// + +static INLINE int MixREVERBRight(void) +{ + if(iUseReverb==0) return 0; + else + if(iUseReverb==2) // Neill's reverb: + { + int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2; + rvb.iLastRVBRight=rvb.iRVBRight; + return i; // -> just return the last right reverb val (little bit scaled by the previous right val) + } + else // easy fake reverb: + { + const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value + *sRVBPlay++=0; // -> init it after + if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds + return iRV; // -> return reverb mix buf val + } +} + +//////////////////////////////////////////////////////////////////////// + +#endif + +/* +----------------------------------------------------------------------------- +PSX reverb hardware notes +by Neill Corlett +----------------------------------------------------------------------------- + +Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway +yadda yadda. + +----------------------------------------------------------------------------- + +Basics +------ + +- The reverb buffer is 22khz 16-bit mono PCM. +- It starts at the reverb address given by 1DA2, extends to + the end of sound RAM, and wraps back to the 1DA2 address. + +Setting the address at 1DA2 resets the current reverb work address. + +This work address ALWAYS increments every 1/22050 sec., regardless of +whether reverb is enabled (bit 7 of 1DAA set). + +And the contents of the reverb buffer ALWAYS play, scaled by the +"reverberation depth left/right" volumes (1D84/1D86). +(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0) + +----------------------------------------------------------------------------- + +Register names +-------------- + +These are probably not their real names. +These are probably not even correct names. +We will use them anyway, because we can. + +1DC0: FB_SRC_A (offset) +1DC2: FB_SRC_B (offset) +1DC4: IIR_ALPHA (coef.) +1DC6: ACC_COEF_A (coef.) +1DC8: ACC_COEF_B (coef.) +1DCA: ACC_COEF_C (coef.) +1DCC: ACC_COEF_D (coef.) +1DCE: IIR_COEF (coef.) +1DD0: FB_ALPHA (coef.) +1DD2: FB_X (coef.) +1DD4: IIR_DEST_A0 (offset) +1DD6: IIR_DEST_A1 (offset) +1DD8: ACC_SRC_A0 (offset) +1DDA: ACC_SRC_A1 (offset) +1DDC: ACC_SRC_B0 (offset) +1DDE: ACC_SRC_B1 (offset) +1DE0: IIR_SRC_A0 (offset) +1DE2: IIR_SRC_A1 (offset) +1DE4: IIR_DEST_B0 (offset) +1DE6: IIR_DEST_B1 (offset) +1DE8: ACC_SRC_C0 (offset) +1DEA: ACC_SRC_C1 (offset) +1DEC: ACC_SRC_D0 (offset) +1DEE: ACC_SRC_D1 (offset) +1DF0: IIR_SRC_B1 (offset) +1DF2: IIR_SRC_B0 (offset) +1DF4: MIX_DEST_A0 (offset) +1DF6: MIX_DEST_A1 (offset) +1DF8: MIX_DEST_B0 (offset) +1DFA: MIX_DEST_B1 (offset) +1DFC: IN_COEF_L (coef.) +1DFE: IN_COEF_R (coef.) + +The coefficients are signed fractional values. +-32768 would be -1.0 + 32768 would be 1.0 (if it were possible... the highest is of course 32767) + +The offsets are (byte/8) offsets into the reverb buffer. +i.e. you multiply them by 8, you get byte offsets. +You can also think of them as (samples/4) offsets. +They appear to be signed. They can be negative. +None of the documented presets make them negative, though. + +Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo. + +----------------------------------------------------------------------------- + +What it does +------------ + +We take all reverb sources: +- regular channels that have the reverb bit on +- cd and external sources, if their reverb bits are on +and mix them into one stereo 44100hz signal. + +Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting +algorithm here, but I haven't figured out the hysterically exact specifics. +I use an 8-tap filter with these coefficients, which are nice but probably +not the real ones: + +0.037828187894 +0.157538631280 +0.321159685278 +0.449322115345 +0.449322115345 +0.321159685278 +0.157538631280 +0.037828187894 + +So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz. + +* IN MY EMULATION, I divide these by 2 to make it clip less. + (and of course the L/R output coefficients are adjusted to compensate) + The real thing appears to not do this. + +At every 22050hz tick: +- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb + steady-state algorithm described below +- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer + (This part may not be exactly right and I guessed at the coefs. TODO: check later.) + L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0]) + R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1]) +- Advance the current buffer position by 1 sample + +The wet out L and R are then upsampled to 44100hz and played at the +"reverberation depth left/right" (1D84/1D86) volume, independent of the main +volume. + +----------------------------------------------------------------------------- + +Reverb steady-state +------------------- + +The reverb steady-state algorithm is fairly clever, and of course by +"clever" I mean "batshit insane". + +buffer[x] is relative to the current buffer position, not the beginning of +the buffer. Note that all buffer offsets must wrap around so they're +contained within the reverb work area. + +Clipping is performed at the end... maybe also sooner, but definitely at +the end. + +IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; +IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; +IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L; +IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R; + +IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA); +IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA); +IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA); +IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA); + +buffer[IIR_DEST_A0 + 1sample] = IIR_A0; +buffer[IIR_DEST_A1 + 1sample] = IIR_A1; +buffer[IIR_DEST_B0 + 1sample] = IIR_B0; +buffer[IIR_DEST_B1 + 1sample] = IIR_B1; + +ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A + + buffer[ACC_SRC_B0] * ACC_COEF_B + + buffer[ACC_SRC_C0] * ACC_COEF_C + + buffer[ACC_SRC_D0] * ACC_COEF_D; +ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A + + buffer[ACC_SRC_B1] * ACC_COEF_B + + buffer[ACC_SRC_C1] * ACC_COEF_C + + buffer[ACC_SRC_D1] * ACC_COEF_D; + +FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A]; +FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A]; +FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B]; +FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B]; + +buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA; +buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA; +buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X; +buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X; + +----------------------------------------------------------------------------- +*/ + diff --git a/plugins/dfsound/sdl.c b/plugins/dfsound/sdl.c index 06acd686..f3cf92d2 100644 --- a/plugins/dfsound/sdl.c +++ b/plugins/dfsound/sdl.c @@ -18,6 +18,8 @@ #include "stdafx.h" +#include "dsoundoss.h" + #include "externals.h" #include <SDL.h> diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c index f263cb8f..36e916a1 100644 --- a/plugins/dfsound/spu.c +++ b/plugins/dfsound/spu.c @@ -23,22 +23,13 @@ #include "cfg.h" #include "dsoundoss.h" #include "regs.h" +#include "spu.h" #ifdef _WINDOWS #include "debug.h" #include "record.h" #endif -#ifdef ENABLE_NLS -#include <libintl.h> -#include <locale.h> -#define _(x) gettext(x) -#define N_(x) (x) -#else -#define _(x) (x) -#define N_(x) (x) -#endif - #if defined (_WINDOWS) static char * libraryName = N_("DirectSound Driver"); #elif defined (USEMACOSX) @@ -54,8 +45,9 @@ static char * libraryName = N_("PulseAudio Sound"); #else static char * libraryName = N_("NULL Sound"); #endif - +#if 0 static char * libraryInfo = N_("P.E.Op.S. Sound Driver V1.7\nCoded by Pete Bernert and the P.E.Op.S. team\n"); +#endif // globals @@ -183,7 +175,7 @@ static int iSecureStart=0; // secure start counter // -INLINE void InterpolateUp(int ch) +static 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 { @@ -231,7 +223,7 @@ INLINE void InterpolateUp(int ch) // even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm // -INLINE void InterpolateDown(int ch) +static INLINE void InterpolateDown(int ch) { if(s_chan[ch].sinc>=0x20000L) // we would skip at least one val? { @@ -257,7 +249,7 @@ INLINE void InterpolateDown(int ch) // START SOUND... called by main thread to setup a new sound on a channel //////////////////////////////////////////////////////////////////////// -INLINE void StartSound(int ch) +static INLINE void StartSound(int ch) { StartADSR(ch); StartREVERB(ch); @@ -287,7 +279,7 @@ INLINE void StartSound(int ch) // ALL KIND OF HELPERS //////////////////////////////////////////////////////////////////////// -INLINE void VoiceChangeFrequency(int ch) +static INLINE void VoiceChangeFrequency(int ch) { s_chan[ch].iUsedFreq=s_chan[ch].iActFreq; // -> take it and calc steps s_chan[ch].sinc=s_chan[ch].iRawPitch<<4; @@ -297,7 +289,7 @@ INLINE void VoiceChangeFrequency(int ch) //////////////////////////////////////////////////////////////////////// -INLINE void FModChangeFrequency(int ch,int ns) +static INLINE void FModChangeFrequency(int ch,int ns) { int NP=s_chan[ch].iRawPitch; @@ -361,7 +353,7 @@ unsigned short NoiseFreqAdd[5] = { 0, 84, 140, 180, 210 }; -INLINE void NoiseClock() +static INLINE void NoiseClock() { unsigned int level; @@ -387,7 +379,7 @@ INLINE void NoiseClock() } } -INLINE int iGetNoiseVal(int ch) +static INLINE int iGetNoiseVal(int ch) { int fa; @@ -408,7 +400,7 @@ INLINE int iGetNoiseVal(int ch) //////////////////////////////////////////////////////////////////////// -INLINE void StoreInterpolationVal(int ch,int fa) +static INLINE void StoreInterpolationVal(int ch,int fa) { /* // fmod channel = sound output @@ -446,7 +438,7 @@ INLINE void StoreInterpolationVal(int ch,int fa) //////////////////////////////////////////////////////////////////////// -INLINE int iGetInterpolationVal(int ch) +static INLINE int iGetInterpolationVal(int ch) { int fa; @@ -1012,7 +1004,7 @@ DWORD WINAPI MAINThreadEx(LPVOID lpParameter) // 1 time every 'cycle' cycles... harhar long cpu_cycles; -void CALLBACK SPUasync(unsigned long cycle) +void CALLBACK SPUasync(uint32_t cycle) { cpu_cycles += cycle; @@ -1065,10 +1057,12 @@ void CALLBACK SPUasync(unsigned long cycle) // leave that func in the linux port, until epsxe linux is using // the async function as well +#if 0 void CALLBACK SPUupdate(void) { SPUasync(0); } +#endif // XA AUDIO @@ -1158,7 +1152,7 @@ void RemoveTimer(void) } // SETUPSTREAMS: init most of the spu buffers -void SetupStreams(void) +static void SetupStreams(void) { int i; @@ -1201,7 +1195,7 @@ void SetupStreams(void) } // REMOVESTREAMS: free most buffer -void RemoveStreams(void) +static void RemoveStreams(void) { free(pSpuBuffer); // free mixing buffer pSpuBuffer = NULL; @@ -1355,10 +1349,12 @@ void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void)) irqCallback = callback; } +#if 0 void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short)) { cddavCallback = CDDAVcallback; } +#endif // COMMON PLUGIN INFO FUNCS char * CALLBACK PSEgetLibName(void) @@ -1376,7 +1372,9 @@ unsigned long CALLBACK PSEgetLibVersion(void) return (1 << 16) | (1 << 8); } +#if 0 char * SPUgetLibInfos(void) { return _(libraryInfo); } +#endif diff --git a/plugins/dfsound/spu.h b/plugins/dfsound/spu.h index 8912684b..8d492485 100644 --- a/plugins/dfsound/spu.h +++ b/plugins/dfsound/spu.h @@ -1,21 +1,22 @@ -/***************************************************************************
- spu.h - description
- -------------------
- begin : Wed May 15 2002
- copyright : (C) 2002 by Pete Bernert
- email : BlackDove@addcom.de
- ***************************************************************************/
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. See also the license.txt file for *
- * additional informations. *
- * *
- ***************************************************************************/
-
-void SetupTimer(void);
-void RemoveTimer(void);
-void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap);
-void CALLBACK SPUplayCDDAchannel(short *pcm, int bytes);
\ No newline at end of file +/*************************************************************************** + spu.h - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +void SetupTimer(void); +void RemoveTimer(void); +void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap); +void CALLBACK SPUplayCDDAchannel(short *pcm, int bytes); +extern int lastch;
\ No newline at end of file diff --git a/plugins/dfsound/spucfg-0.1df/main.c b/plugins/dfsound/spucfg-0.1df/main.c index ce2efbaf..8bea2b58 100644 --- a/plugins/dfsound/spucfg-0.1df/main.c +++ b/plugins/dfsound/spucfg-0.1df/main.c @@ -22,7 +22,7 @@ void SaveConfig(GtkWidget *widget, gpointer user_datal); /* This function checks for the value being outside the accepted range, and returns the appropriate boundary value */ -int set_limit (char *p, int len, int lower, int upper) +static int set_limit (char *p, int len, int lower, int upper) { int val = 0; @@ -37,13 +37,13 @@ int set_limit (char *p, int len, int lower, int upper) return val; } -void on_about_clicked (GtkWidget *widget, gpointer user_data) +static void on_about_clicked (GtkWidget *widget, gpointer user_data) { gtk_widget_destroy (widget); exit (0); } -void OnConfigClose(GtkWidget *widget, gpointer user_data) +static void OnConfigClose(GtkWidget *widget, gpointer user_data) { GladeXML *xml = (GladeXML *)user_data; diff --git a/plugins/dfsound/stdafx.h b/plugins/dfsound/stdafx.h index 3f28ceb9..5ba6c625 100644 --- a/plugins/dfsound/stdafx.h +++ b/plugins/dfsound/stdafx.h @@ -14,26 +14,26 @@ * additional informations. * * * ***************************************************************************/ -
-#ifdef _WINDOWS
-
-#define WIN32_LEAN_AND_MEAN
-#define STRICT
-#include <windows.h>
-#include <windowsx.h>
-#include "mmsystem.h"
-#include <process.h>
-#include <stdlib.h>
-
-#ifndef INLINE
-#define INLINE __inline
-#endif
-
-#include "resource.h"
-
-#pragma warning (disable:4996)
-
-#else
+ +#ifdef _WINDOWS + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#include <windows.h> +#include <windowsx.h> +#include "mmsystem.h" +#include <process.h> +#include <stdlib.h> + +#ifndef INLINE +#define INLINE __inline +#endif + +#include "resource.h" + +#pragma warning (disable:4996) + +#else #ifndef _MACOSX #include "config.h" @@ -62,7 +62,5 @@ #ifndef INLINE #define INLINE inline #endif -
-#endif
-#include "psemuxa.h" +#endif diff --git a/plugins/dfsound/xa.c b/plugins/dfsound/xa.c index 32614a29..a0ba86e8 100644 --- a/plugins/dfsound/xa.c +++ b/plugins/dfsound/xa.c @@ -15,7 +15,9 @@ * * ***************************************************************************/ -#include "stdafx.h"
+#include "stdafx.h" + +#include "xa.h" #define _IN_XA #include <stdint.h> @@ -45,6 +47,7 @@ uint32_t * CDDAEnd = NULL; int iLeftXAVol = 0x8000; int iRightXAVol = 0x8000; +#if 0 static int gauss_ptr = 0; static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -52,132 +55,133 @@ static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0}; #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)] -
-long cdxa_dbuf_ptr;
+#endif + +long cdxa_dbuf_ptr; //////////////////////////////////////////////////////////////////////// // MIX XA & CDDA //////////////////////////////////////////////////////////////////////// -static int lastxa_lc, lastxa_rc;
-static int lastcd_lc, lastcd_rc;
-
+static int lastxa_lc, lastxa_rc; +static int lastcd_lc, lastcd_rc; + INLINE void MixXA(void) { - int ns;
- int lc,rc;
- unsigned long cdda_l;
-
- lc = 0;
- rc = 0;
-
- for(ns=0;ns<NSSIZE && XAPlay!=XAFeed;ns++)
- {
- XALastVal=*XAPlay++;
- if(XAPlay==XAEnd) XAPlay=XAStart;
-
- lc = (short)(XALastVal&0xffff);
- rc = (short)((XALastVal>>16) & 0xffff);
-
- if( lc < -32768 ) lc = -32768;
- if( rc < -32768 ) rc = -32768;
- if( lc > 32767 ) lc = 32767;
- if( rc > 32767 ) rc = 32767;
-
- SSumL[ns]+=lc;
- SSumR[ns]+=rc;
-
- // improve crackle - buffer under
- // - not update fast enough
- lastxa_lc = lc;
- lastxa_rc = rc;
-
-
-#if 0
- if( cdxa_dbuf_ptr >= 0x400 )
- cdxa_dbuf_ptr = 0;
- spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lc;
- spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = rc;
- cdxa_dbuf_ptr += 2;
-#endif
- }
-
- if(XAPlay==XAFeed && XARepeat)
- {
- //XARepeat--;
- for(;ns<NSSIZE;ns++)
- {
- SSumL[ns]+=lastxa_rc;
- SSumR[ns]+=lastxa_rc;
-
-#if 0
- // Tales of Phantasia - voice meter
- if( cdxa_dbuf_ptr >= 0x400 )
- cdxa_dbuf_ptr = 0;
- spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lastxa_rc;
- spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = lastxa_rc;
- cdxa_dbuf_ptr += 2;
-#endif
- }
- }
+ int ns; + int lc,rc; + unsigned long cdda_l; + + lc = 0; + rc = 0; + + for(ns=0;ns<NSSIZE && XAPlay!=XAFeed;ns++) + { + XALastVal=*XAPlay++; + if(XAPlay==XAEnd) XAPlay=XAStart; + + lc = (short)(XALastVal&0xffff); + rc = (short)((XALastVal>>16) & 0xffff); + + if( lc < -32768 ) lc = -32768; + if( rc < -32768 ) rc = -32768; + if( lc > 32767 ) lc = 32767; + if( rc > 32767 ) rc = 32767; + + SSumL[ns]+=lc; + SSumR[ns]+=rc; + + // improve crackle - buffer under + // - not update fast enough + lastxa_lc = lc; + lastxa_rc = rc; + + +#if 0 + if( cdxa_dbuf_ptr >= 0x400 ) + cdxa_dbuf_ptr = 0; + spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lc; + spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = rc; + cdxa_dbuf_ptr += 2; +#endif + } + + if(XAPlay==XAFeed && XARepeat) + { + //XARepeat--; + for(;ns<NSSIZE;ns++) + { + SSumL[ns]+=lastxa_rc; + SSumR[ns]+=lastxa_rc; + +#if 0 + // Tales of Phantasia - voice meter + if( cdxa_dbuf_ptr >= 0x400 ) + cdxa_dbuf_ptr = 0; + spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lastxa_rc; + spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = lastxa_rc; + cdxa_dbuf_ptr += 2; +#endif + } + } for(ns=0;ns<NSSIZE && CDDAPlay!=CDDAFeed && (CDDAPlay!=CDDAEnd-1||CDDAFeed!=CDDAStart);ns++) { cdda_l=*CDDAPlay++; - if(CDDAPlay==CDDAEnd) CDDAPlay=CDDAStart;
- - lc = (short)(cdda_l&0xffff);
- rc = (short)((cdda_l>>16) & 0xffff);
-
- if( lc < -32768 ) lc = -32768;
- if( rc < -32768 ) rc = -32768;
- if( lc > 32767 ) lc = 32767;
- if( rc > 32767 ) rc = 32767;
-
- SSumL[ns]+=lc;
- SSumR[ns]+=rc;
-
-#if 0
- // Vib Ribbon - playback
- if( cdxa_dbuf_ptr >= 0x400 )
- cdxa_dbuf_ptr = 0;
- spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lc;
- spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = rc;
- cdxa_dbuf_ptr += 2;
-#endif
-
- // improve crackle - buffer under
- // - not update fast enough
- lastcd_lc = lc;
- lastcd_rc = rc;
- }
-
-
- if(CDDAPlay==CDDAFeed && XARepeat)
- {
- //XARepeat--;
- for(;ns<NSSIZE;ns++)
- {
-#if 0
- // Vib Ribbon - playback
- if( cdxa_dbuf_ptr >= 0x400 )
- cdxa_dbuf_ptr = 0;
- spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lastcd_lc;
- spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = lastcd_rc;
- cdxa_dbuf_ptr += 2;
-#endif
-
- SSumL[ns]+=lastcd_lc;
- SSumR[ns]+=lastcd_rc;
- }
- }
+ if(CDDAPlay==CDDAEnd) CDDAPlay=CDDAStart; + + lc = (short)(cdda_l&0xffff); + rc = (short)((cdda_l>>16) & 0xffff); + + if( lc < -32768 ) lc = -32768; + if( rc < -32768 ) rc = -32768; + if( lc > 32767 ) lc = 32767; + if( rc > 32767 ) rc = 32767; + + SSumL[ns]+=lc; + SSumR[ns]+=rc; + +#if 0 + // Vib Ribbon - playback + if( cdxa_dbuf_ptr >= 0x400 ) + cdxa_dbuf_ptr = 0; + spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lc; + spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = rc; + cdxa_dbuf_ptr += 2; +#endif + + // improve crackle - buffer under + // - not update fast enough + lastcd_lc = lc; + lastcd_rc = rc; + } + + + if(CDDAPlay==CDDAFeed && XARepeat) + { + //XARepeat--; + for(;ns<NSSIZE;ns++) + { +#if 0 + // Vib Ribbon - playback + if( cdxa_dbuf_ptr >= 0x400 ) + cdxa_dbuf_ptr = 0; + spuMem[ (cdxa_dbuf_ptr + 0)/2 ] = lastcd_lc; + spuMem[ (cdxa_dbuf_ptr + 0x400)/2 ] = lastcd_rc; + cdxa_dbuf_ptr += 2; +#endif + + SSumL[ns]+=lastcd_lc; + SSumR[ns]+=lastcd_rc; + } + } } //////////////////////////////////////////////////////////////////////// // small linux time helper... only used for watchdog //////////////////////////////////////////////////////////////////////// -
-#ifndef _WINDOWS
+ +#ifndef _WINDOWS unsigned long timeGetTime_spu() { @@ -185,8 +189,8 @@ unsigned long timeGetTime_spu() gettimeofday(&tv, 0); // well, maybe there are better ways return tv.tv_sec * 1000 + tv.tv_usec/1000; // to do that, but at least it works } -
-#endif
+ +#endif //////////////////////////////////////////////////////////////////////// // FEED XA @@ -194,7 +198,7 @@ unsigned long timeGetTime_spu() INLINE void FeedXA(xa_decode_t *xap) { - int sinc,spos,i,iSize,iPlace,vl,vr; + int sinc,spos,i,iSize,iPlace; if(!bSPUIsOpen) return; @@ -381,8 +385,8 @@ INLINE void FeedXA(xa_decode_t *xap) // FEED CDDA //////////////////////////////////////////////////////////////////////// -unsigned int cdda_ptr;
-
+unsigned int cdda_ptr; + INLINE void FeedCDDA(unsigned char *pcm, int nBytes) { while(nBytes>0) @@ -390,13 +394,13 @@ INLINE void FeedCDDA(unsigned char *pcm, int nBytes) if(CDDAFeed==CDDAEnd) CDDAFeed=CDDAStart; while(CDDAFeed==CDDAPlay-1|| (CDDAFeed==CDDAEnd-1&&CDDAPlay==CDDAStart)) - {
-#ifdef _WINDOWS
- if (!iUseTimer) Sleep(1);
- else return;
+ { +#ifdef _WINDOWS + if (!iUseTimer) Sleep(1); + else return; #else if (!iUseTimer) usleep(1000); - else return;
+ else return; #endif } *CDDAFeed++=(*pcm | (*(pcm+1)<<8) | (*(pcm+2)<<16) | (*(pcm+3)<<24)); |
