diff options
| author | SND\weimingzhi_cp <SND\weimingzhi_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2011-03-13 08:26:16 +0000 |
|---|---|---|
| committer | SND\weimingzhi_cp <SND\weimingzhi_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2011-03-13 08:26:16 +0000 |
| commit | 379a8879f7dae1a9074317c0270e12dd203b32c0 (patch) | |
| tree | 348efb7ecd4f7cbc030f4b5db6683a857f2ae6cf /plugins/dfsound | |
| parent | d34b4220bde29d7937d927e9d17a50470a36c500 (diff) | |
| download | pcsxr-379a8879f7dae1a9074317c0270e12dd203b32c0.tar.gz | |
Temporarily reverted r64524 until I (or someone else) find the time to sort out the stuff.
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@64536 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'plugins/dfsound')
| -rw-r--r-- | plugins/dfsound/Makefile.am | 102 | ||||
| -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 | 50 | ||||
| -rw-r--r-- | plugins/dfsound/oss.c | 319 | ||||
| -rw-r--r-- | plugins/dfsound/pulseaudio.c | 710 | ||||
| -rw-r--r-- | plugins/dfsound/reverb.c | 925 | ||||
| -rw-r--r-- | plugins/dfsound/sdl.c | 272 | ||||
| -rw-r--r-- | plugins/dfsound/spu.c | 2762 | ||||
| -rw-r--r-- | plugins/dfsound/spu.h | 43 | ||||
| -rw-r--r-- | plugins/dfsound/spucfg-0.1df/main.c | 548 | ||||
| -rw-r--r-- | plugins/dfsound/stdafx.h | 134 | ||||
| -rw-r--r-- | plugins/dfsound/xa.c | 820 |
16 files changed, 4887 insertions, 4892 deletions
diff --git a/plugins/dfsound/Makefile.am b/plugins/dfsound/Makefile.am index ca62b32e..e5e17917 100644 --- a/plugins/dfsound/Makefile.am +++ b/plugins/dfsound/Makefile.am @@ -1,51 +1,51 @@ -AM_CPPFLAGS = -I../../include -I../../libpcsxcore - -bindir = @libdir@/games/psemu/ -libdir = @libdir@/games/psemu/ - -lib_LTLIBRARIES = libDFSound.la - -libDFSound_la_SOURCES = spu.c cfg.c dma.c freeze.c registers.c - -libDFSound_la_CPPFLAGS = $(AM_CPPFLAGS) -libDFSound_la_LDFLAGS = -module -avoid-version -libDFSound_la_LIBADD = -lpthread -lm - -if SOUND_ALSA -libDFSound_la_SOURCES += alsa.c -libDFSound_la_CPPFLAGS += -DUSEALSA=1 -libDFSound_la_LIBADD += $(ALSA_LIBS) -endif - -if SOUND_OSS -libDFSound_la_SOURCES += oss.c -libDFSound_la_CPPFLAGS += -DUSEOSS=1 -endif - -if SOUND_PULSEAUDIO -libDFSound_la_SOURCES += pulseaudio.c -libDFSound_la_CPPFLAGS += -DUSEPULSEAUDIO=1 $(PULSEAUDIO_CFLAGS) -libDFSound_la_LIBADD += $(PULSEAUDIO_LIBS) -endif - -if SOUND_SDL -libDFSound_la_SOURCES += sdl.c -libDFSound_la_CPPFLAGS += -DUSESDL=1 $(SDL_CFLAGS) -libDFSound_la_LIBADD += $(SDL_LIBS) -endif - -if SOUND_NULL -libDFSound_la_SOURCES += nullsnd.c -libDFSound_la_CPPFLAGS += -DUSENULL=1 -endif - -bin_PROGRAMS = cfgDFSound -cfgDFSound_CPPFLAGS = -DLOCALE_DIR=\"${datadir}/locale/\" \ - -DDATADIR=\"${datadir}/psemu/\" \ - $(GTK2_CFLAGS) $(GLADE2_CFLAGS) $(AM_CPPFLAGS) -cfgDFSound_SOURCES = spucfg-0.1df/main.c -cfgDFSound_LDADD = $(GTK2_LIBS) $(GLADE2_LIBS) - -glade_DATA = spucfg-0.1df/dfsound.glade2 -gladedir = $(datadir)/psemu/ -EXTRA_DIST = $(glade_DATA) +AM_CPPFLAGS = -I../../include
+
+bindir = @libdir@/games/psemu/
+libdir = @libdir@/games/psemu/
+
+lib_LTLIBRARIES = libDFSound.la
+
+libDFSound_la_SOURCES = spu.c cfg.c dma.c freeze.c registers.c
+
+libDFSound_la_CPPFLAGS = $(AM_CPPFLAGS)
+libDFSound_la_LDFLAGS = -module -avoid-version
+libDFSound_la_LIBADD = -lpthread -lm
+
+if SOUND_ALSA
+libDFSound_la_SOURCES += alsa.c
+libDFSound_la_CPPFLAGS += -DUSEALSA=1
+libDFSound_la_LIBADD += $(ALSA_LIBS)
+endif
+
+if SOUND_OSS
+libDFSound_la_SOURCES += oss.c
+libDFSound_la_CPPFLAGS += -DUSEOSS=1
+endif
+
+if SOUND_PULSEAUDIO
+libDFSound_la_SOURCES += pulseaudio.c
+libDFSound_la_CPPFLAGS += -DUSEPULSEAUDIO=1 $(PULSEAUDIO_CFLAGS)
+libDFSound_la_LIBADD += $(PULSEAUDIO_LIBS)
+endif
+
+if SOUND_SDL
+libDFSound_la_SOURCES += sdl.c
+libDFSound_la_CPPFLAGS += -DUSESDL=1 $(SDL_CFLAGS)
+libDFSound_la_LIBADD += $(SDL_LIBS)
+endif
+
+if SOUND_NULL
+libDFSound_la_SOURCES += nullsnd.c
+libDFSound_la_CPPFLAGS += -DUSENULL=1
+endif
+
+bin_PROGRAMS = cfgDFSound
+cfgDFSound_CPPFLAGS = -DLOCALE_DIR=\"${datadir}/locale/\" \
+ -DDATADIR=\"${datadir}/psemu/\" \
+ $(GTK2_CFLAGS) $(GLADE2_CFLAGS) $(AM_CPPFLAGS)
+cfgDFSound_SOURCES = spucfg-0.1df/main.c
+cfgDFSound_LDADD = $(GTK2_LIBS) $(GLADE2_LIBS)
+
+glade_DATA = spucfg-0.1df/dfsound.glade2
+gladedir = $(datadir)/psemu/
+EXTRA_DIST = $(glade_DATA)
diff --git a/plugins/dfsound/adsr.c b/plugins/dfsound/adsr.c index d0f7808e..f75a5127 100644 --- a/plugins/dfsound/adsr.c +++ b/plugins/dfsound/adsr.c @@ -1,764 +1,763 @@ -/*************************************************************************** - 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] - ------------------------------------------------------------------------------ -*/ - +/***************************************************************************
+ 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]
+
+-----------------------------------------------------------------------------
+*/
+
diff --git a/plugins/dfsound/adsr.h b/plugins/dfsound/adsr.h index e15031dc..ff2af1ff 100644 --- a/plugins/dfsound/adsr.h +++ b/plugins/dfsound/adsr.h @@ -1,20 +1,19 @@ -/*************************************************************************** - 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); +/***************************************************************************
+ 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);
diff --git a/plugins/dfsound/cfg.c b/plugins/dfsound/cfg.c index 2bf42c9c..a98a695f 100644 --- a/plugins/dfsound/cfg.c +++ b/plugins/dfsound/cfg.c @@ -1,177 +1,173 @@ -/*************************************************************************** - 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(); -} +/***************************************************************************
+ 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();
+}
diff --git a/plugins/dfsound/externals.h b/plugins/dfsound/externals.h index 011205b8..b8a5c43d 100644 --- a/plugins/dfsound/externals.h +++ b/plugins/dfsound/externals.h @@ -1,361 +1,359 @@ -/*************************************************************************** - 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 +/***************************************************************************
+ 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
diff --git a/plugins/dfsound/freeze.c b/plugins/dfsound/freeze.c index f724568f..156fd693 100644 --- a/plugins/dfsound/freeze.c +++ b/plugins/dfsound/freeze.c @@ -1,223 +1,235 @@ -/*************************************************************************** - 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]); - } -} - -//////////////////////////////////////////////////////////////////////// +/***************************************************************************
+ 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]);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
diff --git a/plugins/dfsound/nullsnd.c b/plugins/dfsound/nullsnd.c index 87046642..70323f5d 100644 --- a/plugins/dfsound/nullsnd.c +++ b/plugins/dfsound/nullsnd.c @@ -1,26 +1,24 @@ -#include "stdafx.h" -#define _IN_OSS -#include "externals.h" - -#include "dsoundoss.h" - -// SETUP SOUND -void SetupSound(void) -{ -} - -// REMOVE SOUND -void RemoveSound(void) -{ -} - -// GET BYTES BUFFERED -unsigned long SoundGetBytesBuffered(void) -{ - return 0; -} - -// FEED SOUND DATA -void SoundFeedStreamData(unsigned char* pSound,long lBytes) -{ -} +#include "stdafx.h"
+#define _IN_OSS
+#include "externals.h"
+
+// SETUP SOUND
+void SetupSound(void)
+{
+}
+
+// REMOVE SOUND
+void RemoveSound(void)
+{
+}
+
+// GET BYTES BUFFERED
+unsigned long SoundGetBytesBuffered(void)
+{
+ return 0;
+}
+
+// FEED SOUND DATA
+void SoundFeedStreamData(unsigned char* pSound,long lBytes)
+{
+}
diff --git a/plugins/dfsound/oss.c b/plugins/dfsound/oss.c index d39412d9..26cca761 100644 --- a/plugins/dfsound/oss.c +++ b/plugins/dfsound/oss.c @@ -1,160 +1,159 @@ -/*************************************************************************** - oss.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_OSS - -#include "externals.h" - -#include <errno.h> - -//////////////////////////////////////////////////////////////////////// -// oss globals -//////////////////////////////////////////////////////////////////////// - -#define OSS_MODE_STEREO 1 -#define OSS_MODE_MONO 0 - -#define OSS_SPEED_44100 44100 - -static int oss_audio_fd = -1; - -//////////////////////////////////////////////////////////////////////// -// SETUP SOUND -//////////////////////////////////////////////////////////////////////// - -void SetupSound(void) -{ - int pspeed=44100; - int pstereo; - int format; - int fragsize = 0; - int myfrag; - int oss_speed, oss_stereo; - - if(iDisStereo) pstereo=OSS_MODE_MONO; - else pstereo=OSS_MODE_STEREO; - - oss_speed = pspeed; - oss_stereo = pstereo; - - if((oss_audio_fd=open("/dev/dsp",O_WRONLY,0))==-1) - { - printf("Sound device not available!\n"); - return; - } - - if(ioctl(oss_audio_fd,SNDCTL_DSP_RESET,0)==-1) - { - printf("Sound reset failed\n"); - return; - } - - // we use 64 fragments with 1024 bytes each - - fragsize=10; - myfrag=(63<<16)|fragsize; - - if(ioctl(oss_audio_fd,SNDCTL_DSP_SETFRAGMENT,&myfrag)==-1) - { - printf("Sound set fragment failed!\n"); - return; - } - - format = AFMT_S16_NE; - - if(ioctl(oss_audio_fd,SNDCTL_DSP_SETFMT,&format) == -1) - { - printf("Sound format not supported!\n"); - return; - } - - if(format!=AFMT_S16_NE) - { - printf("Sound format not supported!\n"); - return; - } - - if(ioctl(oss_audio_fd,SNDCTL_DSP_STEREO,&oss_stereo)==-1) - { - printf("Stereo mode not supported!\n"); - return; - } - - if(oss_stereo!=1) - { - iDisStereo=1; - } - - if(ioctl(oss_audio_fd,SNDCTL_DSP_SPEED,&oss_speed)==-1) - { - printf("Sound frequency not supported\n"); - return; - } - - if(oss_speed!=pspeed) - { - printf("Sound frequency not supported\n"); - return; - } -} - -//////////////////////////////////////////////////////////////////////// -// REMOVE SOUND -//////////////////////////////////////////////////////////////////////// - -void RemoveSound(void) -{ - if(oss_audio_fd != -1 ) - { - close(oss_audio_fd); - oss_audio_fd = -1; - } -} - -//////////////////////////////////////////////////////////////////////// -// GET BYTES BUFFERED -//////////////////////////////////////////////////////////////////////// - -unsigned long SoundGetBytesBuffered(void) -{ - audio_buf_info info; - unsigned long l; - - if(oss_audio_fd == -1) return SOUNDSIZE; - if(ioctl(oss_audio_fd,SNDCTL_DSP_GETOSPACE,&info)==-1) - l=0; - else - { - if(info.fragments<(info.fragstotal>>1)) // can we write in at least the half of fragments? - l=SOUNDSIZE; // -> no? wait - else l=0; // -> else go on - } - - return l; -} - -//////////////////////////////////////////////////////////////////////// -// FEED SOUND DATA -//////////////////////////////////////////////////////////////////////// - -void SoundFeedStreamData(unsigned char* pSound,long lBytes) -{ - if(oss_audio_fd == -1) return; - write(oss_audio_fd,pSound,lBytes); -} +/***************************************************************************
+ oss.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_OSS
+
+#include "externals.h"
+
+////////////////////////////////////////////////////////////////////////
+// oss globals
+////////////////////////////////////////////////////////////////////////
+
+#define OSS_MODE_STEREO 1
+#define OSS_MODE_MONO 0
+
+#define OSS_SPEED_44100 44100
+
+static int oss_audio_fd = -1;
+extern int errno;
+
+////////////////////////////////////////////////////////////////////////
+// SETUP SOUND
+////////////////////////////////////////////////////////////////////////
+
+void SetupSound(void)
+{
+ int pspeed=44100;
+ int pstereo;
+ int format;
+ int fragsize = 0;
+ int myfrag;
+ int oss_speed, oss_stereo;
+
+ if(iDisStereo) pstereo=OSS_MODE_MONO;
+ else pstereo=OSS_MODE_STEREO;
+
+ oss_speed = pspeed;
+ oss_stereo = pstereo;
+
+ if((oss_audio_fd=open("/dev/dsp",O_WRONLY,0))==-1)
+ {
+ printf("Sound device not available!\n");
+ return;
+ }
+
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_RESET,0)==-1)
+ {
+ printf("Sound reset failed\n");
+ return;
+ }
+
+ // we use 64 fragments with 1024 bytes each
+
+ fragsize=10;
+ myfrag=(63<<16)|fragsize;
+
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_SETFRAGMENT,&myfrag)==-1)
+ {
+ printf("Sound set fragment failed!\n");
+ return;
+ }
+
+ format = AFMT_S16_NE;
+
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_SETFMT,&format) == -1)
+ {
+ printf("Sound format not supported!\n");
+ return;
+ }
+
+ if(format!=AFMT_S16_NE)
+ {
+ printf("Sound format not supported!\n");
+ return;
+ }
+
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_STEREO,&oss_stereo)==-1)
+ {
+ printf("Stereo mode not supported!\n");
+ return;
+ }
+
+ if(oss_stereo!=1)
+ {
+ iDisStereo=1;
+ }
+
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_SPEED,&oss_speed)==-1)
+ {
+ printf("Sound frequency not supported\n");
+ return;
+ }
+
+ if(oss_speed!=pspeed)
+ {
+ printf("Sound frequency not supported\n");
+ return;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// REMOVE SOUND
+////////////////////////////////////////////////////////////////////////
+
+void RemoveSound(void)
+{
+ if(oss_audio_fd != -1 )
+ {
+ close(oss_audio_fd);
+ oss_audio_fd = -1;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// GET BYTES BUFFERED
+////////////////////////////////////////////////////////////////////////
+
+unsigned long SoundGetBytesBuffered(void)
+{
+ audio_buf_info info;
+ unsigned long l;
+
+ if(oss_audio_fd == -1) return SOUNDSIZE;
+ if(ioctl(oss_audio_fd,SNDCTL_DSP_GETOSPACE,&info)==-1)
+ l=0;
+ else
+ {
+ if(info.fragments<(info.fragstotal>>1)) // can we write in at least the half of fragments?
+ l=SOUNDSIZE; // -> no? wait
+ else l=0; // -> else go on
+ }
+
+ return l;
+}
+
+////////////////////////////////////////////////////////////////////////
+// FEED SOUND DATA
+////////////////////////////////////////////////////////////////////////
+
+void SoundFeedStreamData(unsigned char* pSound,long lBytes)
+{
+ if(oss_audio_fd == -1) return;
+ write(oss_audio_fd,pSound,lBytes);
+}
diff --git a/plugins/dfsound/pulseaudio.c b/plugins/dfsound/pulseaudio.c index 2185d149..19b1e0b8 100644 --- a/plugins/dfsound/pulseaudio.c +++ b/plugins/dfsound/pulseaudio.c @@ -1,356 +1,354 @@ -/*************************************************************************** - pulseaudio.c - description - ------------------- -begin : Thu Feb 04 2010 -copyright : (C) 2010 by Tristin Celestin -email : cetris1@umbc.edu -comment : Much of this was taken from simple.c, in the pulseaudio - library -***************************************************************************/ -/*************************************************************************** - * * - * 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" - -#ifdef USEPULSEAUDIO - -#define _IN_OSS - -#include "externals.h" -#include <pulse/pulseaudio.h> - -#include "dsoundoss.h" - -//////////////////////////////////////////////////////////////////////// -// pulseaudio structs -//////////////////////////////////////////////////////////////////////// - -typedef struct { - pa_threaded_mainloop *mainloop; - pa_context *context; - pa_mainloop_api *api; - pa_stream *stream; - pa_sample_spec spec; - int first; -} Device; - -typedef struct { - unsigned int frequency; - unsigned int latency_in_msec; -} Settings; - -//////////////////////////////////////////////////////////////////////// -// pulseaudio globals -//////////////////////////////////////////////////////////////////////// - -static Device device = { - .mainloop = NULL, - .api = NULL, - .context = NULL, - .stream = NULL -}; - -static Settings settings = { - .frequency = 44100, - .latency_in_msec = 20, -}; - -// the number of bytes written in SoundFeedStreamData -const int mixlen = 3240; - -// used to calculate how much space is used in the buffer, for debugging purposes -//int maxlength = 0; - -//////////////////////////////////////////////////////////////////////// -// CALLBACKS FOR THREADED MAINLOOP -//////////////////////////////////////////////////////////////////////// -static void context_state_cb (pa_context *context, void *userdata) -{ - Device *dev = userdata; - - if ((context == NULL) || (dev == NULL)) - return; - - switch (pa_context_get_state (context)) - { - case PA_CONTEXT_READY: - case PA_CONTEXT_TERMINATED: - case PA_CONTEXT_FAILED: - pa_threaded_mainloop_signal (dev->mainloop, 0); - break; - - case PA_CONTEXT_UNCONNECTED: - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - } -} - -static void stream_state_cb (pa_stream *stream, void * userdata) -{ - Device *dev = userdata; - - if ((stream == NULL) || (dev == NULL)) - return; - - switch (pa_stream_get_state (stream)) - { - case PA_STREAM_READY: - case PA_STREAM_FAILED: - case PA_STREAM_TERMINATED: - pa_threaded_mainloop_signal (dev->mainloop, 0); - break; - - case PA_STREAM_UNCONNECTED: - case PA_STREAM_CREATING: - break; - } -} - -static void stream_latency_update_cb (pa_stream *stream, void *userdata) -{ - Device *dev = userdata; - - if ((stream == NULL) || (dev == NULL)) - return; - - pa_threaded_mainloop_signal (dev->mainloop, 0); -} - -static void stream_request_cb (pa_stream *stream, size_t length, void *userdata) -{ - Device *dev = userdata; - - if ((stream == NULL) || (dev == NULL)) - return; - pa_threaded_mainloop_signal (dev->mainloop, 0); -} - -//////////////////////////////////////////////////////////////////////// -// SETUP SOUND -//////////////////////////////////////////////////////////////////////// - -void SetupSound (void) -{ - int error_number; - - // Acquire mainloop /////////////////////////////////////////////////////// - device.mainloop = pa_threaded_mainloop_new (); - if (device.mainloop == NULL) - { - fprintf (stderr, "Could not acquire PulseAudio main loop\n"); - return; - } - - // Acquire context //////////////////////////////////////////////////////// - device.api = pa_threaded_mainloop_get_api (device.mainloop); - device.context = pa_context_new (device.api, "PCSX"); - pa_context_set_state_callback (device.context, context_state_cb, &device); - - if (device.context == NULL) - { - fprintf (stderr, "Could not acquire PulseAudio device context\n"); - return; - } - - // Connect to PulseAudio server /////////////////////////////////////////// - if (pa_context_connect (device.context, NULL, 0, NULL) < 0) - { - error_number = pa_context_errno (device.context); - fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number)); - return; - } - - // Run mainloop until sever context is ready ////////////////////////////// - pa_threaded_mainloop_lock (device.mainloop); - if (pa_threaded_mainloop_start (device.mainloop) < 0) - { - fprintf (stderr, "Could not start mainloop\n"); - return; - } - - pa_context_state_t context_state; - context_state = pa_context_get_state (device.context); - while (context_state != PA_CONTEXT_READY) - { - context_state = pa_context_get_state (device.context); - if (! PA_CONTEXT_IS_GOOD (context_state)) - { - error_number = pa_context_errno (device.context); - fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number)); - return; - } - else if (context_state == PA_CONTEXT_READY) - break; - else - fprintf (stderr, "PulseAudio context state is %d\n", context_state); - pa_threaded_mainloop_wait (device.mainloop); - } - - // Set sample spec //////////////////////////////////////////////////////// - device.spec.format = PA_SAMPLE_S16NE; - if (iDisStereo) - device.spec.channels = 1; - else - device.spec.channels = 2; - device.spec.rate = settings.frequency; - - pa_buffer_attr buffer_attributes; - buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5; - buffer_attributes.maxlength = buffer_attributes.tlength * 3; - buffer_attributes.minreq = buffer_attributes.tlength / 3; - buffer_attributes.prebuf = buffer_attributes.tlength; - - //maxlength = buffer_attributes.maxlength; - //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength); - //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq); - //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf); - //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength); - - // Acquire new stream using spec ////////////////////////////////////////// - device.stream = pa_stream_new (device.context, "PCSX", &device.spec, NULL); - if (device.stream == NULL) - { - error_number = pa_context_errno (device.context); - fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number)); - return; - } - - // Set callbacks for server events //////////////////////////////////////// - pa_stream_set_state_callback (device.stream, stream_state_cb, &device); - pa_stream_set_write_callback (device.stream, stream_request_cb, &device); - pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device); - - // Ready stream for playback ////////////////////////////////////////////// - pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE); - //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS); - if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0) - { - pa_context_errno (device.context); - fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number)); - return; - } - - // Run mainloop until stream is ready ///////////////////////////////////// - pa_stream_state_t stream_state; - stream_state = pa_stream_get_state (device.stream); - while (stream_state != PA_STREAM_READY) - { - stream_state = pa_stream_get_state (device.stream); - - if (stream_state == PA_STREAM_READY) - break; - - else if (! PA_STREAM_IS_GOOD (stream_state)) - { - error_number = pa_context_errno (device.context); - fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number)); - return; - } - else - fprintf (stderr, "PulseAudio stream state is %d\n", stream_state); - pa_threaded_mainloop_wait (device.mainloop); - } - - pa_threaded_mainloop_unlock (device.mainloop); - - fprintf (stderr, "PulseAudio should be connected\n"); - return; -} - -//////////////////////////////////////////////////////////////////////// -// REMOVE SOUND -//////////////////////////////////////////////////////////////////////// -void RemoveSound (void) -{ - if (device.mainloop != NULL) - pa_threaded_mainloop_stop (device.mainloop); - - // Release in reverse order of acquisition - if (device.stream != NULL) - { - pa_stream_unref (device.stream); - device.stream = NULL; - - } - if (device.context != NULL) - { - pa_context_disconnect (device.context); - pa_context_unref (device.context); - device.context = NULL; - } - - if (device.mainloop != NULL) - { - pa_threaded_mainloop_free (device.mainloop); - device.mainloop = NULL; - } - -} - -//////////////////////////////////////////////////////////////////////// -// GET BYTES BUFFERED -//////////////////////////////////////////////////////////////////////// - -unsigned long SoundGetBytesBuffered (void) -{ - int free_space; - int error_code; - long latency; - int playing = 0; - - if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL)) - return SOUNDSIZE; - - pa_threaded_mainloop_lock (device.mainloop); - free_space = pa_stream_writable_size (device.stream); - pa_threaded_mainloop_unlock (device.mainloop); - - //fprintf (stderr, "Free space: %d\n", free_space); - //fprintf (stderr, "Used space: %d\n", maxlength - free_space); - if (free_space < mixlen * 3) - { - // Don't buffer anymore, just play - //fprintf (stderr, "Not buffering.\n"); - return SOUNDSIZE; - } - else - { - // Buffer some sound - //fprintf (stderr, "Buffering.\n"); - return 0; - } -} - -//////////////////////////////////////////////////////////////////////// -// FEED SOUND DATA -//////////////////////////////////////////////////////////////////////// - -void SoundFeedStreamData (unsigned char *pSound, long lBytes) -{ - int error_code; - int size; - - if (device.mainloop != NULL) - { - pa_threaded_mainloop_lock (device.mainloop); - if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0) - { - fprintf (stderr, "Could not perform write\n"); - } - else - { - //fprintf (stderr, "Wrote %d bytes\n", lBytes); - pa_threaded_mainloop_unlock (device.mainloop); - } - } -} -#endif +/***************************************************************************
+ pulseaudio.c - description
+ -------------------
+begin : Thu Feb 04 2010
+copyright : (C) 2010 by Tristin Celestin
+email : cetris1@umbc.edu
+comment : Much of this was taken from simple.c, in the pulseaudio
+ library
+***************************************************************************/
+/***************************************************************************
+ * *
+ * 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"
+
+#ifdef USEPULSEAUDIO
+
+#define _IN_OSS
+
+#include "externals.h"
+#include <pulse/pulseaudio.h>
+
+////////////////////////////////////////////////////////////////////////
+// pulseaudio structs
+////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+ pa_mainloop_api *api;
+ pa_stream *stream;
+ pa_sample_spec spec;
+ int first;
+} Device;
+
+typedef struct {
+ unsigned int frequency;
+ unsigned int latency_in_msec;
+} Settings;
+
+////////////////////////////////////////////////////////////////////////
+// pulseaudio globals
+////////////////////////////////////////////////////////////////////////
+
+static Device device = {
+ .mainloop = NULL,
+ .api = NULL,
+ .context = NULL,
+ .stream = NULL
+};
+
+static Settings settings = {
+ .frequency = 44100,
+ .latency_in_msec = 20,
+};
+
+// the number of bytes written in SoundFeedStreamData
+const int mixlen = 3240;
+
+// used to calculate how much space is used in the buffer, for debugging purposes
+//int maxlength = 0;
+
+////////////////////////////////////////////////////////////////////////
+// CALLBACKS FOR THREADED MAINLOOP
+////////////////////////////////////////////////////////////////////////
+static void context_state_cb (pa_context *context, void *userdata)
+{
+ Device *dev = userdata;
+
+ if ((context == NULL) || (dev == NULL))
+ return;
+
+ switch (pa_context_get_state (context))
+ {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal (dev->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
+}
+
+static void stream_state_cb (pa_stream *stream, void * userdata)
+{
+ Device *dev = userdata;
+
+ if ((stream == NULL) || (dev == NULL))
+ return;
+
+ switch (pa_stream_get_state (stream))
+ {
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal (dev->mainloop, 0);
+ break;
+
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
+ }
+}
+
+static void stream_latency_update_cb (pa_stream *stream, void *userdata)
+{
+ Device *dev = userdata;
+
+ if ((stream == NULL) || (dev == NULL))
+ return;
+
+ pa_threaded_mainloop_signal (dev->mainloop, 0);
+}
+
+static void stream_request_cb (pa_stream *stream, size_t length, void *userdata)
+{
+ Device *dev = userdata;
+
+ if ((stream == NULL) || (dev == NULL))
+ return;
+ pa_threaded_mainloop_signal (dev->mainloop, 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+// SETUP SOUND
+////////////////////////////////////////////////////////////////////////
+
+void SetupSound (void)
+{
+ int error_number;
+
+ // Acquire mainloop ///////////////////////////////////////////////////////
+ device.mainloop = pa_threaded_mainloop_new ();
+ if (device.mainloop == NULL)
+ {
+ fprintf (stderr, "Could not acquire PulseAudio main loop\n");
+ return;
+ }
+
+ // Acquire context ////////////////////////////////////////////////////////
+ device.api = pa_threaded_mainloop_get_api (device.mainloop);
+ device.context = pa_context_new (device.api, "PCSX");
+ pa_context_set_state_callback (device.context, context_state_cb, &device);
+
+ if (device.context == NULL)
+ {
+ fprintf (stderr, "Could not acquire PulseAudio device context\n");
+ return;
+ }
+
+ // Connect to PulseAudio server ///////////////////////////////////////////
+ if (pa_context_connect (device.context, NULL, 0, NULL) < 0)
+ {
+ error_number = pa_context_errno (device.context);
+ fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number));
+ return;
+ }
+
+ // Run mainloop until sever context is ready //////////////////////////////
+ pa_threaded_mainloop_lock (device.mainloop);
+ if (pa_threaded_mainloop_start (device.mainloop) < 0)
+ {
+ fprintf (stderr, "Could not start mainloop\n");
+ return;
+ }
+
+ pa_context_state_t context_state;
+ context_state = pa_context_get_state (device.context);
+ while (context_state != PA_CONTEXT_READY)
+ {
+ context_state = pa_context_get_state (device.context);
+ if (! PA_CONTEXT_IS_GOOD (context_state))
+ {
+ error_number = pa_context_errno (device.context);
+ fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number));
+ return;
+ }
+ else if (context_state == PA_CONTEXT_READY)
+ break;
+ else
+ fprintf (stderr, "PulseAudio context state is %d\n", context_state);
+ pa_threaded_mainloop_wait (device.mainloop);
+ }
+
+ // Set sample spec ////////////////////////////////////////////////////////
+ device.spec.format = PA_SAMPLE_S16NE;
+ if (iDisStereo)
+ device.spec.channels = 1;
+ else
+ device.spec.channels = 2;
+ device.spec.rate = settings.frequency;
+
+ pa_buffer_attr buffer_attributes;
+ buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5;
+ buffer_attributes.maxlength = buffer_attributes.tlength * 3;
+ buffer_attributes.minreq = buffer_attributes.tlength / 3;
+ buffer_attributes.prebuf = buffer_attributes.tlength;
+
+ //maxlength = buffer_attributes.maxlength;
+ //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength);
+ //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq);
+ //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf);
+ //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength);
+
+ // Acquire new stream using spec //////////////////////////////////////////
+ device.stream = pa_stream_new (device.context, "PCSX", &device.spec, NULL);
+ if (device.stream == NULL)
+ {
+ error_number = pa_context_errno (device.context);
+ fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number));
+ return;
+ }
+
+ // Set callbacks for server events ////////////////////////////////////////
+ pa_stream_set_state_callback (device.stream, stream_state_cb, &device);
+ pa_stream_set_write_callback (device.stream, stream_request_cb, &device);
+ pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device);
+
+ // Ready stream for playback //////////////////////////////////////////////
+ pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
+ //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS);
+ if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0)
+ {
+ pa_context_errno (device.context);
+ fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
+ return;
+ }
+
+ // Run mainloop until stream is ready /////////////////////////////////////
+ pa_stream_state_t stream_state;
+ stream_state = pa_stream_get_state (device.stream);
+ while (stream_state != PA_STREAM_READY)
+ {
+ stream_state = pa_stream_get_state (device.stream);
+
+ if (stream_state == PA_STREAM_READY)
+ break;
+
+ else if (! PA_STREAM_IS_GOOD (stream_state))
+ {
+ error_number = pa_context_errno (device.context);
+ fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number));
+ return;
+ }
+ else
+ fprintf (stderr, "PulseAudio stream state is %d\n", stream_state);
+ pa_threaded_mainloop_wait (device.mainloop);
+ }
+
+ pa_threaded_mainloop_unlock (device.mainloop);
+
+ fprintf (stderr, "PulseAudio should be connected\n");
+ return;
+}
+
+////////////////////////////////////////////////////////////////////////
+// REMOVE SOUND
+////////////////////////////////////////////////////////////////////////
+void RemoveSound (void)
+{
+ if (device.mainloop != NULL)
+ pa_threaded_mainloop_stop (device.mainloop);
+
+ // Release in reverse order of acquisition
+ if (device.stream != NULL)
+ {
+ pa_stream_unref (device.stream);
+ device.stream = NULL;
+
+ }
+ if (device.context != NULL)
+ {
+ pa_context_disconnect (device.context);
+ pa_context_unref (device.context);
+ device.context = NULL;
+ }
+
+ if (device.mainloop != NULL)
+ {
+ pa_threaded_mainloop_free (device.mainloop);
+ device.mainloop = NULL;
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////
+// GET BYTES BUFFERED
+////////////////////////////////////////////////////////////////////////
+
+unsigned long SoundGetBytesBuffered (void)
+{
+ int free_space;
+ int error_code;
+ long latency;
+ int playing = 0;
+
+ if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL))
+ return SOUNDSIZE;
+
+ pa_threaded_mainloop_lock (device.mainloop);
+ free_space = pa_stream_writable_size (device.stream);
+ pa_threaded_mainloop_unlock (device.mainloop);
+
+ //fprintf (stderr, "Free space: %d\n", free_space);
+ //fprintf (stderr, "Used space: %d\n", maxlength - free_space);
+ if (free_space < mixlen * 3)
+ {
+ // Don't buffer anymore, just play
+ //fprintf (stderr, "Not buffering.\n");
+ return SOUNDSIZE;
+ }
+ else
+ {
+ // Buffer some sound
+ //fprintf (stderr, "Buffering.\n");
+ return 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// FEED SOUND DATA
+////////////////////////////////////////////////////////////////////////
+
+void SoundFeedStreamData (unsigned char *pSound, long lBytes)
+{
+ int error_code;
+ int size;
+
+ if (device.mainloop != NULL)
+ {
+ pa_threaded_mainloop_lock (device.mainloop);
+ if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
+ {
+ fprintf (stderr, "Could not perform write\n");
+ }
+ else
+ {
+ //fprintf (stderr, "Wrote %d bytes\n", lBytes);
+ pa_threaded_mainloop_unlock (device.mainloop);
+ }
+ }
+}
+#endif
diff --git a/plugins/dfsound/reverb.c b/plugins/dfsound/reverb.c index dfab03a4..92e31fcb 100644 --- a/plugins/dfsound/reverb.c +++ b/plugins/dfsound/reverb.c @@ -1,463 +1,462 @@ -/*************************************************************************** - 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; - ------------------------------------------------------------------------------ -*/ - +/***************************************************************************
+ 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;
+
+-----------------------------------------------------------------------------
+*/
+
diff --git a/plugins/dfsound/sdl.c b/plugins/dfsound/sdl.c index f3cf92d2..5f80a1ff 100644 --- a/plugins/dfsound/sdl.c +++ b/plugins/dfsound/sdl.c @@ -1,137 +1,135 @@ -/* SDL Driver for P.E.Op.S Sound Plugin - * Copyright (c) 2010, Wei Mingzhi <whistler_wmz@users.sf.net>. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA - */ - -#include "stdafx.h" - -#include "dsoundoss.h" - -#include "externals.h" -#include <SDL.h> - -#define BUFFER_SIZE 22050 - -short *pSndBuffer = NULL; -int iBufSize = 0; -volatile int iReadPos = 0, iWritePos = 0; - -static void SOUND_FillAudio(void *unused, Uint8 *stream, int len) { - short *p = (short *)stream; - - len /= sizeof(short); - - while (iReadPos != iWritePos && len > 0) { - *p++ = pSndBuffer[iReadPos++]; - if (iReadPos >= iBufSize) iReadPos = 0; - --len; - } - - // Fill remaining space with zero - while (len > 0) { - *p++ = 0; - --len; - } -} - -static void InitSDL() { - if (SDL_WasInit(SDL_INIT_EVERYTHING)) { - SDL_InitSubSystem(SDL_INIT_AUDIO); - } else { - SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE); - } -} - -static void DestroySDL() { - if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO)) { - SDL_QuitSubSystem(SDL_INIT_AUDIO); - } else { - SDL_Quit(); - } -} - -void SetupSound(void) { - SDL_AudioSpec spec; - - if (pSndBuffer != NULL) return; - - InitSDL(); - - spec.freq = 44100; - spec.format = AUDIO_S16SYS; - spec.channels = iDisStereo ? 1 : 2; - spec.samples = 1024; - spec.callback = SOUND_FillAudio; - - if (SDL_OpenAudio(&spec, NULL) < 0) { - DestroySDL(); - return; - } - - iBufSize = BUFFER_SIZE; - if (iDisStereo) iBufSize /= 2; - - pSndBuffer = (short *)malloc(iBufSize * sizeof(short)); - if (pSndBuffer == NULL) { - SDL_CloseAudio(); - return; - } - - iReadPos = 0; - iWritePos = 0; - - SDL_PauseAudio(0); -} - -void RemoveSound(void) { - if (pSndBuffer == NULL) return; - - SDL_CloseAudio(); - DestroySDL(); - - free(pSndBuffer); - pSndBuffer = NULL; -} - -unsigned long SoundGetBytesBuffered(void) { - int size; - - if (pSndBuffer == NULL) return SOUNDSIZE; - - size = iReadPos - iWritePos; - if (size <= 0) size += iBufSize; - - if (size < iBufSize / 2) return SOUNDSIZE; - - return 0; -} - -void SoundFeedStreamData(unsigned char *pSound, long lBytes) { - short *p = (short *)pSound; - - if (pSndBuffer == NULL) return; - - while (lBytes > 0) { - if (((iWritePos + 1) % iBufSize) == iReadPos) break; - - pSndBuffer[iWritePos] = *p++; - - ++iWritePos; - if (iWritePos >= iBufSize) iWritePos = 0; - - lBytes -= sizeof(short); - } -} +/* SDL Driver for P.E.Op.S Sound Plugin
+ * Copyright (c) 2010, Wei Mingzhi <whistler_wmz@users.sf.net>.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA
+ */
+
+#include "stdafx.h"
+
+#include "externals.h"
+#include <SDL.h>
+
+#define BUFFER_SIZE 22050
+
+short *pSndBuffer = NULL;
+int iBufSize = 0;
+volatile int iReadPos = 0, iWritePos = 0;
+
+static void SOUND_FillAudio(void *unused, Uint8 *stream, int len) {
+ short *p = (short *)stream;
+
+ len /= sizeof(short);
+
+ while (iReadPos != iWritePos && len > 0) {
+ *p++ = pSndBuffer[iReadPos++];
+ if (iReadPos >= iBufSize) iReadPos = 0;
+ --len;
+ }
+
+ // Fill remaining space with zero
+ while (len > 0) {
+ *p++ = 0;
+ --len;
+ }
+}
+
+static void InitSDL() {
+ if (SDL_WasInit(SDL_INIT_EVERYTHING)) {
+ SDL_InitSubSystem(SDL_INIT_AUDIO);
+ } else {
+ SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
+ }
+}
+
+static void DestroySDL() {
+ if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO)) {
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ } else {
+ SDL_Quit();
+ }
+}
+
+void SetupSound(void) {
+ SDL_AudioSpec spec;
+
+ if (pSndBuffer != NULL) return;
+
+ InitSDL();
+
+ spec.freq = 44100;
+ spec.format = AUDIO_S16SYS;
+ spec.channels = iDisStereo ? 1 : 2;
+ spec.samples = 1024;
+ spec.callback = SOUND_FillAudio;
+
+ if (SDL_OpenAudio(&spec, NULL) < 0) {
+ DestroySDL();
+ return;
+ }
+
+ iBufSize = BUFFER_SIZE;
+ if (iDisStereo) iBufSize /= 2;
+
+ pSndBuffer = (short *)malloc(iBufSize * sizeof(short));
+ if (pSndBuffer == NULL) {
+ SDL_CloseAudio();
+ return;
+ }
+
+ iReadPos = 0;
+ iWritePos = 0;
+
+ SDL_PauseAudio(0);
+}
+
+void RemoveSound(void) {
+ if (pSndBuffer == NULL) return;
+
+ SDL_CloseAudio();
+ DestroySDL();
+
+ free(pSndBuffer);
+ pSndBuffer = NULL;
+}
+
+unsigned long SoundGetBytesBuffered(void) {
+ int size;
+
+ if (pSndBuffer == NULL) return SOUNDSIZE;
+
+ size = iReadPos - iWritePos;
+ if (size <= 0) size += iBufSize;
+
+ if (size < iBufSize / 2) return SOUNDSIZE;
+
+ return 0;
+}
+
+void SoundFeedStreamData(unsigned char *pSound, long lBytes) {
+ short *p = (short *)pSound;
+
+ if (pSndBuffer == NULL) return;
+
+ while (lBytes > 0) {
+ if (((iWritePos + 1) % iBufSize) == iReadPos) break;
+
+ pSndBuffer[iWritePos] = *p++;
+
+ ++iWritePos;
+ if (iWritePos >= iBufSize) iWritePos = 0;
+
+ lBytes -= sizeof(short);
+ }
+}
diff --git a/plugins/dfsound/spu.c b/plugins/dfsound/spu.c index 36e916a1..56fc6ac8 100644 --- a/plugins/dfsound/spu.c +++ b/plugins/dfsound/spu.c @@ -1,1380 +1,1382 @@ -/*************************************************************************** - spu.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#include "stdafx.h" - -#define _IN_SPU - -#include "externals.h" -#include "cfg.h" -#include "dsoundoss.h" -#include "regs.h" -#include "spu.h" - -#ifdef _WINDOWS -#include "debug.h" -#include "record.h" -#endif - -#if defined (_WINDOWS) -static char * libraryName = N_("DirectSound Driver"); -#elif defined (USEMACOSX) -static char * libraryName = N_("Mac OS X Sound"); -#elif defined (USEALSA) -static char * libraryName = N_("ALSA Sound"); -#elif defined (USEOSS) -static char * libraryName = N_("OSS Sound"); -#elif defined (USESDL) -static char * libraryName = N_("SDL Sound"); -#elif defined (USEPULSEAUDIO) -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 - -// psx buffer / addresses - -unsigned short regArea[10000]; -unsigned short spuMem[256*1024]; -unsigned char * spuMemC; -unsigned char * pSpuIrq=0; -unsigned char * pSpuBuffer; -unsigned char * pMixIrq=0; - -// user settings - -int iVolume=3; -int iXAPitch=1; -int iUseTimer=2; -int iSPUIRQWait=1; -int iDebugMode=0; -int iRecordMode=0; -int iUseReverb=2; -int iUseInterpolation=2; -int iDisStereo=0; -int iFreqResponse=0; - -// MAIN infos struct for each channel - -SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) -REVERBInfo rvb; - -unsigned long dwNoiseVal=1; // global noise generator -unsigned long dwNoiseCount; // global noise generator -unsigned long dwNoiseClock; // global noise generator -int iSpuAsyncWait=0; - -unsigned short spuCtrl=0; // some vars to store psx reg infos -unsigned short spuStat=0; -unsigned short spuIrq=0; -unsigned long spuAddr=0xffffffff; // address into spu mem -int bEndThread=0; // thread handlers -int bThreadEnded=0; -int bSpuInit=0; -int bSPUIsOpen=0; - -#ifdef _WINDOWS -HWND hWMain=0; // window handle -HWND hWDebug=0; -HWND hWRecord=0; -static HANDLE hMainThread; -#else -static pthread_t thread = (pthread_t)-1; // thread id (linux) -#endif - -uint32_t dwNewChannel=0; // flags for faster testing, if new channel starts - -void (CALLBACK *irqCallback)(void)=0; // func of main emu, called on spu irq -void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0; - -// certain globals (were local before, but with the new timeproc I need em global) - -static const int f[5][2] = { { 0, 0 }, - { 60, 0 }, - { 115, -52 }, - { 98, -55 }, - { 122, -60 } }; -int SSumR[NSSIZE]; -int SSumL[NSSIZE]; -int iFMod[NSSIZE]; -int iCycle = 0; -short * pS; - -int lastch=-1; // last channel processed on spu irq in timer mode -static int lastns=0; // last ns pos -static int iSecureStart=0; // secure start counter - -//////////////////////////////////////////////////////////////////////// -// CODE AREA -//////////////////////////////////////////////////////////////////////// - -// dirty inline func includes - -#include "reverb.c" -#include "adsr.c" - -//////////////////////////////////////////////////////////////////////// -// helpers for simple interpolation - -// -// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm -// -// instead of having n equal sample values in a row like: -// ____ -// |____ -// -// we compare the current delta change with the next delta change. -// -// if curr_delta is positive, -// -// - and next delta is smaller (or changing direction): -// \. -// -__ -// -// - and next delta significant (at least twice) bigger: -// --_ -// \. -// -// - and next delta is nearly same: -// \. -// \. -// -// -// if curr_delta is negative, -// -// - and next delta is smaller (or changing direction): -// _-- -// / -// -// - and next delta significant (at least twice) bigger: -// / -// __- -// -// - and next delta is nearly same: -// / -// / -// - - -static INLINE void InterpolateUp(int ch) -{ - if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass - { - const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val - const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :) - - s_chan[ch].SB[32]=0; - - if(id1>0) // curr delta positive - { - if(id2<id1) - {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} - else - if(id2<(id1<<1)) - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; - else - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; - } - else // curr delta negative - { - if(id2>id1) - {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} - else - if(id2>(id1<<1)) - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; - else - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; - } - } - else - if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass - { - s_chan[ch].SB[32]=0; - - s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L; - if(s_chan[ch].sinc<=0x8000) - s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1)); - else s_chan[ch].SB[29]+=s_chan[ch].SB[28]; - } - else // no flags? add bigger val (if possible), calc smaller step, set flag1 - s_chan[ch].SB[29]+=s_chan[ch].SB[28]; -} - -// -// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm -// - -static INLINE void InterpolateDown(int ch) -{ - if(s_chan[ch].sinc>=0x20000L) // we would skip at least one val? - { - s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight - if(s_chan[ch].sinc>=0x30000L) // we would skip even more vals? - s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight - } -} - -//////////////////////////////////////////////////////////////////////// -// helpers for gauss interpolation - -#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos]) -#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3]) - -#include "gauss_i.h" - -//////////////////////////////////////////////////////////////////////// - -#include "xa.c" - -//////////////////////////////////////////////////////////////////////// -// START SOUND... called by main thread to setup a new sound on a channel -//////////////////////////////////////////////////////////////////////// - -static INLINE void StartSound(int ch) -{ - StartADSR(ch); - StartREVERB(ch); - - // fussy timing issues - do in VoiceOn - //s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start - //s_chan[ch].bStop=0; - //s_chan[ch].bOn=1; - - s_chan[ch].s_1=0; // init mixing vars - s_chan[ch].s_2=0; - s_chan[ch].iSBPos=28; - - s_chan[ch].bNew=0; // init channel flags - - s_chan[ch].SB[29]=0; // init our interpolation helpers - s_chan[ch].SB[30]=0; - - if(iUseInterpolation>=2) // gauss interpolation? - {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding - else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding - - dwNewChannel&=~(1<<ch); // clear new channel bit -} - -//////////////////////////////////////////////////////////////////////// -// ALL KIND OF HELPERS -//////////////////////////////////////////////////////////////////////// - -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; - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE void FModChangeFrequency(int ch,int ns) -{ - int NP=s_chan[ch].iRawPitch; - - NP=((32768L+iFMod[ns])*NP)/32768L; - - if(NP>0x3fff) NP=0x3fff; - if(NP<0x1) NP=0x1; - - NP=(44100L*NP)/(4096L); // calc frequency - - s_chan[ch].iActFreq=NP; - s_chan[ch].iUsedFreq=NP; - s_chan[ch].sinc=(((NP/10)<<16)/4410); - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - if(iUseInterpolation==1) // freq change in simple interpolation mode - s_chan[ch].SB[32]=1; - iFMod[ns]=0; -} - -//////////////////////////////////////////////////////////////////////// - -/* -Noise Algorithm -- Dr.Hell (Xebra PS1 emu) -- 100% accurate (waveform + frequency) -- http://drhell.web.fc2.com - - -Level change cycle -Freq = 0x8000 >> (NoiseClock >> 2); - -Frequency of half cycle -Half = ((NoiseClock & 3) * 2) / (4 + (NoiseClock & 3)); -- 0 = (0*2)/(4+0) = 0/4 -- 1 = (1*2)/(4+1) = 2/5 -- 2 = (2*2)/(4+2) = 4/6 -- 3 = (3*2)/(4+3) = 6/7 - -------------------------------- - -5*6*7 = 210 -4 - 0*0 = 0 -5 - 42*2 = 84 -6 - 35*4 = 140 -7 - 30*6 = 180 -*/ - -// Noise Waveform - Dr. Hell (Xebra) -char NoiseWaveAdd [64] = { - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1 -}; - -unsigned short NoiseFreqAdd[5] = { - 0, 84, 140, 180, 210 -}; - -static INLINE void NoiseClock() -{ - unsigned int level; - - level = 0x8000 >> (dwNoiseClock >> 2); - level <<= 16; - - dwNoiseCount += 0x10000; - - // Dr. Hell - fraction - dwNoiseCount += NoiseFreqAdd[ dwNoiseClock & 3 ]; - if( (dwNoiseCount&0xffff) >= NoiseFreqAdd[4] ) { - dwNoiseCount += 0x10000; - dwNoiseCount -= NoiseFreqAdd[ dwNoiseClock & 3 ]; - } - - if( dwNoiseCount >= level ) - { - while( dwNoiseCount >= level ) - dwNoiseCount -= level; - - // Dr. Hell - form - dwNoiseVal = (dwNoiseVal<<1) | NoiseWaveAdd[ (dwNoiseVal>>10) & 63 ]; - } -} - -static INLINE int iGetNoiseVal(int ch) -{ - int fa; - - fa = (short) dwNoiseVal; - - // no clip need - //if(fa>32767L) fa=32767L; - //if(fa<-32767L) fa=-32767L; - - // don't upset VAG decoder - //if(iUseInterpolation<2) // no gauss/cubic interpolation? - //pChannel->SB[29] = fa; // -> store noise val in "current sample" slot - - // boost volume - no more! - //return fa * 3 / 2; - return fa; -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE void StoreInterpolationVal(int ch,int fa) -{ - /* - // fmod channel = sound output - if(s_chan[ch].bFMod==2) // fmod freq channel - s_chan[ch].SB[29]=fa; - else - */ - { - if((spuCtrl&0x4000)==0) fa=0; // muted? - else // else adjust - { - if(fa>32767L) fa=32767L; - if(fa<-32767L) fa=-32767L; - } - - if(iUseInterpolation>=2) // gauss/cubic interpolation - { - int gpos = s_chan[ch].SB[28]; - gval0 = fa; - gpos = (gpos+1) & 3; - s_chan[ch].SB[28] = gpos; - } - else - if(iUseInterpolation==1) // simple interpolation - { - s_chan[ch].SB[28] = 0; - s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour' - s_chan[ch].SB[30] = s_chan[ch].SB[31]; - s_chan[ch].SB[31] = fa; - s_chan[ch].SB[32] = 1; // -> flag: calc new interolation - } - else s_chan[ch].SB[29]=fa; // no interpolation - } -} - -//////////////////////////////////////////////////////////////////////// - -static INLINE int iGetInterpolationVal(int ch) -{ - int fa; - - // fmod channel = sound output - //if(s_chan[ch].bFMod==2) return s_chan[ch].SB[29]; - - switch(iUseInterpolation) - { - //--------------------------------------------------// - case 3: // cubic interpolation - { - long xd;int gpos; - xd = ((s_chan[ch].spos) >> 1)+1; - gpos = s_chan[ch].SB[28]; - - fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0; - fa *= (xd - (2<<15)) / 6; - fa >>= 15; - fa += gval(2) - gval(1) - gval(1) + gval0; - fa *= (xd - (1<<15)) >> 1; - fa >>= 15; - fa += gval(1) - gval0; - fa *= xd; - fa >>= 15; - fa = fa + gval0; - - } break; - //--------------------------------------------------// - case 2: // gauss interpolation - { - int vl, vr;int gpos; - vl = (s_chan[ch].spos >> 6) & ~3; - gpos = s_chan[ch].SB[28]; - vr=(gauss[vl]*gval0)&~2047; - vr+=(gauss[vl+1]*gval(1))&~2047; - vr+=(gauss[vl+2]*gval(2))&~2047; - vr+=(gauss[vl+3]*gval(3))&~2047; - fa = vr>>11; - } break; - //--------------------------------------------------// - case 1: // simple interpolation - { - if(s_chan[ch].sinc<0x10000L) // -> upsampling? - InterpolateUp(ch); // --> interpolate up - else InterpolateDown(ch); // --> else down - fa=s_chan[ch].SB[29]; - } break; - //--------------------------------------------------// - default: // no interpolation - { - fa=s_chan[ch].SB[29]; - } break; - //--------------------------------------------------// - } - - return fa; -} - -//////////////////////////////////////////////////////////////////////// -// MAIN SPU FUNCTION -// here is the main job handler... thread, timer or direct func call -// basically the whole sound processing is done in this fat func! -//////////////////////////////////////////////////////////////////////// - -// 5 ms waiting phase, if buffer is full and no new sound has to get started -// .. can be made smaller (smallest val: 1 ms), but bigger waits give -// better performance - -#define PAUSE_W 1 -#define PAUSE_L 1000 - -//////////////////////////////////////////////////////////////////////// - -#ifdef _WINDOWS -static VOID CALLBACK MAINProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) -#else -static void *MAINThread(void *arg) -#endif -{ - int s_1,s_2,fa,ns; - int voldiv = iVolume; - - unsigned char * start;unsigned int nSample; - int ch,predict_nr,shift_factor,flags,d,s; - int bIRQReturn=0; - - // mute output - if( voldiv == 5 ) voldiv = 0x7fffffff; - - while(!bEndThread) // until we are shutting down - { - // ok, at the beginning we are looking if there is - // enuff free place in the dsound/oss buffer to - // fill in new data, or if there is a new channel to start. - // if not, we wait (thread) or return (timer/spuasync) - // until enuff free place is available/a new channel gets - // started - - if(dwNewChannel) // new channel should start immedately? - { // (at least one bit 0 ... MAXCHANNEL is set?) - iSecureStart++; // -> set iSecure - if(iSecureStart>1) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance) - } - else iSecureStart=0; // 0: no new channel should start - - while(!iSecureStart && !bEndThread && // no new start? no thread end? - (SoundGetBytesBuffered()>TESTSIZE)) // and still enuff data in sound buffer? - { - iSecureStart=0; // reset secure - -#ifdef _WINDOWS - if(iUseTimer) // no-thread mode? - { - if(iUseTimer==1) // -> ok, timer mode 1: setup a oneshot timer of x ms to wait - timeSetEvent(PAUSE_W,1,MAINProc,0,TIME_ONESHOT); - return; // -> and done this time (timer mode 1 or 2) - } - // win thread mode: - Sleep(PAUSE_W); // sleep for x ms (win) -#else - if(iUseTimer) return 0; // linux no-thread mode? bye - usleep(PAUSE_L); // else sleep for x ms (linux) -#endif - - if(dwNewChannel) iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop - } - - //--------------------------------------------------// continue from irq handling in timer mode? - - if(lastch>=0) // will be -1 if no continue is pending - { - ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue - goto GOON; // -> directly jump to the continue point - } - - //--------------------------------------------------// - //- main channel loop -// - //--------------------------------------------------// - { - ns=0; - while(ns<NSSIZE) // loop until 1 ms of data is reached - { - NoiseClock(); - - for(ch=0;ch<MAXCHAN;ch++) // loop em all... we will collect 1 ms of sound of each playing channel - { - if(s_chan[ch].bNew) { -#if 1 - StartSound(ch); // start new sound - dwNewChannel&=~(1<<ch); // clear new channel bit -#else - if( s_chan[ch].ADSRX.StartDelay == 0 ) { - StartSound(ch); // start new sound - dwNewChannel&=~(1<<ch); // clear new channel bit - } else { - s_chan[ch].ADSRX.StartDelay--; - } -#endif - } - if(!s_chan[ch].bOn) continue; // channel not playing? next - - if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq) // new psx frequency? - VoiceChangeFrequency(ch); - - if(s_chan[ch].bFMod==1 && iFMod[ns]) // fmod freq channel - FModChangeFrequency(ch,ns); - - while(s_chan[ch].spos>=0x10000L) - { - if(s_chan[ch].iSBPos==28) // 28 reached? - { -#if 0 - // Xenogears - Anima Relic dungeon (exp gain) - if( s_chan[ch].bLoopJump == 1 ) - s_chan[ch].pCurr = s_chan[ch].pLoop; - - s_chan[ch].bLoopJump = 0; -#endif - - - start=s_chan[ch].pCurr; // set up the current pos - - if (s_chan[ch].iSilent==1 || start == (unsigned char*)-1) // special "stop" sign - { - // silence = let channel keep running (IRQs) - //s_chan[ch].bOn=0; // -> turn everything off - s_chan[ch].iSilent=2; - - s_chan[ch].ADSRX.lVolume=0; - s_chan[ch].ADSRX.EnvelopeVol=0; - } - - s_chan[ch].iSBPos=0; - - //////////////////////////////////////////// spu irq handler here? mmm... do it later - - s_1=s_chan[ch].s_1; - s_2=s_chan[ch].s_2; - - predict_nr=(int)*start;start++; - shift_factor=predict_nr&0xf; - predict_nr >>= 4; - flags=(int)*start;start++; - - // -------------------------------------- // - - for (nSample=0;nSample<28;start++) - { - d=(int)*start; - s=((d&0xf)<<12); - if(s&0x8000) s|=0xffff0000; - - fa=(s >> shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1;s_1=fa; - s=((d & 0xf0) << 8); - - s_chan[ch].SB[nSample++]=fa; - - if(s&0x8000) s|=0xffff0000; - fa=(s>>shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1;s_1=fa; - - s_chan[ch].SB[nSample++]=fa; - } - - //////////////////////////////////////////// irq check - - if(irqCallback && (spuCtrl&0x40)) // some callback and irq active? - { - if((pSpuIrq > start-16 && // irq address reached? - pSpuIrq <= start) || - ((flags&1) && // special: irq on looping addr, when stop/loop flag is set - (pSpuIrq > s_chan[ch].pLoop-16 && - pSpuIrq <= s_chan[ch].pLoop))) - { - s_chan[ch].iIrqDone=1; // -> debug flag - irqCallback(); // -> call main emu - - if(iSPUIRQWait) // -> option: wait after irq for main emu - { - iSpuAsyncWait=1; - bIRQReturn=1; - } - } - } - - //////////////////////////////////////////// flag handler - - /* - SPU2-X: - $4 = set loop to current block - $2 = keep envelope on (no mute) - $1 = jump to loop address - - silence means no volume (ADSR keeps playing!!) - */ - -#if 0 - if(flags&4) - s_chan[ch].pLoop=start-16; -#else - // Jungle Book - Rhythm 'n Groove - use external loop address - // - fixes music player (+IRQ generate) - if((flags&4) && (s_chan[ch].bIgnoreLoop == 0)) - s_chan[ch].pLoop=start-16; -#endif - - // Jungle Book - Rhythm 'n Groove - don't reset ignore status - // - fixes gameplay speed (IRQ hits) - //s_chan[ch].bIgnoreLoop = 0; - - - if(flags&1) - { - // ...? - //s_chan[ch].bIgnoreLoop = 0; - - // Xenogears - 7 = play missing sounds -#if 0 - // set jump flag - pChannel->bLoopJump = 1; -#else - start = s_chan[ch].pLoop; -#endif - - // silence = keep playing..? - if( (flags&2) == 0 ) { - s_chan[ch].iSilent = 1; - - // silence = don't start release phase - //s_chan[ch].bStop = 1; - - //start = (unsigned char *) -1; - } - } - -#if 0 - // crash check - if( start == 0 ) - start = (unsigned char *) -1; - if( start >= spuMemC + 0x80000 ) - start = spuMemC - 0x80000; -#endif - - s_chan[ch].pCurr=start; // store values for next cycle - s_chan[ch].s_1=s_1; - s_chan[ch].s_2=s_2; - - if(bIRQReturn) // special return for "spu irq - wait for cpu action" - { - bIRQReturn=0; - if(iUseTimer!=2) - { - DWORD dwWatchTime=timeGetTime_spu()+2500; - - while(iSpuAsyncWait && !bEndThread && - timeGetTime_spu()<dwWatchTime) -#ifdef _WINDOWS - Sleep(1); -#else - usleep(1000L); -#endif - } - else - { - lastch=ch; - lastns=ns; - -#ifdef _WINDOWS - return; -#else - return 0; -#endif - } - } - -GOON: ; - } - - fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data - - StoreInterpolationVal(ch,fa); // store val for later interpolation - - s_chan[ch].spos -= 0x10000L; - } - - if(s_chan[ch].bNoise) - fa=iGetNoiseVal(ch); // get noise val - else fa=iGetInterpolationVal(ch); // get sample val - - -#if 0 - // Voice 1/3 decoded buffer - if( ch == 0 ) { - spuMem[ (0x800 + voice_dbuf_ptr) / 2 ] = (short) fa; - } else if( ch == 2 ) { - spuMem[ (0xc00 + voice_dbuf_ptr) / 2 ] = (short) fa; - } -#endif - - - s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // mix adsr - - if(s_chan[ch].bFMod==2) // fmod freq channel - iFMod[ns]=s_chan[ch].sval; // -> store 1T sample data, use that to do fmod on next channel - - // mix fmod channel into output - // - Xenogears save icon (high pitch) - { - ////////////////////////////////////////////// - // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) - - if(s_chan[ch].iMute) - s_chan[ch].sval=0; // debug mute - else - { - SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L; - SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L; - } - - ////////////////////////////////////////////// - // now let us store sound data for reverb - - if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns); - } - - s_chan[ch].spos += s_chan[ch].sinc; - } - //////////////////////////////////////////////// - // ok, go on until 1 ms data of this channel is collected - - ns++; - } // end ns - } - - //---------------------------------------------------// - //- here we have another 1 ms of sound data - //---------------------------------------------------// - // mix XA infos (if any) - - MixXA(); - - /////////////////////////////////////////////////////// - // mix all channels (including reverb) into one buffer - - if(iDisStereo) // no stereo? - { - int dl, dr; - for (ns = 0; ns < NSSIZE; ns++) - { - SSumL[ns] += MixREVERBLeft(ns); - - dl = SSumL[ns] / voldiv; SSumL[ns] = 0; - if (dl < -32767) dl = -32767; if (dl > 32767) dl = 32767; - - SSumR[ns] += MixREVERBRight(); - - dr = SSumR[ns] / voldiv; SSumR[ns] = 0; - if (dr < -32767) dr = -32767; if (dr > 32767) dr = 32767; - *pS++ = (dl + dr) / 2; - } - } - else // stereo: - for (ns = 0; ns < NSSIZE; ns++) - { - static double _interpolation_coefficient = 3.759285613; - - if(iFreqResponse) { - int sl,sr; - double ldiff, rdiff, avg, tmp; - - SSumL[ns]+=MixREVERBLeft(ns); - SSumR[ns]+=MixREVERBRight(); - - sl = SSumL[ns]; SSumL[ns]=0; - sr = SSumR[ns]; SSumR[ns]=0; - - - /* - Frequency Response - - William Pitcock (nenolod) (UPSE PSF player) - - accurate (!) - - http://nenolod.net - */ - - avg = ((sl + sr) / 2); - ldiff = sl - avg; - rdiff = sr - avg; - - tmp = avg + ldiff * _interpolation_coefficient; - if (tmp < -32768) - tmp = -32768; - if (tmp > 32767) - tmp = 32767; - sl = (int)tmp; - - tmp = avg + rdiff * _interpolation_coefficient; - if (tmp < -32768) - tmp = -32768; - if (tmp > 32767) - tmp = 32767; - sr = (int)tmp; - - - *pS++=sl/voldiv; - *pS++=sr/voldiv; - } else { - SSumL[ns]+=MixREVERBLeft(ns); - - d=SSumL[ns]/voldiv;SSumL[ns]=0; - if(d<-32767) d=-32767;if(d>32767) d=32767; - *pS++=d; - - SSumR[ns]+=MixREVERBRight(); - - d=SSumR[ns]/voldiv;SSumR[ns]=0; - if(d<-32767) d=-32767;if(d>32767) d=32767; - *pS++=d; - } - } - - ////////////////////////////////////////////////////// - // special irq handling in the decode buffers (0x0000-0x1000) - // we know: - // the decode buffers are located in spu memory in the following way: - // 0x0000-0x03ff CD audio left - // 0x0400-0x07ff CD audio right - // 0x0800-0x0bff Voice 1 - // 0x0c00-0x0fff Voice 3 - // and decoded data is 16 bit for one sample - // we assume: - // even if voices 1/3 are off or no cd audio is playing, the internal - // play positions will move on and wrap after 0x400 bytes. - // Therefore: we just need a pointer from spumem+0 to spumem+3ff, and - // increase this pointer on each sample by 2 bytes. If this pointer - // (or 0x400 offsets of this pointer) hits the spuirq address, we generate - // an IRQ. Only problem: the "wait for cpu" option is kinda hard to do here - // in some of Peops timer modes. So: we ignore this option here (for now). - - if(pMixIrq && irqCallback) - { - for(ns=0;ns<NSSIZE;ns++) - { - if((spuCtrl&0x40) && pSpuIrq && pSpuIrq<spuMemC+0x1000) - { - for(ch=0;ch<4;ch++) - { - if(pSpuIrq>=pMixIrq+(ch*0x400) && pSpuIrq<pMixIrq+(ch*0x400)+2) - {irqCallback();s_chan[ch].iIrqDone=1;} - } - } - pMixIrq+=2;if(pMixIrq>spuMemC+0x3ff) pMixIrq=spuMemC; - } - } - - InitREVERB(); - - ////////////////////////////////////////////////////// - // feed the sound - // latency = 25 ms (less pops, crackles, smoother) - - //if(iCycle++>=20) - iCycle += APU_CYCLES_UPDATE; - if(iCycle > 44000/1000*LATENCY + 100*LATENCY/1000) - { - SoundFeedStreamData((unsigned char *)pSpuBuffer, - ((unsigned char *)pS) - ((unsigned char *)pSpuBuffer)); - pS = (short *)pSpuBuffer; - iCycle = 0; - } - - - if( iUseTimer == 2 ) - break; - } - - // end of big main loop... - - bThreadEnded = 1; - -#ifndef _WINDOWS - return 0; -#endif -} - -//////////////////////////////////////////////////////////////////////// -// WINDOWS THREAD... simply calls the timer func and stays forever :) -//////////////////////////////////////////////////////////////////////// - -#ifdef _WINDOWS - -DWORD WINAPI MAINThreadEx(LPVOID lpParameter) -{ - MAINProc(0,0,0,0,0); - return 0; -} - -#endif - -// SPU ASYNC... even newer epsxe func -// 1 time every 'cycle' cycles... harhar - -long cpu_cycles; -void CALLBACK SPUasync(uint32_t cycle) -{ - cpu_cycles += cycle; - - if(iSpuAsyncWait) - { - iSpuAsyncWait++; - if(iSpuAsyncWait<=64) return; - iSpuAsyncWait=0; - - cpu_cycles = cycle; - } - -#ifdef _WINDOWS - if(iDebugMode==2) - { - if(IsWindow(hWDebug)) DestroyWindow(hWDebug); - hWDebug=0;iDebugMode=0; - } - if(iRecordMode==2) - { - if(IsWindow(hWRecord)) DestroyWindow(hWRecord); - hWRecord=0;iRecordMode=0; - } -#endif - - if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode) - { - if(!bSpuInit) return; // -> no init, no call - - // note: usable precision difference (not using interval_time) - while( cpu_cycles >= CPU_CLOCK / 44100 * NSSIZE ) - { - #ifdef _WINDOWS - MAINProc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks - #else - MAINThread(0); // -> linux high-compat mode - #endif - - cpu_cycles -= CPU_CLOCK / 44100 * NSSIZE; - } - } -} - -// SPU UPDATE... new epsxe func -// 1 time every 32 hsync lines -// (312/32)x50 in pal -// (262/32)x60 in ntsc - -// since epsxe 1.5.2 (linux) uses SPUupdate, not SPUasync, I will -// leave that func in the linux port, until epsxe linux is using -// the async function as well - -#if 0 -void CALLBACK SPUupdate(void) -{ - SPUasync(0); -} -#endif - -// XA AUDIO - -void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap) -{ - if(!xap) return; - if(!xap->freq) return; // no xa freq ? bye - - FeedXA(xap); // call main XA feeder -} - -// CDDA AUDIO -void CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes) -{ - if (!pcm) return; - if (nbytes<=0) return; - - FeedCDDA((unsigned char *)pcm, nbytes); -} - -// SETUPTIMER: init of certain buffers and threads/timers -void SetupTimer(void) -{ - memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers - memset(SSumL,0,NSSIZE*sizeof(int)); - memset(iFMod,0,NSSIZE*sizeof(int)); - pS=(short *)pSpuBuffer; // setup soundbuffer pointer - - bEndThread=0; // init thread vars - bThreadEnded=0; - bSpuInit=1; // flag: we are inited - -#ifdef _WINDOWS - - if(iUseTimer==1) // windows: use timer - { - timeBeginPeriod(1); - timeSetEvent(1,1,MAINProc,0,TIME_ONESHOT); - } - else - if(iUseTimer==0) // windows: use thread - { - //_beginthread(MAINThread,0,NULL); - DWORD dw; - hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw); - SetThreadPriority(hMainThread, - //THREAD_PRIORITY_TIME_CRITICAL); - THREAD_PRIORITY_HIGHEST); - } - -#else - - if(!iUseTimer) // linux: use thread - { - pthread_create(&thread, NULL, MAINThread, NULL); - } - -#endif -} - -// REMOVETIMER: kill threads/timers -void RemoveTimer(void) -{ - bEndThread=1; // raise flag to end thread - -#ifdef _WINDOWS - - if(iUseTimer!=2) // windows thread? - { - while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended - Sleep(5L); - } - if(iUseTimer==1) timeEndPeriod(1); // windows timer? stop it - -#else - if(!iUseTimer) // linux tread? - { - int i=0; - while(!bThreadEnded && i<2000) {usleep(1000L);i++;} // -> wait until thread has ended - if(thread!=(pthread_t)-1) {pthread_cancel(thread);thread=(pthread_t)-1;} // -> cancel thread anyway - } - -#endif - - bThreadEnded=0; // no more spu is running - bSpuInit=0; -} - -// SETUPSTREAMS: init most of the spu buffers -static void SetupStreams(void) -{ - int i; - - pSpuBuffer=(unsigned char *)malloc(32768); // alloc mixing buffer - - if(iUseReverb==1) i=88200*2; - else i=NSSIZE*2; - - sRVBStart = (int *)malloc(i*4); // alloc reverb buffer - memset(sRVBStart,0,i*4); - sRVBEnd = sRVBStart + i; - sRVBPlay = sRVBStart; - - XAStart = // alloc xa buffer - (uint32_t *)malloc(44100 * sizeof(uint32_t)); - XAEnd = XAStart + 44100; - XAPlay = XAStart; - XAFeed = XAStart; - - CDDAStart = // alloc cdda buffer - (uint32_t *)malloc(44100 * sizeof(uint32_t)); - CDDAEnd = CDDAStart + 44100; - CDDAPlay = CDDAStart; - CDDAFeed = CDDAStart; - - for(i=0;i<MAXCHAN;i++) // loop sound channels - { -// we don't use mutex sync... not needed, would only -// slow us down: -// s_chan[i].hMutex=CreateMutex(NULL,FALSE,NULL); - s_chan[i].ADSRX.SustainLevel = 1024; // -> init sustain - s_chan[i].iMute=0; - s_chan[i].iIrqDone=0; - s_chan[i].pLoop=spuMemC; - s_chan[i].pStart=spuMemC; - s_chan[i].pCurr=spuMemC; - } - - pMixIrq=spuMemC; // enable decoded buffer irqs by setting the address -} - -// REMOVESTREAMS: free most buffer -static void RemoveStreams(void) -{ - free(pSpuBuffer); // free mixing buffer - pSpuBuffer = NULL; - free(sRVBStart); // free reverb buffer - sRVBStart = NULL; - free(XAStart); // free XA buffer - XAStart = NULL; - free(CDDAStart); // free CDDA buffer - CDDAStart = NULL; -} - -// INIT/EXIT STUFF - -// SPUINIT: this func will be called first by the main emu -long CALLBACK SPUinit(void) -{ - spuMemC = (unsigned char *)spuMem; // just small setup - memset((void *)&rvb, 0, sizeof(REVERBInfo)); - InitADSR(); - - iVolume = 3; - iReverbOff = -1; - spuIrq = 0; - spuAddr = 0xffffffff; - bEndThread = 0; - bThreadEnded = 0; - spuMemC = (unsigned char *)spuMem; - pMixIrq = 0; - memset((void *)s_chan, 0, (MAXCHAN + 1) * sizeof(SPUCHAN)); - pSpuIrq = 0; - iSPUIRQWait = 1; - lastch = -1; - - ReadConfig(); // read user stuff - SetupStreams(); // prepare streaming - - return 0; -} - -// SPUOPEN: called by main emu after init -#ifdef _WINDOWS -long CALLBACK SPUopen(HWND hW) -#else -long SPUopen(void) -#endif -{ - if (bSPUIsOpen) return 0; // security for some stupid main emus - -#ifdef _WINDOWS - LastWrite=0xffffffff;LastPlay=0; // init some play vars - if(!IsWindow(hW)) hW=GetActiveWindow(); - hWMain = hW; // store hwnd -#endif - - SetupSound(); // setup sound (before init!) - SetupTimer(); // timer for feeding data - - bSPUIsOpen = 1; - -#ifdef _WINDOWS - if(iDebugMode) // windows debug dialog - { - hWDebug=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DEBUG), - NULL,(DLGPROC)DebugDlgProc); - SetWindowPos(hWDebug,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); - UpdateWindow(hWDebug); - SetFocus(hWMain); - } - - if(iRecordMode) // windows recording dialog - { - hWRecord=CreateDialog(hInst,MAKEINTRESOURCE(IDD_RECORD), - NULL,(DLGPROC)RecordDlgProc); - SetWindowPos(hWRecord,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); - UpdateWindow(hWRecord); - SetFocus(hWMain); - } -#endif - - return PSE_SPU_ERR_SUCCESS; -} - -// SPUCLOSE: called before shutdown -long CALLBACK SPUclose(void) -{ - if (!bSPUIsOpen) return 0; // some security - - bSPUIsOpen = 0; // no more open - -#ifdef _WINDOWS - if(IsWindow(hWDebug)) DestroyWindow(hWDebug); - hWDebug=0; - if(IsWindow(hWRecord)) DestroyWindow(hWRecord); - hWRecord=0; -#endif - - RemoveTimer(); // no more feeding - RemoveSound(); // no more sound handling - - return 0; -} - -// SPUSHUTDOWN: called by main emu on final exit -long CALLBACK SPUshutdown(void) -{ - SPUclose(); - RemoveStreams(); // no more streaming - - return 0; -} - -// SPUTEST: we don't test, we are always fine ;) -long CALLBACK SPUtest(void) -{ - return 0; -} - -// SPUCONFIGURE: call config dialog -long CALLBACK SPUconfigure(void) -{ -#if defined (_WINDOWS) - DialogBox(hInst,MAKEINTRESOURCE(IDD_CFGDLG), - GetActiveWindow(),(DLGPROC)DSoundDlgProc); -#elif defined (_MACOSX) - DoConfiguration(); -#else - StartCfgTool("CFG"); -#endif - - return 0; -} - -// SPUABOUT: show about window -void CALLBACK SPUabout(void) -{ -#if defined (_WINDOWS) - DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT), - GetActiveWindow(),(DLGPROC)AboutDlgProc); -#elif defined (_MACOSX) - DoAbout(); -#else - StartCfgTool("ABOUT"); -#endif -} - -// SETUP CALLBACKS -// this functions will be called once, -// passes a callback that should be called on SPU-IRQ/cdda volume change -void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void)) -{ - irqCallback = callback; -} - -#if 0 -void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short)) -{ - cddavCallback = CDDAVcallback; -} -#endif - -// COMMON PLUGIN INFO FUNCS -char * CALLBACK PSEgetLibName(void) -{ - return _(libraryName); -} - -unsigned long CALLBACK PSEgetLibType(void) -{ - return PSE_LT_SPU; -} - -unsigned long CALLBACK PSEgetLibVersion(void) -{ - return (1 << 16) | (1 << 8); -} - -#if 0 -char * SPUgetLibInfos(void) -{ - return _(libraryInfo); -} -#endif +/***************************************************************************
+ spu.c - description
+ -------------------
+ begin : Wed May 15 2002
+ copyright : (C) 2002 by Pete Bernert
+ email : BlackDove@addcom.de
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. See also the license.txt file for *
+ * additional informations. *
+ * *
+ ***************************************************************************/
+
+#include "stdafx.h"
+
+#define _IN_SPU
+
+#include "externals.h"
+#include "cfg.h"
+#include "dsoundoss.h"
+#include "regs.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)
+static char * libraryName = N_("Mac OS X Sound");
+#elif defined (USEALSA)
+static char * libraryName = N_("ALSA Sound");
+#elif defined (USEOSS)
+static char * libraryName = N_("OSS Sound");
+#elif defined (USESDL)
+static char * libraryName = N_("SDL Sound");
+#elif defined (USEPULSEAUDIO)
+static char * libraryName = N_("PulseAudio Sound");
+#else
+static char * libraryName = N_("NULL Sound");
+#endif
+
+static char * libraryInfo = N_("P.E.Op.S. Sound Driver V1.7\nCoded by Pete Bernert and the P.E.Op.S. team\n");
+
+// globals
+
+// psx buffer / addresses
+
+unsigned short regArea[10000];
+unsigned short spuMem[256*1024];
+unsigned char * spuMemC;
+unsigned char * pSpuIrq=0;
+unsigned char * pSpuBuffer;
+unsigned char * pMixIrq=0;
+
+// user settings
+
+int iVolume=3;
+int iXAPitch=1;
+int iUseTimer=2;
+int iSPUIRQWait=1;
+int iDebugMode=0;
+int iRecordMode=0;
+int iUseReverb=2;
+int iUseInterpolation=2;
+int iDisStereo=0;
+int iFreqResponse=0;
+
+// MAIN infos struct for each channel
+
+SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling)
+REVERBInfo rvb;
+
+unsigned long dwNoiseVal=1; // global noise generator
+unsigned long dwNoiseCount; // global noise generator
+unsigned long dwNoiseClock; // global noise generator
+int iSpuAsyncWait=0;
+
+unsigned short spuCtrl=0; // some vars to store psx reg infos
+unsigned short spuStat=0;
+unsigned short spuIrq=0;
+unsigned long spuAddr=0xffffffff; // address into spu mem
+int bEndThread=0; // thread handlers
+int bThreadEnded=0;
+int bSpuInit=0;
+int bSPUIsOpen=0;
+
+#ifdef _WINDOWS
+HWND hWMain=0; // window handle
+HWND hWDebug=0;
+HWND hWRecord=0;
+static HANDLE hMainThread;
+#else
+static pthread_t thread = (pthread_t)-1; // thread id (linux)
+#endif
+
+uint32_t dwNewChannel=0; // flags for faster testing, if new channel starts
+
+void (CALLBACK *irqCallback)(void)=0; // func of main emu, called on spu irq
+void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0;
+
+// certain globals (were local before, but with the new timeproc I need em global)
+
+static const int f[5][2] = { { 0, 0 },
+ { 60, 0 },
+ { 115, -52 },
+ { 98, -55 },
+ { 122, -60 } };
+int SSumR[NSSIZE];
+int SSumL[NSSIZE];
+int iFMod[NSSIZE];
+int iCycle = 0;
+short * pS;
+
+int lastch=-1; // last channel processed on spu irq in timer mode
+static int lastns=0; // last ns pos
+static int iSecureStart=0; // secure start counter
+
+////////////////////////////////////////////////////////////////////////
+// CODE AREA
+////////////////////////////////////////////////////////////////////////
+
+// dirty inline func includes
+
+#include "reverb.c"
+#include "adsr.c"
+
+////////////////////////////////////////////////////////////////////////
+// helpers for simple interpolation
+
+//
+// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm
+//
+// instead of having n equal sample values in a row like:
+// ____
+// |____
+//
+// we compare the current delta change with the next delta change.
+//
+// if curr_delta is positive,
+//
+// - and next delta is smaller (or changing direction):
+// \.
+// -__
+//
+// - and next delta significant (at least twice) bigger:
+// --_
+// \.
+//
+// - and next delta is nearly same:
+// \.
+// \.
+//
+//
+// if curr_delta is negative,
+//
+// - and next delta is smaller (or changing direction):
+// _--
+// /
+//
+// - and next delta significant (at least twice) bigger:
+// /
+// __-
+//
+// - and next delta is nearly same:
+// /
+// /
+//
+
+
+INLINE void InterpolateUp(int ch)
+{
+ if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass
+ {
+ const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val
+ const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :)
+
+ s_chan[ch].SB[32]=0;
+
+ if(id1>0) // curr delta positive
+ {
+ if(id2<id1)
+ {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
+ else
+ if(id2<(id1<<1))
+ s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
+ else
+ s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L;
+ }
+ else // curr delta negative
+ {
+ if(id2>id1)
+ {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
+ else
+ if(id2>(id1<<1))
+ s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
+ else
+ s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L;
+ }
+ }
+ else
+ if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass
+ {
+ s_chan[ch].SB[32]=0;
+
+ s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L;
+ if(s_chan[ch].sinc<=0x8000)
+ s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1));
+ else s_chan[ch].SB[29]+=s_chan[ch].SB[28];
+ }
+ else // no flags? add bigger val (if possible), calc smaller step, set flag1
+ s_chan[ch].SB[29]+=s_chan[ch].SB[28];
+}
+
+//
+// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
+//
+
+INLINE void InterpolateDown(int ch)
+{
+ if(s_chan[ch].sinc>=0x20000L) // we would skip at least one val?
+ {
+ s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight
+ if(s_chan[ch].sinc>=0x30000L) // we would skip even more vals?
+ s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// helpers for gauss interpolation
+
+#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos])
+#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3])
+
+#include "gauss_i.h"
+
+////////////////////////////////////////////////////////////////////////
+
+#include "xa.c"
+
+////////////////////////////////////////////////////////////////////////
+// START SOUND... called by main thread to setup a new sound on a channel
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StartSound(int ch)
+{
+ StartADSR(ch);
+ StartREVERB(ch);
+
+ // fussy timing issues - do in VoiceOn
+ //s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start
+ //s_chan[ch].bStop=0;
+ //s_chan[ch].bOn=1;
+
+ s_chan[ch].s_1=0; // init mixing vars
+ s_chan[ch].s_2=0;
+ s_chan[ch].iSBPos=28;
+
+ s_chan[ch].bNew=0; // init channel flags
+
+ s_chan[ch].SB[29]=0; // init our interpolation helpers
+ s_chan[ch].SB[30]=0;
+
+ if(iUseInterpolation>=2) // gauss interpolation?
+ {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding
+ else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding
+
+ dwNewChannel&=~(1<<ch); // clear new channel bit
+}
+
+////////////////////////////////////////////////////////////////////////
+// ALL KIND OF HELPERS
+////////////////////////////////////////////////////////////////////////
+
+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;
+ if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
+ if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void FModChangeFrequency(int ch,int ns)
+{
+ int NP=s_chan[ch].iRawPitch;
+
+ NP=((32768L+iFMod[ns])*NP)/32768L;
+
+ if(NP>0x3fff) NP=0x3fff;
+ if(NP<0x1) NP=0x1;
+
+ NP=(44100L*NP)/(4096L); // calc frequency
+
+ s_chan[ch].iActFreq=NP;
+ s_chan[ch].iUsedFreq=NP;
+ s_chan[ch].sinc=(((NP/10)<<16)/4410);
+ if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
+ if(iUseInterpolation==1) // freq change in simple interpolation mode
+ s_chan[ch].SB[32]=1;
+ iFMod[ns]=0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/*
+Noise Algorithm
+- Dr.Hell (Xebra PS1 emu)
+- 100% accurate (waveform + frequency)
+- http://drhell.web.fc2.com
+
+
+Level change cycle
+Freq = 0x8000 >> (NoiseClock >> 2);
+
+Frequency of half cycle
+Half = ((NoiseClock & 3) * 2) / (4 + (NoiseClock & 3));
+- 0 = (0*2)/(4+0) = 0/4
+- 1 = (1*2)/(4+1) = 2/5
+- 2 = (2*2)/(4+2) = 4/6
+- 3 = (3*2)/(4+3) = 6/7
+
+-------------------------------
+
+5*6*7 = 210
+4 - 0*0 = 0
+5 - 42*2 = 84
+6 - 35*4 = 140
+7 - 30*6 = 180
+*/
+
+// Noise Waveform - Dr. Hell (Xebra)
+char NoiseWaveAdd [64] = {
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1
+};
+
+unsigned short NoiseFreqAdd[5] = {
+ 0, 84, 140, 180, 210
+};
+
+INLINE void NoiseClock()
+{
+ unsigned int level;
+
+ level = 0x8000 >> (dwNoiseClock >> 2);
+ level <<= 16;
+
+ dwNoiseCount += 0x10000;
+
+ // Dr. Hell - fraction
+ dwNoiseCount += NoiseFreqAdd[ dwNoiseClock & 3 ];
+ if( (dwNoiseCount&0xffff) >= NoiseFreqAdd[4] ) {
+ dwNoiseCount += 0x10000;
+ dwNoiseCount -= NoiseFreqAdd[ dwNoiseClock & 3 ];
+ }
+
+ if( dwNoiseCount >= level )
+ {
+ while( dwNoiseCount >= level )
+ dwNoiseCount -= level;
+
+ // Dr. Hell - form
+ dwNoiseVal = (dwNoiseVal<<1) | NoiseWaveAdd[ (dwNoiseVal>>10) & 63 ];
+ }
+}
+
+INLINE int iGetNoiseVal(int ch)
+{
+ int fa;
+
+ fa = (short) dwNoiseVal;
+
+ // no clip need
+ //if(fa>32767L) fa=32767L;
+ //if(fa<-32767L) fa=-32767L;
+
+ // don't upset VAG decoder
+ //if(iUseInterpolation<2) // no gauss/cubic interpolation?
+ //pChannel->SB[29] = fa; // -> store noise val in "current sample" slot
+
+ // boost volume - no more!
+ //return fa * 3 / 2;
+ return fa;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StoreInterpolationVal(int ch,int fa)
+{
+ /*
+ // fmod channel = sound output
+ if(s_chan[ch].bFMod==2) // fmod freq channel
+ s_chan[ch].SB[29]=fa;
+ else
+ */
+ {
+ if((spuCtrl&0x4000)==0) fa=0; // muted?
+ else // else adjust
+ {
+ if(fa>32767L) fa=32767L;
+ if(fa<-32767L) fa=-32767L;
+ }
+
+ if(iUseInterpolation>=2) // gauss/cubic interpolation
+ {
+ int gpos = s_chan[ch].SB[28];
+ gval0 = fa;
+ gpos = (gpos+1) & 3;
+ s_chan[ch].SB[28] = gpos;
+ }
+ else
+ if(iUseInterpolation==1) // simple interpolation
+ {
+ s_chan[ch].SB[28] = 0;
+ s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
+ s_chan[ch].SB[30] = s_chan[ch].SB[31];
+ s_chan[ch].SB[31] = fa;
+ s_chan[ch].SB[32] = 1; // -> flag: calc new interolation
+ }
+ else s_chan[ch].SB[29]=fa; // no interpolation
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int iGetInterpolationVal(int ch)
+{
+ int fa;
+
+ // fmod channel = sound output
+ //if(s_chan[ch].bFMod==2) return s_chan[ch].SB[29];
+
+ switch(iUseInterpolation)
+ {
+ //--------------------------------------------------//
+ case 3: // cubic interpolation
+ {
+ long xd;int gpos;
+ xd = ((s_chan[ch].spos) >> 1)+1;
+ gpos = s_chan[ch].SB[28];
+
+ fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0;
+ fa *= (xd - (2<<15)) / 6;
+ fa >>= 15;
+ fa += gval(2) - gval(1) - gval(1) + gval0;
+ fa *= (xd - (1<<15)) >> 1;
+ fa >>= 15;
+ fa += gval(1) - gval0;
+ fa *= xd;
+ fa >>= 15;
+ fa = fa + gval0;
+
+ } break;
+ //--------------------------------------------------//
+ case 2: // gauss interpolation
+ {
+ int vl, vr;int gpos;
+ vl = (s_chan[ch].spos >> 6) & ~3;
+ gpos = s_chan[ch].SB[28];
+ vr=(gauss[vl]*gval0)&~2047;
+ vr+=(gauss[vl+1]*gval(1))&~2047;
+ vr+=(gauss[vl+2]*gval(2))&~2047;
+ vr+=(gauss[vl+3]*gval(3))&~2047;
+ fa = vr>>11;
+ } break;
+ //--------------------------------------------------//
+ case 1: // simple interpolation
+ {
+ if(s_chan[ch].sinc<0x10000L) // -> upsampling?
+ InterpolateUp(ch); // --> interpolate up
+ else InterpolateDown(ch); // --> else down
+ fa=s_chan[ch].SB[29];
+ } break;
+ //--------------------------------------------------//
+ default: // no interpolation
+ {
+ fa=s_chan[ch].SB[29];
+ } break;
+ //--------------------------------------------------//
+ }
+
+ return fa;
+}
+
+////////////////////////////////////////////////////////////////////////
+// MAIN SPU FUNCTION
+// here is the main job handler... thread, timer or direct func call
+// basically the whole sound processing is done in this fat func!
+////////////////////////////////////////////////////////////////////////
+
+// 5 ms waiting phase, if buffer is full and no new sound has to get started
+// .. can be made smaller (smallest val: 1 ms), but bigger waits give
+// better performance
+
+#define PAUSE_W 1
+#define PAUSE_L 1000
+
+////////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+static VOID CALLBACK MAINProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+#else
+static void *MAINThread(void *arg)
+#endif
+{
+ int s_1,s_2,fa,ns;
+ int voldiv = iVolume;
+
+ unsigned char * start;unsigned int nSample;
+ int ch,predict_nr,shift_factor,flags,d,s;
+ int bIRQReturn=0;
+
+ // mute output
+ if( voldiv == 5 ) voldiv = 0x7fffffff;
+
+ while(!bEndThread) // until we are shutting down
+ {
+ // ok, at the beginning we are looking if there is
+ // enuff free place in the dsound/oss buffer to
+ // fill in new data, or if there is a new channel to start.
+ // if not, we wait (thread) or return (timer/spuasync)
+ // until enuff free place is available/a new channel gets
+ // started
+
+ if(dwNewChannel) // new channel should start immedately?
+ { // (at least one bit 0 ... MAXCHANNEL is set?)
+ iSecureStart++; // -> set iSecure
+ if(iSecureStart>1) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance)
+ }
+ else iSecureStart=0; // 0: no new channel should start
+
+ while(!iSecureStart && !bEndThread && // no new start? no thread end?
+ (SoundGetBytesBuffered()>TESTSIZE)) // and still enuff data in sound buffer?
+ {
+ iSecureStart=0; // reset secure
+
+#ifdef _WINDOWS
+ if(iUseTimer) // no-thread mode?
+ {
+ if(iUseTimer==1) // -> ok, timer mode 1: setup a oneshot timer of x ms to wait
+ timeSetEvent(PAUSE_W,1,MAINProc,0,TIME_ONESHOT);
+ return; // -> and done this time (timer mode 1 or 2)
+ }
+ // win thread mode:
+ Sleep(PAUSE_W); // sleep for x ms (win)
+#else
+ if(iUseTimer) return 0; // linux no-thread mode? bye
+ usleep(PAUSE_L); // else sleep for x ms (linux)
+#endif
+
+ if(dwNewChannel) iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop
+ }
+
+ //--------------------------------------------------// continue from irq handling in timer mode?
+
+ if(lastch>=0) // will be -1 if no continue is pending
+ {
+ ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue
+ goto GOON; // -> directly jump to the continue point
+ }
+
+ //--------------------------------------------------//
+ //- main channel loop -//
+ //--------------------------------------------------//
+ {
+ ns=0;
+ while(ns<NSSIZE) // loop until 1 ms of data is reached
+ {
+ NoiseClock();
+
+ for(ch=0;ch<MAXCHAN;ch++) // loop em all... we will collect 1 ms of sound of each playing channel
+ {
+ if(s_chan[ch].bNew) {
+#if 1
+ StartSound(ch); // start new sound
+ dwNewChannel&=~(1<<ch); // clear new channel bit
+#else
+ if( s_chan[ch].ADSRX.StartDelay == 0 ) {
+ StartSound(ch); // start new sound
+ dwNewChannel&=~(1<<ch); // clear new channel bit
+ } else {
+ s_chan[ch].ADSRX.StartDelay--;
+ }
+#endif
+ }
+ if(!s_chan[ch].bOn) continue; // channel not playing? next
+
+ if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq) // new psx frequency?
+ VoiceChangeFrequency(ch);
+
+ if(s_chan[ch].bFMod==1 && iFMod[ns]) // fmod freq channel
+ FModChangeFrequency(ch,ns);
+
+ while(s_chan[ch].spos>=0x10000L)
+ {
+ if(s_chan[ch].iSBPos==28) // 28 reached?
+ {
+#if 0
+ // Xenogears - Anima Relic dungeon (exp gain)
+ if( s_chan[ch].bLoopJump == 1 )
+ s_chan[ch].pCurr = s_chan[ch].pLoop;
+
+ s_chan[ch].bLoopJump = 0;
+#endif
+
+
+ start=s_chan[ch].pCurr; // set up the current pos
+
+ if (s_chan[ch].iSilent==1 || start == (unsigned char*)-1) // special "stop" sign
+ {
+ // silence = let channel keep running (IRQs)
+ //s_chan[ch].bOn=0; // -> turn everything off
+ s_chan[ch].iSilent=2;
+
+ s_chan[ch].ADSRX.lVolume=0;
+ s_chan[ch].ADSRX.EnvelopeVol=0;
+ }
+
+ s_chan[ch].iSBPos=0;
+
+ //////////////////////////////////////////// spu irq handler here? mmm... do it later
+
+ s_1=s_chan[ch].s_1;
+ s_2=s_chan[ch].s_2;
+
+ predict_nr=(int)*start;start++;
+ shift_factor=predict_nr&0xf;
+ predict_nr >>= 4;
+ flags=(int)*start;start++;
+
+ // -------------------------------------- //
+
+ for (nSample=0;nSample<28;start++)
+ {
+ d=(int)*start;
+ s=((d&0xf)<<12);
+ if(s&0x8000) s|=0xffff0000;
+
+ fa=(s >> shift_factor);
+ fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+ s_2=s_1;s_1=fa;
+ s=((d & 0xf0) << 8);
+
+ s_chan[ch].SB[nSample++]=fa;
+
+ if(s&0x8000) s|=0xffff0000;
+ fa=(s>>shift_factor);
+ fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+ s_2=s_1;s_1=fa;
+
+ s_chan[ch].SB[nSample++]=fa;
+ }
+
+ //////////////////////////////////////////// irq check
+
+ if(irqCallback && (spuCtrl&0x40)) // some callback and irq active?
+ {
+ if((pSpuIrq > start-16 && // irq address reached?
+ pSpuIrq <= start) ||
+ ((flags&1) && // special: irq on looping addr, when stop/loop flag is set
+ (pSpuIrq > s_chan[ch].pLoop-16 &&
+ pSpuIrq <= s_chan[ch].pLoop)))
+ {
+ s_chan[ch].iIrqDone=1; // -> debug flag
+ irqCallback(); // -> call main emu
+
+ if(iSPUIRQWait) // -> option: wait after irq for main emu
+ {
+ iSpuAsyncWait=1;
+ bIRQReturn=1;
+ }
+ }
+ }
+
+ //////////////////////////////////////////// flag handler
+
+ /*
+ SPU2-X:
+ $4 = set loop to current block
+ $2 = keep envelope on (no mute)
+ $1 = jump to loop address
+
+ silence means no volume (ADSR keeps playing!!)
+ */
+
+#if 0
+ if(flags&4)
+ s_chan[ch].pLoop=start-16;
+#else
+ // Jungle Book - Rhythm 'n Groove - use external loop address
+ // - fixes music player (+IRQ generate)
+ if((flags&4) && (s_chan[ch].bIgnoreLoop == 0))
+ s_chan[ch].pLoop=start-16;
+#endif
+
+ // Jungle Book - Rhythm 'n Groove - don't reset ignore status
+ // - fixes gameplay speed (IRQ hits)
+ //s_chan[ch].bIgnoreLoop = 0;
+
+
+ if(flags&1)
+ {
+ // ...?
+ //s_chan[ch].bIgnoreLoop = 0;
+
+ // Xenogears - 7 = play missing sounds
+#if 0
+ // set jump flag
+ pChannel->bLoopJump = 1;
+#else
+ start = s_chan[ch].pLoop;
+#endif
+
+ // silence = keep playing..?
+ if( (flags&2) == 0 ) {
+ s_chan[ch].iSilent = 1;
+
+ // silence = don't start release phase
+ //s_chan[ch].bStop = 1;
+
+ //start = (unsigned char *) -1;
+ }
+ }
+
+#if 0
+ // crash check
+ if( start == 0 )
+ start = (unsigned char *) -1;
+ if( start >= spuMemC + 0x80000 )
+ start = spuMemC - 0x80000;
+#endif
+
+ s_chan[ch].pCurr=start; // store values for next cycle
+ s_chan[ch].s_1=s_1;
+ s_chan[ch].s_2=s_2;
+
+ if(bIRQReturn) // special return for "spu irq - wait for cpu action"
+ {
+ bIRQReturn=0;
+ if(iUseTimer!=2)
+ {
+ DWORD dwWatchTime=timeGetTime_spu()+2500;
+
+ while(iSpuAsyncWait && !bEndThread &&
+ timeGetTime_spu()<dwWatchTime)
+#ifdef _WINDOWS
+ Sleep(1);
+#else
+ usleep(1000L);
+#endif
+ }
+ else
+ {
+ lastch=ch;
+ lastns=ns;
+
+#ifdef _WINDOWS
+ return;
+#else
+ return 0;
+#endif
+ }
+ }
+
+GOON: ;
+ }
+
+ fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data
+
+ StoreInterpolationVal(ch,fa); // store val for later interpolation
+
+ s_chan[ch].spos -= 0x10000L;
+ }
+
+ if(s_chan[ch].bNoise)
+ fa=iGetNoiseVal(ch); // get noise val
+ else fa=iGetInterpolationVal(ch); // get sample val
+
+
+#if 0
+ // Voice 1/3 decoded buffer
+ if( ch == 0 ) {
+ spuMem[ (0x800 + voice_dbuf_ptr) / 2 ] = (short) fa;
+ } else if( ch == 2 ) {
+ spuMem[ (0xc00 + voice_dbuf_ptr) / 2 ] = (short) fa;
+ }
+#endif
+
+
+ s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // mix adsr
+
+ if(s_chan[ch].bFMod==2) // fmod freq channel
+ iFMod[ns]=s_chan[ch].sval; // -> store 1T sample data, use that to do fmod on next channel
+
+ // mix fmod channel into output
+ // - Xenogears save icon (high pitch)
+ {
+ //////////////////////////////////////////////
+ // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff)
+
+ if(s_chan[ch].iMute)
+ s_chan[ch].sval=0; // debug mute
+ else
+ {
+ SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L;
+ SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L;
+ }
+
+ //////////////////////////////////////////////
+ // now let us store sound data for reverb
+
+ if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns);
+ }
+
+ s_chan[ch].spos += s_chan[ch].sinc;
+ }
+ ////////////////////////////////////////////////
+ // ok, go on until 1 ms data of this channel is collected
+
+ ns++;
+ } // end ns
+ }
+
+ //---------------------------------------------------//
+ //- here we have another 1 ms of sound data
+ //---------------------------------------------------//
+ // mix XA infos (if any)
+
+ MixXA();
+
+ ///////////////////////////////////////////////////////
+ // mix all channels (including reverb) into one buffer
+
+ if(iDisStereo) // no stereo?
+ {
+ int dl, dr;
+ for (ns = 0; ns < NSSIZE; ns++)
+ {
+ SSumL[ns] += MixREVERBLeft(ns);
+
+ dl = SSumL[ns] / voldiv; SSumL[ns] = 0;
+ if (dl < -32767) dl = -32767; if (dl > 32767) dl = 32767;
+
+ SSumR[ns] += MixREVERBRight();
+
+ dr = SSumR[ns] / voldiv; SSumR[ns] = 0;
+ if (dr < -32767) dr = -32767; if (dr > 32767) dr = 32767;
+ *pS++ = (dl + dr) / 2;
+ }
+ }
+ else // stereo:
+ for (ns = 0; ns < NSSIZE; ns++)
+ {
+ static double _interpolation_coefficient = 3.759285613;
+
+ if(iFreqResponse) {
+ int sl,sr;
+ double ldiff, rdiff, avg, tmp;
+
+ SSumL[ns]+=MixREVERBLeft(ns);
+ SSumR[ns]+=MixREVERBRight();
+
+ sl = SSumL[ns]; SSumL[ns]=0;
+ sr = SSumR[ns]; SSumR[ns]=0;
+
+
+ /*
+ Frequency Response
+ - William Pitcock (nenolod) (UPSE PSF player)
+ - accurate (!)
+ - http://nenolod.net
+ */
+
+ avg = ((sl + sr) / 2);
+ ldiff = sl - avg;
+ rdiff = sr - avg;
+
+ tmp = avg + ldiff * _interpolation_coefficient;
+ if (tmp < -32768)
+ tmp = -32768;
+ if (tmp > 32767)
+ tmp = 32767;
+ sl = (int)tmp;
+
+ tmp = avg + rdiff * _interpolation_coefficient;
+ if (tmp < -32768)
+ tmp = -32768;
+ if (tmp > 32767)
+ tmp = 32767;
+ sr = (int)tmp;
+
+
+ *pS++=sl/voldiv;
+ *pS++=sr/voldiv;
+ } else {
+ SSumL[ns]+=MixREVERBLeft(ns);
+
+ d=SSumL[ns]/voldiv;SSumL[ns]=0;
+ if(d<-32767) d=-32767;if(d>32767) d=32767;
+ *pS++=d;
+
+ SSumR[ns]+=MixREVERBRight();
+
+ d=SSumR[ns]/voldiv;SSumR[ns]=0;
+ if(d<-32767) d=-32767;if(d>32767) d=32767;
+ *pS++=d;
+ }
+ }
+
+ //////////////////////////////////////////////////////
+ // special irq handling in the decode buffers (0x0000-0x1000)
+ // we know:
+ // the decode buffers are located in spu memory in the following way:
+ // 0x0000-0x03ff CD audio left
+ // 0x0400-0x07ff CD audio right
+ // 0x0800-0x0bff Voice 1
+ // 0x0c00-0x0fff Voice 3
+ // and decoded data is 16 bit for one sample
+ // we assume:
+ // even if voices 1/3 are off or no cd audio is playing, the internal
+ // play positions will move on and wrap after 0x400 bytes.
+ // Therefore: we just need a pointer from spumem+0 to spumem+3ff, and
+ // increase this pointer on each sample by 2 bytes. If this pointer
+ // (or 0x400 offsets of this pointer) hits the spuirq address, we generate
+ // an IRQ. Only problem: the "wait for cpu" option is kinda hard to do here
+ // in some of Peops timer modes. So: we ignore this option here (for now).
+
+ if(pMixIrq && irqCallback)
+ {
+ for(ns=0;ns<NSSIZE;ns++)
+ {
+ if((spuCtrl&0x40) && pSpuIrq && pSpuIrq<spuMemC+0x1000)
+ {
+ for(ch=0;ch<4;ch++)
+ {
+ if(pSpuIrq>=pMixIrq+(ch*0x400) && pSpuIrq<pMixIrq+(ch*0x400)+2)
+ {irqCallback();s_chan[ch].iIrqDone=1;}
+ }
+ }
+ pMixIrq+=2;if(pMixIrq>spuMemC+0x3ff) pMixIrq=spuMemC;
+ }
+ }
+
+ InitREVERB();
+
+ //////////////////////////////////////////////////////
+ // feed the sound
+ // latency = 25 ms (less pops, crackles, smoother)
+
+ //if(iCycle++>=20)
+ iCycle += APU_CYCLES_UPDATE;
+ if(iCycle > 44000/1000*LATENCY + 100*LATENCY/1000)
+ {
+ SoundFeedStreamData((unsigned char *)pSpuBuffer,
+ ((unsigned char *)pS) - ((unsigned char *)pSpuBuffer));
+ pS = (short *)pSpuBuffer;
+ iCycle = 0;
+ }
+
+
+ if( iUseTimer == 2 )
+ break;
+ }
+
+ // end of big main loop...
+
+ bThreadEnded = 1;
+
+#ifndef _WINDOWS
+ return 0;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+// WINDOWS THREAD... simply calls the timer func and stays forever :)
+////////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+
+DWORD WINAPI MAINThreadEx(LPVOID lpParameter)
+{
+ MAINProc(0,0,0,0,0);
+ return 0;
+}
+
+#endif
+
+// SPU ASYNC... even newer epsxe func
+// 1 time every 'cycle' cycles... harhar
+
+long cpu_cycles;
+void CALLBACK SPUasync(unsigned long cycle)
+{
+ cpu_cycles += cycle;
+
+ if(iSpuAsyncWait)
+ {
+ iSpuAsyncWait++;
+ if(iSpuAsyncWait<=64) return;
+ iSpuAsyncWait=0;
+
+ cpu_cycles = cycle;
+ }
+
+#ifdef _WINDOWS
+ if(iDebugMode==2)
+ {
+ if(IsWindow(hWDebug)) DestroyWindow(hWDebug);
+ hWDebug=0;iDebugMode=0;
+ }
+ if(iRecordMode==2)
+ {
+ if(IsWindow(hWRecord)) DestroyWindow(hWRecord);
+ hWRecord=0;iRecordMode=0;
+ }
+#endif
+
+ if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode)
+ {
+ if(!bSpuInit) return; // -> no init, no call
+
+ // note: usable precision difference (not using interval_time)
+ while( cpu_cycles >= CPU_CLOCK / 44100 * NSSIZE )
+ {
+ #ifdef _WINDOWS
+ MAINProc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks
+ #else
+ MAINThread(0); // -> linux high-compat mode
+ #endif
+
+ cpu_cycles -= CPU_CLOCK / 44100 * NSSIZE;
+ }
+ }
+}
+
+// SPU UPDATE... new epsxe func
+// 1 time every 32 hsync lines
+// (312/32)x50 in pal
+// (262/32)x60 in ntsc
+
+// since epsxe 1.5.2 (linux) uses SPUupdate, not SPUasync, I will
+// leave that func in the linux port, until epsxe linux is using
+// the async function as well
+
+void CALLBACK SPUupdate(void)
+{
+ SPUasync(0);
+}
+
+// XA AUDIO
+
+void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap)
+{
+ if(!xap) return;
+ if(!xap->freq) return; // no xa freq ? bye
+
+ FeedXA(xap); // call main XA feeder
+}
+
+// CDDA AUDIO
+void CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes)
+{
+ if (!pcm) return;
+ if (nbytes<=0) return;
+
+ FeedCDDA((unsigned char *)pcm, nbytes);
+}
+
+// SETUPTIMER: init of certain buffers and threads/timers
+void SetupTimer(void)
+{
+ memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers
+ memset(SSumL,0,NSSIZE*sizeof(int));
+ memset(iFMod,0,NSSIZE*sizeof(int));
+ pS=(short *)pSpuBuffer; // setup soundbuffer pointer
+
+ bEndThread=0; // init thread vars
+ bThreadEnded=0;
+ bSpuInit=1; // flag: we are inited
+
+#ifdef _WINDOWS
+
+ if(iUseTimer==1) // windows: use timer
+ {
+ timeBeginPeriod(1);
+ timeSetEvent(1,1,MAINProc,0,TIME_ONESHOT);
+ }
+ else
+ if(iUseTimer==0) // windows: use thread
+ {
+ //_beginthread(MAINThread,0,NULL);
+ DWORD dw;
+ hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw);
+ SetThreadPriority(hMainThread,
+ //THREAD_PRIORITY_TIME_CRITICAL);
+ THREAD_PRIORITY_HIGHEST);
+ }
+
+#else
+
+ if(!iUseTimer) // linux: use thread
+ {
+ pthread_create(&thread, NULL, MAINThread, NULL);
+ }
+
+#endif
+}
+
+// REMOVETIMER: kill threads/timers
+void RemoveTimer(void)
+{
+ bEndThread=1; // raise flag to end thread
+
+#ifdef _WINDOWS
+
+ if(iUseTimer!=2) // windows thread?
+ {
+ while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended
+ Sleep(5L);
+ }
+ if(iUseTimer==1) timeEndPeriod(1); // windows timer? stop it
+
+#else
+ if(!iUseTimer) // linux tread?
+ {
+ int i=0;
+ while(!bThreadEnded && i<2000) {usleep(1000L);i++;} // -> wait until thread has ended
+ if(thread!=(pthread_t)-1) {pthread_cancel(thread);thread=(pthread_t)-1;} // -> cancel thread anyway
+ }
+
+#endif
+
+ bThreadEnded=0; // no more spu is running
+ bSpuInit=0;
+}
+
+// SETUPSTREAMS: init most of the spu buffers
+void SetupStreams(void)
+{
+ int i;
+
+ pSpuBuffer=(unsigned char *)malloc(32768); // alloc mixing buffer
+
+ if(iUseReverb==1) i=88200*2;
+ else i=NSSIZE*2;
+
+ sRVBStart = (int *)malloc(i*4); // alloc reverb buffer
+ memset(sRVBStart,0,i*4);
+ sRVBEnd = sRVBStart + i;
+ sRVBPlay = sRVBStart;
+
+ XAStart = // alloc xa buffer
+ (uint32_t *)malloc(44100 * sizeof(uint32_t));
+ XAEnd = XAStart + 44100;
+ XAPlay = XAStart;
+ XAFeed = XAStart;
+
+ CDDAStart = // alloc cdda buffer
+ (uint32_t *)malloc(44100 * sizeof(uint32_t));
+ CDDAEnd = CDDAStart + 44100;
+ CDDAPlay = CDDAStart;
+ CDDAFeed = CDDAStart;
+
+ for(i=0;i<MAXCHAN;i++) // loop sound channels
+ {
+// we don't use mutex sync... not needed, would only
+// slow us down:
+// s_chan[i].hMutex=CreateMutex(NULL,FALSE,NULL);
+ s_chan[i].ADSRX.SustainLevel = 1024; // -> init sustain
+ s_chan[i].iMute=0;
+ s_chan[i].iIrqDone=0;
+ s_chan[i].pLoop=spuMemC;
+ s_chan[i].pStart=spuMemC;
+ s_chan[i].pCurr=spuMemC;
+ }
+
+ pMixIrq=spuMemC; // enable decoded buffer irqs by setting the address
+}
+
+// REMOVESTREAMS: free most buffer
+void RemoveStreams(void)
+{
+ free(pSpuBuffer); // free mixing buffer
+ pSpuBuffer = NULL;
+ free(sRVBStart); // free reverb buffer
+ sRVBStart = NULL;
+ free(XAStart); // free XA buffer
+ XAStart = NULL;
+ free(CDDAStart); // free CDDA buffer
+ CDDAStart = NULL;
+}
+
+// INIT/EXIT STUFF
+
+// SPUINIT: this func will be called first by the main emu
+long CALLBACK SPUinit(void)
+{
+ spuMemC = (unsigned char *)spuMem; // just small setup
+ memset((void *)&rvb, 0, sizeof(REVERBInfo));
+ InitADSR();
+
+ iVolume = 3;
+ iReverbOff = -1;
+ spuIrq = 0;
+ spuAddr = 0xffffffff;
+ bEndThread = 0;
+ bThreadEnded = 0;
+ spuMemC = (unsigned char *)spuMem;
+ pMixIrq = 0;
+ memset((void *)s_chan, 0, (MAXCHAN + 1) * sizeof(SPUCHAN));
+ pSpuIrq = 0;
+ iSPUIRQWait = 1;
+ lastch = -1;
+
+ ReadConfig(); // read user stuff
+ SetupStreams(); // prepare streaming
+
+ return 0;
+}
+
+// SPUOPEN: called by main emu after init
+#ifdef _WINDOWS
+long CALLBACK SPUopen(HWND hW)
+#else
+long SPUopen(void)
+#endif
+{
+ if (bSPUIsOpen) return 0; // security for some stupid main emus
+
+#ifdef _WINDOWS
+ LastWrite=0xffffffff;LastPlay=0; // init some play vars
+ if(!IsWindow(hW)) hW=GetActiveWindow();
+ hWMain = hW; // store hwnd
+#endif
+
+ SetupSound(); // setup sound (before init!)
+ SetupTimer(); // timer for feeding data
+
+ bSPUIsOpen = 1;
+
+#ifdef _WINDOWS
+ if(iDebugMode) // windows debug dialog
+ {
+ hWDebug=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DEBUG),
+ NULL,(DLGPROC)DebugDlgProc);
+ SetWindowPos(hWDebug,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
+ UpdateWindow(hWDebug);
+ SetFocus(hWMain);
+ }
+
+ if(iRecordMode) // windows recording dialog
+ {
+ hWRecord=CreateDialog(hInst,MAKEINTRESOURCE(IDD_RECORD),
+ NULL,(DLGPROC)RecordDlgProc);
+ SetWindowPos(hWRecord,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
+ UpdateWindow(hWRecord);
+ SetFocus(hWMain);
+ }
+#endif
+
+ return PSE_SPU_ERR_SUCCESS;
+}
+
+// SPUCLOSE: called before shutdown
+long CALLBACK SPUclose(void)
+{
+ if (!bSPUIsOpen) return 0; // some security
+
+ bSPUIsOpen = 0; // no more open
+
+#ifdef _WINDOWS
+ if(IsWindow(hWDebug)) DestroyWindow(hWDebug);
+ hWDebug=0;
+ if(IsWindow(hWRecord)) DestroyWindow(hWRecord);
+ hWRecord=0;
+#endif
+
+ RemoveTimer(); // no more feeding
+ RemoveSound(); // no more sound handling
+
+ return 0;
+}
+
+// SPUSHUTDOWN: called by main emu on final exit
+long CALLBACK SPUshutdown(void)
+{
+ SPUclose();
+ RemoveStreams(); // no more streaming
+
+ return 0;
+}
+
+// SPUTEST: we don't test, we are always fine ;)
+long CALLBACK SPUtest(void)
+{
+ return 0;
+}
+
+// SPUCONFIGURE: call config dialog
+long CALLBACK SPUconfigure(void)
+{
+#if defined (_WINDOWS)
+ DialogBox(hInst,MAKEINTRESOURCE(IDD_CFGDLG),
+ GetActiveWindow(),(DLGPROC)DSoundDlgProc);
+#elif defined (_MACOSX)
+ DoConfiguration();
+#else
+ StartCfgTool("CFG");
+#endif
+
+ return 0;
+}
+
+// SPUABOUT: show about window
+void CALLBACK SPUabout(void)
+{
+#if defined (_WINDOWS)
+ DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT),
+ GetActiveWindow(),(DLGPROC)AboutDlgProc);
+#elif defined (_MACOSX)
+ DoAbout();
+#else
+ StartCfgTool("ABOUT");
+#endif
+}
+
+// SETUP CALLBACKS
+// this functions will be called once,
+// passes a callback that should be called on SPU-IRQ/cdda volume change
+void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void))
+{
+ irqCallback = callback;
+}
+
+void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short))
+{
+ cddavCallback = CDDAVcallback;
+}
+
+// COMMON PLUGIN INFO FUNCS
+char * CALLBACK PSEgetLibName(void)
+{
+ return _(libraryName);
+}
+
+unsigned long CALLBACK PSEgetLibType(void)
+{
+ return PSE_LT_SPU;
+}
+
+unsigned long CALLBACK PSEgetLibVersion(void)
+{
+ return (1 << 16) | (1 << 8);
+}
+
+char * SPUgetLibInfos(void)
+{
+ return _(libraryInfo);
+}
diff --git a/plugins/dfsound/spu.h b/plugins/dfsound/spu.h index 8d492485..8912684b 100644 --- a/plugins/dfsound/spu.h +++ b/plugins/dfsound/spu.h @@ -1,22 +1,21 @@ -/*************************************************************************** - 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 +/***************************************************************************
+ 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 diff --git a/plugins/dfsound/spucfg-0.1df/main.c b/plugins/dfsound/spucfg-0.1df/main.c index 8bea2b58..fc5976cb 100644 --- a/plugins/dfsound/spucfg-0.1df/main.c +++ b/plugins/dfsound/spucfg-0.1df/main.c @@ -1,274 +1,274 @@ -#include "config.h" - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -#include <glade/glade.h> -#include <gtk/gtk.h> - -#ifdef ENABLE_NLS -#include <libintl.h> -#include <locale.h> -#endif - -#define READBINARY "rb" -#define WRITEBINARY "wb" -#define CONFIG_FILENAME "dfsound.cfg" - -void SaveConfig(GtkWidget *widget, gpointer user_datal); - -/* This function checks for the value being outside the accepted range, - and returns the appropriate boundary value */ -static int set_limit (char *p, int len, int lower, int upper) -{ - int val = 0; - - if (p) - val = atoi(p + len); - - if (val < lower) - val = lower; - if (val > upper) - val = upper; - - return val; -} - -static void on_about_clicked (GtkWidget *widget, gpointer user_data) -{ - gtk_widget_destroy (widget); - exit (0); -} - -static void OnConfigClose(GtkWidget *widget, gpointer user_data) -{ - GladeXML *xml = (GladeXML *)user_data; - - gtk_widget_destroy(glade_xml_get_widget(xml, "CfgWnd")); - gtk_exit(0); -} - -int main(int argc, char *argv[]) -{ - GtkWidget *widget; - GladeXML *xml; - FILE *in; - char t[256]; - int len, val = 0; - char *pB, *p; - char cfg[255]; - -#ifdef ENABLE_NLS - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); -#endif - - if (argc != 2) { - printf ("Usage: cfgDFSound {ABOUT | CFG}\n"); - return 0; - } - - if (strcmp(argv[1], "CFG") != 0 && strcmp(argv[1], "ABOUT") != 0) { - printf ("Usage: cfgDFSound {ABOUT | CFG}\n"); - return 0; - } - - gtk_set_locale(); - gtk_init(&argc, &argv); - - if (strcmp(argv[1], "ABOUT") == 0) { - const char *authors[]= {"Pete Bernert and the P.E.Op.S. team", "Ryan Schultz", "Andrew Burton", NULL}; - widget = gtk_about_dialog_new (); - gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (widget), "dfsound PCSX Sound Plugin"); - gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (widget), "1.6"); - gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG (widget), authors); - gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (widget), "http://pcsx-df.sourceforge.net/"); - - g_signal_connect_data(GTK_OBJECT(widget), "response", - G_CALLBACK(on_about_clicked), NULL, NULL, G_CONNECT_AFTER); - - gtk_widget_show (widget); - gtk_main(); - - return 0; - } - - xml = glade_xml_new(DATADIR "dfsound.glade2", "CfgWnd", NULL); - if (!xml) { - g_warning("We could not load the interface!"); - return 255; - } - - strcpy(cfg, CONFIG_FILENAME); - - in = fopen(cfg, READBINARY); - if (in) { - pB = (char *)malloc(32767); - memset(pB, 0, 32767); - len = fread(pB, 1, 32767, in); - fclose(in); - } else { - pB = 0; - printf ("Error - no configuration file\n"); - /* TODO Raise error - no configuration file */ - } - - /* ADB TODO Replace a lot of the following with common functions */ - if (pB) { - strcpy(t, "\nVolume"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - val = set_limit (p, len, -1, 4) + 1; - } else val = 2; - - gtk_combo_box_set_active(GTK_COMBO_BOX (glade_xml_get_widget(xml, "cbVolume2")), val); - - if (pB) { - strcpy(t, "\nUseInterpolation"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - val = set_limit (p, len, 0, 3); - } else val = 2; - - gtk_combo_box_set_active(GTK_COMBO_BOX (glade_xml_get_widget(xml, "cbInterpolation2")), val); - - if (pB) { - strcpy(t, "\nXAPitch"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - val = set_limit (p, len, 0, 1); - } else val = 0; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkXASpeed")), val); - - if (pB) { - strcpy(t, "\nHighCompMode"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - val = set_limit (p, len, 0, 1); - } else val = 1; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkHiCompat")), val); - - if (pB) { - strcpy(t, "\nSPUIRQWait"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - - val = set_limit (p, len, 0, 1); - } else val = 1; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkIRQWait")), val); - - if (pB) { - strcpy(t, "\nDisStereo"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - - val = set_limit (p, len, 0, 1); - } else val = 0; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkDisStereo")), val); - - if (pB) { - strcpy(t, "\nFreqResponse"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - - val = set_limit (p, len, 0, 1); - } else val = 0; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkFreqResponse")), val); - - if (pB) { - strcpy(t, "\nUseReverb"); - p = strstr(pB, t); - if (p) { - p = strstr(p, "="); - len = 1; - } - val = set_limit (p, len, 0, 2); - } else val = 2; - - gtk_combo_box_set_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbReverb2")), val); - - if (pB) - free(pB); - - widget = glade_xml_get_widget(xml, "CfgWnd"); - g_signal_connect_data(GTK_OBJECT(widget), "destroy", - G_CALLBACK(SaveConfig), xml, NULL, 0); - - widget = glade_xml_get_widget(xml, "btn_close"); - g_signal_connect_data(GTK_OBJECT(widget), "clicked", - G_CALLBACK(OnConfigClose), xml, NULL, G_CONNECT_AFTER); - - gtk_main(); - return 0; -} - -void SaveConfig(GtkWidget *widget, gpointer user_data) -{ - GladeXML *xml = (GladeXML *)user_data; - FILE *fp; - int val; - - fp = fopen(CONFIG_FILENAME, WRITEBINARY); - if (fp == NULL) { - fprintf(stderr, "Unable to write to configuration file %s!\n", CONFIG_FILENAME); - gtk_exit(0); - } - - val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbVolume2"))); - fprintf(fp, "\nVolume = %d\n", val - 1); - - val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbInterpolation2"))); - fprintf(fp, "\nUseInterpolation = %d\n", val); - - val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkXASpeed"))); - fprintf(fp, "\nXAPitch = %d\n", val); - - val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkHiCompat"))); - fprintf(fp, "\nHighCompMode = %d\n", val); - - val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkIRQWait"))); - fprintf(fp, "\nSPUIRQWait = %d\n", val); - - val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkDisStereo"))); - fprintf(fp, "\nDisStereo = %d\n", val); - - val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkFreqResponse"))); - fprintf(fp, "\nFreqResponse = %d\n", val); - - val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbReverb2"))); - fprintf(fp, "\nUseReverb = %d\n", val); - - fclose(fp); - gtk_exit(0); -} +#include "config.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#include <locale.h>
+#endif
+
+#define READBINARY "rb"
+#define WRITEBINARY "wb"
+#define CONFIG_FILENAME "dfsound.cfg"
+
+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)
+{
+ int val = 0;
+
+ if (p)
+ val = atoi(p + len);
+
+ if (val < lower)
+ val = lower;
+ if (val > upper)
+ val = upper;
+
+ return val;
+}
+
+void on_about_clicked (GtkWidget *widget, gpointer user_data)
+{
+ gtk_widget_destroy (widget);
+ exit (0);
+}
+
+void OnConfigClose(GtkWidget *widget, gpointer user_data)
+{
+ GladeXML *xml = (GladeXML *)user_data;
+
+ gtk_widget_destroy(glade_xml_get_widget(xml, "CfgWnd"));
+ gtk_exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ GtkWidget *widget;
+ GladeXML *xml;
+ FILE *in;
+ char t[256];
+ int len, val = 0;
+ char *pB, *p;
+ char cfg[255];
+
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (argc != 2) {
+ printf ("Usage: cfgDFSound {ABOUT | CFG}\n");
+ return 0;
+ }
+
+ if (strcmp(argv[1], "CFG") != 0 && strcmp(argv[1], "ABOUT") != 0) {
+ printf ("Usage: cfgDFSound {ABOUT | CFG}\n");
+ return 0;
+ }
+
+ gtk_set_locale();
+ gtk_init(&argc, &argv);
+
+ if (strcmp(argv[1], "ABOUT") == 0) {
+ const char *authors[]= {"Pete Bernert and the P.E.Op.S. team", "Ryan Schultz", "Andrew Burton", NULL};
+ widget = gtk_about_dialog_new ();
+ gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (widget), "dfsound PCSX Sound Plugin");
+ gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (widget), "1.6");
+ gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG (widget), authors);
+ gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (widget), "http://pcsx-df.sourceforge.net/");
+
+ g_signal_connect_data(GTK_OBJECT(widget), "response",
+ G_CALLBACK(on_about_clicked), NULL, NULL, G_CONNECT_AFTER);
+
+ gtk_widget_show (widget);
+ gtk_main();
+
+ return 0;
+ }
+
+ xml = glade_xml_new(DATADIR "dfsound.glade2", "CfgWnd", NULL);
+ if (!xml) {
+ g_warning("We could not load the interface!");
+ return 255;
+ }
+
+ strcpy(cfg, CONFIG_FILENAME);
+
+ in = fopen(cfg, READBINARY);
+ if (in) {
+ pB = (char *)malloc(32767);
+ memset(pB, 0, 32767);
+ len = fread(pB, 1, 32767, in);
+ fclose(in);
+ } else {
+ pB = 0;
+ printf ("Error - no configuration file\n");
+ /* TODO Raise error - no configuration file */
+ }
+
+ /* ADB TODO Replace a lot of the following with common functions */
+ if (pB) {
+ strcpy(t, "\nVolume");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+ val = set_limit (p, len, -1, 4) + 1;
+ } else val = 2;
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX (glade_xml_get_widget(xml, "cbVolume2")), val);
+
+ if (pB) {
+ strcpy(t, "\nUseInterpolation");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+ val = set_limit (p, len, 0, 3);
+ } else val = 2;
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX (glade_xml_get_widget(xml, "cbInterpolation2")), val);
+
+ if (pB) {
+ strcpy(t, "\nXAPitch");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+ val = set_limit (p, len, 0, 1);
+ } else val = 0;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkXASpeed")), val);
+
+ if (pB) {
+ strcpy(t, "\nHighCompMode");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+ val = set_limit (p, len, 0, 1);
+ } else val = 1;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkHiCompat")), val);
+
+ if (pB) {
+ strcpy(t, "\nSPUIRQWait");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+
+ val = set_limit (p, len, 0, 1);
+ } else val = 1;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkIRQWait")), val);
+
+ if (pB) {
+ strcpy(t, "\nDisStereo");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+
+ val = set_limit (p, len, 0, 1);
+ } else val = 0;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkDisStereo")), val);
+
+ if (pB) {
+ strcpy(t, "\nFreqResponse");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+
+ val = set_limit (p, len, 0, 1);
+ } else val = 0;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "chkFreqResponse")), val);
+
+ if (pB) {
+ strcpy(t, "\nUseReverb");
+ p = strstr(pB, t);
+ if (p) {
+ p = strstr(p, "=");
+ len = 1;
+ }
+ val = set_limit (p, len, 0, 2);
+ } else val = 2;
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbReverb2")), val);
+
+ if (pB)
+ free(pB);
+
+ widget = glade_xml_get_widget(xml, "CfgWnd");
+ g_signal_connect_data(GTK_OBJECT(widget), "destroy",
+ G_CALLBACK(SaveConfig), xml, NULL, 0);
+
+ widget = glade_xml_get_widget(xml, "btn_close");
+ g_signal_connect_data(GTK_OBJECT(widget), "clicked",
+ G_CALLBACK(OnConfigClose), xml, NULL, G_CONNECT_AFTER);
+
+ gtk_main();
+ return 0;
+}
+
+void SaveConfig(GtkWidget *widget, gpointer user_data)
+{
+ GladeXML *xml = (GladeXML *)user_data;
+ FILE *fp;
+ int val;
+
+ fp = fopen(CONFIG_FILENAME, WRITEBINARY);
+ if (fp == NULL) {
+ fprintf(stderr, "Unable to write to configuration file %s!\n", CONFIG_FILENAME);
+ gtk_exit(0);
+ }
+
+ val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbVolume2")));
+ fprintf(fp, "\nVolume = %d\n", val - 1);
+
+ val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbInterpolation2")));
+ fprintf(fp, "\nUseInterpolation = %d\n", val);
+
+ val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkXASpeed")));
+ fprintf(fp, "\nXAPitch = %d\n", val);
+
+ val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkHiCompat")));
+ fprintf(fp, "\nHighCompMode = %d\n", val);
+
+ val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkIRQWait")));
+ fprintf(fp, "\nSPUIRQWait = %d\n", val);
+
+ val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkDisStereo")));
+ fprintf(fp, "\nDisStereo = %d\n", val);
+
+ val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "chkFreqResponse")));
+ fprintf(fp, "\nFreqResponse = %d\n", val);
+
+ val = gtk_combo_box_get_active(GTK_COMBO_BOX(glade_xml_get_widget(xml, "cbReverb2")));
+ fprintf(fp, "\nUseReverb = %d\n", val);
+
+ fclose(fp);
+ gtk_exit(0);
+}
diff --git a/plugins/dfsound/stdafx.h b/plugins/dfsound/stdafx.h index 5ba6c625..15ad99fa 100644 --- a/plugins/dfsound/stdafx.h +++ b/plugins/dfsound/stdafx.h @@ -1,66 +1,68 @@ -/*************************************************************************** - StdAfx.h - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#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" -#endif -#include <stdio.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <unistd.h> -#include <fcntl.h> -#ifdef USEOSS -#include <sys/soundcard.h> -#endif -#include <unistd.h> -#include <pthread.h> -#define RRand(range) (random()%range) -#include <string.h> -#include <sys/time.h> -#include <math.h> - -#undef CALLBACK -#define CALLBACK -#define DWORD unsigned long -#define LOWORD(l) ((unsigned short)(l)) -#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF)) - -#ifndef INLINE -#define INLINE inline -#endif - -#endif +/***************************************************************************
+ StdAfx.h - description
+ -------------------
+ begin : Wed May 15 2002
+ copyright : (C) 2002 by Pete Bernert
+ email : BlackDove@addcom.de
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. See also the license.txt file for *
+ * additional informations. *
+ * *
+ ***************************************************************************/
+
+#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"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef USEOSS
+#include <sys/soundcard.h>
+#endif
+#include <unistd.h>
+#include <pthread.h>
+#define RRand(range) (random()%range)
+#include <string.h>
+#include <sys/time.h>
+#include <math.h>
+
+#undef CALLBACK
+#define CALLBACK
+#define DWORD unsigned long
+#define LOWORD(l) ((unsigned short)(l))
+#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF))
+
+#ifndef INLINE
+#define INLINE inline
+#endif
+
+#endif
+
+#include "psemuxa.h"
diff --git a/plugins/dfsound/xa.c b/plugins/dfsound/xa.c index a0ba86e8..bec405bd 100644 --- a/plugins/dfsound/xa.c +++ b/plugins/dfsound/xa.c @@ -1,412 +1,408 @@ -/*************************************************************************** - xa.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -#include "stdafx.h" - -#include "xa.h" - -#define _IN_XA -#include <stdint.h> - -// will be included from spu.c -#ifdef _IN_SPU - -//////////////////////////////////////////////////////////////////////// -// XA GLOBALS -//////////////////////////////////////////////////////////////////////// - -xa_decode_t * xapGlobal=0; - -uint32_t * XAFeed = NULL; -uint32_t * XAPlay = NULL; -uint32_t * XAStart = NULL; -uint32_t * XAEnd = NULL; - -uint32_t XARepeat = 0; -uint32_t XALastVal = 0; - -uint32_t * CDDAFeed = NULL; -uint32_t * CDDAPlay = NULL; -uint32_t * CDDAStart = NULL; -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}; - -#define gvall0 gauss_window[gauss_ptr] -#define gvall(x) gauss_window[(gauss_ptr+x)&3] -#define gvalr0 gauss_window[4+gauss_ptr] -#define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)] -#endif - -long cdxa_dbuf_ptr; - -//////////////////////////////////////////////////////////////////////// -// MIX XA & CDDA -//////////////////////////////////////////////////////////////////////// - -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 - } - } - - 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; - } - } -} - -//////////////////////////////////////////////////////////////////////// -// small linux time helper... only used for watchdog -//////////////////////////////////////////////////////////////////////// - -#ifndef _WINDOWS - -unsigned long timeGetTime_spu() -{ - struct timeval tv; - gettimeofday(&tv, 0); // well, maybe there are better ways - return tv.tv_sec * 1000 + tv.tv_usec/1000; // to do that, but at least it works -} - -#endif - -//////////////////////////////////////////////////////////////////////// -// FEED XA -//////////////////////////////////////////////////////////////////////// - -INLINE void FeedXA(xa_decode_t *xap) -{ - int sinc,spos,i,iSize,iPlace; - - if(!bSPUIsOpen) return; - - xapGlobal = xap; // store info for save states - XARepeat = 100; // set up repeat - -#ifdef XA_HACK - iSize=((45500*xap->nsamples)/xap->freq); // get size -#else - iSize=((44100*xap->nsamples)/xap->freq); // get size -#endif - if(!iSize) return; // none? bye - - if(XAFeed<XAPlay) iPlace=XAPlay-XAFeed; // how much space in my buf? - else iPlace=(XAEnd-XAFeed) + (XAPlay-XAStart); - - if(iPlace==0) return; // no place at all - - //----------------------------------------------------// - if(iXAPitch) // pitch change option? - { - static DWORD dwLT=0; - static DWORD dwFPS=0; - static int iFPSCnt=0; - static int iLastSize=0; - static DWORD dwL1=0; - DWORD dw=timeGetTime_spu(),dw1,dw2; - - iPlace=iSize; - - dwFPS+=dw-dwLT;iFPSCnt++; - - dwLT=dw; - - if(iFPSCnt>=10) - { - if(!dwFPS) dwFPS=1; - dw1=1000000/dwFPS; - if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1; - else dwL1=dw1; - dw2=(xap->freq*100/xap->nsamples); - if((!dw1)||((dw2+100)>=dw1)) iLastSize=0; - else - { - iLastSize=iSize*dw2/dw1; - if(iLastSize>iPlace) iLastSize=iPlace; - iSize=iLastSize; - } - iFPSCnt=0;dwFPS=0; - } - else - { - if(iLastSize) iSize=iLastSize; - } - } - //----------------------------------------------------// - - spos=0x10000L; - sinc = (xap->nsamples << 16) / iSize; // calc freq by num / size - - if(xap->stereo) -{ - uint32_t * pS=(uint32_t *)xap->pcm; - uint32_t l=0; - - if(iXAPitch) - { - int32_t l1,l2;short s; - for(i=0;i<iSize;i++) - { - while(spos>=0x10000L) - { - l = *pS++; - spos -= 0x10000L; - } - - s=(short)LOWORD(l); - l1=s; - l1=(l1*iPlace)/iSize; - if(l1<-32767) l1=-32767; - if(l1> 32767) l1=32767; - s=(short)HIWORD(l); - l2=s; - l2=(l2*iPlace)/iSize; - if(l2<-32767) l2=-32767; - if(l2> 32767) l2=32767; - l=(l1&0xffff)|(l2<<16); - - *XAFeed++=l; - - if(XAFeed==XAEnd) XAFeed=XAStart; - if(XAFeed==XAPlay) - { - if(XAPlay!=XAStart) XAFeed=XAPlay-1; - break; - } - - spos += sinc; - } - } - else - { - for(i=0;i<iSize;i++) - { - while(spos>=0x10000L) - { - l = *pS++; - spos -= 0x10000L; - } - - *XAFeed++=l; - - if(XAFeed==XAEnd) XAFeed=XAStart; - if(XAFeed==XAPlay) - { - if(XAPlay!=XAStart) XAFeed=XAPlay-1; - break; - } - - spos += sinc; - } - } - } - else - { - unsigned short * pS=(unsigned short *)xap->pcm; - uint32_t l;short s=0; - - if(iXAPitch) - { - int32_t l1; - for(i=0;i<iSize;i++) - { - while(spos>=0x10000L) - { - s = *pS++; - spos -= 0x10000L; - } - l1=s; - - l1=(l1*iPlace)/iSize; - if(l1<-32767) l1=-32767; - if(l1> 32767) l1=32767; - l=(l1&0xffff)|(l1<<16); - *XAFeed++=l; - - if(XAFeed==XAEnd) XAFeed=XAStart; - if(XAFeed==XAPlay) - { - if(XAPlay!=XAStart) XAFeed=XAPlay-1; - break; - } - - spos += sinc; - } - } - else - { - for(i=0;i<iSize;i++) - { - while(spos>=0x10000L) - { - s = *pS++; - spos -= 0x10000L; - } - l=s; - - *XAFeed++=((l&0xffff)|(l<<16)); - - if(XAFeed==XAEnd) XAFeed=XAStart; - if(XAFeed==XAPlay) - { - if(XAPlay!=XAStart) XAFeed=XAPlay-1; - break; - } - - spos += sinc; - } - } - } -} - -//////////////////////////////////////////////////////////////////////// -// FEED CDDA -//////////////////////////////////////////////////////////////////////// - -unsigned int cdda_ptr; - -INLINE void FeedCDDA(unsigned char *pcm, int nBytes) -{ - while(nBytes>0) - { - if(CDDAFeed==CDDAEnd) CDDAFeed=CDDAStart; - while(CDDAFeed==CDDAPlay-1|| - (CDDAFeed==CDDAEnd-1&&CDDAPlay==CDDAStart)) - { -#ifdef _WINDOWS - if (!iUseTimer) Sleep(1); - else return; -#else - if (!iUseTimer) usleep(1000); - else return; -#endif - } - *CDDAFeed++=(*pcm | (*(pcm+1)<<8) | (*(pcm+2)<<16) | (*(pcm+3)<<24)); - nBytes-=4; - pcm+=4; - } -} - -#endif +/***************************************************************************
+ xa.c - description
+ -------------------
+ begin : Wed May 15 2002
+ copyright : (C) 2002 by Pete Bernert
+ email : BlackDove@addcom.de
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. See also the license.txt file for *
+ * additional informations. *
+ * *
+ ***************************************************************************/
+
+#include "stdafx.h"
+
+#define _IN_XA
+#include <stdint.h>
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// XA GLOBALS
+////////////////////////////////////////////////////////////////////////
+
+xa_decode_t * xapGlobal=0;
+
+uint32_t * XAFeed = NULL;
+uint32_t * XAPlay = NULL;
+uint32_t * XAStart = NULL;
+uint32_t * XAEnd = NULL;
+
+uint32_t XARepeat = 0;
+uint32_t XALastVal = 0;
+
+uint32_t * CDDAFeed = NULL;
+uint32_t * CDDAPlay = NULL;
+uint32_t * CDDAStart = NULL;
+uint32_t * CDDAEnd = NULL;
+
+int iLeftXAVol = 0x8000;
+int iRightXAVol = 0x8000;
+
+static int gauss_ptr = 0;
+static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+#define gvall0 gauss_window[gauss_ptr]
+#define gvall(x) gauss_window[(gauss_ptr+x)&3]
+#define gvalr0 gauss_window[4+gauss_ptr]
+#define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)]
+
+long cdxa_dbuf_ptr;
+
+////////////////////////////////////////////////////////////////////////
+// MIX XA & CDDA
+////////////////////////////////////////////////////////////////////////
+
+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
+ }
+ }
+
+ 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;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// small linux time helper... only used for watchdog
+////////////////////////////////////////////////////////////////////////
+
+#ifndef _WINDOWS
+
+unsigned long timeGetTime_spu()
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0); // well, maybe there are better ways
+ return tv.tv_sec * 1000 + tv.tv_usec/1000; // to do that, but at least it works
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////
+// FEED XA
+////////////////////////////////////////////////////////////////////////
+
+INLINE void FeedXA(xa_decode_t *xap)
+{
+ int sinc,spos,i,iSize,iPlace,vl,vr;
+
+ if(!bSPUIsOpen) return;
+
+ xapGlobal = xap; // store info for save states
+ XARepeat = 100; // set up repeat
+
+#ifdef XA_HACK
+ iSize=((45500*xap->nsamples)/xap->freq); // get size
+#else
+ iSize=((44100*xap->nsamples)/xap->freq); // get size
+#endif
+ if(!iSize) return; // none? bye
+
+ if(XAFeed<XAPlay) iPlace=XAPlay-XAFeed; // how much space in my buf?
+ else iPlace=(XAEnd-XAFeed) + (XAPlay-XAStart);
+
+ if(iPlace==0) return; // no place at all
+
+ //----------------------------------------------------//
+ if(iXAPitch) // pitch change option?
+ {
+ static DWORD dwLT=0;
+ static DWORD dwFPS=0;
+ static int iFPSCnt=0;
+ static int iLastSize=0;
+ static DWORD dwL1=0;
+ DWORD dw=timeGetTime_spu(),dw1,dw2;
+
+ iPlace=iSize;
+
+ dwFPS+=dw-dwLT;iFPSCnt++;
+
+ dwLT=dw;
+
+ if(iFPSCnt>=10)
+ {
+ if(!dwFPS) dwFPS=1;
+ dw1=1000000/dwFPS;
+ if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1;
+ else dwL1=dw1;
+ dw2=(xap->freq*100/xap->nsamples);
+ if((!dw1)||((dw2+100)>=dw1)) iLastSize=0;
+ else
+ {
+ iLastSize=iSize*dw2/dw1;
+ if(iLastSize>iPlace) iLastSize=iPlace;
+ iSize=iLastSize;
+ }
+ iFPSCnt=0;dwFPS=0;
+ }
+ else
+ {
+ if(iLastSize) iSize=iLastSize;
+ }
+ }
+ //----------------------------------------------------//
+
+ spos=0x10000L;
+ sinc = (xap->nsamples << 16) / iSize; // calc freq by num / size
+
+ if(xap->stereo)
+{
+ uint32_t * pS=(uint32_t *)xap->pcm;
+ uint32_t l=0;
+
+ if(iXAPitch)
+ {
+ int32_t l1,l2;short s;
+ for(i=0;i<iSize;i++)
+ {
+ while(spos>=0x10000L)
+ {
+ l = *pS++;
+ spos -= 0x10000L;
+ }
+
+ s=(short)LOWORD(l);
+ l1=s;
+ l1=(l1*iPlace)/iSize;
+ if(l1<-32767) l1=-32767;
+ if(l1> 32767) l1=32767;
+ s=(short)HIWORD(l);
+ l2=s;
+ l2=(l2*iPlace)/iSize;
+ if(l2<-32767) l2=-32767;
+ if(l2> 32767) l2=32767;
+ l=(l1&0xffff)|(l2<<16);
+
+ *XAFeed++=l;
+
+ if(XAFeed==XAEnd) XAFeed=XAStart;
+ if(XAFeed==XAPlay)
+ {
+ if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+ break;
+ }
+
+ spos += sinc;
+ }
+ }
+ else
+ {
+ for(i=0;i<iSize;i++)
+ {
+ while(spos>=0x10000L)
+ {
+ l = *pS++;
+ spos -= 0x10000L;
+ }
+
+ *XAFeed++=l;
+
+ if(XAFeed==XAEnd) XAFeed=XAStart;
+ if(XAFeed==XAPlay)
+ {
+ if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+ break;
+ }
+
+ spos += sinc;
+ }
+ }
+ }
+ else
+ {
+ unsigned short * pS=(unsigned short *)xap->pcm;
+ uint32_t l;short s=0;
+
+ if(iXAPitch)
+ {
+ int32_t l1;
+ for(i=0;i<iSize;i++)
+ {
+ while(spos>=0x10000L)
+ {
+ s = *pS++;
+ spos -= 0x10000L;
+ }
+ l1=s;
+
+ l1=(l1*iPlace)/iSize;
+ if(l1<-32767) l1=-32767;
+ if(l1> 32767) l1=32767;
+ l=(l1&0xffff)|(l1<<16);
+ *XAFeed++=l;
+
+ if(XAFeed==XAEnd) XAFeed=XAStart;
+ if(XAFeed==XAPlay)
+ {
+ if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+ break;
+ }
+
+ spos += sinc;
+ }
+ }
+ else
+ {
+ for(i=0;i<iSize;i++)
+ {
+ while(spos>=0x10000L)
+ {
+ s = *pS++;
+ spos -= 0x10000L;
+ }
+ l=s;
+
+ *XAFeed++=((l&0xffff)|(l<<16));
+
+ if(XAFeed==XAEnd) XAFeed=XAStart;
+ if(XAFeed==XAPlay)
+ {
+ if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+ break;
+ }
+
+ spos += sinc;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// FEED CDDA
+////////////////////////////////////////////////////////////////////////
+
+unsigned int cdda_ptr;
+
+INLINE void FeedCDDA(unsigned char *pcm, int nBytes)
+{
+ while(nBytes>0)
+ {
+ if(CDDAFeed==CDDAEnd) CDDAFeed=CDDAStart;
+ while(CDDAFeed==CDDAPlay-1||
+ (CDDAFeed==CDDAEnd-1&&CDDAPlay==CDDAStart))
+ {
+#ifdef _WINDOWS
+ if (!iUseTimer) Sleep(1);
+ else return;
+#else
+ if (!iUseTimer) usleep(1000);
+ else return;
+#endif
+ }
+ *CDDAFeed++=(*pcm | (*(pcm+1)<<8) | (*(pcm+2)<<16) | (*(pcm+3)<<24));
+ nBytes-=4;
+ pcm+=4;
+ }
+}
+
+#endif
|
