diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index af259422..92f5bca3 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,160 @@
+March 12, 2008 (Changes by Graf Zahl)
+- Bumped the minimum savegame version because the current version crashes
+ each time an old savegame is loaded.
+
+March 11, 2008
+- Removed lots of spc_* cvars that are no longer meaningful and changed
+ spc_amp from a x.4 fixed point number to a normal float.
+- Switched SPC playback from the external SNESAPU.DLL to Blargg's LGPL
+ snes_spc library. I've compiled it with the fast DSP rather than the
+ highly accurate one, since I didn't notice a meaningful difference between
+ the two in my limited testing. In short: SPC playback is now built in to
+ ZDoom. You don't need to download anything extra to make it work, and it
+ also works on Linux as well as Windows (though building with Linux is
+ currently untested).
+- Fixed: Stereo separation was calculated very wrongly when in 2D sound mode.
+
+March 10, 2008
+- EAX is a pain in the butt to get it to work reliably. Why? I can get the
+ reverb to work just fine with software, but with hardware, I hear nothing
+ special at all.
+- Fixed: Sounds apparently don't default to location 0,0,0 so I need to set
+ that explicitly for 2D sounds in 3D mode.
+- Fixed: I had forgotten to actually set the head relative flag for 2D sounds
+ played in 3D.
+- Fixed: Reverb was applied to digital music in software 3D mode. I tried
+ turning off reverb for 2D sounds too, but that turned it off for _all_
+ subsequent sounds and not that specific channel.
+
+March 9, 2008 (Changes by Graf Zahl)
+- fixed: StreamSong::SetPosition required a NULL pointer check.
+- fixed: The release build still linked to the old FMOD version.
+- fixed: SPCSong only works for Win32 so its definition must be excluded for Linux.
+
+March 8, 2008
+- Fixed: If you wanted to make cleandep with MinGW, you had to specifically
+ specify Makefile.mingw as the makefile to use.
+- Added a normalizer to the OPL synth. It helped bring up the volume a little,
+ but not nearly as much as I would have liked.
+- Removed MIDI Mapper references. It doesn't work with the stream API, and
+ it doesn't really exist on NT kernels, either.
+- Reworked music volume: Except for MIDI, all music volume is controlled
+ through GSnd and not at the individual song level.
+- Removed the mididevice global variable.
+- Added back support for custom streams.
+
+March 7, 2008
+- I think the new FMOD Ex code should support everything the old FMOD 3 code
+ did, sans custom streaming sounds.
+- Removed snd_midivolume. Now that all music uses a linear volume scale,
+ there's no need for two separate music volume controls.
+- Increased snd_samplerate default up to 48000.
+- Added snd_format, defaulting to "PCM-16".
+- Added snd_speakermode, defaulting to "Auto".
+- Replaced snd_fpu with snd_resampler, defaulting to "Linear".
+- Bumped the snd_channels default up from a pitiful 12 to 32.
+- Changed snd_3d default to true. The new cvar snd_hw3d determines if
+ hardware 3D support is used and default to false.
+- Removed the libFLAC source, since FMOD Ex has native FLAC support.
+- Removed the altsound code, since it was terribly gimped in comparison to
+ the FMOD code. Its original purpose was to have been as a springboard for
+ writing a non-FMOD sound system for Unix-y systems, but that never
+ happened.
+- Finished preliminary FMOD Ex support.
+
+March 6, 2008
+- Fixed: If P_BounceWall() can't find a wall when it does its trace, but it
+ was entered because a line blocked the projectile, then it should still use
+ that blocking line for the bounce.
+- The full master volume SysEx is now always sent to the MIDI device, even if
+ it seems to have a working volume control.
+- Renamed music_midi_stream.cpp to music_midi_base.cpp.
+- Moved the WinMM MIDI code into a new container class.
+- Fixed: StatusBar and screen need to be barriered, because they are roots
+ but can also change at runtime.
+- Fixed: Objects that are forcibly deleted outside the GC need to be removed
+ from the gray list too.
+
+March 5, 2008
+- Fixed: Thinkers needed write barriers when they were removed from their
+ lists.
+- Fixed: DLevelScript::Link() and Unlink() needed write barriers.
+
+March 4, 2008
+- Moved the identical code between the MUS and MIDI streamers into a new base
+ class so that all the low-level details of MIDI streaming are kept in
+ one place.
+- Converted the SMF MIDI playback to use the same MIDI streams as MUS
+ playback.
+- Moved MUS playback back into its own thread so that it can continue
+ uninterrupted if the main thread is too busy to service it in a timely
+ manner.
+- Fixed: The MEVT_* values are not defined shifted into their spot for a
+ MIDIEVENT, so I need to do it myself.
+- Fixed: Pausing a MUS and then changing snd_midivolume caused the paused
+ notes to become audible.
+
+March 3, 2008
+- Changed MUS playback to use MIDI streams, like it did during the early days
+ of ZDoom, except now the entire song isn't prebuffered in large chunks, so
+ I can insert MIDI events into the playback with fairly low latency. This
+ should offer more precise timing than the combination of low-level MIDI and
+ WaitForMultipleObjects timeouts that it replaces.
+- Fixed: PTR_BounceTraverse only checked for projectiles that were too
+ high to pass through two-sided lines, but not ones that were too low.
+- Fixed: SBARINFO couldn't detect the extreme death damage type for the
+ player face animation.
+
+March 1, 2008 (Changes by Graf Zahl)
+- fixed: A_CountdownArg used 0 based indices although all uses of it assumed
+ it is 1-based.
+- added MF5_DONTRIP flag.
+- added CheckActorFloorTexture, CheckActorCeilingTexture and
+ GetActorLightLevel ACS functions.
+- added IF_ADDITIVETIME flag to create powerups that add their duration
+ to the one of the currently active item of the same type.
+- fixed: bouncecount wasn't decreased when bouncing on walls.
+- Added MF5_ALWAYSRESPAWN and MF5_NEVERRESPAWN flags that selectively
+ enable or disable monster respawning regardless of skill setting.
+- Prettified deprecated flag handling.
+- Fixed: When starting a level while the music has been paused S_Start has
+ to stop the old music so the new one always starts at the beginning.
+- Fixed:: AActor::master was not serialized.
+- Fixed: Sound targets pointing to dead players should be cleared before
+ respawning the player.
+- Fixed: When the DONTMOVE flag is set A_Chase must not call P_NewChaseDir.
+- Changed PowerupGiver initialization so that the actual powerup class is looked
+ up during postprocessing.
+- Gave Strife's instant death sector type its own damage type.
+
+February 29, 2008
+- Changed the default GC parameters to make it run more aggressively.
+- Added NULL checks to the GC::WriteBarrier() wrappers.
+- Fixed: The DObject destructor set GC::SweepPos incorrectly if it pointed
+ at that object being deleted.
+- Added "soft roots" to the garbage collector. These are similar in
+ purpose to setting the OF_Fixed bit, but they behave as full-fledged roots
+ as far as the collector is concerned, so any objects reachable through
+ them will not be collected.
+- Added support for the OF_Fixed bit. This is ONLY for simple objects which
+ do not contain pointers to other objects, because all it does is prevent
+ an object that is otherwise dead from being freed. It does not include
+ the object in the propagate stage if it is unreachable, so any objects
+ that are only reachable through it will still be freed unless they are
+ also fixed.
+- Fixed: R_SetupAddClampCol() checked add4cols' memory instead of
+ adclamp4cols' memory when deciding if it should skip modification.
+
+February 28, 2008
+- Fixed: The colormap changes by column, so the assembly rt_* routines need
+ to be setup for every column group, not once per image.
+- Fixed: rt_Translate1col() had a couple of bugs.
+
+February 27, 2008
+- Added assembly versions of rt_add4cols and rt_addclamp4cols.
+- Added a read barrier to FArchive::SerializeObject() to make sure that
+ objects that are being euthanized are not stored in the archive.
+
February 26, 2008
- Added an assembly version of rt_shaded4cols, since that's the main decal
drawing function. The most improvement came from being able to turn some
@@ -11,12 +168,54 @@ February 26, 2008
the translation in one step and the drawing in another. This lets me call
the untranslated drawer to do the real drawing instead of mostly duplicating
them. Performance wise, there is practically no difference from before.
+- Fixed: Using +stat from the command line caused a crash.
+- More write barriers and pointer declarations. Here are rules I've come up
+ with so far for when write barriers can be ommitted:
+ * Initializing pointers for a newly created object: A new object can never
+ black, so none of its pointers that get set by the constructor (or code
+ just following its creation) need to be behind write barriers.
+ * Creating a new thinker and storing it in a pointer: The thinker
+ constructor already puts it behind a write barrier when it links it into
+ the thinker list, so you don't need to do it again.
+ * As a corollary of the above: No pointers to thinkers should need to be
+ behind write barriers, because it is incorrect to have a thinker that is
+ live but not in a thinker list. I realized this while I was going through
+ the long list of actor target references, so there are some write barriers
+ for those in place already, since they don't hurt to have them around.
+ As a consequence of the last point, I think I'm done placing write barriers
+ in all their necessary places without even touching most of the code. I'm
+ going to let it sit like this for a few days to see if I can think of a
+ counter-example where a live thinker wouldn't be in a thinker list, but it
+ feels correct to me right now.
February 25, 2008 (Changes by Graf Zahl)
- Fixed: The DECORATE expression evaluator's random function could produce
incorrect results for ranges > 255. Changed so that FRandom's default
implementation is used instead.
+February 24, 2008
+- Fixed: DDecalFader::Tick() still referenced TheDecal even after destroying
+ it.
+- Added write barriers around the thinker list insertions in dthinker.cpp.
+
+February 23, 2008 (Changes by Graf Zahl)
+- Fixed: AWeaponPiece didn't declare its FullWeapon pointer as such.
+- Fixed: Hexen's random player class skill menu was missing the '$' indicating
+ a string table identifier.
+- Fixed: DCorpsePointer::Serialize didn't call the super method.
+
+February 22, 2008
+- Moved DSeqNode's and its children's destructor code into their Destroy()
+ methods.
+- Moved DSectorEffect's destructor code into a Destroy() method.
+- Changed the myoffsetof definition to match DECLARE_POINTER's because GCC
+ started complaining about its use in p_setup.cpp, though I'm not sure why.
+- Added a pointer list to DACSThinker so that the collector can find scripts
+ instead of thinking they're all dead.
+- Added read barriers to sector_t's pointers.
+- Used the HAS_OBJECT_POINTERS macros to locate places to place TObjPtrs to
+ form read barriers.
+
February 21, 2008
- Fixed: DThinker::SerializeAll() did not serialize any thinkers in the
FreshThinkers lists. These thinkers would still be saved in the savegame if
@@ -34,10 +233,30 @@ February 21, 2008
work for GCC (and presumably VC++, though I never ran into that case with
it) because it tried to stringify something that wasn't a macro argument.
+February 20, 2008
+- Changed the DHUDMessage deletions into destroys.
+- Added read barriers to the pointers inside AActor.
+- Split the AActor::LastLook union into its constituent parts.
+
February 20, 2008 (Changes by Graf Zahl)
- Added a modified version of Karate Chris's submission for killing specific
monsters with the 'Kill' ccmd.
+February 19, 2008
+- Added a rough initial garbage collector, based largely on Lua's. There are
+ no write or read barriers in place yet, so it's not a very close
+ approximation of the pointer cleaning code that was in place before, but it
+ does collect stuff. But guess what! Nuts.wad ran at 1-2 FPS before. Now
+ it's way, way more and easily stays above 35 FPS. The performance hit when
+ the GC is running is pretty minimal compared to the old way, which was in
+ many ways like a full garbage collection every tic.
+- Changed the status bars into DObjects.
+- Fixed: FBaseStatusBar::Tick() deleted hud messages instead of destroying
+ them.
+- Changed the global Args object to a pointer to one.
+- Changed several DArgs deletions into destroys.
+- Removed DBoundingBox from the DObject hierarchy.
+
February 18, 2008
- Added vid_refreshrate cvar to override Windows' automatic refresh rate
selection.
@@ -632,7 +851,7 @@ January 1, 2008
- Fixed a D3D memory leak on every frame in windowed mode and the same thing
for the screen wipes. Note to self: If it's an interface, be sure to
Release it, because it will be AddRef'ed before being returned to you.
-- Moved the BlendView() call out of FBaseStatusBar::Draw() so that it can be
+- Moved the BlendView() call out of DBaseStatusBar::Draw() so that it can be
applied before copying the 3D scene to the screen underneath the 2D parts.
- Restored the console's darkening level to its old table-based amount.
- Fixed D3DFB::SetColorOverlay()'s incorrect calculations.
@@ -4330,7 +4549,7 @@ April 18, 2006 (Changes by Graf Zahl)
fighting the programmer.
April 18, 2006
-- Fixed: FBaseStatusBar::DrBNumber() should behave like Doom's
+- Fixed: DBaseStatusBar::DrBNumber() should behave like Doom's
STlib_drawNum(), and FDoomStatusBarTexture::DrawToBar() should add
the textures left offset to the x coordinate before drawing.
These fix Twice Risen's status bar.
@@ -5143,7 +5362,7 @@ February 11, 2005
February 10, 2005
- Fixed: The screen fading ACS commands did not work when viewing through a camera
- because FBaseStatusBar::BlendView() did not think there was a player to get those
+ because DBaseStatusBar::BlendView() did not think there was a player to get those
values from. Now, the blend comes from either the player viewing from or the
console player if the current view is not from a player.
- Fixed: Returning to a previously visited level in a hub with fewer players than you
@@ -5318,7 +5537,7 @@ January 26, 2005
Unfortunately, I didn't bump the demo version between 2.0.63a and 2.0.90, so
anything pre-2.0.97 won't play with 2.0.97, even though there's probably
no changes that would cause desynching between 96 and 97. Oh well.
-- Fixed FBaseStatusBar::DrBNumber(Outer) to work when some of the big number
+- Fixed DBaseStatusBar::DrBNumber(Outer) to work when some of the big number
graphics are missing.
January 15, 2005
@@ -5494,7 +5713,7 @@ December 18, 2004
on top of the next digit.
- Fixed: The Doom HUD drew the selected inventory item on top of the secondary
ammo if both were present.
-- Moved the DrawPowerups() call out of FBaseStatusBar::Draw() and into
+- Moved the DrawPowerups() call out of DBaseStatusBar::Draw() and into
DrawTopStuff(), so the Doom HUD key display won't cover them up
- Fixed: If you started flying and then pressed the land key, you wouldn't be
able to fly again until the Wings of Wrath wore off. This was done by changing
@@ -6350,7 +6569,7 @@ July 15, 2004
- Updated to FMOD 3.73.
July 14, 2004
-- Moved the full-screen HUD coordinate logic out of FBaseStatusBar and into
+- Moved the full-screen HUD coordinate logic out of DBaseStatusBar and into
DCanvas::DrawTexture. This is so that powerups can draw their status icons
themselves without needing to hook into the status bar.
- Reimplemented the Tome of Power.
@@ -6919,7 +7138,7 @@ December 12, 2003
up okay in Doom.)
- Added a fix to the blockmap tracers where all the blocks along the trace are
crossed on their corners.
-- Fixed a potential crash in FBaseStatusBar::DrawMessages() when a HUD message
+- Fixed a potential crash in DBaseStatusBar::DrawMessages() when a HUD message
removes itself as part of its drawing process.
- Fixed: A few of the Strife weapons were erroneously defined with WIF_NOALERT.
- Fixed: Sky textures defaulted to 0 if a map had a MAPINFO that did not
@@ -7067,7 +7286,7 @@ November 23, 2003
things spawned afterward.
- Added calls to SetWindowLongPtr to take away the window's border in
fullscreen mode and put it back in windowed mode.
-- Fixed FBaseStatusBar::RepositionCoords() so that LilWhiteMouse's Chosen
+- Fixed DBaseStatusBar::RepositionCoords() so that LilWhiteMouse's Chosen
ammo icons appear on the fullscreen HUD again.
November 21, 2003
@@ -7799,7 +8018,7 @@ July 30, 2003
- Added nonexistant texture check to the warping texture setup.
July 28, 2003
-- Removed the ScaleCopy canvas from FBaseStatusBar, because everything is now
+- Removed the ScaleCopy canvas from DBaseStatusBar, because everything is now
drawn to the screen directly even when the status bar is scaled.
- Changed the pitch calculations in the DSimpleCanvas constructor to provide
better wall drawing performance at all resolutions.
@@ -10941,7 +11160,7 @@ October 23, 2001
torch lighting up the entire sky, so skies should ignore the setting of
fixedlightlev.
- Fixed the scaled status bar. I was simply forgetting to add the image
- offsets in FBaseStatusBar::DrawImage() before calling CopyToScreen().
+ offsets in DBaseStatusBar::DrawImage() before calling CopyToScreen().
- Added code to let decals move with sliding and rotating polyobjects.
- Fixed: The wait console command waited one tic too few.
- Fixed a resizing problem with playing movies windowed.
@@ -11761,7 +11980,7 @@ April 7, 2001
big ammo count when switching to a weapon that does not use ammo.
- Fixed: At certain screen heights, scaled status bars would not touch the
bottom of the screen because of inaccuracy in the calculation of ::ST_Y
- in FBaseStatusBar::SetScaled().
+ in DBaseStatusBar::SetScaled().
- Added separate pickup sounds for health, armor, and ammo items.
- Improved sound link resolution in S_StartSound() so that regular sounds
can alias player sounds.
diff --git a/gzdoom.sln b/gzdoom.sln
index 19ece822..e079b135 100644
--- a/gzdoom.sln
+++ b/gzdoom.sln
@@ -11,8 +11,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zdoom", "gzdoom.vcproj", "{
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcproj", "{F9D9E7D4-E1A2-4866-9E85-B1B14137EE63}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FLAC", "FLAC\FLAC.vcproj", "{873F2EEA-24DF-454C-B245-CB9738BA993E}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lemon", "tools\lemon\lemon.vcproj", "{0F80ACBF-460E-44F0-B28E-B3272D1774A7}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "re2c", "tools\re2c\re2c.vcproj", "{667D2EE7-C357-49E2-9BAB-0A4A45F0F76E}"
@@ -37,10 +35,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dehsupp", "tools\dehsupp\de
{0F80ACBF-460E-44F0-B28E-B3272D1774A7} = {0F80ACBF-460E-44F0-B28E-B3272D1774A7}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updaterevision", "tools\updaterevision\updaterevision.vcproj", "{6077B7D6-349F-4077-B552-3BC302EF5859}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg-6b", "jpeg-6b\jpeg-6b.vcproj", "{AC3F5340-40CB-4C3A-8AA7-CB7158DB4466}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fixrtext", "tools\fixrtext\fixrtext.vcproj", "{DA47396F-60C1-4BDE-A977-7F7DE461CF77}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "snes_spc", "snes_spc\snes_spc.vcproj", "{E83FD370-2E72-4D4C-9427-FF9D9DED1E88}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "brightmaps", "wadsrc_bm\brightmaps.vcproj", "{087B206F-F49E-4EFB-92CB-E1F6E32D1278}"
ProjectSection(ProjectDependencies) = postProject
{24A19C02-F041-4AB0-A1A1-02E1E88EDBD3} = {24A19C02-F041-4AB0-A1A1-02E1E88EDBD3}
diff --git a/gzdoom.vcproj b/gzdoom.vcproj
index dcd7a4fc..af410061 100644
--- a/gzdoom.vcproj
+++ b/gzdoom.vcproj
@@ -28,6 +28,8 @@
>
+
+
diff --git a/snes_spc/Makefile b/snes_spc/Makefile
new file mode 100644
index 00000000..c32c06cc
--- /dev/null
+++ b/snes_spc/Makefile
@@ -0,0 +1,52 @@
+# Makefile for snes_spc, derived from zlib/Makefile.mgw.
+
+ifeq (Windows_NT,$(OS))
+ CMD=1
+endif
+ifeq ($(findstring msys,$(shell sh --version 2>nul)),msys)
+ CMD=0
+endif
+
+STATICLIB = libsnes_spc.a
+
+CCDV = @../ccdv
+
+CC = gcc
+CFLAGS = $(LOC) -O3 -Wall -fomit-frame-pointer
+
+LD = $(CC)
+LDFLAGS = $(LOC) -s
+
+AR = ar
+ARFLAGS = rcs
+
+OBJS = snes_spc/dsp.o snes_spc/SNES_SPC.o snes_spc/SNES_SPC_misc.o snes_spc/SNES_SPC_state.o \
+ snes_spc/spc.o snes_spc/SPC_DSP.o snes_spc/SPC_Filter.o
+
+all: $(STATICLIB)
+
+.cpp.o:
+ $(CCDV) $(CC) $(CFLAGS) -c -o $@ $<
+
+$(STATICLIB): $(OBJS)
+ $(CCDV) $(AR) $(ARFLAGS) $@ $(OBJS)
+
+
+.PHONY: clean
+
+clean:
+ifeq (0,$(CMD))
+ rm -f $(STATICLIB)
+ rm -f snes_spc/*.o
+else
+ -del /q /f $(STATICLIB) 2>nul
+ -del /q /f snes_spc\*.o 2>nul
+endif
+
+dsp.o: snes_spc/dsp.cpp snes_spc/dsp.h snes_spc/SPC_DSP.h
+SNES_SPC.o: snes_spc/SNES_SPC.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h
+SNES_SPC_misc.o: snes_spc/SNES_SPC_misc.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h
+SNES_SPC_state.o: snes_spc/SNES_SPC_state.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h
+spc.o: snes_spc/spc.cpp snes_spc/spc.h snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h snes_spc/SPC_Filter.h
+SPC_DSP.o: snes_spc/SPC_DSP.cpp snes_spc/SPC_DSP.h
+SPC_Filter.o: snes_spc/SPC_Filter.cpp snes_spc/SPC_Filter.h
diff --git a/snes_spc/changes.txt b/snes_spc/changes.txt
new file mode 100644
index 00000000..33661832
--- /dev/null
+++ b/snes_spc/changes.txt
@@ -0,0 +1,107 @@
+snes_spc Change Log
+-------------------
+
+snes_spc 0.9.0
+--------------
+- Improved documentation
+
+- SPC: Added spc_skip() function for quickly seeking in an SPC music
+file. Runs 3-4x faster than normal playback using the fast DSP (or about
+43-60X real-time on my 400 MHz Mac).
+
+- SPC: Added spc_set_tempo() to change tempo of SPC music playback.
+
+- SPC: Sample generation is now corrected to generate exactly one pair
+of samples every 32 clocks without exception. Before it could generate a
+few samples more or less depending on how far ahead or behind DSP was at
+the moment.
+
+- SPC: Changed spc_reset() and spc_soft_reset() to also reset output
+buffer (see spc.h).
+
+- SPC: Fixed minor timer counting bug.
+
+- SPC: Stack pointer wrap-around is now emulated (and without any
+noticeable performance hit).
+
+- SPC: Runs about 5% faster due to various optimizations.
+
+- SPC: Found way to make fast DSP register accesses cycle-accurate in
+most cases, without reducing performance. Allows fast DSP to pass most
+of my validation tests.
+
+- DSP: Added surround disable support to fast DSP again.
+
+- DSP: Improved voice un-muting to take effect immediately on fast DSP.
+
+- DSP: Noise shift register now starts at 0x4000 instead of 0x4001 as it
+incorrectly did before.
+
+- Converted library to C++ code internally. A C interface is still
+included in spc.h and dsp.h. Note that these are different than the
+previous interface, so your code will require minor changes:
+
+ Old SPC code New SPC code
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #include "spc/spc.h" #include "snes_spc/spc.h"
+
+ snes_spc_t* spc; SNES_SPC* spc;
+ spc = malloc( sizeof (snes_spc_t) ); spc = spc_new();
+ spc_init( spc );
+
+ spc_end_frame( time ); spc_end_frame( spc, time );
+ /* etc. */
+
+ /* done using SPC */ spc_delete( spc );
+
+
+ Old DSP code New DSP code
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ #include "spc/spc_dsp.h" #include "snes_spc/dsp.h"
+
+ spc_dsp_init( ram ); SPC_DSP* dsp;
+ dsp = spc_dsp_new();
+ spc_dsp_init( dsp, ram );
+
+ spc_dsp_run( count ); spc_dsp_run( dsp, count );
+ /* etc. */
+
+ /* done using DSP */ spc_dsp_delete( dsp );
+
+
+snes_spc 0.8.0
+--------------
+- Added several demos
+
+- Added high-pass/low-pass filter to better match SNES sound
+
+- Added save state functionality for SPC and accurate DSP (but not fast
+DSP)
+
+- Added emulation of reset switch on NES (soft reset)
+
+- Made source more compatible with pre-C99 compilers by eliminating
+mid-block declarations
+
+- SPC: Many S-SMP accuracy improvements, mostly in memory access times
+
+- SPC: S-SMP speed improvements
+
+- SPC: Added SPC load/save functions and KON checking to help trim
+silence from beginning
+
+- SPC: Changed spc_init() to have you allocate most of the memory used
+by the library so you have more control over it
+
+- DSP: New highly accurate DSP and faster version derived from same code
+
+- DSP: Changed prefix from dsp_ to spc_dsp_. Your DSP code will require
+changes.
+
+- DSP: Removed surround disable and gain. Gain can now be done with the
+dsp_filter module, and surround disable will probably only be
+implemented in the fast DSP at some point.
+
+- DSP: Changed interface to work in clocks rather than samples,
+necessary for the new accurate DSP. Sample output is now done with
+separate functions. Your DSP code will require changes.
diff --git a/snes_spc/demo/benchmark.c b/snes_spc/demo/benchmark.c
new file mode 100644
index 00000000..6b91faaf
--- /dev/null
+++ b/snes_spc/demo/benchmark.c
@@ -0,0 +1,58 @@
+/* Measures performance of SPC emulator. Takes about 4 seconds.
+NOTE: This assumes that the program is getting all processor time; you might need to
+arrange for this or else the performance will be reported lower than it really is.
+
+Usage: benchmark [test.spc]
+*/
+
+#include "snes_spc/spc.h"
+
+#include "demo_util.h" /* error(), load_file() */
+#include
+
+clock_t start_timing( int seconds );
+
+int main( int argc, char** argv )
+{
+ /* Load SPC */
+ long spc_size;
+ void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size );
+ SNES_SPC* snes_spc = spc_new();
+ if ( !snes_spc ) error( "Out of memory" );
+ spc_load_spc( snes_spc, spc, spc_size );
+ free( spc );
+
+ {
+ /* Repeatedly fill buffer for 4 seconds */
+ int const seconds = 4;
+ #define BUF_SIZE 4096
+ clock_t end = start_timing( seconds );
+ int count = 0;
+ while ( clock() < end )
+ {
+ static short buf [BUF_SIZE];
+ error( spc_play( snes_spc, BUF_SIZE, buf ) );
+ count++;
+ }
+
+ /* Report performance based on how many buffer fills were possible */
+ {
+ double rate = (double) count * BUF_SIZE / (spc_sample_rate * 2 * seconds);
+ printf( "Performance: %.3fx real-time, or %.0f%% CPU for normal rate\n",
+ rate, 100.0 / rate );
+ }
+ }
+
+ return 0;
+}
+
+/* Synchronizes with host clock and returns clock() time that is duration seconds from now */
+clock_t start_timing( int duration )
+{
+ clock_t clock_dur = duration * CLOCKS_PER_SEC;
+ clock_t time = clock();
+ while ( clock() == time ) { }
+ if ( clock() - time > clock_dur )
+ error( "Insufficient clock() time resolution" );
+ return clock() + clock_dur;
+}
diff --git a/snes_spc/demo/comm.c b/snes_spc/demo/comm.c
new file mode 100644
index 00000000..4198a2de
--- /dev/null
+++ b/snes_spc/demo/comm.c
@@ -0,0 +1,70 @@
+/* Communicates with SPC the way the SNES would.
+
+Note: You'll need an "spc_rom.h" file that contains the 64-byte IPL ROM contents */
+
+#include "snes_spc/spc.h"
+
+#include "demo_util.h"
+#include
+#include
+
+static SNES_SPC* snes_spc;
+
+/* Make port access more convenient. Fakes time by simply incrementing it each call. */
+static spc_time_t stime;
+static int pread ( int port ) { return spc_read_port( snes_spc, stime++, port ); }
+static void pwrite( int port, int data ) { spc_write_port( snes_spc, stime++, port, data ); }
+
+static unsigned char const spc_rom [spc_rom_size] = {
+ /* ROM data not provided with emulator */
+ #include "spc_rom.h"
+};
+
+int main()
+{
+ int i;
+
+ /* Data to upload */
+ static unsigned char const data [4] = "\xFA\xDE\xD1";
+ unsigned const data_addr = 0xF5; /* second I/O port */
+
+ snes_spc = spc_new();
+ if ( !snes_spc ) error( "Out of memory" );
+ spc_init_rom( snes_spc, spc_rom );
+ spc_reset( snes_spc );
+
+ /* Simulate reads and writes that SNES code would do */
+
+ /* Wait for SPC to be ready */
+ while ( pread( 0 ) != 0xAA || pread( 1 ) != 0xBB ) { }
+
+ /* Start address */
+ pwrite( 2, data_addr & 0xFF );
+ pwrite( 3, data_addr >> 8 );
+
+ /* Tell SPC to start transfer and wait for acknowledgement */
+ pwrite( 0, 0xCC );
+ pwrite( 1, 0x01 );
+ while ( pread( 0 ) != 0xCC ) { }
+
+ /* Send each byte and wait for acknowledgement */
+ for ( i = 0; i < 4; i++ )
+ {
+ printf( "%02X ", data [i] );
+ pwrite( 1, data [i] );
+ pwrite( 0, i );
+ while ( pread( 0 ) != i ) { }
+ }
+ printf( "\n" );
+
+ /* Verify that data was transferred properly */
+ for ( i = 0; i < 3; i++ )
+ printf( "%02X ", pread( i + 1 ) );
+ printf( "\n" );
+
+ printf( "Cycles: %ld\n", (long) stime );
+
+ spc_delete( snes_spc );
+
+ return 0;
+}
diff --git a/snes_spc/demo/demo_util.c b/snes_spc/demo/demo_util.c
new file mode 100644
index 00000000..61f250d7
--- /dev/null
+++ b/snes_spc/demo/demo_util.c
@@ -0,0 +1,57 @@
+#include "demo_util.h"
+
+#include
+#include
+#include
+#include
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+unsigned char* load_file( const char* path, long* size_out )
+{
+ size_t size;
+ unsigned char* data;
+
+ FILE* in = fopen( path, "rb" );
+ if ( !in ) error( "Couldn't open file" );
+
+ fseek( in, 0, SEEK_END );
+ size = ftell( in );
+ if ( size_out )
+ *size_out = size;
+ rewind( in );
+
+ data = (unsigned char*) malloc( size );
+ if ( !data ) error( "Out of memory" );
+
+ if ( fread( data, 1, size, in ) < size ) error( "Couldn't read file" );
+ fclose( in );
+
+ return data;
+}
+
+void write_file( const char* path, void const* in, long size )
+{
+ FILE* out = fopen( path, "wb" );
+ if ( !out ) error( "Couldn't create file" );
+ if ( (long) fwrite( in, 1, size, out ) < size ) error( "Couldn't write file" );
+ fclose( out );
+}
+
+void error( const char* str )
+{
+ if ( str )
+ {
+ fprintf( stderr, "Error: %s\n", str );
+ exit( EXIT_FAILURE );
+ }
+}
diff --git a/snes_spc/demo/demo_util.h b/snes_spc/demo/demo_util.h
new file mode 100644
index 00000000..d52e571b
--- /dev/null
+++ b/snes_spc/demo/demo_util.h
@@ -0,0 +1,31 @@
+/* General-purpose utilities used by demos */
+
+/* snes_spc 0.9.0 */
+#ifndef DEMO_UTIL_H
+#define DEMO_UTIL_H
+
+/* commonly used headers */
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* If str is not NULL, prints it and exits program, otherwise returns */
+void error( const char* str );
+
+/* Loads file and returns pointer to data in memory, allocated with malloc().
+If size_out != NULL, sets *size_out to size of data. */
+unsigned char* load_file( const char* path, long* size_out );
+
+/* Writes data to file */
+void write_file( const char* path, void const* in, long size );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/snes_spc/demo/play_spc.c b/snes_spc/demo/play_spc.c
new file mode 100644
index 00000000..f3e4d5ae
--- /dev/null
+++ b/snes_spc/demo/play_spc.c
@@ -0,0 +1,57 @@
+/* Records SPC into wave file. Uses dsp_filter to give more authentic sound.
+
+Usage: play_spc [test.spc]
+*/
+
+#include "snes_spc/spc.h"
+
+#include "wave_writer.h"
+#include "demo_util.h" /* error(), load_file() */
+
+int main( int argc, char** argv )
+{
+ /* Create emulator and filter */
+ SNES_SPC* snes_spc = spc_new();
+ SPC_Filter* filter = spc_filter_new();
+ if ( !snes_spc || !filter ) error( "Out of memory" );
+
+ /* Load SPC */
+ {
+ /* Load file into memory */
+ long spc_size;
+ void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size );
+
+ /* Load SPC data into emulator */
+ error( spc_load_spc( snes_spc, spc, spc_size ) );
+ free( spc ); /* emulator makes copy of data */
+
+ /* Most SPC files have garbage data in the echo buffer, so clear that */
+ spc_clear_echo( snes_spc );
+
+ /* Clear filter before playing */
+ spc_filter_clear( filter );
+ }
+
+ /* Record 20 seconds to wave file */
+ wave_open( spc_sample_rate, "out.wav" );
+ wave_enable_stereo();
+ while ( wave_sample_count() < 20 * spc_sample_rate * 2 )
+ {
+ /* Play into buffer */
+ #define BUF_SIZE 2048
+ short buf [BUF_SIZE];
+ error( spc_play( snes_spc, BUF_SIZE, buf ) );
+
+ /* Filter samples */
+ spc_filter_run( filter, buf, BUF_SIZE );
+
+ wave_write( buf, BUF_SIZE );
+ }
+
+ /* Cleanup */
+ spc_filter_delete( filter );
+ spc_delete( snes_spc );
+ wave_close();
+
+ return 0;
+}
diff --git a/snes_spc/demo/save_state.c b/snes_spc/demo/save_state.c
new file mode 100644
index 00000000..1a6674cc
--- /dev/null
+++ b/snes_spc/demo/save_state.c
@@ -0,0 +1,107 @@
+/* Loads "test.spc", skips 5 seconds, saves exact emulator state to "state.bin",
+hen records 5 seconds to "first.wav". When run again, loads this state back into
+emulator and records 5 seconds to "second.wav". These two wave files should
+be identical.
+
+Usage: save_state [test.spc]
+*/
+
+#include "snes_spc/spc.h"
+
+#include "wave_writer.h"
+#include "demo_util.h" /* error(), load_file() */
+
+static SNES_SPC* snes_spc;
+
+void record_wav( const char* path, int secs )
+{
+ /* Start writing wave file */
+ wave_open( spc_sample_rate, path );
+ wave_enable_stereo();
+ while ( wave_sample_count() < secs * spc_sample_rate * 2 )
+ {
+ /* Play into buffer */
+ #define BUF_SIZE 2048
+ short buf [BUF_SIZE];
+ error( spc_play( snes_spc, BUF_SIZE, buf ) );
+
+ wave_write( buf, BUF_SIZE );
+ }
+ wave_close();
+}
+
+void state_save( unsigned char** out, void* in, size_t size )
+{
+ memcpy( *out, in, size );
+ *out += size;
+}
+
+void make_save_state( const char* path )
+{
+ /* Load SPC */
+ long spc_size;
+ void* spc = load_file( path, &spc_size );
+ error( spc_load_spc( snes_spc, spc, spc_size ) );
+ free( spc );
+ spc_clear_echo( snes_spc );
+
+ /* Skip several seconds */
+ error( spc_play( snes_spc, 5 * spc_sample_rate * 2, 0 ) );
+
+ /* Save state to file */
+ {
+ static unsigned char state [spc_state_size]; /* buffer large enough for data */
+ unsigned char* out = state;
+ spc_copy_state( snes_spc, &out, state_save );
+ write_file( "state.bin", state, out - state );
+ }
+
+ record_wav( "first.wav", 5 );
+}
+
+void state_load( unsigned char** in, void* out, size_t size )
+{
+ memcpy( out, *in, size );
+ *in += size;
+}
+
+void use_save_state()
+{
+ /* Load state into memory */
+ long state_size;
+ unsigned char* state = load_file( "state.bin", &state_size );
+
+ /* Load into emulator */
+ unsigned char* in = state;
+ spc_copy_state( snes_spc, &in, state_load );
+ assert( in - state <= state_size ); /* be sure it didn't read past end */
+
+ record_wav( "second.wav", 5 );
+}
+
+int file_exists( const char* path )
+{
+ FILE* file = fopen( path, "rb" );
+ if ( !file )
+ return 0;
+
+ fclose( file );
+ return 1;
+}
+
+int main( int argc, char** argv )
+{
+ snes_spc = spc_new();
+ if ( !snes_spc ) error( "Out of memory" );
+
+ /* Make new state if it doesn't exist, otherwise load it and
+ record to wave file */
+ if ( !file_exists( "state.bin" ) )
+ make_save_state( (argc > 1) ? argv [1] : "test.spc" );
+ else
+ use_save_state();
+
+ spc_delete( snes_spc );
+
+ return 0;
+}
diff --git a/snes_spc/demo/trim_spc.c b/snes_spc/demo/trim_spc.c
new file mode 100644
index 00000000..cf91c11e
--- /dev/null
+++ b/snes_spc/demo/trim_spc.c
@@ -0,0 +1,83 @@
+/* Trims silence off beginning of SPC file.
+Requires the accurate DSP; won't compile with the fast DSP.
+Please note that the algorithm could be improved; this is just
+a simple example showing the idea.
+
+Usage: trim_spc [test.spc [trimmed.spc]]
+*/
+
+#include "snes_spc/spc.h"
+
+#include "demo_util.h" /* error(), load_file() */
+
+/* Change to 1 to have it trim to next key on event rather than trim silence */
+enum { use_kon_check = 0 };
+
+/* True if all count samples from in are silent (or very close to it) */
+int is_silent( short const* in, int count );
+
+int main( int argc, char** argv )
+{
+ /* Load file into memory */
+ long spc_size;
+ void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size );
+
+ /* Load into emulator */
+ SNES_SPC* snes_spc = spc_new();
+ if ( !snes_spc ) error( "Out of memory" );
+ error( spc_load_spc( snes_spc, spc, spc_size ) );
+
+ /* Expand SPC data so there's enough room for emulator to save to.
+ We simply overwrite the emulator data in the old SPC file rather
+ than creating new SPC data. This preserves the text tags from
+ the old file. */
+ if ( spc_size < spc_file_size )
+ {
+ spc_size = spc_file_size;
+ spc = realloc( spc, spc_size ); /* leaks memory if allocation fails */
+ if ( !spc ) error( "Out of memory" );
+ }
+
+ /* Keep saving SPC, then playing a little more. Once SPC becomes non-silent,
+ write the SPC data saved just before this. */
+ {
+ long samples_trimmed = 0;
+ while ( 1 )
+ {
+ #define BUF_SIZE 1024
+ short buf [BUF_SIZE];
+
+ if ( samples_trimmed > 10 * spc_sample_rate * 2 )
+ error( "Excess silence found" );
+
+ spc_clear_echo( snes_spc );
+ spc_save_spc( snes_spc, spc );
+
+ /* Fill buffer */
+ error( spc_play( snes_spc, BUF_SIZE, buf ) );
+ samples_trimmed += BUF_SIZE;
+
+ /* See if SPC became non-silent */
+ if ( use_kon_check ? spc_check_kon( snes_spc ) : !is_silent( buf, BUF_SIZE ) )
+ break;
+ }
+
+ printf( "Trimmed %.1f seconds\n", (double) samples_trimmed / spc_sample_rate / 2 );
+ }
+
+ spc_delete( snes_spc );
+ write_file( (argc > 2) ? argv [2] : "trimmed.spc", spc, spc_size );
+
+ return 0;
+}
+
+int is_silent( short const* in, int count )
+{
+ unsigned const threshold = 0x10;
+ while ( count-- )
+ {
+ if ( (unsigned) (*in++ + threshold / 2) > threshold )
+ return 0;
+ }
+ return 1;
+}
diff --git a/snes_spc/demo/wave_writer.c b/snes_spc/demo/wave_writer.c
new file mode 100644
index 00000000..b8da5d60
--- /dev/null
+++ b/snes_spc/demo/wave_writer.c
@@ -0,0 +1,153 @@
+/* snes_spc 0.9.0. http://www.slack.net/~ant/ */
+
+#include "wave_writer.h"
+
+#include
+#include
+#include
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+enum { buf_size = 32768 * 2 };
+enum { header_size = 0x2C };
+
+typedef short sample_t;
+
+static unsigned char* buf;
+static FILE* file;
+static long sample_count_;
+static long sample_rate_;
+static long buf_pos;
+static int chan_count;
+
+static void exit_with_error( const char* str )
+{
+ printf( "Error: %s\n", str ); getchar();
+ exit( EXIT_FAILURE );
+}
+
+void wave_open( long sample_rate, const char* filename )
+{
+ sample_count_ = 0;
+ sample_rate_ = sample_rate;
+ buf_pos = header_size;
+ chan_count = 1;
+
+ buf = (unsigned char*) malloc( buf_size * sizeof *buf );
+ if ( !buf )
+ exit_with_error( "Out of memory" );
+
+ file = fopen( filename, "wb" );
+ if ( !file )
+ exit_with_error( "Couldn't open WAVE file for writing" );
+
+ setvbuf( file, 0, _IOFBF, 32 * 1024L );
+}
+
+void wave_enable_stereo( void )
+{
+ chan_count = 2;
+}
+
+static void flush_()
+{
+ if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) )
+ exit_with_error( "Couldn't write WAVE data" );
+ buf_pos = 0;
+}
+
+void wave_write( short const* in, long remain )
+{
+ sample_count_ += remain;
+ while ( remain )
+ {
+ if ( buf_pos >= buf_size )
+ flush_();
+
+ {
+ unsigned char* p = &buf [buf_pos];
+ long n = (buf_size - buf_pos) / sizeof (sample_t);
+ if ( n > remain )
+ n = remain;
+ remain -= n;
+
+ /* convert to LSB first format */
+ while ( n-- )
+ {
+ int s = *in++;
+ *p++ = (unsigned char) s;
+ *p++ = (unsigned char) (s >> 8);
+ }
+
+ buf_pos = p - buf;
+ assert( buf_pos <= buf_size );
+ }
+ }
+}
+
+long wave_sample_count( void )
+{
+ return sample_count_;
+}
+
+static void set_le32( void* p, unsigned long n )
+{
+ ((unsigned char*) p) [0] = (unsigned char) n;
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+}
+
+void wave_close( void )
+{
+ if ( file )
+ {
+ /* generate header */
+ unsigned char h [header_size] =
+ {
+ 'R','I','F','F',
+ 0,0,0,0, /* length of rest of file */
+ 'W','A','V','E',
+ 'f','m','t',' ',
+ 0x10,0,0,0, /* size of fmt chunk */
+ 1,0, /* uncompressed format */
+ 0,0, /* channel count */
+ 0,0,0,0, /* sample rate */
+ 0,0,0,0, /* bytes per second */
+ 0,0, /* bytes per sample frame */
+ 16,0, /* bits per sample */
+ 'd','a','t','a',
+ 0,0,0,0, /* size of sample data */
+ /* ... */ /* sample data */
+ };
+ long ds = sample_count_ * sizeof (sample_t);
+ int frame_size = chan_count * sizeof (sample_t);
+
+ set_le32( h + 0x04, header_size - 8 + ds );
+ h [0x16] = chan_count;
+ set_le32( h + 0x18, sample_rate_ );
+ set_le32( h + 0x1C, sample_rate_ * frame_size );
+ h [0x20] = frame_size;
+ set_le32( h + 0x28, ds );
+
+ flush_();
+
+ /* write header */
+ fseek( file, 0, SEEK_SET );
+ fwrite( h, sizeof h, 1, file );
+
+ fclose( file );
+ file = 0;
+ free( buf );
+ buf = 0;
+ }
+}
diff --git a/snes_spc/demo/wave_writer.h b/snes_spc/demo/wave_writer.h
new file mode 100644
index 00000000..5472293b
--- /dev/null
+++ b/snes_spc/demo/wave_writer.h
@@ -0,0 +1,20 @@
+/* WAVE sound file writer for recording 16-bit output during program development */
+
+#ifndef WAVE_WRITER_H
+#define WAVE_WRITER_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+void wave_open( long sample_rate, const char* filename );
+void wave_enable_stereo( void );
+void wave_write( short const* in, long count );
+long wave_sample_count( void );
+void wave_close( void );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/snes_spc/fast_dsp/SPC_DSP.cpp b/snes_spc/fast_dsp/SPC_DSP.cpp
new file mode 100644
index 00000000..96719683
--- /dev/null
+++ b/snes_spc/fast_dsp/SPC_DSP.cpp
@@ -0,0 +1,703 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SPC_DSP.h"
+
+#include "blargg_endian.h"
+#include
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+#if INT_MAX < 0x7FFFFFFF
+ #error "Requires that int type have at least 32 bits"
+#endif
+
+
+// TODO: add to blargg_endian.h
+#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))
+#define GET_LE16A( addr ) GET_LE16( addr )
+#define SET_LE16A( addr, data ) SET_LE16( addr, data )
+
+static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
+{
+ 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,
+ 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,
+ 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,
+ 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,
+ 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,
+ 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,
+ 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,
+ 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF
+};
+
+// if ( io < -32768 ) io = -32768;
+// if ( io > 32767 ) io = 32767;
+#define CLAMP16( io )\
+{\
+ if ( (int16_t) io != io )\
+ io = (io >> 31) ^ 0x7FFF;\
+}
+
+// Access global DSP register
+#define REG(n) m.regs [r_##n]
+
+// Access voice DSP register
+#define VREG(r,n) r [v_##n]
+
+#define WRITE_SAMPLES( l, r, out ) \
+{\
+ out [0] = l;\
+ out [1] = r;\
+ out += 2;\
+ if ( out >= m.out_end )\
+ {\
+ check( out == m.out_end );\
+ check( m.out_end != &m.extra [extra_size] || \
+ (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
+ out = m.extra;\
+ m.out_end = &m.extra [extra_size];\
+ }\
+}\
+
+void SPC_DSP::set_output( sample_t* out, int size )
+{
+ require( (size & 1) == 0 ); // must be even
+ if ( !out )
+ {
+ out = m.extra;
+ size = extra_size;
+ }
+ m.out_begin = out;
+ m.out = out;
+ m.out_end = out + size;
+}
+
+// Volume registers and efb are signed! Easy to forget int8_t cast.
+// Prefixes are to avoid accidental use of locals with same names.
+
+// Interleved gauss table (to improve cache coherency)
+// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)]
+static short const interleved_gauss [512] =
+{
+ 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
+ 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
+ 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
+ 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
+ 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
+ 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
+ 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
+ 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
+ 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
+ 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
+ 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
+ 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
+ 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
+ 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
+ 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
+ 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
+ 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
+ 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
+ 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
+ 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
+ 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
+ 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
+ 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
+ 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
+ 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
+ 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
+ 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
+ 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
+ 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
+ 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
+ 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
+ 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
+};
+
+
+//// Counters
+
+#define RATE( rate, div )\
+ (rate >= div ? rate / div * 8 - 1 : rate - 1)
+
+static unsigned const counter_mask [32] =
+{
+ RATE( 2,2), RATE(2048,4), RATE(1536,3),
+ RATE(1280,5), RATE(1024,4), RATE( 768,3),
+ RATE( 640,5), RATE( 512,4), RATE( 384,3),
+ RATE( 320,5), RATE( 256,4), RATE( 192,3),
+ RATE( 160,5), RATE( 128,4), RATE( 96,3),
+ RATE( 80,5), RATE( 64,4), RATE( 48,3),
+ RATE( 40,5), RATE( 32,4), RATE( 24,3),
+ RATE( 20,5), RATE( 16,4), RATE( 12,3),
+ RATE( 10,5), RATE( 8,4), RATE( 6,3),
+ RATE( 5,5), RATE( 4,4), RATE( 3,3),
+ RATE( 2,4),
+ RATE( 1,4)
+};
+#undef RATE
+
+inline void SPC_DSP::init_counter()
+{
+ // counters start out with this synchronization
+ m.counters [0] = 1;
+ m.counters [1] = 0;
+ m.counters [2] = -0x20u;
+ m.counters [3] = 0x0B;
+
+ int n = 2;
+ for ( int i = 1; i < 32; i++ )
+ {
+ m.counter_select [i] = &m.counters [n];
+ if ( !--n )
+ n = 3;
+ }
+ m.counter_select [ 0] = &m.counters [0];
+ m.counter_select [30] = &m.counters [2];
+}
+
+inline void SPC_DSP::run_counter( int i )
+{
+ int n = m.counters [i];
+ if ( !(n-- & 7) )
+ n -= 6 - i;
+ m.counters [i] = n;
+}
+
+#define READ_COUNTER( rate )\
+ (*m.counter_select [rate] & counter_mask [rate])
+
+
+//// Emulation
+
+void SPC_DSP::run( int clock_count )
+{
+ int new_phase = m.phase + clock_count;
+ int count = new_phase >> 5;
+ m.phase = new_phase & 31;
+ if ( !count )
+ return;
+
+ uint8_t* const ram = m.ram;
+ uint8_t const* const dir = &ram [REG(dir) * 0x100];
+ int const slow_gaussian = (REG(pmon) >> 1) | REG(non);
+ int const noise_rate = REG(flg) & 0x1F;
+
+ // Global volume
+ int mvoll = (int8_t) REG(mvoll);
+ int mvolr = (int8_t) REG(mvolr);
+ if ( mvoll * mvolr < m.surround_threshold )
+ mvoll = -mvoll; // eliminate surround
+
+ do
+ {
+ // KON/KOFF reading
+ if ( (m.every_other_sample ^= 1) != 0 )
+ {
+ m.new_kon &= ~m.kon;
+ m.kon = m.new_kon;
+ m.t_koff = REG(koff);
+ }
+
+ run_counter( 1 );
+ run_counter( 2 );
+ run_counter( 3 );
+
+ // Noise
+ if ( !READ_COUNTER( noise_rate ) )
+ {
+ int feedback = (m.noise << 13) ^ (m.noise << 14);
+ m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
+ }
+
+ // Voices
+ int pmon_input = 0;
+ int main_out_l = 0;
+ int main_out_r = 0;
+ int echo_out_l = 0;
+ int echo_out_r = 0;
+ voice_t* v = m.voices;
+ uint8_t* v_regs = m.regs;
+ int vbit = 1;
+ do
+ {
+ #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] )
+
+ int brr_header = ram [v->brr_addr];
+ int kon_delay = v->kon_delay;
+
+ // Pitch
+ int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF;
+ if ( REG(pmon) & vbit )
+ pitch += ((pmon_input >> 5) * pitch) >> 10;
+
+ // KON phases
+ if ( --kon_delay >= 0 )
+ {
+ v->kon_delay = kon_delay;
+
+ // Get ready to start BRR decoding on next sample
+ if ( kon_delay == 4 )
+ {
+ v->brr_addr = SAMPLE_PTR( 0 );
+ v->brr_offset = 1;
+ v->buf_pos = v->buf;
+ brr_header = 0; // header is ignored on this sample
+ }
+
+ // Envelope is never run during KON
+ v->env = 0;
+ v->hidden_env = 0;
+
+ // Disable BRR decoding until last three samples
+ v->interp_pos = (kon_delay & 3 ? 0x4000 : 0);
+
+ // Pitch is never added during KON
+ pitch = 0;
+ }
+
+ int env = v->env;
+
+ // Gaussian interpolation
+ {
+ int output = 0;
+ VREG(v_regs,envx) = (uint8_t) (env >> 4);
+ if ( env )
+ {
+ // Make pointers into gaussian based on fractional position between samples
+ int offset = (unsigned) v->interp_pos >> 3 & 0x1FE;
+ short const* fwd = interleved_gauss + offset;
+ short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian
+
+ int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12];
+
+ if ( !(slow_gaussian & vbit) ) // 99%
+ {
+ // Faster approximation when exact sample value isn't necessary for pitch mod
+ output = (fwd [0] * in [0] +
+ fwd [1] * in [1] +
+ rev [1] * in [2] +
+ rev [0] * in [3]) >> 11;
+ output = (output * env) >> 11;
+ }
+ else
+ {
+ output = (int16_t) (m.noise * 2);
+ if ( !(REG(non) & vbit) )
+ {
+ output = (fwd [0] * in [0]) >> 11;
+ output += (fwd [1] * in [1]) >> 11;
+ output += (rev [1] * in [2]) >> 11;
+ output = (int16_t) output;
+ output += (rev [0] * in [3]) >> 11;
+
+ CLAMP16( output );
+ output &= ~1;
+ }
+ output = (output * env) >> 11 & ~1;
+ }
+
+ // Output
+ int l = output * v->volume [0];
+ int r = output * v->volume [1];
+
+ main_out_l += l;
+ main_out_r += r;
+
+ if ( REG(eon) & vbit )
+ {
+ echo_out_l += l;
+ echo_out_r += r;
+ }
+ }
+
+ pmon_input = output;
+ VREG(v_regs,outx) = (uint8_t) (output >> 8);
+ }
+
+ // Soft reset or end of sample
+ if ( REG(flg) & 0x80 || (brr_header & 3) == 1 )
+ {
+ v->env_mode = env_release;
+ env = 0;
+ }
+
+ if ( m.every_other_sample )
+ {
+ // KOFF
+ if ( m.t_koff & vbit )
+ v->env_mode = env_release;
+
+ // KON
+ if ( m.kon & vbit )
+ {
+ v->kon_delay = 5;
+ v->env_mode = env_attack;
+ REG(endx) &= ~vbit;
+ }
+ }
+
+ // Envelope
+ if ( !v->kon_delay )
+ {
+ if ( v->env_mode == env_release ) // 97%
+ {
+ env -= 0x8;
+ v->env = env;
+ if ( env <= 0 )
+ {
+ v->env = 0;
+ goto skip_brr; // no BRR decoding for you!
+ }
+ }
+ else // 3%
+ {
+ int rate;
+ int const adsr0 = VREG(v_regs,adsr0);
+ int env_data = VREG(v_regs,adsr1);
+ if ( adsr0 >= 0x80 ) // 97% ADSR
+ {
+ if ( v->env_mode > env_decay ) // 89%
+ {
+ env--;
+ env -= env >> 8;
+ rate = env_data & 0x1F;
+
+ // optimized handling
+ v->hidden_env = env;
+ if ( READ_COUNTER( rate ) )
+ goto exit_env;
+ v->env = env;
+ goto exit_env;
+ }
+ else if ( v->env_mode == env_decay )
+ {
+ env--;
+ env -= env >> 8;
+ rate = (adsr0 >> 3 & 0x0E) + 0x10;
+ }
+ else // env_attack
+ {
+ rate = (adsr0 & 0x0F) * 2 + 1;
+ env += rate < 31 ? 0x20 : 0x400;
+ }
+ }
+ else // GAIN
+ {
+ int mode;
+ env_data = VREG(v_regs,gain);
+ mode = env_data >> 5;
+ if ( mode < 4 ) // direct
+ {
+ env = env_data * 0x10;
+ rate = 31;
+ }
+ else
+ {
+ rate = env_data & 0x1F;
+ if ( mode == 4 ) // 4: linear decrease
+ {
+ env -= 0x20;
+ }
+ else if ( mode < 6 ) // 5: exponential decrease
+ {
+ env--;
+ env -= env >> 8;
+ }
+ else // 6,7: linear increase
+ {
+ env += 0x20;
+ if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )
+ env += 0x8 - 0x20; // 7: two-slope linear increase
+ }
+ }
+ }
+
+ // Sustain level
+ if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
+ v->env_mode = env_sustain;
+
+ v->hidden_env = env;
+
+ // unsigned cast because linear decrease going negative also triggers this
+ if ( (unsigned) env > 0x7FF )
+ {
+ env = (env < 0 ? 0 : 0x7FF);
+ if ( v->env_mode == env_attack )
+ v->env_mode = env_decay;
+ }
+
+ if ( !READ_COUNTER( rate ) )
+ v->env = env; // nothing else is controlled by the counter
+ }
+ }
+ exit_env:
+
+ {
+ // Apply pitch
+ int old_pos = v->interp_pos;
+ int interp_pos = (old_pos & 0x3FFF) + pitch;
+ if ( interp_pos > 0x7FFF )
+ interp_pos = 0x7FFF;
+ v->interp_pos = interp_pos;
+
+ // BRR decode if necessary
+ if ( old_pos >= 0x4000 )
+ {
+ // Arrange the four input nybbles in 0xABCD order for easy decoding
+ int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 +
+ ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
+
+ // Advance read position
+ int const brr_block_size = 9;
+ int brr_offset = v->brr_offset;
+ if ( (brr_offset += 2) >= brr_block_size )
+ {
+ // Next BRR block
+ int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
+ assert( brr_offset == brr_block_size );
+ if ( brr_header & 1 )
+ {
+ brr_addr = SAMPLE_PTR( 1 );
+ if ( !v->kon_delay )
+ REG(endx) |= vbit;
+ }
+ v->brr_addr = brr_addr;
+ brr_offset = 1;
+ }
+ v->brr_offset = brr_offset;
+
+ // Decode
+
+ // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11
+ static unsigned char const shifts [16 * 2] = {
+ 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
+ };
+ int const scale = brr_header >> 4;
+ int const right_shift = shifts [scale];
+ int const left_shift = shifts [scale + 16];
+
+ // Write to next four samples in circular buffer
+ int* pos = v->buf_pos;
+ int* end;
+
+ // Decode four samples
+ for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
+ {
+ // Extract upper nybble and scale appropriately
+ int s = ((int16_t) nybbles >> right_shift) << left_shift;
+
+ // Apply IIR filter (8 is the most commonly used)
+ int const filter = brr_header & 0x0C;
+ int const p1 = pos [brr_buf_size - 1];
+ int const p2 = pos [brr_buf_size - 2] >> 1;
+ if ( filter >= 8 )
+ {
+ s += p1;
+ s -= p2;
+ if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875
+ {
+ s += p2 >> 4;
+ s += (p1 * -3) >> 6;
+ }
+ else // s += p1 * 0.8984375 - p2 * 0.40625
+ {
+ s += (p1 * -13) >> 7;
+ s += (p2 * 3) >> 4;
+ }
+ }
+ else if ( filter ) // s += p1 * 0.46875
+ {
+ s += p1 >> 1;
+ s += (-p1) >> 5;
+ }
+
+ // Adjust and write sample
+ CLAMP16( s );
+ s = (int16_t) (s * 2);
+ pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around
+ }
+
+ if ( pos >= &v->buf [brr_buf_size] )
+ pos = v->buf;
+ v->buf_pos = pos;
+ }
+ }
+skip_brr:
+ // Next voice
+ vbit <<= 1;
+ v_regs += 0x10;
+ v++;
+ }
+ while ( vbit < 0x100 );
+
+ // Echo position
+ int echo_offset = m.echo_offset;
+ uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF];
+ if ( !echo_offset )
+ m.echo_length = (REG(edl) & 0x0F) * 0x800;
+ echo_offset += 4;
+ if ( echo_offset >= m.echo_length )
+ echo_offset = 0;
+ m.echo_offset = echo_offset;
+
+ // FIR
+ int echo_in_l = GET_LE16SA( echo_ptr + 0 );
+ int echo_in_r = GET_LE16SA( echo_ptr + 2 );
+
+ int (*echo_hist_pos) [2] = m.echo_hist_pos;
+ if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] )
+ echo_hist_pos = m.echo_hist;
+ m.echo_hist_pos = echo_hist_pos;
+
+ echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l;
+ echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r;
+
+ #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10))
+ echo_in_l = CALC_FIR_( 7, echo_in_l );
+ echo_in_r = CALC_FIR_( 7, echo_in_r );
+
+ #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] )
+ #define DO_FIR( i )\
+ echo_in_l += CALC_FIR( i, 0 );\
+ echo_in_r += CALC_FIR( i, 1 );
+ DO_FIR( 0 );
+ DO_FIR( 1 );
+ DO_FIR( 2 );
+ #if defined (__MWERKS__) && __MWERKS__ < 0x3200
+ __eieio(); // keeps compiler from stupidly "caching" things in memory
+ #endif
+ DO_FIR( 3 );
+ DO_FIR( 4 );
+ DO_FIR( 5 );
+ DO_FIR( 6 );
+
+ // Echo out
+ if ( !(REG(flg) & 0x20) )
+ {
+ int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14);
+ int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14);
+
+ // just to help pass more validation tests
+ #if SPC_MORE_ACCURACY
+ l &= ~1;
+ r &= ~1;
+ #endif
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ SET_LE16A( echo_ptr + 0, l );
+ SET_LE16A( echo_ptr + 2, r );
+ }
+
+ // Sound out
+ int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14;
+ int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14;
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ if ( (REG(flg) & 0x40) )
+ {
+ l = 0;
+ r = 0;
+ }
+
+ sample_t* out = m.out;
+ WRITE_SAMPLES( l, r, out );
+ m.out = out;
+ }
+ while ( --count );
+}
+
+
+//// Setup
+
+void SPC_DSP::mute_voices( int mask )
+{
+ m.mute_mask = mask;
+ for ( int i = 0; i < voice_count; i++ )
+ {
+ m.voices [i].enabled = (mask >> i & 1) - 1;
+ update_voice_vol( i * 0x10 );
+ }
+}
+
+void SPC_DSP::init( void* ram_64k )
+{
+ m.ram = (uint8_t*) ram_64k;
+ mute_voices( 0 );
+ disable_surround( false );
+ set_output( 0, 0 );
+ reset();
+
+ #ifndef NDEBUG
+ // be sure this sign-extends
+ assert( (int16_t) 0x8000 == -0x8000 );
+
+ // be sure right shift preserves sign
+ assert( (-1 >> 1) == -1 );
+
+ // check clamp macro
+ int i;
+ i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
+ i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
+
+ blargg_verify_byte_order();
+ #endif
+}
+
+void SPC_DSP::soft_reset_common()
+{
+ require( m.ram ); // init() must have been called already
+
+ m.noise = 0x4000;
+ m.echo_hist_pos = m.echo_hist;
+ m.every_other_sample = 1;
+ m.echo_offset = 0;
+ m.phase = 0;
+
+ init_counter();
+}
+
+void SPC_DSP::soft_reset()
+{
+ REG(flg) = 0xE0;
+ soft_reset_common();
+}
+
+void SPC_DSP::load( uint8_t const regs [register_count] )
+{
+ memcpy( m.regs, regs, sizeof m.regs );
+ memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
+
+ // Internal state
+ int i;
+ for ( i = voice_count; --i >= 0; )
+ {
+ voice_t& v = m.voices [i];
+ v.brr_offset = 1;
+ v.buf_pos = v.buf;
+ }
+ m.new_kon = REG(kon);
+
+ mute_voices( m.mute_mask );
+ soft_reset_common();
+}
+
+void SPC_DSP::reset() { load( initial_regs ); }
diff --git a/snes_spc/fast_dsp/SPC_DSP.h b/snes_spc/fast_dsp/SPC_DSP.h
new file mode 100644
index 00000000..045ce3c2
--- /dev/null
+++ b/snes_spc/fast_dsp/SPC_DSP.h
@@ -0,0 +1,212 @@
+// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one)
+
+// snes_spc 0.9.0
+#ifndef SPC_DSP_H
+#define SPC_DSP_H
+
+#include "blargg_common.h"
+
+struct SPC_DSP {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+// Setup
+
+ // Initializes DSP and has it use the 64K RAM provided
+ void init( void* ram_64k );
+
+ // Sets destination for output samples. If out is NULL or out_size is 0,
+ // doesn't generate any.
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since it was last set, always
+ // a multiple of 2. Undefined if more samples were generated than
+ // output buffer could hold.
+ int sample_count() const;
+
+// Emulation
+
+ // Resets DSP to power-on state
+ void reset();
+
+ // Emulates pressing reset switch on SNES
+ void soft_reset();
+
+ // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp()
+ // to catch the DSP up to present.
+ int read ( int addr ) const;
+ void write( int addr, int data );
+
+ // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
+ // a pair of samples is be generated.
+ void run( int clock_count );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+ // If true, prevents channels and global volumes from being phase-negated
+ void disable_surround( bool disable = true );
+
+// State
+
+ // Resets DSP and uses supplied values to initialize registers
+ enum { register_count = 128 };
+ void load( uint8_t const regs [register_count] );
+
+// DSP register addresses
+
+ // Global registers
+ enum {
+ r_mvoll = 0x0C, r_mvolr = 0x1C,
+ r_evoll = 0x2C, r_evolr = 0x3C,
+ r_kon = 0x4C, r_koff = 0x5C,
+ r_flg = 0x6C, r_endx = 0x7C,
+ r_efb = 0x0D, r_pmon = 0x2D,
+ r_non = 0x3D, r_eon = 0x4D,
+ r_dir = 0x5D, r_esa = 0x6D,
+ r_edl = 0x7D,
+ r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
+ };
+
+ // Voice registers
+ enum {
+ v_voll = 0x00, v_volr = 0x01,
+ v_pitchl = 0x02, v_pitchh = 0x03,
+ v_srcn = 0x04, v_adsr0 = 0x05,
+ v_adsr1 = 0x06, v_gain = 0x07,
+ v_envx = 0x08, v_outx = 0x09
+ };
+
+public:
+ enum { extra_size = 16 };
+ sample_t* extra() { return m.extra; }
+ sample_t const* out_pos() const { return m.out; }
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::int8_t int8_t;
+ typedef BOOST::int16_t int16_t;
+
+ enum { echo_hist_size = 8 };
+
+ enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
+ enum { brr_buf_size = 12 };
+ struct voice_t
+ {
+ int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
+ int* buf_pos; // place in buffer where next samples will be decoded
+ int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
+ int brr_addr; // address of current BRR block
+ int brr_offset; // current decoding offset in BRR block
+ int kon_delay; // KON delay/current setup phase
+ env_mode_t env_mode;
+ int env; // current envelope level
+ int hidden_env; // used by GAIN mode 7, very obscure quirk
+ int volume [2]; // copy of volume from DSP registers, with surround disabled
+ int enabled; // -1 if enabled, 0 if muted
+ };
+private:
+ struct state_t
+ {
+ uint8_t regs [register_count];
+
+ // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
+ int echo_hist [echo_hist_size * 2] [2];
+ int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
+
+ int every_other_sample; // toggles every sample
+ int kon; // KON value when last checked
+ int noise;
+ int echo_offset; // offset from ESA in echo buffer
+ int echo_length; // number of bytes that echo_offset will stop at
+ int phase; // next clock cycle to run (0-31)
+ unsigned counters [4];
+
+ int new_kon;
+ int t_koff;
+
+ voice_t voices [voice_count];
+
+ unsigned* counter_select [32];
+
+ // non-emulation state
+ uint8_t* ram; // 64K shared RAM between DSP and SMP
+ int mute_mask;
+ int surround_threshold;
+ sample_t* out;
+ sample_t* out_end;
+ sample_t* out_begin;
+ sample_t extra [extra_size];
+ };
+ state_t m;
+
+ void init_counter();
+ void run_counter( int );
+ void soft_reset_common();
+ void write_outline( int addr, int data );
+ void update_voice_vol( int addr );
+};
+
+#include
+
+inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); }
+
+inline int SPC_DSP::read( int addr ) const
+{
+ assert( (unsigned) addr < register_count );
+ return m.regs [addr];
+}
+
+inline void SPC_DSP::update_voice_vol( int addr )
+{
+ int l = (int8_t) m.regs [addr + v_voll];
+ int r = (int8_t) m.regs [addr + v_volr];
+
+ if ( l * r < m.surround_threshold )
+ {
+ // signs differ, so negate those that are negative
+ l ^= l >> 7;
+ r ^= r >> 7;
+ }
+
+ voice_t& v = m.voices [addr >> 4];
+ int enabled = v.enabled;
+ v.volume [0] = l & enabled;
+ v.volume [1] = r & enabled;
+}
+
+inline void SPC_DSP::write( int addr, int data )
+{
+ assert( (unsigned) addr < register_count );
+
+ m.regs [addr] = (uint8_t) data;
+ int low = addr & 0x0F;
+ if ( low < 0x2 ) // voice volumes
+ {
+ update_voice_vol( low ^ addr );
+ }
+ else if ( low == 0xC )
+ {
+ if ( addr == r_kon )
+ m.new_kon = (uint8_t) data;
+
+ if ( addr == r_endx ) // always cleared, regardless of data written
+ m.regs [r_endx] = 0;
+ }
+}
+
+inline void SPC_DSP::disable_surround( bool disable )
+{
+ m.surround_threshold = disable ? 0 : -0x4000;
+}
+
+#define SPC_NO_COPY_STATE_FUNCS 1
+
+#define SPC_LESS_ACCURATE 1
+
+#endif
diff --git a/snes_spc/license.txt b/snes_spc/license.txt
new file mode 100644
index 00000000..5faba9d4
--- /dev/null
+++ b/snes_spc/license.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/snes_spc/readme.txt b/snes_spc/readme.txt
new file mode 100644
index 00000000..979913f0
--- /dev/null
+++ b/snes_spc/readme.txt
@@ -0,0 +1,86 @@
+snes_spc 0.9.0: SNES SPC-700 APU Emulator
+-----------------------------------------
+This library includes a full SPC emulator and an S-DSP emulator that can
+be used on its own. Two S-DSP emulators are available: a highly accurate
+one for use in a SNES emulator, and a 3x faster one for use in an SPC
+music player or a resource-limited SNES emulator.
+
+* Can be used from C and C++ code
+* Full SPC-700 APU emulator with cycle accuracy in most cases
+* Loads, plays, and saves SPC music files
+* Can save and load exact full emulator state
+* DSP voice muting, surround sound disable, and song tempo adjustment
+* Uses 7% CPU average on 400 MHz Mac to play an SPC using fast DSP
+
+The accurate DSP emulator is based on past research by others and
+hundreds of hours of recent research by me. It passes over a hundred
+strenuous timing and behavior validation tests that were also run on the
+SNES. As far as I know, it's the first DSP emulator with cycle accuracy,
+properly emulating every DSP register and memory access at the exact SPC
+cycle it occurs at, whereas previous DSP emulators emulated these only
+to the nearest sample (which occurs every 32 clocks).
+
+Author : Shay Green
+Website: http://www.slack.net/~ant/
+Forum : http://groups.google.com/group/blargg-sound-libs
+License: GNU Lesser General Public License (LGPL)
+
+
+Getting Started
+---------------
+Build a program consisting of demo/play_spc.c, demo/demo_util.c,
+demo/wave_writer.c, and all source files in snes_spc/. Put an SPC music
+file in the same directory and name it "test.spc". Running the program
+should generate the recording "out.wav".
+
+Read snes_spc.txt for more information. Post to the discussion forum for
+assistance.
+
+
+Files
+-----
+snes_spc.txt Documentation
+changes.txt Change log
+license.txt GNU LGPL license
+
+demo/
+ play_spc.c Records SPC file to wave sound file
+ benchmark.c Finds how fast emulator runs on your computer
+ trim_spc.c Trims silence off beginning of an SPC file
+ save_state.c Saves/loads exact emulator state to/from file
+ comm.c Communicates with SPC how SNES would
+ demo_util.h General utility functions used by demos
+ demo_util.c
+ wave_writer.h WAVE sound file writer used for demo output
+ wave_writer.c
+
+fast_dsp/ Optional standalone fast DSP emulator
+ SPC_DSP.h To use with full SPC emulator, move into
+ SPC_DSP.cpp snes_spc/ and replace original files
+
+snes_spc/ Library sources
+ blargg_config.h Configuration (modify as necessary)
+
+ spc.h C interface to SPC emulator and sound filter
+ spc.cpp
+
+ SPC_Filter.h Optional filter to make sound more authentic
+ SPC_Filter.cpp
+
+ SNES_SPC.h Full SPC emulator
+ SNES_SPC.cpp
+ SNES_SPC_misc.cpp
+ SNES_SPC_state.cpp
+ SPC_CPU.h
+
+ dsp.h C interface to DSP emulator
+ dsp.cpp
+
+ SPC_DSP.h Standalone accurate DSP emulator
+ SPC_DSP.cpp
+ blargg_common.h
+ blargg_endian.h
+ blargg_source.h
+
+--
+Shay Green
diff --git a/snes_spc/slow_dsp/SPC_DSP.cpp b/snes_spc/slow_dsp/SPC_DSP.cpp
new file mode 100644
index 00000000..dd180506
--- /dev/null
+++ b/snes_spc/slow_dsp/SPC_DSP.cpp
@@ -0,0 +1,1018 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SPC_DSP.h"
+
+#include "blargg_endian.h"
+#include
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+#if INT_MAX < 0x7FFFFFFF
+ #error "Requires that int type have at least 32 bits"
+#endif
+
+// TODO: add to blargg_endian.h
+#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))
+#define GET_LE16A( addr ) GET_LE16( addr )
+#define SET_LE16A( addr, data ) SET_LE16( addr, data )
+
+static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
+{
+ 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,
+ 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,
+ 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,
+ 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,
+ 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,
+ 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,
+ 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,
+ 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF
+};
+
+// if ( io < -32768 ) io = -32768;
+// if ( io > 32767 ) io = 32767;
+#define CLAMP16( io )\
+{\
+ if ( (int16_t) io != io )\
+ io = (io >> 31) ^ 0x7FFF;\
+}
+
+// Access global DSP register
+#define REG(n) m.regs [r_##n]
+
+// Access voice DSP register
+#define VREG(r,n) r [v_##n]
+
+#define WRITE_SAMPLES( l, r, out ) \
+{\
+ out [0] = l;\
+ out [1] = r;\
+ out += 2;\
+ if ( out >= m.out_end )\
+ {\
+ check( out == m.out_end );\
+ check( m.out_end != &m.extra [extra_size] || \
+ (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
+ out = m.extra;\
+ m.out_end = &m.extra [extra_size];\
+ }\
+}\
+
+void SPC_DSP::set_output( sample_t* out, int size )
+{
+ require( (size & 1) == 0 ); // must be even
+ if ( !out )
+ {
+ out = m.extra;
+ size = extra_size;
+ }
+ m.out_begin = out;
+ m.out = out;
+ m.out_end = out + size;
+}
+
+// Volume registers and efb are signed! Easy to forget int8_t cast.
+// Prefixes are to avoid accidental use of locals with same names.
+
+// Gaussian interpolation
+
+static short const gauss [512] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
+ 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
+ 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
+ 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
+ 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
+ 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
+ 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
+ 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
+ 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
+ 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
+ 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
+ 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
+ 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
+ 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
+ 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
+ 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
+ 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
+ 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
+ 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
+ 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036,
+1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102,
+1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160,
+1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210,
+1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251,
+1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280,
+1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298,
+1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305,
+};
+
+inline int SPC_DSP::interpolate( voice_t const* v )
+{
+ // Make pointers into gaussian based on fractional position between samples
+ int offset = v->interp_pos >> 4 & 0xFF;
+ short const* fwd = gauss + 255 - offset;
+ short const* rev = gauss + offset; // mirror left half of gaussian
+
+ int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
+ int out;
+ out = (fwd [ 0] * in [0]) >> 11;
+ out += (fwd [256] * in [1]) >> 11;
+ out += (rev [256] * in [2]) >> 11;
+ out = (int16_t) out;
+ out += (rev [ 0] * in [3]) >> 11;
+
+ CLAMP16( out );
+ out &= ~1;
+ return out;
+}
+
+
+//// Counters
+
+int const simple_counter_range = 2048 * 5 * 3; // 30720
+
+static unsigned const counter_rates [32] =
+{
+ simple_counter_range + 1, // never fires
+ 2048, 1536,
+ 1280, 1024, 768,
+ 640, 512, 384,
+ 320, 256, 192,
+ 160, 128, 96,
+ 80, 64, 48,
+ 40, 32, 24,
+ 20, 16, 12,
+ 10, 8, 6,
+ 5, 4, 3,
+ 2,
+ 1
+};
+
+static unsigned const counter_offsets [32] =
+{
+ 1, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 0,
+ 0
+};
+
+inline void SPC_DSP::init_counter()
+{
+ m.counter = 0;
+}
+
+inline void SPC_DSP::run_counters()
+{
+ if ( --m.counter < 0 )
+ m.counter = simple_counter_range - 1;
+}
+
+inline unsigned SPC_DSP::read_counter( int rate )
+{
+ return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate];
+}
+
+
+//// Envelope
+
+inline void SPC_DSP::run_envelope( voice_t* const v )
+{
+ int env = v->env;
+ if ( v->env_mode == env_release ) // 60%
+ {
+ if ( (env -= 0x8) < 0 )
+ env = 0;
+ v->env = env;
+ }
+ else
+ {
+ int rate;
+ int env_data = VREG(v->regs,adsr1);
+ if ( m.t_adsr0 & 0x80 ) // 99% ADSR
+ {
+ if ( v->env_mode >= env_decay ) // 99%
+ {
+ env--;
+ env -= env >> 8;
+ rate = env_data & 0x1F;
+ if ( v->env_mode == env_decay ) // 1%
+ rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10;
+ }
+ else // env_attack
+ {
+ rate = (m.t_adsr0 & 0x0F) * 2 + 1;
+ env += rate < 31 ? 0x20 : 0x400;
+ }
+ }
+ else // GAIN
+ {
+ int mode;
+ env_data = VREG(v->regs,gain);
+ mode = env_data >> 5;
+ if ( mode < 4 ) // direct
+ {
+ env = env_data * 0x10;
+ rate = 31;
+ }
+ else
+ {
+ rate = env_data & 0x1F;
+ if ( mode == 4 ) // 4: linear decrease
+ {
+ env -= 0x20;
+ }
+ else if ( mode < 6 ) // 5: exponential decrease
+ {
+ env--;
+ env -= env >> 8;
+ }
+ else // 6,7: linear increase
+ {
+ env += 0x20;
+ if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )
+ env += 0x8 - 0x20; // 7: two-slope linear increase
+ }
+ }
+ }
+
+ // Sustain level
+ if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
+ v->env_mode = env_sustain;
+
+ v->hidden_env = env;
+
+ // unsigned cast because linear decrease going negative also triggers this
+ if ( (unsigned) env > 0x7FF )
+ {
+ env = (env < 0 ? 0 : 0x7FF);
+ if ( v->env_mode == env_attack )
+ v->env_mode = env_decay;
+ }
+
+ if ( !read_counter( rate ) )
+ v->env = env; // nothing else is controlled by the counter
+ }
+}
+
+
+//// BRR Decoding
+
+inline void SPC_DSP::decode_brr( voice_t* v )
+{
+ // Arrange the four input nybbles in 0xABCD order for easy decoding
+ int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
+
+ int const header = m.t_brr_header;
+
+ // Write to next four samples in circular buffer
+ int* pos = &v->buf [v->buf_pos];
+ int* end;
+ if ( (v->buf_pos += 4) >= brr_buf_size )
+ v->buf_pos = 0;
+
+ // Decode four samples
+ for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
+ {
+ // Extract nybble and sign-extend
+ int s = (int16_t) nybbles >> 12;
+
+ // Shift sample based on header
+ int const shift = header >> 4;
+ s = (s << shift) >> 1;
+ if ( shift >= 0xD ) // handle invalid range
+ s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)
+
+ // Apply IIR filter (8 is the most commonly used)
+ int const filter = header & 0x0C;
+ int const p1 = pos [brr_buf_size - 1];
+ int const p2 = pos [brr_buf_size - 2] >> 1;
+ if ( filter >= 8 )
+ {
+ s += p1;
+ s -= p2;
+ if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875
+ {
+ s += p2 >> 4;
+ s += (p1 * -3) >> 6;
+ }
+ else // s += p1 * 0.8984375 - p2 * 0.40625
+ {
+ s += (p1 * -13) >> 7;
+ s += (p2 * 3) >> 4;
+ }
+ }
+ else if ( filter ) // s += p1 * 0.46875
+ {
+ s += p1 >> 1;
+ s += (-p1) >> 5;
+ }
+
+ // Adjust and write sample
+ CLAMP16( s );
+ s = (int16_t) (s * 2);
+ pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around
+ }
+}
+
+
+//// Misc
+
+#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n()
+
+MISC_CLOCK( 27 )
+{
+ m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON
+}
+MISC_CLOCK( 28 )
+{
+ m.t_non = REG(non);
+ m.t_eon = REG(eon);
+ m.t_dir = REG(dir);
+}
+MISC_CLOCK( 29 )
+{
+ if ( (m.every_other_sample ^= 1) != 0 )
+ m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read
+}
+MISC_CLOCK( 30 )
+{
+ if ( m.every_other_sample )
+ {
+ m.kon = m.new_kon;
+ m.t_koff = REG(koff) | m.mute_mask;
+ }
+
+ run_counters();
+
+ // Noise
+ if ( !read_counter( REG(flg) & 0x1F ) )
+ {
+ int feedback = (m.noise << 13) ^ (m.noise << 14);
+ m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
+ }
+}
+
+
+//// Voices
+
+#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v )
+
+inline VOICE_CLOCK( V1 )
+{
+ m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4;
+ m.t_srcn = VREG(v->regs,srcn);
+}
+inline VOICE_CLOCK( V2 )
+{
+ // Read sample pointer (ignored if not needed)
+ uint8_t const* entry = &m.ram [m.t_dir_addr];
+ if ( !v->kon_delay )
+ entry += 2;
+ m.t_brr_next_addr = GET_LE16A( entry );
+
+ m.t_adsr0 = VREG(v->regs,adsr0);
+
+ // Read pitch, spread over two clocks
+ m.t_pitch = VREG(v->regs,pitchl);
+}
+inline VOICE_CLOCK( V3a )
+{
+ m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8;
+}
+inline VOICE_CLOCK( V3b )
+{
+ // Read BRR header and byte
+ m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF];
+ m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking
+}
+VOICE_CLOCK( V3c )
+{
+ // Pitch modulation using previous voice's output
+ if ( m.t_pmon & v->vbit )
+ m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10;
+
+ if ( v->kon_delay )
+ {
+ // Get ready to start BRR decoding on next sample
+ if ( v->kon_delay == 5 )
+ {
+ v->brr_addr = m.t_brr_next_addr;
+ v->brr_offset = 1;
+ v->buf_pos = 0;
+ m.t_brr_header = 0; // header is ignored on this sample
+ m.kon_check = true;
+ }
+
+ // Envelope is never run during KON
+ v->env = 0;
+ v->hidden_env = 0;
+
+ // Disable BRR decoding until last three samples
+ v->interp_pos = 0;
+ if ( --v->kon_delay & 3 )
+ v->interp_pos = 0x4000;
+
+ // Pitch is never added during KON
+ m.t_pitch = 0;
+ }
+
+ // Gaussian interpolation
+ {
+ int output = interpolate( v );
+
+ // Noise
+ if ( m.t_non & v->vbit )
+ output = (int16_t) (m.noise * 2);
+
+ // Apply envelope
+ m.t_output = (output * v->env) >> 11 & ~1;
+ v->t_envx_out = (uint8_t) (v->env >> 4);
+ }
+
+ // Immediate silence due to end of sample or soft reset
+ if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 )
+ {
+ v->env_mode = env_release;
+ v->env = 0;
+ }
+
+ if ( m.every_other_sample )
+ {
+ // KOFF
+ if ( m.t_koff & v->vbit )
+ v->env_mode = env_release;
+
+ // KON
+ if ( m.kon & v->vbit )
+ {
+ v->kon_delay = 5;
+ v->env_mode = env_attack;
+ }
+ }
+
+ // Run envelope for next sample
+ if ( !v->kon_delay )
+ run_envelope( v );
+}
+inline void SPC_DSP::voice_output( voice_t const* v, int ch )
+{
+ // Apply left/right volume
+ int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7;
+
+ // Add to output total
+ m.t_main_out [ch] += amp;
+ CLAMP16( m.t_main_out [ch] );
+
+ // Optionally add to echo total
+ if ( m.t_eon & v->vbit )
+ {
+ m.t_echo_out [ch] += amp;
+ CLAMP16( m.t_echo_out [ch] );
+ }
+}
+VOICE_CLOCK( V4 )
+{
+ // Decode BRR
+ m.t_looped = 0;
+ if ( v->interp_pos >= 0x4000 )
+ {
+ decode_brr( v );
+
+ if ( (v->brr_offset += 2) >= brr_block_size )
+ {
+ // Start decoding next BRR block
+ assert( v->brr_offset == brr_block_size );
+ v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
+ if ( m.t_brr_header & 1 )
+ {
+ v->brr_addr = m.t_brr_next_addr;
+ m.t_looped = v->vbit;
+ }
+ v->brr_offset = 1;
+ }
+ }
+
+ // Apply pitch
+ v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch;
+
+ // Keep from getting too far ahead (when using pitch modulation)
+ if ( v->interp_pos > 0x7FFF )
+ v->interp_pos = 0x7FFF;
+
+ // Output left
+ voice_output( v, 0 );
+}
+inline VOICE_CLOCK( V5 )
+{
+ // Output right
+ voice_output( v, 1 );
+
+ // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier
+ int endx_buf = REG(endx) | m.t_looped;
+
+ // Clear bit in ENDX if KON just began
+ if ( v->kon_delay == 5 )
+ endx_buf &= ~v->vbit;
+ m.endx_buf = (uint8_t) endx_buf;
+}
+inline VOICE_CLOCK( V6 )
+{
+ (void) v; // avoid compiler warning about unused v
+ m.outx_buf = (uint8_t) (m.t_output >> 8);
+}
+inline VOICE_CLOCK( V7 )
+{
+ // Update ENDX
+ REG(endx) = m.endx_buf;
+
+ m.envx_buf = v->t_envx_out;
+}
+inline VOICE_CLOCK( V8 )
+{
+ // Update OUTX
+ VREG(v->regs,outx) = m.outx_buf;
+}
+inline VOICE_CLOCK( V9 )
+{
+ // Update ENVX
+ VREG(v->regs,envx) = m.envx_buf;
+}
+
+// Most voices do all these in one clock, so make a handy composite
+inline VOICE_CLOCK( V3 )
+{
+ voice_V3a( v );
+ voice_V3b( v );
+ voice_V3c( v );
+}
+
+// Common combinations of voice steps on different voices. This greatly reduces
+// code size and allows everything to be inlined in these functions.
+VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); }
+VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); }
+VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }
+
+
+//// Echo
+
+// Current echo buffer pointer for left/right channel
+#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2])
+
+// Sample in echo history buffer, where 0 is the oldest
+#define ECHO_FIR( i ) (m.echo_hist_pos [i])
+
+// Calculate FIR point for left/right channel
+#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6)
+
+#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n()
+
+inline void SPC_DSP::echo_read( int ch )
+{
+ int s = GET_LE16SA( ECHO_PTR( ch ) );
+ // second copy simplifies wrap-around handling
+ ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1;
+}
+
+ECHO_CLOCK( 22 )
+{
+ // History
+ if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] )
+ m.echo_hist_pos = m.echo_hist;
+
+ m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF;
+ echo_read( 0 );
+
+ // FIR (using l and r temporaries below helps compiler optimize)
+ int l = CALC_FIR( 0, 0 );
+ int r = CALC_FIR( 0, 1 );
+
+ m.t_echo_in [0] = l;
+ m.t_echo_in [1] = r;
+}
+ECHO_CLOCK( 23 )
+{
+ int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 );
+ int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 );
+
+ m.t_echo_in [0] += l;
+ m.t_echo_in [1] += r;
+
+ echo_read( 1 );
+}
+ECHO_CLOCK( 24 )
+{
+ int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 );
+ int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 );
+
+ m.t_echo_in [0] += l;
+ m.t_echo_in [1] += r;
+}
+ECHO_CLOCK( 25 )
+{
+ int l = m.t_echo_in [0] + CALC_FIR( 6, 0 );
+ int r = m.t_echo_in [1] + CALC_FIR( 6, 1 );
+
+ l = (int16_t) l;
+ r = (int16_t) r;
+
+ l += (int16_t) CALC_FIR( 7, 0 );
+ r += (int16_t) CALC_FIR( 7, 1 );
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ m.t_echo_in [0] = l & ~1;
+ m.t_echo_in [1] = r & ~1;
+}
+inline int SPC_DSP::echo_output( int ch )
+{
+ int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) +
+ (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7);
+ CLAMP16( out );
+ return out;
+}
+ECHO_CLOCK( 26 )
+{
+ // Left output volumes
+ // (save sample for next clock so we can output both together)
+ m.t_main_out [0] = echo_output( 0 );
+
+ // Echo feedback
+ int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7);
+ int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7);
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ m.t_echo_out [0] = l & ~1;
+ m.t_echo_out [1] = r & ~1;
+}
+ECHO_CLOCK( 27 )
+{
+ // Output
+ int l = m.t_main_out [0];
+ int r = echo_output( 1 );
+ m.t_main_out [0] = 0;
+ m.t_main_out [1] = 0;
+
+ // TODO: global muting isn't this simple (turns DAC on and off
+ // or something, causing small ~37-sample pulse when first muted)
+ if ( REG(flg) & 0x40 )
+ {
+ l = 0;
+ r = 0;
+ }
+
+ // Output sample to DAC
+ #ifdef SPC_DSP_OUT_HOOK
+ SPC_DSP_OUT_HOOK( l, r );
+ #else
+ sample_t* out = m.out;
+ WRITE_SAMPLES( l, r, out );
+ m.out = out;
+ #endif
+}
+ECHO_CLOCK( 28 )
+{
+ m.t_echo_enabled = REG(flg);
+}
+inline void SPC_DSP::echo_write( int ch )
+{
+ if ( !(m.t_echo_enabled & 0x20) )
+ SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] );
+ m.t_echo_out [ch] = 0;
+}
+ECHO_CLOCK( 29 )
+{
+ m.t_esa = REG(esa);
+
+ if ( !m.echo_offset )
+ m.echo_length = (REG(edl) & 0x0F) * 0x800;
+
+ m.echo_offset += 4;
+ if ( m.echo_offset >= m.echo_length )
+ m.echo_offset = 0;
+
+ // Write left echo
+ echo_write( 0 );
+
+ m.t_echo_enabled = REG(flg);
+}
+ECHO_CLOCK( 30 )
+{
+ // Write right echo
+ echo_write( 1 );
+}
+
+
+//// Timing
+
+// Execute clock for a particular voice
+#define V( clock, voice ) voice_##clock( &m.voices [voice] );
+
+/* The most common sequence of clocks uses composite operations
+for efficiency. For example, the following are equivalent to the
+individual steps on the right:
+
+V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5)
+V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4)
+V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */
+
+// Voice 0 1 2 3 4 5 6 7
+#define GEN_DSP_TIMING \
+PHASE( 0) V(V5,0)V(V2,1)\
+PHASE( 1) V(V6,0)V(V3,1)\
+PHASE( 2) V(V7_V4_V1,0)\
+PHASE( 3) V(V8_V5_V2,0)\
+PHASE( 4) V(V9_V6_V3,0)\
+PHASE( 5) V(V7_V4_V1,1)\
+PHASE( 6) V(V8_V5_V2,1)\
+PHASE( 7) V(V9_V6_V3,1)\
+PHASE( 8) V(V7_V4_V1,2)\
+PHASE( 9) V(V8_V5_V2,2)\
+PHASE(10) V(V9_V6_V3,2)\
+PHASE(11) V(V7_V4_V1,3)\
+PHASE(12) V(V8_V5_V2,3)\
+PHASE(13) V(V9_V6_V3,3)\
+PHASE(14) V(V7_V4_V1,4)\
+PHASE(15) V(V8_V5_V2,4)\
+PHASE(16) V(V9_V6_V3,4)\
+PHASE(17) V(V1,0) V(V7,5)V(V4,6)\
+PHASE(18) V(V8_V5_V2,5)\
+PHASE(19) V(V9_V6_V3,5)\
+PHASE(20) V(V1,1) V(V7,6)V(V4,7)\
+PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\
+PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\
+PHASE(23) V(V7,7) echo_23();\
+PHASE(24) V(V8,7) echo_24();\
+PHASE(25) V(V3b,0) V(V9,7) echo_25();\
+PHASE(26) echo_26();\
+PHASE(27) misc_27(); echo_27();\
+PHASE(28) misc_28(); echo_28();\
+PHASE(29) misc_29(); echo_29();\
+PHASE(30) misc_30();V(V3c,0) echo_30();\
+PHASE(31) V(V4,0) V(V1,2)\
+
+#if !SPC_DSP_CUSTOM_RUN
+
+void SPC_DSP::run( int clocks_remain )
+{
+ require( clocks_remain > 0 );
+
+ int const phase = m.phase;
+ m.phase = (phase + clocks_remain) & 31;
+ switch ( phase )
+ {
+ loop:
+
+ #define PHASE( n ) if ( n && !--clocks_remain ) break; case n:
+ GEN_DSP_TIMING
+ #undef PHASE
+
+ if ( --clocks_remain )
+ goto loop;
+ }
+}
+
+#endif
+
+
+//// Setup
+
+void SPC_DSP::init( void* ram_64k )
+{
+ m.ram = (uint8_t*) ram_64k;
+ mute_voices( 0 );
+ disable_surround( false );
+ set_output( 0, 0 );
+ reset();
+
+ #ifndef NDEBUG
+ // be sure this sign-extends
+ assert( (int16_t) 0x8000 == -0x8000 );
+
+ // be sure right shift preserves sign
+ assert( (-1 >> 1) == -1 );
+
+ // check clamp macro
+ int i;
+ i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
+ i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
+
+ blargg_verify_byte_order();
+ #endif
+}
+
+void SPC_DSP::soft_reset_common()
+{
+ require( m.ram ); // init() must have been called already
+
+ m.noise = 0x4000;
+ m.echo_hist_pos = m.echo_hist;
+ m.every_other_sample = 1;
+ m.echo_offset = 0;
+ m.phase = 0;
+
+ init_counter();
+}
+
+void SPC_DSP::soft_reset()
+{
+ REG(flg) = 0xE0;
+ soft_reset_common();
+}
+
+void SPC_DSP::load( uint8_t const regs [register_count] )
+{
+ memcpy( m.regs, regs, sizeof m.regs );
+ memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
+
+ // Internal state
+ for ( int i = voice_count; --i >= 0; )
+ {
+ voice_t* v = &m.voices [i];
+ v->brr_offset = 1;
+ v->vbit = 1 << i;
+ v->regs = &m.regs [i * 0x10];
+ }
+ m.new_kon = REG(kon);
+ m.t_dir = REG(dir);
+ m.t_esa = REG(esa);
+
+ soft_reset_common();
+}
+
+void SPC_DSP::reset() { load( initial_regs ); }
+
+
+//// State save/load
+
+#if !SPC_NO_COPY_STATE_FUNCS
+
+void SPC_State_Copier::copy( void* state, size_t size )
+{
+ func( buf, state, size );
+}
+
+int SPC_State_Copier::copy_int( int state, int size )
+{
+ BOOST::uint8_t s [2];
+ SET_LE16( s, state );
+ func( buf, &s, size );
+ return GET_LE16( s );
+}
+
+void SPC_State_Copier::skip( int count )
+{
+ if ( count > 0 )
+ {
+ char temp [64];
+ memset( temp, 0, sizeof temp );
+ do
+ {
+ int n = sizeof temp;
+ if ( n > count )
+ n = count;
+ count -= n;
+ func( buf, temp, n );
+ }
+ while ( count );
+ }
+}
+
+void SPC_State_Copier::extra()
+{
+ int n = 0;
+ SPC_State_Copier& copier = *this;
+ SPC_COPY( uint8_t, n );
+ skip( n );
+}
+
+void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
+{
+ SPC_State_Copier copier( io, copy );
+
+ // DSP registers
+ copier.copy( m.regs, register_count );
+
+ // Internal state
+
+ // Voices
+ int i;
+ for ( i = 0; i < voice_count; i++ )
+ {
+ voice_t* v = &m.voices [i];
+
+ // BRR buffer
+ int i;
+ for ( i = 0; i < brr_buf_size; i++ )
+ {
+ int s = v->buf [i];
+ SPC_COPY( int16_t, s );
+ v->buf [i] = v->buf [i + brr_buf_size] = s;
+ }
+
+ SPC_COPY( uint16_t, v->interp_pos );
+ SPC_COPY( uint16_t, v->brr_addr );
+ SPC_COPY( uint16_t, v->env );
+ SPC_COPY( int16_t, v->hidden_env );
+ SPC_COPY( uint8_t, v->buf_pos );
+ SPC_COPY( uint8_t, v->brr_offset );
+ SPC_COPY( uint8_t, v->kon_delay );
+ {
+ int m = v->env_mode;
+ SPC_COPY( uint8_t, m );
+ v->env_mode = (enum env_mode_t) m;
+ }
+ SPC_COPY( uint8_t, v->t_envx_out );
+
+ copier.extra();
+ }
+
+ // Echo history
+ for ( i = 0; i < echo_hist_size; i++ )
+ {
+ int j;
+ for ( j = 0; j < 2; j++ )
+ {
+ int s = m.echo_hist_pos [i] [j];
+ SPC_COPY( int16_t, s );
+ m.echo_hist [i] [j] = s; // write back at offset 0
+ }
+ }
+ m.echo_hist_pos = m.echo_hist;
+ memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] );
+
+ // Misc
+ SPC_COPY( uint8_t, m.every_other_sample );
+ SPC_COPY( uint8_t, m.kon );
+
+ SPC_COPY( uint16_t, m.noise );
+ SPC_COPY( uint16_t, m.counter );
+ SPC_COPY( uint16_t, m.echo_offset );
+ SPC_COPY( uint16_t, m.echo_length );
+ SPC_COPY( uint8_t, m.phase );
+
+ SPC_COPY( uint8_t, m.new_kon );
+ SPC_COPY( uint8_t, m.endx_buf );
+ SPC_COPY( uint8_t, m.envx_buf );
+ SPC_COPY( uint8_t, m.outx_buf );
+
+ SPC_COPY( uint8_t, m.t_pmon );
+ SPC_COPY( uint8_t, m.t_non );
+ SPC_COPY( uint8_t, m.t_eon );
+ SPC_COPY( uint8_t, m.t_dir );
+ SPC_COPY( uint8_t, m.t_koff );
+
+ SPC_COPY( uint16_t, m.t_brr_next_addr );
+ SPC_COPY( uint8_t, m.t_adsr0 );
+ SPC_COPY( uint8_t, m.t_brr_header );
+ SPC_COPY( uint8_t, m.t_brr_byte );
+ SPC_COPY( uint8_t, m.t_srcn );
+ SPC_COPY( uint8_t, m.t_esa );
+ SPC_COPY( uint8_t, m.t_echo_enabled );
+
+ SPC_COPY( int16_t, m.t_main_out [0] );
+ SPC_COPY( int16_t, m.t_main_out [1] );
+ SPC_COPY( int16_t, m.t_echo_out [0] );
+ SPC_COPY( int16_t, m.t_echo_out [1] );
+ SPC_COPY( int16_t, m.t_echo_in [0] );
+ SPC_COPY( int16_t, m.t_echo_in [1] );
+
+ SPC_COPY( uint16_t, m.t_dir_addr );
+ SPC_COPY( uint16_t, m.t_pitch );
+ SPC_COPY( int16_t, m.t_output );
+ SPC_COPY( uint16_t, m.t_echo_ptr );
+ SPC_COPY( uint8_t, m.t_looped );
+
+ copier.extra();
+}
+#endif
diff --git a/snes_spc/slow_dsp/SPC_DSP.h b/snes_spc/slow_dsp/SPC_DSP.h
new file mode 100644
index 00000000..0428d5fc
--- /dev/null
+++ b/snes_spc/slow_dsp/SPC_DSP.h
@@ -0,0 +1,304 @@
+// Highly accurate SNES SPC-700 DSP emulator
+
+// snes_spc 0.9.0
+#ifndef SPC_DSP_H
+#define SPC_DSP_H
+
+#include "blargg_common.h"
+
+extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
+
+struct SPC_DSP {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+// Setup
+
+ // Initializes DSP and has it use the 64K RAM provided
+ void init( void* ram_64k );
+
+ // Sets destination for output samples. If out is NULL or out_size is 0,
+ // doesn't generate any.
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since it was last set, always
+ // a multiple of 2. Undefined if more samples were generated than
+ // output buffer could hold.
+ int sample_count() const;
+
+// Emulation
+
+ // Resets DSP to power-on state
+ void reset();
+
+ // Emulates pressing reset switch on SNES
+ void soft_reset();
+
+ // Reads/writes DSP registers. For accuracy, you must first call run()
+ // to catch the DSP up to present.
+ int read ( int addr ) const;
+ void write( int addr, int data );
+
+ // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
+ // a pair of samples is be generated.
+ void run( int clock_count );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+// State
+
+ // Resets DSP and uses supplied values to initialize registers
+ enum { register_count = 128 };
+ void load( uint8_t const regs [register_count] );
+
+ // Saves/loads exact emulator state
+ enum { state_size = 640 }; // maximum space needed when saving
+ typedef dsp_copy_func_t copy_func_t;
+ void copy_state( unsigned char** io, copy_func_t );
+
+ // Returns non-zero if new key-on events occurred since last call
+ bool check_kon();
+
+// DSP register addresses
+
+ // Global registers
+ enum {
+ r_mvoll = 0x0C, r_mvolr = 0x1C,
+ r_evoll = 0x2C, r_evolr = 0x3C,
+ r_kon = 0x4C, r_koff = 0x5C,
+ r_flg = 0x6C, r_endx = 0x7C,
+ r_efb = 0x0D, r_pmon = 0x2D,
+ r_non = 0x3D, r_eon = 0x4D,
+ r_dir = 0x5D, r_esa = 0x6D,
+ r_edl = 0x7D,
+ r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
+ };
+
+ // Voice registers
+ enum {
+ v_voll = 0x00, v_volr = 0x01,
+ v_pitchl = 0x02, v_pitchh = 0x03,
+ v_srcn = 0x04, v_adsr0 = 0x05,
+ v_adsr1 = 0x06, v_gain = 0x07,
+ v_envx = 0x08, v_outx = 0x09
+ };
+
+public:
+ enum { extra_size = 16 };
+ sample_t* extra() { return m.extra; }
+ sample_t const* out_pos() const { return m.out; }
+ void disable_surround( bool ) { } // not supported
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::int8_t int8_t;
+ typedef BOOST::int16_t int16_t;
+
+ enum { echo_hist_size = 8 };
+
+ enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
+ enum { brr_buf_size = 12 };
+ struct voice_t
+ {
+ int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
+ int buf_pos; // place in buffer where next samples will be decoded
+ int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
+ int brr_addr; // address of current BRR block
+ int brr_offset; // current decoding offset in BRR block
+ uint8_t* regs; // pointer to voice's DSP registers
+ int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
+ int kon_delay; // KON delay/current setup phase
+ env_mode_t env_mode;
+ int env; // current envelope level
+ int hidden_env; // used by GAIN mode 7, very obscure quirk
+ uint8_t t_envx_out;
+ };
+private:
+ enum { brr_block_size = 9 };
+
+ struct state_t
+ {
+ uint8_t regs [register_count];
+
+ // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
+ int echo_hist [echo_hist_size * 2] [2];
+ int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
+
+ int every_other_sample; // toggles every sample
+ int kon; // KON value when last checked
+ int noise;
+ int counter;
+ int echo_offset; // offset from ESA in echo buffer
+ int echo_length; // number of bytes that echo_offset will stop at
+ int phase; // next clock cycle to run (0-31)
+ bool kon_check; // set when a new KON occurs
+
+ // Hidden registers also written to when main register is written to
+ int new_kon;
+ uint8_t endx_buf;
+ uint8_t envx_buf;
+ uint8_t outx_buf;
+
+ // Temporary state between clocks
+
+ // read once per sample
+ int t_pmon;
+ int t_non;
+ int t_eon;
+ int t_dir;
+ int t_koff;
+
+ // read a few clocks ahead then used
+ int t_brr_next_addr;
+ int t_adsr0;
+ int t_brr_header;
+ int t_brr_byte;
+ int t_srcn;
+ int t_esa;
+ int t_echo_enabled;
+
+ // internal state that is recalculated every sample
+ int t_dir_addr;
+ int t_pitch;
+ int t_output;
+ int t_looped;
+ int t_echo_ptr;
+
+ // left/right sums
+ int t_main_out [2];
+ int t_echo_out [2];
+ int t_echo_in [2];
+
+ voice_t voices [voice_count];
+
+ // non-emulation state
+ uint8_t* ram; // 64K shared RAM between DSP and SMP
+ int mute_mask;
+ sample_t* out;
+ sample_t* out_end;
+ sample_t* out_begin;
+ sample_t extra [extra_size];
+ };
+ state_t m;
+
+ void init_counter();
+ void run_counters();
+ unsigned read_counter( int rate );
+
+ int interpolate( voice_t const* v );
+ void run_envelope( voice_t* const v );
+ void decode_brr( voice_t* v );
+
+ void misc_27();
+ void misc_28();
+ void misc_29();
+ void misc_30();
+
+ void voice_output( voice_t const* v, int ch );
+ void voice_V1( voice_t* const );
+ void voice_V2( voice_t* const );
+ void voice_V3( voice_t* const );
+ void voice_V3a( voice_t* const );
+ void voice_V3b( voice_t* const );
+ void voice_V3c( voice_t* const );
+ void voice_V4( voice_t* const );
+ void voice_V5( voice_t* const );
+ void voice_V6( voice_t* const );
+ void voice_V7( voice_t* const );
+ void voice_V8( voice_t* const );
+ void voice_V9( voice_t* const );
+ void voice_V7_V4_V1( voice_t* const );
+ void voice_V8_V5_V2( voice_t* const );
+ void voice_V9_V6_V3( voice_t* const );
+
+ void echo_read( int ch );
+ int echo_output( int ch );
+ void echo_write( int ch );
+ void echo_22();
+ void echo_23();
+ void echo_24();
+ void echo_25();
+ void echo_26();
+ void echo_27();
+ void echo_28();
+ void echo_29();
+ void echo_30();
+
+ void soft_reset_common();
+};
+
+#include
+
+inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); }
+
+inline int SPC_DSP::read( int addr ) const
+{
+ assert( (unsigned) addr < register_count );
+ return m.regs [addr];
+}
+
+inline void SPC_DSP::write( int addr, int data )
+{
+ assert( (unsigned) addr < register_count );
+
+ m.regs [addr] = (uint8_t) data;
+ switch ( addr & 0x0F )
+ {
+ case v_envx:
+ m.envx_buf = (uint8_t) data;
+ break;
+
+ case v_outx:
+ m.outx_buf = (uint8_t) data;
+ break;
+
+ case 0x0C:
+ if ( addr == r_kon )
+ m.new_kon = (uint8_t) data;
+
+ if ( addr == r_endx ) // always cleared, regardless of data written
+ {
+ m.endx_buf = 0;
+ m.regs [r_endx] = 0;
+ }
+ break;
+ }
+}
+
+inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
+
+inline bool SPC_DSP::check_kon()
+{
+ bool old = m.kon_check;
+ m.kon_check = 0;
+ return old;
+}
+
+#if !SPC_NO_COPY_STATE_FUNCS
+
+class SPC_State_Copier {
+ SPC_DSP::copy_func_t func;
+ unsigned char** buf;
+public:
+ SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
+ void copy( void* state, size_t size );
+ int copy_int( int state, int size );
+ void skip( int count );
+ void extra();
+};
+
+#define SPC_COPY( type, state )\
+{\
+ state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
+ assert( (BOOST::type) state == state );\
+}
+
+#endif
+
+#endif
diff --git a/snes_spc/snes_spc.txt b/snes_spc/snes_spc.txt
new file mode 100644
index 00000000..d37b3434
--- /dev/null
+++ b/snes_spc/snes_spc.txt
@@ -0,0 +1,318 @@
+snes_spc 0.9.0: SNES SPC-700 APU Emulator
+-----------------------------------------
+Author : Shay Green
+Website: http://www.slack.net/~ant/
+Forum : http://groups.google.com/group/blargg-sound-libs
+License: GNU Lesser General Public License (LGPL)
+
+
+Contents
+--------
+* C and C++ Interfaces
+* Overview
+* Full SPC Emulation
+* DSP Emulation
+* SPC Music Playback
+* State Copying
+* Library Compilation
+* Error handling
+* Solving Problems
+* Accurate S-DSP Limitations
+* Fast S-DSP Limitations
+* S-SMP Limitations
+* To Do
+* Thanks
+
+
+C and C++ Interfaces
+--------------------
+The library includes a C interface in spc.h and dsp.h, which can be used
+from C and C++. This C interface is referred to in the following
+documentation. If you're building this as a shared library (rather than
+linking statically), you should use the C interface since it will change
+less in future versions.
+
+The native C++ interface is in the header files SNES_SPC.h, SPC_DSP.h,
+and SPC_Filter.h, and the two interfaces can be freely mixed in C++
+code. Conversion between the two interfaces generally follows a pattern:
+
+ C interface C++ interface
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ SNES_SPC* spc; SNES_SPC* spc;
+
+ spc = spc_new(); spc = new SNES_SPC;
+
+ spc_play( spc, count, buf ); spc->play( count, buf );
+
+ spc_sample_rate SNES_SPC::sample_rate
+
+ spc_delete( spc ); delete spc;
+
+
+Overview
+--------
+There are three main roles for this library:
+* Full SPC emulation in a SNES emulator
+* DSP emulation in a SNES emulator (where you emulate the SMP CPU)
+* SPC playback in an SPC music file player
+
+Each of these uses are described separately below.
+
+
+Full SPC Emulation
+------------------
+See spc.h for full function reference (SNES_SPC.h if using C++).
+
+* Create SPC emulator with spc_new() and check for NULL.
+
+* Call spc_init_rom() with a pointer to the 64-byte IPL ROM dump (not
+included with library).
+
+* When your emulated SNES is powered on, call spc_reset(). When the
+reset switch is pressed, call spc_soft_reset().
+
+* Call spc_set_output() with your output buffer, then do emulation.
+
+* When the SNES CPU accesses APU ports, call spc_read_port() and
+spc_write_port().
+
+* When your emulator's timebase is going back to 0, call
+spc_end_frame(), usually at the end of a video frame or scanline.
+
+* Periodically play samples from your buffer. Use spc_sample_count() to
+find out how many samples have been written, then spc_set_output() after
+you've made more space in your buffer.
+
+* Save/load full emulator state with spc_copy_state().
+
+* You can save as an SPC music file with spc_save_spc().
+
+* When done, use spc_delete() to free memory.
+
+
+DSP Emulation
+-------------
+See dsp.h for full function reference (SPC_DSP.h if using C++).
+
+* Create DSP emulator with spc_dsp_new() and check for NULL.
+
+* Let the DSP know where your 64K RAM is with spc_dsp_init().
+
+* When your emulated SNES is powered on, call spc_dsp_reset(). When the
+reset switch is pressed, call spc_dsp_soft_reset().
+
+* Call spc_dsp_set_output() with your output buffer, then do emulation.
+
+* Use spc_dsp_run() to run DSP for specified number of clocks (1024000
+per second). Every 32 clocks a pair of samples is added to your output
+buffer.
+
+* Use spc_dsp_read() and spc_dsp_write() to handle DSP reads/writes from
+the S-SMP. Before calling these always catch the DSP up to present time
+with spc_dsp_run().
+
+* Periodically play samples from your buffer. Use spc_dsp_sample_count()
+to find out how many samples have been written, then
+spc_dsp_set_output() after you've made more space in your buffer.
+
+* Use spc_dsp_copy_state() to save/load full DSP state.
+
+* When done, use spc_delete() to free memory.
+
+
+SPC Music Playback
+------------------
+See spc.h for full function reference (SNES_SPC.h if using C++).
+
+* Create SPC emulator with spc_new() and check for NULL.
+
+* Load SPC with spc_load_spc() and check for error.
+
+* Optionally cear echo buffer with spc_clear_echo(). Many SPCs have
+garbage in echo buffer, which causes noise at the beginning.
+
+* Generate samples as needed with spc_play().
+
+* When done, use spc_delete() to free memory.
+
+* For a more complete game music playback library, use Game_Music_Emu
+
+
+State Copying
+-------------
+The SPC and DSP modules include state save/load functions. They take a
+pointer to a pointer to a buffer to store state, and a copy function.
+This copy function can either copy data to the buffer or from it,
+allowing state save and restore with the same library function. The
+internal save state format allows for future expansion without making
+previous save states unusable.
+
+The SPC save state format puts the most important parts first to make it
+easier to manually examine. It's organized as follows:
+
+Offset Size Data
+- - - - - - - - - - - - - - - - - -
+ 0 $10000 SPC RAM
+$10000 $10 SMP $F0-$FF registers
+$10010 4 SMP $F4-$F8 output registers
+$10014 2 PC
+$10016 1 A
+$10017 1 X
+$10018 1 Y
+$10019 1 PSW
+$1001A 1 SP
+$1001B 5 internal
+$10020 $80 DSP registers
+$100A0 ... internal
+
+
+Library Compilation
+-------------------
+While this library is in C++, it has been written to easily link in a C
+program *without* needing the standard C++ library. It doesn't use
+exception handling or run-time type information (RTTI), so you can
+disable these in your C++ compiler to increase efficiency.
+
+If you're building a shared library (DLL), I recommend only exporting
+the C interfaces in spc.h and dsp.h, as the C++ interfaces expose
+implementation details that will break link compatibility across
+versions.
+
+If you're using C and compiling with GCC, I recommend the following
+command-line options when compiling the library source, otherwise GCC
+will insert calls to the standard C++ library and require that it be
+linked in:
+
+ -fno-rtti -fno-exceptions
+
+For maximum optimization, see the NDEBUG and BLARGG_NONPORTABLE options
+in blargg_config. If using GCC, you can enable these by adding the
+following command-line options when you invoke gcc. If you encounter
+problems, try without -DBLARGG_NONPORTABLE; if that works, contact me so
+I can figure out why BLARGG_NONPORTABLE was causing it to fail.
+
+ -O3 -DNDEBUG -DBLARGG_NONPORTABLE -fno-rtti -fno-exceptions
+
+
+
+Error handling
+--------------
+Functions which can fail have a return type of spc_err_t (blargg_err_t
+in the C++ interfaces), which is a pointer to an error string (const
+char*). If a function is successful it returns NULL. Errors that you can
+easily avoid are checked with debug assertions; spc_err_t return values
+are only used for genuine run-time errors that can't be easily predicted
+in advance (out of memory, I/O errors, incompatible file data). Your
+code should check all error values.
+
+To improve usability for C programmers, C++ programmers unfamiliar with
+exceptions, and compatibility with older C++ compilers, the library does
+*not* throw any C++ exceptions and uses malloc() instead of the standard
+operator new. This means that you *must* check for NULL when creating a
+library object with the new operator.
+
+
+Solving Problems
+----------------
+If you're having problems, try the following:
+
+* If you're getting garbled sound, try this simple siren generator in
+place of your call to play(). This will quickly tell whether the problem
+is in the library or in your code.
+
+ static void play_siren( long count, short* out )
+ {
+ static double a, a2;
+ while ( count-- )
+ *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
+ }
+
+* Enable debugging support in your environment. This enables assertions
+and other run-time checks.
+
+* Turn the compiler's optimizer is off. Sometimes an optimizer generates
+bad code.
+
+* If multiple threads are being used, ensure that only one at a time is
+accessing a given set of objects from the library. This library is not
+in general thread-safe, though independent objects can be used in
+separate threads.
+
+* If all else fails, see if the demos work.
+
+
+Accurate S-DSP Limitations
+--------------------------
+* Power-up and soft reset behavior might have slight inaccuracies.
+
+* Muting (FLG bit 6) behavior when toggling bit very rapidly is not
+emulated properly.
+
+* No other known inaccuracies. Has passed 100+ strenuous tests.
+
+
+Fast S-DSP Limitations
+----------------------
+* Uses faster sample calculations except in cases where exact value is
+actually important (BRR decoding, and gaussian interpolation combined
+with pitch modulation).
+
+* Stops decoding BRR data when a voice's envelope has released to
+silence.
+
+* Emulates 32 clocks at a time, so DSP register and memory accesses are
+all done in a bunch rather than spread out. Even though, some clever
+code makes register accesses separated by 40 or so clocks occur with
+cycle-accurate timing.
+
+
+S-SMP Limitations
+-----------------
+* Opcode fetches and indirect pointers are always read directly from
+memory, even for the $F0-$FF region, and the DSP is not caught up for
+these fetches.
+
+* Attempts to perversely execute data in registers or an area being
+modified by echo will not be emulated properly.
+
+* Has not been thoroughly tested.
+
+* Test register ($F0) is not implemented.
+
+* Echo buffer can overwrite IPL ROM area, and does not correctly update
+extra RAM there.
+
+
+To Do
+-----
+* I'd like feedback on the interface and any ways to improve it. In
+particular, the differing features between the accurate and fast DSP
+emulators might make it harder to cleanly switch between them without
+modifying source code.
+
+* Finish thorough tests on SMP memory access times.
+
+* Finish thorough tests on SMP instruction behavior (flags, registers).
+
+* Finish thorough tests on SMP timers.
+
+* Finish power-up and reset behavior testing.
+
+* Come up with best starting conditions to play an SPC and implement in
+hardware SNES SPC player for verification.
+
+
+Thanks
+------
+Thanks to Anti-Resonance's SPC2ROM and help getting SPCs playing on my
+SNES in the first place, then Brad Martin's openspc and Chris Moeller's
+openspc++ C++ adaptation, giving me a good SPC emulator to start with
+several years ago. Thanks to Richard Bannister, Mahendra Tallur, Shazz,
+nenolod, theHobbit, Johan Samuelsson, nes6502, and Micket for helping
+test my Game_Music_Emu library. Thanks to hcs for help in converting to
+C for the Rockbox port. Thanks to byuu (bsnes author) and pagefault and
+Nach (zsnes team) for testing and using my new rewritten DSP in their
+emulators. Thanks to anomie for his good SNES documentation and
+discussions with me to keep it up to date with my latest findings.
+--
+Shay Green
diff --git a/snes_spc/snes_spc.vcproj b/snes_spc/snes_spc.vcproj
new file mode 100644
index 00000000..42560a6d
--- /dev/null
+++ b/snes_spc/snes_spc.vcproj
@@ -0,0 +1,397 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/snes_spc/snes_spc/SNES_SPC.cpp b/snes_spc/snes_spc/SNES_SPC.cpp
new file mode 100644
index 00000000..fb3b147a
--- /dev/null
+++ b/snes_spc/snes_spc/SNES_SPC.cpp
@@ -0,0 +1,564 @@
+// Core SPC emulation: CPU, timers, SMP registers, memory
+
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SNES_SPC.h"
+
+#include
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define RAM (m.ram.ram)
+#define REGS (m.smp_regs [0])
+#define REGS_IN (m.smp_regs [1])
+
+// (n ? n : 256)
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
+
+// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
+// do crazy echo buffer accesses.
+#ifndef SPC_MORE_ACCURACY
+ #define SPC_MORE_ACCURACY 0
+#endif
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+
+//// Timers
+
+#if SPC_DISABLE_TEMPO
+ #define TIMER_DIV( t, n ) ((n) >> t->prescaler)
+ #define TIMER_MUL( t, n ) ((n) << t->prescaler)
+#else
+ #define TIMER_DIV( t, n ) ((n) / t->prescaler)
+ #define TIMER_MUL( t, n ) ((n) * t->prescaler)
+#endif
+
+SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
+{
+ int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
+ t->next_time += TIMER_MUL( t, elapsed );
+
+ if ( t->enabled )
+ {
+ int remain = IF_0_THEN_256( t->period - t->divider );
+ int divider = t->divider + elapsed;
+ int over = elapsed - remain;
+ if ( over >= 0 )
+ {
+ int n = over / t->period;
+ t->counter = (t->counter + 1 + n) & 0x0F;
+ divider = over - n * t->period;
+ }
+ t->divider = (uint8_t) divider;
+ }
+ return t;
+}
+
+inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
+{
+ if ( time >= t->next_time )
+ t = run_timer_( t, time );
+ return t;
+}
+
+
+//// ROM
+
+void SNES_SPC::enable_rom( int enable )
+{
+ if ( m.rom_enabled != enable )
+ {
+ m.rom_enabled = enable;
+ if ( enable )
+ memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
+ memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
+ // TODO: ROM can still get overwritten when DSP writes to echo buffer
+ }
+}
+
+
+//// DSP
+
+#if SPC_LESS_ACCURATE
+ int const max_reg_time = 29;
+
+ signed char const SNES_SPC::reg_times_ [256] =
+ {
+ -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
+ 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23,
+ 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23,
+ 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24,
+ 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
+ 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
+ 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
+ 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
+
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ };
+
+ #define RUN_DSP( time, offset ) \
+ int count = (time) - (offset) - m.dsp_time;\
+ if ( count >= 0 )\
+ {\
+ int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
+ m.dsp_time += clock_count;\
+ dsp.run( clock_count );\
+ }
+#else
+ #define RUN_DSP( time, offset ) \
+ {\
+ int count = (time) - m.dsp_time;\
+ if ( !SPC_MORE_ACCURACY || count )\
+ {\
+ assert( count > 0 );\
+ m.dsp_time = (time);\
+ dsp.run( count );\
+ }\
+ }
+#endif
+
+int SNES_SPC::dsp_read( rel_time_t time )
+{
+ RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
+
+ int result = dsp.read( REGS [r_dspaddr] & 0x7F );
+
+ #ifdef SPC_DSP_READ_HOOK
+ SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
+ #endif
+
+ return result;
+}
+
+inline void SNES_SPC::dsp_write( int data, rel_time_t time )
+{
+ RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
+ #if SPC_LESS_ACCURATE
+ else if ( m.dsp_time == skipping_time )
+ {
+ int r = REGS [r_dspaddr];
+ if ( r == SPC_DSP::r_kon )
+ m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
+
+ if ( r == SPC_DSP::r_koff )
+ {
+ m.skipped_koff |= data;
+ m.skipped_kon &= ~data;
+ }
+ }
+ #endif
+
+ #ifdef SPC_DSP_WRITE_HOOK
+ SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
+ #endif
+
+ if ( REGS [r_dspaddr] <= 0x7F )
+ dsp.write( REGS [r_dspaddr], data );
+ else if ( !SPC_MORE_ACCURACY )
+ dprintf( "SPC wrote to DSP register > $7F\n" );
+}
+
+
+//// Memory access extras
+
+#if SPC_MORE_ACCURACY
+ #define MEM_ACCESS( time, addr ) \
+ {\
+ if ( time >= m.dsp_time )\
+ {\
+ RUN_DSP( time, max_reg_time );\
+ }\
+ }
+#elif !defined (NDEBUG)
+ // Debug-only check for read/write within echo buffer, since this might result in
+ // inaccurate emulation due to the DSP not being caught up to the present.
+
+ bool SNES_SPC::check_echo_access( int addr )
+ {
+ if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
+ {
+ int start = 0x100 * dsp.read( SPC_DSP::r_esa );
+ int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
+ int end = start + (size ? size : 4);
+ if ( start <= addr && addr < end )
+ {
+ if ( !m.echo_accessed )
+ {
+ m.echo_accessed = 1;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
+#else
+ #define MEM_ACCESS( time, addr )
+#endif
+
+
+//// CPU write
+
+#if SPC_MORE_ACCURACY
+static unsigned char const glitch_probs [3] [256] =
+{
+ 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
+ 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
+ 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
+ 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
+ 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
+ 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
+ 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
+ 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
+ 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
+ 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
+ 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
+ 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
+ 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
+ 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
+ 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
+ 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
+
+ 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
+ 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
+ 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
+ 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
+ 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
+ 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
+ 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
+ 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
+ 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
+ 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
+ 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
+ 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
+ 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
+ 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
+ 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
+ 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
+
+ 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
+ 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
+ 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
+ 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
+ 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
+ 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
+ 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
+ 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
+ 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
+ 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
+ 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
+ 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
+ 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
+ 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
+ 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
+ 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
+};
+#endif
+
+// divided into multiple functions to keep rarely-used functionality separate
+// so often-used functionality can be optimized better by compiler
+
+// If write isn't preceded by read, data has this added to it
+int const no_read_before_write = 0x2000;
+
+void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
+{
+ switch ( addr )
+ {
+ case r_t0target:
+ case r_t1target:
+ case r_t2target: {
+ Timer* t = &m.timers [addr - r_t0target];
+ int period = IF_0_THEN_256( data );
+ if ( t->period != period )
+ {
+ t = run_timer( t, time );
+ #if SPC_MORE_ACCURACY
+ // Insane behavior when target is written just after counter is
+ // clocked and counter matches new period and new period isn't 1, 2, 4, or 8
+ if ( t->divider == (period & 0xFF) &&
+ t->next_time == time + TIMER_MUL( t, 1 ) &&
+ ((period - 1) | ~0x0F) & period )
+ {
+ //dprintf( "SPC pathological timer target write\n" );
+
+ // If the period is 3, 5, or 9, there's a probability this behavior won't occur,
+ // based on the previous period
+ int prob = 0xFF;
+ int old_period = t->period & 0xFF;
+ if ( period == 3 ) prob = glitch_probs [0] [old_period];
+ if ( period == 5 ) prob = glitch_probs [1] [old_period];
+ if ( period == 9 ) prob = glitch_probs [2] [old_period];
+
+ // The glitch suppresses incrementing of one of the counter bits, based on
+ // the lowest set bit in the new period
+ int b = 1;
+ while ( !(period & b) )
+ b <<= 1;
+
+ if ( (rand() >> 4 & 0xFF) <= prob )
+ t->divider = (t->divider - b) & 0xFF;
+ }
+ #endif
+ t->period = period;
+ }
+ break;
+ }
+
+ case r_t0out:
+ case r_t1out:
+ case r_t2out:
+ if ( !SPC_MORE_ACCURACY )
+ dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
+
+ if ( data < no_read_before_write / 2 )
+ run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
+ break;
+
+ // Registers that act like RAM
+ case 0x8:
+ case 0x9:
+ REGS_IN [addr] = (uint8_t) data;
+ break;
+
+ case r_test:
+ if ( (uint8_t) data != 0x0A )
+ dprintf( "SPC wrote to test register\n" );
+ break;
+
+ case r_control:
+ // port clears
+ if ( data & 0x10 )
+ {
+ REGS_IN [r_cpuio0] = 0;
+ REGS_IN [r_cpuio1] = 0;
+ }
+ if ( data & 0x20 )
+ {
+ REGS_IN [r_cpuio2] = 0;
+ REGS_IN [r_cpuio3] = 0;
+ }
+
+ // timers
+ {
+ for ( int i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ int enabled = data >> i & 1;
+ if ( t->enabled != enabled )
+ {
+ t = run_timer( t, time );
+ t->enabled = enabled;
+ if ( enabled )
+ {
+ t->divider = 0;
+ t->counter = 0;
+ }
+ }
+ }
+ }
+ enable_rom( data & 0x80 );
+ break;
+ }
+}
+
+void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
+{
+ if ( addr == r_dspdata ) // 99%
+ dsp_write( data, time );
+ else
+ cpu_write_smp_reg_( data, time, addr );
+}
+
+void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
+{
+ if ( i < rom_size )
+ {
+ m.hi_ram [i] = (uint8_t) data;
+ if ( m.rom_enabled )
+ RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
+ }
+ else
+ {
+ assert( RAM [i + rom_addr] == (uint8_t) data );
+ RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding
+ cpu_write( data, i + rom_addr - 0x10000, time );
+ }
+}
+
+int const bits_in_int = CHAR_BIT * sizeof (int);
+
+void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
+{
+ MEM_ACCESS( time, addr )
+
+ // RAM
+ RAM [addr] = (uint8_t) data;
+ int reg = addr - 0xF0;
+ if ( reg >= 0 ) // 64%
+ {
+ // $F0-$FF
+ if ( reg < reg_count ) // 87%
+ {
+ REGS [reg] = (uint8_t) data;
+
+ // Ports
+ #ifdef SPC_PORT_WRITE_HOOK
+ if ( (unsigned) (reg - r_cpuio0) < port_count )
+ SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
+ (uint8_t) data, ®S [r_cpuio0] );
+ #endif
+
+ // Registers other than $F2 and $F4-$F7
+ //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
+ // TODO: this is a bit on the fragile side
+ if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
+ cpu_write_smp_reg( data, time, reg );
+ }
+ // High mem/address wrap-around
+ else
+ {
+ reg -= rom_addr - 0xF0;
+ if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
+ cpu_write_high( data, reg, time );
+ }
+ }
+}
+
+
+//// CPU read
+
+inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
+{
+ int result = REGS_IN [reg];
+ reg -= r_dspaddr;
+ // DSP addr and data
+ if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
+ {
+ result = REGS [r_dspaddr];
+ if ( (unsigned) reg == 1 )
+ result = dsp_read( time ); // 0xF3
+ }
+ return result;
+}
+
+int SNES_SPC::cpu_read( int addr, rel_time_t time )
+{
+ MEM_ACCESS( time, addr )
+
+ // RAM
+ int result = RAM [addr];
+ int reg = addr - 0xF0;
+ if ( reg >= 0 ) // 40%
+ {
+ reg -= 0x10;
+ if ( (unsigned) reg >= 0xFF00 ) // 21%
+ {
+ reg += 0x10 - r_t0out;
+
+ // Timers
+ if ( (unsigned) reg < timer_count ) // 90%
+ {
+ Timer* t = &m.timers [reg];
+ if ( time >= t->next_time )
+ t = run_timer_( t, time );
+ result = t->counter;
+ t->counter = 0;
+ }
+ // Other registers
+ else if ( reg < 0 ) // 10%
+ {
+ result = cpu_read_smp_reg( reg + r_t0out, time );
+ }
+ else // 1%
+ {
+ assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
+ result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
+ }
+ }
+ }
+
+ return result;
+}
+
+
+//// Run
+
+// Prefix and suffix for CPU emulator function
+#define SPC_CPU_RUN_FUNC \
+BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
+{\
+ rel_time_t rel_time = m.spc_time - end_time;\
+ assert( rel_time <= 0 );\
+ m.spc_time = end_time;\
+ m.dsp_time += rel_time;\
+ m.timers [0].next_time += rel_time;\
+ m.timers [1].next_time += rel_time;\
+ m.timers [2].next_time += rel_time;
+
+#define SPC_CPU_RUN_FUNC_END \
+ m.spc_time += rel_time;\
+ m.dsp_time -= rel_time;\
+ m.timers [0].next_time -= rel_time;\
+ m.timers [1].next_time -= rel_time;\
+ m.timers [2].next_time -= rel_time;\
+ assert( m.spc_time <= end_time );\
+ return ®S [r_cpuio0];\
+}
+
+int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
+
+void SNES_SPC::end_frame( time_t end_time )
+{
+ // Catch CPU up to as close to end as possible. If final instruction
+ // would exceed end, does NOT execute it and leaves m.spc_time < end.
+ if ( end_time > m.spc_time )
+ run_until_( end_time );
+
+ m.spc_time -= end_time;
+ m.extra_clocks += end_time;
+
+ // Greatest number of clocks early that emulation can stop early due to
+ // not being able to execute current instruction without going over
+ // allowed time.
+ assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
+
+ // Catch timers up to CPU
+ for ( int i = 0; i < timer_count; i++ )
+ run_timer( &m.timers [i], 0 );
+
+ // Catch DSP up to CPU
+ if ( m.dsp_time < 0 )
+ {
+ RUN_DSP( 0, max_reg_time );
+ }
+
+ // Save any extra samples beyond what should be generated
+ if ( m.buf_begin )
+ save_extra();
+}
+
+// Inclusion here allows static memory access functions and better optimization
+#include "SPC_CPU.h"
diff --git a/snes_spc/snes_spc/SNES_SPC.h b/snes_spc/snes_spc/SNES_SPC.h
new file mode 100644
index 00000000..8ea0392f
--- /dev/null
+++ b/snes_spc/snes_spc/SNES_SPC.h
@@ -0,0 +1,279 @@
+// SNES SPC-700 APU emulator
+
+// snes_spc 0.9.0
+#ifndef SNES_SPC_H
+#define SNES_SPC_H
+
+#include "SPC_DSP.h"
+#include "blargg_endian.h"
+
+struct SNES_SPC {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+ // Must be called once before using
+ blargg_err_t init();
+
+ // Sample pairs generated per second
+ enum { sample_rate = 32000 };
+
+// Emulator use
+
+ // Sets IPL ROM data. Library does not include ROM data. Most SPC music files
+ // don't need ROM, but a full emulator must provide this.
+ enum { rom_size = 0x40 };
+ void init_rom( uint8_t const rom [rom_size] );
+
+ // Sets destination for output samples
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since last set
+ int sample_count() const;
+
+ // Resets SPC to power-on state. This resets your output buffer, so you must
+ // call set_output() after this.
+ void reset();
+
+ // Emulates pressing reset switch on SNES. This resets your output buffer, so
+ // you must call set_output() after this.
+ void soft_reset();
+
+ // 1024000 SPC clocks per second, sample pair every 32 clocks
+ typedef int time_t;
+ enum { clock_rate = 1024000 };
+ enum { clocks_per_sample = 32 };
+
+ // Emulated port read/write at specified time
+ enum { port_count = 4 };
+ int read_port ( time_t, int port );
+ void write_port( time_t, int port, int data );
+
+ // Runs SPC to end_time and starts a new time frame at 0
+ void end_frame( time_t end_time );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+ // If true, prevents channels and global volumes from being phase-negated.
+ // Only supported by fast DSP.
+ void disable_surround( bool disable = true );
+
+ // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
+ enum { tempo_unit = 0x100 };
+ void set_tempo( int );
+
+// SPC music files
+
+ // Loads SPC data into emulator
+ enum { spc_min_file_size = 0x10180 };
+ enum { spc_file_size = 0x10200 };
+ blargg_err_t load_spc( void const* in, long size );
+
+ // Clears echo region. Useful after loading an SPC as many have garbage in echo.
+ void clear_echo();
+
+ // Plays for count samples and write samples to out. Discards samples if out
+ // is NULL. Count must be a multiple of 2 since output is stereo.
+ blargg_err_t play( int count, sample_t* out );
+
+ // Skips count samples. Several times faster than play() when using fast DSP.
+ blargg_err_t skip( int count );
+
+// State save/load (only available with accurate DSP)
+
+#if !SPC_NO_COPY_STATE_FUNCS
+ // Saves/loads state
+ enum { state_size = 67 * 1024L }; // maximum space needed when saving
+ typedef SPC_DSP::copy_func_t copy_func_t;
+ void copy_state( unsigned char** io, copy_func_t );
+
+ // Writes minimal header to spc_out
+ static void init_header( void* spc_out );
+
+ // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
+ // Does not set up SPC header; use init_header() for that.
+ void save_spc( void* spc_out );
+
+ // Returns true if new key-on events occurred since last check. Useful for
+ // trimming silence while saving an SPC.
+ bool check_kon();
+#endif
+
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::uint16_t uint16_t;
+
+ // Time relative to m_spc_time. Speeds up code a bit by eliminating need to
+ // constantly add m_spc_time to time from CPU. CPU uses time that ends at
+ // 0 to eliminate reloading end time every instruction. It pays off.
+ typedef int rel_time_t;
+
+ struct Timer
+ {
+ rel_time_t next_time; // time of next event
+ int prescaler;
+ int period;
+ int divider;
+ int enabled;
+ int counter;
+ };
+ enum { reg_count = 0x10 };
+ enum { timer_count = 3 };
+ enum { extra_size = SPC_DSP::extra_size };
+
+ enum { signature_size = 35 };
+
+private:
+ SPC_DSP dsp;
+
+ #if SPC_LESS_ACCURATE
+ static signed char const reg_times_ [256];
+ signed char reg_times [256];
+ #endif
+
+ struct state_t
+ {
+ Timer timers [timer_count];
+
+ uint8_t smp_regs [2] [reg_count];
+
+ struct
+ {
+ int pc;
+ int a;
+ int x;
+ int y;
+ int psw;
+ int sp;
+ } cpu_regs;
+
+ rel_time_t dsp_time;
+ time_t spc_time;
+ bool echo_accessed;
+
+ int tempo;
+ int skipped_kon;
+ int skipped_koff;
+ const char* cpu_error;
+
+ int extra_clocks;
+ sample_t* buf_begin;
+ sample_t const* buf_end;
+ sample_t* extra_pos;
+ sample_t extra_buf [extra_size];
+
+ int rom_enabled;
+ uint8_t rom [rom_size];
+ uint8_t hi_ram [rom_size];
+
+ unsigned char cycle_table [256];
+
+ struct
+ {
+ // padding to neutralize address overflow
+ union {
+ uint8_t padding1 [0x100];
+ uint16_t align; // makes compiler align data for 16-bit access
+ } padding1 [1];
+ uint8_t ram [0x10000];
+ uint8_t padding2 [0x100];
+ } ram;
+ };
+ state_t m;
+
+ enum { rom_addr = 0xFFC0 };
+
+ enum { skipping_time = 127 };
+
+ // Value that padding should be filled with
+ enum { cpu_pad_fill = 0xFF };
+
+ enum {
+ r_test = 0x0, r_control = 0x1,
+ r_dspaddr = 0x2, r_dspdata = 0x3,
+ r_cpuio0 = 0x4, r_cpuio1 = 0x5,
+ r_cpuio2 = 0x6, r_cpuio3 = 0x7,
+ r_f8 = 0x8, r_f9 = 0x9,
+ r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
+ r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
+ };
+
+ void timers_loaded();
+ void enable_rom( int enable );
+ void reset_buf();
+ void save_extra();
+ void load_regs( uint8_t const in [reg_count] );
+ void ram_loaded();
+ void regs_loaded();
+ void reset_time_regs();
+ void reset_common( int timer_counter_init );
+
+ Timer* run_timer_ ( Timer* t, rel_time_t );
+ Timer* run_timer ( Timer* t, rel_time_t );
+ int dsp_read ( rel_time_t );
+ void dsp_write ( int data, rel_time_t );
+ void cpu_write_smp_reg_( int data, rel_time_t, int addr );
+ void cpu_write_smp_reg ( int data, rel_time_t, int addr );
+ void cpu_write_high ( int data, int i, rel_time_t );
+ void cpu_write ( int data, int addr, rel_time_t );
+ int cpu_read_smp_reg ( int i, rel_time_t );
+ int cpu_read ( int addr, rel_time_t );
+ unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );
+
+ bool check_echo_access ( int addr );
+ uint8_t* run_until_( time_t end_time );
+
+ struct spc_file_t
+ {
+ char signature [signature_size];
+ uint8_t has_id666;
+ uint8_t version;
+ uint8_t pcl, pch;
+ uint8_t a;
+ uint8_t x;
+ uint8_t y;
+ uint8_t psw;
+ uint8_t sp;
+ char text [212];
+ uint8_t ram [0x10000];
+ uint8_t dsp [128];
+ uint8_t unused [0x40];
+ uint8_t ipl_rom [0x40];
+ };
+
+ static char const signature [signature_size + 1];
+
+ void save_regs( uint8_t out [reg_count] );
+};
+
+#include
+
+inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
+
+inline int SNES_SPC::read_port( time_t t, int port )
+{
+ assert( (unsigned) port < port_count );
+ return run_until_( t ) [port];
+}
+
+inline void SNES_SPC::write_port( time_t t, int port, int data )
+{
+ assert( (unsigned) port < port_count );
+ run_until_( t ) [0x10 + port] = data;
+}
+
+inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }
+
+inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
+
+#if !SPC_NO_COPY_STATE_FUNCS
+inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }
+#endif
+
+#endif
diff --git a/snes_spc/snes_spc/SNES_SPC_misc.cpp b/snes_spc/snes_spc/SNES_SPC_misc.cpp
new file mode 100644
index 00000000..0ef0275e
--- /dev/null
+++ b/snes_spc/snes_spc/SNES_SPC_misc.cpp
@@ -0,0 +1,380 @@
+// SPC emulation support: init, sample buffering, reset, SPC loading
+
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SNES_SPC.h"
+
+#include
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define RAM (m.ram.ram)
+#define REGS (m.smp_regs [0])
+#define REGS_IN (m.smp_regs [1])
+
+// (n ? n : 256)
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
+
+
+//// Init
+
+blargg_err_t SNES_SPC::init()
+{
+ memset( &m, 0, sizeof m );
+ dsp.init( RAM );
+
+ m.tempo = tempo_unit;
+
+ // Most SPC music doesn't need ROM, and almost all the rest only rely
+ // on these two bytes
+ m.rom [0x3E] = 0xFF;
+ m.rom [0x3F] = 0xC0;
+
+ static unsigned char const cycle_table [128] =
+ {// 01 23 45 67 89 AB CD EF
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
+ 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
+ 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
+ 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
+ 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
+ 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
+ 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
+ 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
+ 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
+ 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
+ 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
+ };
+
+ // unpack cycle table
+ for ( int i = 0; i < 128; i++ )
+ {
+ int n = cycle_table [i];
+ m.cycle_table [i * 2 + 0] = n >> 4;
+ m.cycle_table [i * 2 + 1] = n & 0x0F;
+ }
+
+ #if SPC_LESS_ACCURATE
+ memcpy( reg_times, reg_times_, sizeof reg_times );
+ #endif
+
+ reset();
+ return 0;
+}
+
+void SNES_SPC::init_rom( uint8_t const in [rom_size] )
+{
+ memcpy( m.rom, in, sizeof m.rom );
+}
+
+void SNES_SPC::set_tempo( int t )
+{
+ m.tempo = t;
+ int const timer2_shift = 4; // 64 kHz
+ int const other_shift = 3; // 8 kHz
+
+ #if SPC_DISABLE_TEMPO
+ m.timers [2].prescaler = timer2_shift;
+ m.timers [1].prescaler = timer2_shift + other_shift;
+ m.timers [0].prescaler = timer2_shift + other_shift;
+ #else
+ if ( !t )
+ t = 1;
+ int const timer2_rate = 1 << timer2_shift;
+ int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
+ if ( rate < timer2_rate / 4 )
+ rate = timer2_rate / 4; // max 4x tempo
+ m.timers [2].prescaler = rate;
+ m.timers [1].prescaler = rate << other_shift;
+ m.timers [0].prescaler = rate << other_shift;
+ #endif
+}
+
+// Timer registers have been loaded. Applies these to the timers. Does not
+// reset timer prescalers or dividers.
+void SNES_SPC::timers_loaded()
+{
+ int i;
+ for ( i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ t->period = IF_0_THEN_256( REGS [r_t0target + i] );
+ t->enabled = REGS [r_control] >> i & 1;
+ t->counter = REGS_IN [r_t0out + i] & 0x0F;
+ }
+
+ set_tempo( m.tempo );
+}
+
+// Loads registers from unified 16-byte format
+void SNES_SPC::load_regs( uint8_t const in [reg_count] )
+{
+ memcpy( REGS, in, reg_count );
+ memcpy( REGS_IN, REGS, reg_count );
+
+ // These always read back as 0
+ REGS_IN [r_test ] = 0;
+ REGS_IN [r_control ] = 0;
+ REGS_IN [r_t0target] = 0;
+ REGS_IN [r_t1target] = 0;
+ REGS_IN [r_t2target] = 0;
+}
+
+// RAM was just loaded from SPC, with $F0-$FF containing SMP registers
+// and timer counts. Copies these to proper registers.
+void SNES_SPC::ram_loaded()
+{
+ m.rom_enabled = 0;
+ load_regs( &RAM [0xF0] );
+
+ // Put STOP instruction around memory to catch PC underflow/overflow
+ memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
+ memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
+}
+
+// Registers were just loaded. Applies these new values.
+void SNES_SPC::regs_loaded()
+{
+ enable_rom( REGS [r_control] & 0x80 );
+ timers_loaded();
+}
+
+void SNES_SPC::reset_time_regs()
+{
+ m.cpu_error = 0;
+ m.echo_accessed = 0;
+ m.spc_time = 0;
+ m.dsp_time = 0;
+ #if SPC_LESS_ACCURATE
+ m.dsp_time = clocks_per_sample + 1;
+ #endif
+
+ for ( int i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ t->next_time = 1;
+ t->divider = 0;
+ }
+
+ regs_loaded();
+
+ m.extra_clocks = 0;
+ reset_buf();
+}
+
+void SNES_SPC::reset_common( int timer_counter_init )
+{
+ int i;
+ for ( i = 0; i < timer_count; i++ )
+ REGS_IN [r_t0out + i] = timer_counter_init;
+
+ // Run IPL ROM
+ memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
+ m.cpu_regs.pc = rom_addr;
+
+ REGS [r_test ] = 0x0A;
+ REGS [r_control] = 0xB0; // ROM enabled, clear ports
+ for ( i = 0; i < port_count; i++ )
+ REGS_IN [r_cpuio0 + i] = 0;
+
+ reset_time_regs();
+}
+
+void SNES_SPC::soft_reset()
+{
+ reset_common( 0 );
+ dsp.soft_reset();
+}
+
+void SNES_SPC::reset()
+{
+ memset( RAM, 0xFF, 0x10000 );
+ ram_loaded();
+ reset_common( 0x0F );
+ dsp.reset();
+}
+
+char const SNES_SPC::signature [signature_size + 1] =
+ "SNES-SPC700 Sound File Data v0.30\x1A\x1A";
+
+blargg_err_t SNES_SPC::load_spc( void const* data, long size )
+{
+ spc_file_t const* const spc = (spc_file_t const*) data;
+
+ // be sure compiler didn't insert any padding into fle_t
+ assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
+
+ // Check signature and file size
+ if ( size < signature_size || memcmp( spc, signature, 27 ) )
+ return "Not an SPC file";
+
+ if ( size < spc_min_file_size )
+ return "Corrupt SPC file";
+
+ // CPU registers
+ m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
+ m.cpu_regs.a = spc->a;
+ m.cpu_regs.x = spc->x;
+ m.cpu_regs.y = spc->y;
+ m.cpu_regs.psw = spc->psw;
+ m.cpu_regs.sp = spc->sp;
+
+ // RAM and registers
+ memcpy( RAM, spc->ram, 0x10000 );
+ ram_loaded();
+
+ // DSP registers
+ dsp.load( spc->dsp );
+
+ reset_time_regs();
+
+ return 0;
+}
+
+void SNES_SPC::clear_echo()
+{
+ if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
+ {
+ int addr = 0x100 * dsp.read( SPC_DSP::r_esa );
+ int end = addr + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
+ if ( end > 0x10000 )
+ end = 0x10000;
+ memset( &RAM [addr], 0xFF, end - addr );
+ }
+}
+
+
+//// Sample output
+
+void SNES_SPC::reset_buf()
+{
+ // Start with half extra buffer of silence
+ sample_t* out = m.extra_buf;
+ while ( out < &m.extra_buf [extra_size / 2] )
+ *out++ = 0;
+
+ m.extra_pos = out;
+ m.buf_begin = 0;
+
+ dsp.set_output( 0, 0 );
+}
+
+void SNES_SPC::set_output( sample_t* out, int size )
+{
+ require( (size & 1) == 0 ); // size must be even
+
+ m.extra_clocks &= clocks_per_sample - 1;
+ if ( out )
+ {
+ sample_t const* out_end = out + size;
+ m.buf_begin = out;
+ m.buf_end = out_end;
+
+ // Copy extra to output
+ sample_t const* in = m.extra_buf;
+ while ( in < m.extra_pos && out < out_end )
+ *out++ = *in++;
+
+ // Handle output being full already
+ if ( out >= out_end )
+ {
+ // Have DSP write to remaining extra space
+ out = dsp.extra();
+ out_end = &dsp.extra() [extra_size];
+
+ // Copy any remaining extra samples as if DSP wrote them
+ while ( in < m.extra_pos )
+ *out++ = *in++;
+ assert( out <= out_end );
+ }
+
+ dsp.set_output( out, int(out_end - out) );
+ }
+ else
+ {
+ reset_buf();
+ }
+}
+
+void SNES_SPC::save_extra()
+{
+ // Get end pointers
+ sample_t const* main_end = m.buf_end; // end of data written to buf
+ sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra()
+ if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
+ {
+ main_end = dsp_end;
+ dsp_end = dsp.extra(); // nothing in DSP's extra
+ }
+
+ // Copy any extra samples at these ends into extra_buf
+ sample_t* out = m.extra_buf;
+ sample_t const* in;
+ for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
+ *out++ = *in;
+ for ( in = dsp.extra(); in < dsp_end ; in++ )
+ *out++ = *in;
+
+ m.extra_pos = out;
+ assert( out <= &m.extra_buf [extra_size] );
+}
+
+blargg_err_t SNES_SPC::play( int count, sample_t* out )
+{
+ require( (count & 1) == 0 ); // must be even
+ if ( count )
+ {
+ set_output( out, count );
+ end_frame( count * (clocks_per_sample / 2) );
+ }
+
+ const char* err = m.cpu_error;
+ m.cpu_error = 0;
+ return err;
+}
+
+blargg_err_t SNES_SPC::skip( int count )
+{
+ #if SPC_LESS_ACCURATE
+ if ( count > 2 * sample_rate * 2 )
+ {
+ set_output( 0, 0 );
+
+ // Skip a multiple of 4 samples
+ time_t end = count;
+ count = (count & 3) + 1 * sample_rate * 2;
+ end = (end - count) * (clocks_per_sample / 2);
+
+ m.skipped_kon = 0;
+ m.skipped_koff = 0;
+
+ // Preserve DSP and timer synchronization
+ // TODO: verify that this really preserves it
+ int old_dsp_time = m.dsp_time + m.spc_time;
+ m.dsp_time = end - m.spc_time + skipping_time;
+ end_frame( end );
+ m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
+
+ dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon );
+ dsp.write( SPC_DSP::r_kon , m.skipped_kon );
+ clear_echo();
+ }
+ #endif
+
+ return play( count, 0 );
+}
diff --git a/snes_spc/snes_spc/SNES_SPC_state.cpp b/snes_spc/snes_spc/SNES_SPC_state.cpp
new file mode 100644
index 00000000..a8052b65
--- /dev/null
+++ b/snes_spc/snes_spc/SNES_SPC_state.cpp
@@ -0,0 +1,129 @@
+// SPC emulation state save/load: copy_state(), save_spc()
+// Separate file to avoid linking in unless needed
+
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SNES_SPC.h"
+
+#if !SPC_NO_COPY_STATE_FUNCS
+
+#include
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define RAM (m.ram.ram)
+#define REGS (m.smp_regs [0])
+#define REGS_IN (m.smp_regs [1])
+
+void SNES_SPC::save_regs( uint8_t out [reg_count] )
+{
+ // Use current timer counter values
+ for ( int i = 0; i < timer_count; i++ )
+ out [r_t0out + i] = m.timers [i].counter;
+
+ // Last written values
+ memcpy( out, REGS, r_t0out );
+}
+
+void SNES_SPC::init_header( void* spc_out )
+{
+ spc_file_t* const spc = (spc_file_t*) spc_out;
+
+ spc->has_id666 = 26; // has none
+ spc->version = 30;
+ memcpy( spc, signature, sizeof spc->signature );
+ memset( spc->text, 0, sizeof spc->text );
+}
+
+void SNES_SPC::save_spc( void* spc_out )
+{
+ spc_file_t* const spc = (spc_file_t*) spc_out;
+
+ // CPU
+ spc->pcl = (uint8_t) (m.cpu_regs.pc >> 0);
+ spc->pch = (uint8_t) (m.cpu_regs.pc >> 8);
+ spc->a = m.cpu_regs.a;
+ spc->x = m.cpu_regs.x;
+ spc->y = m.cpu_regs.y;
+ spc->psw = m.cpu_regs.psw;
+ spc->sp = m.cpu_regs.sp;
+
+ // RAM, ROM
+ memcpy( spc->ram, RAM, sizeof spc->ram );
+ if ( m.rom_enabled )
+ memcpy( spc->ram + rom_addr, m.hi_ram, sizeof m.hi_ram );
+ memset( spc->unused, 0, sizeof spc->unused );
+ memcpy( spc->ipl_rom, m.rom, sizeof spc->ipl_rom );
+
+ // SMP registers
+ save_regs( &spc->ram [0xF0] );
+ int i;
+ for ( i = 0; i < port_count; i++ )
+ spc->ram [0xF0 + r_cpuio0 + i] = REGS_IN [r_cpuio0 + i];
+
+ // DSP registers
+ for ( i = 0; i < SPC_DSP::register_count; i++ )
+ spc->dsp [i] = dsp.read( i );
+}
+
+void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy )
+{
+ SPC_State_Copier copier( io, copy );
+
+ // Make state data more readable by putting 64K RAM, 16 SMP registers,
+ // then DSP (with its 128 registers) first
+
+ // RAM
+ enable_rom( 0 ); // will get re-enabled if necessary in regs_loaded() below
+ copier.copy( RAM, 0x10000 );
+
+ {
+ // SMP registers
+ uint8_t out_ports [port_count];
+ uint8_t regs [reg_count];
+ memcpy( out_ports, ®S [r_cpuio0], sizeof out_ports );
+ save_regs( regs );
+ copier.copy( regs, sizeof regs );
+ copier.copy( out_ports, sizeof out_ports );
+ load_regs( regs );
+ regs_loaded();
+ memcpy( ®S [r_cpuio0], out_ports, sizeof out_ports );
+ }
+
+ // CPU registers
+ SPC_COPY( uint16_t, m.cpu_regs.pc );
+ SPC_COPY( uint8_t, m.cpu_regs.a );
+ SPC_COPY( uint8_t, m.cpu_regs.x );
+ SPC_COPY( uint8_t, m.cpu_regs.y );
+ SPC_COPY( uint8_t, m.cpu_regs.psw );
+ SPC_COPY( uint8_t, m.cpu_regs.sp );
+ copier.extra();
+
+ SPC_COPY( int16_t, m.spc_time );
+ SPC_COPY( int16_t, m.dsp_time );
+
+ // DSP
+ dsp.copy_state( io, copy );
+
+ // Timers
+ for ( int i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ SPC_COPY( int16_t, t->next_time );
+ SPC_COPY( uint8_t, t->divider );
+ copier.extra();
+ }
+ copier.extra();
+}
+#endif
diff --git a/snes_spc/snes_spc/SPC_CPU.h b/snes_spc/snes_spc/SPC_CPU.h
new file mode 100644
index 00000000..957a4015
--- /dev/null
+++ b/snes_spc/snes_spc/SPC_CPU.h
@@ -0,0 +1,1220 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+//// Memory access
+
+#if SPC_MORE_ACCURACY
+ #define SUSPICIOUS_OPCODE( name ) ((void) 0)
+#else
+ #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" )
+#endif
+
+#define CPU_READ( time, offset, addr )\
+ cpu_read( addr, time + offset )
+
+#define CPU_WRITE( time, offset, addr, data )\
+ cpu_write( data, addr, time + offset )
+
+#if SPC_MORE_ACCURACY
+ #define CPU_READ_TIMER( time, offset, addr, out )\
+ { out = CPU_READ( time, offset, addr ); }
+
+#else
+ // timers are by far the most common thing read from dp
+ #define CPU_READ_TIMER( time, offset, addr_, out )\
+ {\
+ rel_time_t adj_time = time + offset;\
+ int dp_addr = addr_;\
+ int ti = dp_addr - (r_t0out + 0xF0);\
+ if ( (unsigned) ti < timer_count )\
+ {\
+ Timer* t = &m.timers [ti];\
+ if ( adj_time >= t->next_time )\
+ t = run_timer_( t, adj_time );\
+ out = t->counter;\
+ t->counter = 0;\
+ }\
+ else\
+ {\
+ out = ram [dp_addr];\
+ int i = dp_addr - 0xF0;\
+ if ( (unsigned) i < 0x10 )\
+ out = cpu_read_smp_reg( i, adj_time );\
+ }\
+ }
+#endif
+
+#define TIME_ADJ( n ) (n)
+
+#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out )
+#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) )
+#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) )
+
+#define DP_ADDR( addr ) (dp + (addr))
+
+#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out )
+#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
+#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data )
+
+#define READ_PROG16( addr ) GET_LE16( ram + (addr) )
+
+#define SET_PC( n ) (pc = ram + (n))
+#define GET_PC() (int(pc - ram))
+#define READ_PC( pc ) (*(pc))
+#define READ_PC16( pc ) GET_LE16( pc )
+
+// TODO: remove non-wrapping versions?
+#define SPC_NO_SP_WRAPAROUND 0
+
+#define SET_SP( v ) (sp = ram + 0x101 + (v))
+#define GET_SP() (int(sp - 0x101 - ram))
+
+#if SPC_NO_SP_WRAPAROUND
+#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
+#define PUSH( v ) (void) (*--sp = (uint8_t) (v))
+#define POP( out ) (void) ((out) = *sp++)
+
+#else
+#define PUSH16( data )\
+{\
+ int addr = int((sp -= 2) - ram);\
+ if ( addr > 0x100 )\
+ {\
+ SET_LE16( sp, data );\
+ }\
+ else\
+ {\
+ ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
+ sp [1] = (uint8_t) (data >> 8);\
+ sp += 0x100;\
+ }\
+}
+
+#define PUSH( data )\
+{\
+ *--sp = (uint8_t) (data);\
+ if ( sp - ram == 0x100 )\
+ sp += 0x100;\
+}
+
+#define POP( out )\
+{\
+ out = *sp++;\
+ if ( sp - ram == 0x201 )\
+ {\
+ out = sp [-0x101];\
+ sp -= 0x100;\
+ }\
+}
+
+#endif
+
+#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel )
+
+unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time )
+{
+ unsigned addr = READ_PC16( pc );
+ unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13);
+ return t << 8 & 0x100;
+}
+
+//// Status flag handling
+
+// Hex value in name to clarify code and bit shifting.
+// Flag stored in indicated variable during emulation
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // psw
+int const p20 = 0x20; // dp
+int const b10 = 0x10; // psw
+int const h08 = 0x08; // psw
+int const i04 = 0x04; // psw
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+int const nz_neg_mask = 0x880; // either bit set indicates N flag set
+
+#define GET_PSW( out )\
+{\
+ out = psw & ~(n80 | p20 | z02 | c01);\
+ out |= c >> 8 & c01;\
+ out |= dp >> 3 & p20;\
+ out |= ((nz >> 4) | nz) & n80;\
+ if ( !(uint8_t) nz ) out |= z02;\
+}
+
+#define SET_PSW( in )\
+{\
+ psw = in;\
+ c = in << 8;\
+ dp = in << 3 & 0x100;\
+ nz = (in << 4 & 0x800) | (~in & z02);\
+}
+
+SPC_CPU_RUN_FUNC
+{
+ uint8_t* const ram = RAM;
+ int a = m.cpu_regs.a;
+ int x = m.cpu_regs.x;
+ int y = m.cpu_regs.y;
+ uint8_t const* pc;
+ uint8_t* sp;
+ int psw;
+ int c;
+ int nz;
+ int dp;
+
+ SET_PC( m.cpu_regs.pc );
+ SET_SP( m.cpu_regs.sp );
+ SET_PSW( m.cpu_regs.psw );
+
+ goto loop;
+
+
+ // Main loop
+
+cbranch_taken_loop:
+ pc += *(BOOST::int8_t const*) pc;
+inc_pc_loop:
+ pc++;
+loop:
+{
+ unsigned opcode;
+ unsigned data;
+
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ opcode = *pc;
+ if ( (rel_time += m.cycle_table [opcode]) > 0 )
+ goto out_of_time;
+
+ #ifdef SPC_CPU_OPCODE_HOOK
+ SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );
+ #endif
+ /*
+ //SUB_CASE_COUNTER( 1 );
+ #define PROFILE_TIMER_LOOP( op, addr, len )\
+ if ( opcode == op )\
+ {\
+ int cond = (unsigned) ((addr) - 0xFD) < 3 &&\
+ pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\
+ SUB_CASE_COUNTER( op && cond );\
+ }
+
+ PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );
+ PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );
+ PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 );
+ */
+
+ // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
+ data = *++pc;
+ switch ( opcode )
+ {
+
+// Common instructions
+
+#define BRANCH( cond )\
+{\
+ pc++;\
+ pc += (BOOST::int8_t) data;\
+ if ( cond )\
+ goto loop;\
+ pc -= (BOOST::int8_t) data;\
+ rel_time -= 2;\
+ goto loop;\
+}
+
+ case 0xF0: // BEQ
+ BRANCH( !(uint8_t) nz ) // 89% taken
+
+ case 0xD0: // BNE
+ BRANCH( (uint8_t) nz )
+
+ case 0x3F:{// CALL
+ int old_addr = int(GET_PC() + 2);
+ SET_PC( READ_PC16( pc ) );
+ PUSH16( old_addr );
+ goto loop;
+ }
+
+ case 0x6F:// RET
+ #if SPC_NO_SP_WRAPAROUND
+ {
+ SET_PC( GET_LE16( sp ) );
+ sp += 2;
+ }
+ #else
+ {
+ int addr = int(sp - ram);
+ SET_PC( GET_LE16( sp ) );
+ sp += 2;
+ if ( addr < 0x1FF )
+ goto loop;
+
+ SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] );
+ sp -= 0x100;
+ }
+ #endif
+ goto loop;
+
+ case 0xE4: // MOV a,dp
+ ++pc;
+ // 80% from timer
+ READ_DP_TIMER( 0, data, a = nz );
+ goto loop;
+
+ case 0xFA:{// MOV dp,dp
+ int temp;
+ READ_DP_TIMER( -2, data, temp );
+ data = temp + no_read_before_write ;
+ }
+ // fall through
+ case 0x8F:{// MOV dp,#imm
+ int temp = READ_PC( pc + 1 );
+ pc += 2;
+
+ #if !SPC_MORE_ACCURACY
+ {
+ int i = dp + temp;
+ ram [i] = (uint8_t) data;
+ i -= 0xF0;
+ if ( (unsigned) i < 0x10 ) // 76%
+ {
+ REGS [i] = (uint8_t) data;
+
+ // Registers other than $F2 and $F4-$F7
+ //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 )
+ if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%
+ cpu_write_smp_reg( data, rel_time, i );
+ }
+ }
+ #else
+ WRITE_DP( 0, temp, data );
+ #endif
+ goto loop;
+ }
+
+ case 0xC4: // MOV dp,a
+ ++pc;
+ #if !SPC_MORE_ACCURACY
+ {
+ int i = dp + data;
+ ram [i] = (uint8_t) a;
+ i -= 0xF0;
+ if ( (unsigned) i < 0x10 ) // 39%
+ {
+ unsigned sel = i - 2;
+ REGS [i] = (uint8_t) a;
+
+ if ( sel == 1 ) // 51% $F3
+ dsp_write( a, rel_time );
+ else if ( sel > 1 ) // 1% not $F2 or $F3
+ cpu_write_smp_reg_( a, rel_time, i );
+ }
+ }
+ #else
+ WRITE_DP( 0, data, a );
+ #endif
+ goto loop;
+
+#define CASE( n ) case n:
+
+// Define common address modes based on opcode for immediate mode. Execution
+// ends with data set to the address of the operand.
+#define ADDR_MODES_( op )\
+ CASE( op - 0x02 ) /* (X) */\
+ data = x + dp;\
+ pc--;\
+ goto end_##op;\
+ CASE( op + 0x0F ) /* (dp)+Y */\
+ data = READ_PROG16( data + dp ) + y;\
+ goto end_##op;\
+ CASE( op - 0x01 ) /* (dp+X) */\
+ data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
+ goto end_##op;\
+ CASE( op + 0x0E ) /* abs+Y */\
+ data += y;\
+ goto abs_##op;\
+ CASE( op + 0x0D ) /* abs+X */\
+ data += x;\
+ CASE( op - 0x03 ) /* abs */\
+ abs_##op:\
+ data += 0x100 * READ_PC( ++pc );\
+ goto end_##op;\
+ CASE( op + 0x0C ) /* dp+X */\
+ data = (uint8_t) (data + x);
+
+#define ADDR_MODES_NO_DP( op )\
+ ADDR_MODES_( op )\
+ data += dp;\
+ end_##op:
+
+#define ADDR_MODES( op )\
+ ADDR_MODES_( op )\
+ CASE( op - 0x04 ) /* dp */\
+ data += dp;\
+ end_##op:
+
+// 1. 8-bit Data Transmission Commands. Group I
+
+ ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr
+ a = nz = READ( 0, data );
+ goto inc_pc_loop;
+
+ case 0xBF:{// MOV A,(X)+
+ int temp = x + dp;
+ x = (uint8_t) (x + 1);
+ a = nz = READ( -1, temp );
+ goto loop;
+ }
+
+ case 0xE8: // MOV A,imm
+ a = data;
+ nz = data;
+ goto inc_pc_loop;
+
+ case 0xF9: // MOV X,dp+Y
+ data = (uint8_t) (data + y);
+ case 0xF8: // MOV X,dp
+ READ_DP_TIMER( 0, data, x = nz );
+ goto inc_pc_loop;
+
+ case 0xE9: // MOV X,abs
+ data = READ_PC16( pc );
+ ++pc;
+ data = READ( 0, data );
+ case 0xCD: // MOV X,imm
+ x = data;
+ nz = data;
+ goto inc_pc_loop;
+
+ case 0xFB: // MOV Y,dp+X
+ data = (uint8_t) (data + x);
+ case 0xEB: // MOV Y,dp
+ // 70% from timer
+ pc++;
+ READ_DP_TIMER( 0, data, y = nz );
+ goto loop;
+
+ case 0xEC:{// MOV Y,abs
+ int temp = READ_PC16( pc );
+ pc += 2;
+ READ_TIMER( 0, temp, y = nz );
+ //y = nz = READ( 0, temp );
+ goto loop;
+ }
+
+ case 0x8D: // MOV Y,imm
+ y = data;
+ nz = data;
+ goto inc_pc_loop;
+
+// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
+
+ ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A
+ WRITE( 0, data, a );
+ goto inc_pc_loop;
+
+ {
+ int temp;
+ case 0xCC: // MOV abs,Y
+ temp = y;
+ goto mov_abs_temp;
+ case 0xC9: // MOV abs,X
+ temp = x;
+ mov_abs_temp:
+ WRITE( 0, READ_PC16( pc ), temp );
+ pc += 2;
+ goto loop;
+ }
+
+ case 0xD9: // MOV dp+Y,X
+ data = (uint8_t) (data + y);
+ case 0xD8: // MOV dp,X
+ WRITE( 0, data + dp, x );
+ goto inc_pc_loop;
+
+ case 0xDB: // MOV dp+X,Y
+ data = (uint8_t) (data + x);
+ case 0xCB: // MOV dp,Y
+ WRITE( 0, data + dp, y );
+ goto inc_pc_loop;
+
+// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
+
+ case 0x7D: // MOV A,X
+ a = x;
+ nz = x;
+ goto loop;
+
+ case 0xDD: // MOV A,Y
+ a = y;
+ nz = y;
+ goto loop;
+
+ case 0x5D: // MOV X,A
+ x = a;
+ nz = a;
+ goto loop;
+
+ case 0xFD: // MOV Y,A
+ y = a;
+ nz = a;
+ goto loop;
+
+ case 0x9D: // MOV X,SP
+ x = nz = GET_SP();
+ goto loop;
+
+ case 0xBD: // MOV SP,X
+ SET_SP( x );
+ goto loop;
+
+ //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
+
+ case 0xAF: // MOV (X)+,A
+ WRITE_DP( 0, x, a + no_read_before_write );
+ x++;
+ goto loop;
+
+// 5. 8-BIT LOGIC OPERATION COMMANDS
+
+#define LOGICAL_OP( op, func )\
+ ADDR_MODES( op ) /* addr */\
+ data = READ( 0, data );\
+ case op: /* imm */\
+ nz = a func##= data;\
+ goto inc_pc_loop;\
+ { unsigned addr;\
+ case op + 0x11: /* X,Y */\
+ data = READ_DP( -2, y );\
+ addr = x + dp;\
+ goto addr_##op;\
+ case op + 0x01: /* dp,dp */\
+ data = READ_DP( -3, data );\
+ case op + 0x10:{/*dp,imm*/\
+ uint8_t const* addr2 = pc + 1;\
+ pc += 2;\
+ addr = READ_PC( addr2 ) + dp;\
+ }\
+ addr_##op:\
+ nz = data func READ( -1, addr );\
+ WRITE( 0, addr, nz );\
+ goto loop;\
+ }
+
+ LOGICAL_OP( 0x28, & ); // AND
+
+ LOGICAL_OP( 0x08, | ); // OR
+
+ LOGICAL_OP( 0x48, ^ ); // EOR
+
+// 4. 8-BIT ARITHMETIC OPERATION COMMANDS
+
+ ADDR_MODES( 0x68 ) // CMP addr
+ data = READ( 0, data );
+ case 0x68: // CMP imm
+ nz = a - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x79: // CMP (X),(Y)
+ data = READ_DP( -2, y );
+ nz = READ_DP( -1, x ) - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0x69: // CMP dp,dp
+ data = READ_DP( -3, data );
+ case 0x78: // CMP dp,imm
+ nz = READ_DP( -1, READ_PC( ++pc ) ) - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x3E: // CMP X,dp
+ data += dp;
+ goto cmp_x_addr;
+ case 0x1E: // CMP X,abs
+ data = READ_PC16( pc );
+ pc++;
+ cmp_x_addr:
+ data = READ( 0, data );
+ case 0xC8: // CMP X,imm
+ nz = x - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x7E: // CMP Y,dp
+ data += dp;
+ goto cmp_y_addr;
+ case 0x5E: // CMP Y,abs
+ data = READ_PC16( pc );
+ pc++;
+ cmp_y_addr:
+ data = READ( 0, data );
+ case 0xAD: // CMP Y,imm
+ nz = y - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ {
+ int addr;
+ case 0xB9: // SBC (x),(y)
+ case 0x99: // ADC (x),(y)
+ pc--; // compensate for inc later
+ data = READ_DP( -2, y );
+ addr = x + dp;
+ goto adc_addr;
+ case 0xA9: // SBC dp,dp
+ case 0x89: // ADC dp,dp
+ data = READ_DP( -3, data );
+ case 0xB8: // SBC dp,imm
+ case 0x98: // ADC dp,imm
+ addr = READ_PC( ++pc ) + dp;
+ adc_addr:
+ nz = READ( -1, addr );
+ goto adc_data;
+
+// catch ADC and SBC together, then decode later based on operand
+#undef CASE
+#define CASE( n ) case n: case (n) + 0x20:
+ ADDR_MODES( 0x88 ) // ADC/SBC addr
+ data = READ( 0, data );
+ case 0xA8: // SBC imm
+ case 0x88: // ADC imm
+ addr = -1; // A
+ nz = a;
+ adc_data: {
+ int flags;
+ if ( opcode >= 0xA0 ) // SBC
+ data ^= 0xFF;
+
+ flags = data ^ nz;
+ nz += data + (c >> 8 & 1);
+ flags ^= nz;
+
+ psw = (psw & ~(v40 | h08)) |
+ (flags >> 1 & h08) |
+ ((flags + 0x80) >> 2 & v40);
+ c = nz;
+ if ( addr < 0 )
+ {
+ a = (uint8_t) nz;
+ goto inc_pc_loop;
+ }
+ WRITE( 0, addr, /*(uint8_t)*/ nz );
+ goto inc_pc_loop;
+ }
+
+ }
+
+// 6. ADDITION & SUBTRACTION COMMANDS
+
+#define INC_DEC_REG( reg, op )\
+ nz = reg op;\
+ reg = (uint8_t) nz;\
+ goto loop;
+
+ case 0xBC: INC_DEC_REG( a, + 1 ) // INC A
+ case 0x3D: INC_DEC_REG( x, + 1 ) // INC X
+ case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y
+
+ case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A
+ case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X
+ case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y
+
+ case 0x9B: // DEC dp+X
+ case 0xBB: // INC dp+X
+ data = (uint8_t) (data + x);
+ case 0x8B: // DEC dp
+ case 0xAB: // INC dp
+ data += dp;
+ goto inc_abs;
+ case 0x8C: // DEC abs
+ case 0xAC: // INC abs
+ data = READ_PC16( pc );
+ pc++;
+ inc_abs:
+ nz = (opcode >> 4 & 2) - 1;
+ nz += READ( -1, data );
+ WRITE( 0, data, /*(uint8_t)*/ nz );
+ goto inc_pc_loop;
+
+// 7. SHIFT, ROTATION COMMANDS
+
+ case 0x5C: // LSR A
+ c = 0;
+ case 0x7C:{// ROR A
+ nz = (c >> 1 & 0x80) | (a >> 1);
+ c = a << 8;
+ a = nz;
+ goto loop;
+ }
+
+ case 0x1C: // ASL A
+ c = 0;
+ case 0x3C:{// ROL A
+ int temp = c >> 8 & 1;
+ c = a << 1;
+ nz = c | temp;
+ a = (uint8_t) nz;
+ goto loop;
+ }
+
+ case 0x0B: // ASL dp
+ c = 0;
+ data += dp;
+ goto rol_mem;
+ case 0x1B: // ASL dp+X
+ c = 0;
+ case 0x3B: // ROL dp+X
+ data = (uint8_t) (data + x);
+ case 0x2B: // ROL dp
+ data += dp;
+ goto rol_mem;
+ case 0x0C: // ASL abs
+ c = 0;
+ case 0x2C: // ROL abs
+ data = READ_PC16( pc );
+ pc++;
+ rol_mem:
+ nz = c >> 8 & 1;
+ nz |= (c = READ( -1, data ) << 1);
+ WRITE( 0, data, /*(uint8_t)*/ nz );
+ goto inc_pc_loop;
+
+ case 0x4B: // LSR dp
+ c = 0;
+ data += dp;
+ goto ror_mem;
+ case 0x5B: // LSR dp+X
+ c = 0;
+ case 0x7B: // ROR dp+X
+ data = (uint8_t) (data + x);
+ case 0x6B: // ROR dp
+ data += dp;
+ goto ror_mem;
+ case 0x4C: // LSR abs
+ c = 0;
+ case 0x6C: // ROR abs
+ data = READ_PC16( pc );
+ pc++;
+ ror_mem: {
+ int temp = READ( -1, data );
+ nz = (c >> 1 & 0x80) | (temp >> 1);
+ c = temp << 8;
+ WRITE( 0, data, nz );
+ goto inc_pc_loop;
+ }
+
+ case 0x9F: // XCN
+ nz = a = (a >> 4) | (uint8_t) (a << 4);
+ goto loop;
+
+// 8. 16-BIT TRANSMISION COMMANDS
+
+ case 0xBA: // MOVW YA,dp
+ a = READ_DP( -2, data );
+ nz = (a & 0x7F) | (a >> 1);
+ y = READ_DP( 0, (uint8_t) (data + 1) );
+ nz |= y;
+ goto inc_pc_loop;
+
+ case 0xDA: // MOVW dp,YA
+ WRITE_DP( -1, data, a );
+ WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );
+ goto inc_pc_loop;
+
+// 9. 16-BIT OPERATION COMMANDS
+
+ case 0x3A: // INCW dp
+ case 0x1A:{// DECW dp
+ int temp;
+ // low byte
+ data += dp;
+ temp = READ( -3, data );
+ temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW
+ nz = ((temp >> 1) | temp) & 0x7F;
+ WRITE( -2, data, /*(uint8_t)*/ temp );
+
+ // high byte
+ data = (uint8_t) (data + 1) + dp;
+ temp = (uint8_t) ((temp >> 8) + READ( -1, data ));
+ nz |= temp;
+ WRITE( 0, data, temp );
+
+ goto inc_pc_loop;
+ }
+
+ case 0x7A: // ADDW YA,dp
+ case 0x9A:{// SUBW YA,dp
+ int lo = READ_DP( -2, data );
+ int hi = READ_DP( 0, (uint8_t) (data + 1) );
+ int result;
+ int flags;
+
+ if ( opcode == 0x9A ) // SUBW
+ {
+ lo = (lo ^ 0xFF) + 1;
+ hi ^= 0xFF;
+ }
+
+ lo += a;
+ result = y + hi + (lo >> 8);
+ flags = hi ^ y ^ result;
+
+ psw = (psw & ~(v40 | h08)) |
+ (flags >> 1 & h08) |
+ ((flags + 0x80) >> 2 & v40);
+ c = result;
+ a = (uint8_t) lo;
+ result = (uint8_t) result;
+ y = result;
+ nz = (((lo >> 1) | lo) & 0x7F) | result;
+
+ goto inc_pc_loop;
+ }
+
+ case 0x5A: { // CMPW YA,dp
+ int temp = a - READ_DP( -1, data );
+ nz = ((temp >> 1) | temp) & 0x7F;
+ temp = y + (temp >> 8);
+ temp -= READ_DP( 0, (uint8_t) (data + 1) );
+ nz |= temp;
+ c = ~temp;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+ }
+
+// 10. MULTIPLICATION & DIVISON COMMANDS
+
+ case 0xCF: { // MUL YA
+ unsigned temp = y * a;
+ a = (uint8_t) temp;
+ nz = ((temp >> 1) | temp) & 0x7F;
+ y = temp >> 8;
+ nz |= y;
+ goto loop;
+ }
+
+ case 0x9E: // DIV YA,X
+ {
+ unsigned ya = y * 0x100 + a;
+
+ psw &= ~(h08 | v40);
+
+ if ( y >= x )
+ psw |= v40;
+
+ if ( (y & 15) >= (x & 15) )
+ psw |= h08;
+
+ if ( y < x * 2 )
+ {
+ a = ya / x;
+ y = ya - a * x;
+ }
+ else
+ {
+ a = 255 - (ya - x * 0x200) / (256 - x);
+ y = x + (ya - x * 0x200) % (256 - x);
+ }
+
+ nz = (uint8_t) a;
+ a = (uint8_t) a;
+
+ goto loop;
+ }
+
+// 11. DECIMAL COMPENSATION COMMANDS
+
+ case 0xDF: // DAA
+ SUSPICIOUS_OPCODE( "DAA" );
+ if ( a > 0x99 || c & 0x100 )
+ {
+ a += 0x60;
+ c = 0x100;
+ }
+
+ if ( (a & 0x0F) > 9 || psw & h08 )
+ a += 0x06;
+
+ nz = a;
+ a = (uint8_t) a;
+ goto loop;
+
+ case 0xBE: // DAS
+ SUSPICIOUS_OPCODE( "DAS" );
+ if ( a > 0x99 || !(c & 0x100) )
+ {
+ a -= 0x60;
+ c = 0;
+ }
+
+ if ( (a & 0x0F) > 9 || !(psw & h08) )
+ a -= 0x06;
+
+ nz = a;
+ a = (uint8_t) a;
+ goto loop;
+
+// 12. BRANCHING COMMANDS
+
+ case 0x2F: // BRA rel
+ pc += (BOOST::int8_t) data;
+ goto inc_pc_loop;
+
+ case 0x30: // BMI
+ BRANCH( (nz & nz_neg_mask) )
+
+ case 0x10: // BPL
+ BRANCH( !(nz & nz_neg_mask) )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+ case 0x70: // BVS
+ BRANCH( psw & v40 )
+
+ case 0x50: // BVC
+ BRANCH( !(psw & v40) )
+
+ #define CBRANCH( cond )\
+ {\
+ pc++;\
+ if ( cond )\
+ goto cbranch_taken_loop;\
+ rel_time -= 2;\
+ goto inc_pc_loop;\
+ }
+
+ case 0x03: // BBS dp.bit,rel
+ case 0x23:
+ case 0x43:
+ case 0x63:
+ case 0x83:
+ case 0xA3:
+ case 0xC3:
+ case 0xE3:
+ CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
+
+ case 0x13: // BBC dp.bit,rel
+ case 0x33:
+ case 0x53:
+ case 0x73:
+ case 0x93:
+ case 0xB3:
+ case 0xD3:
+ case 0xF3:
+ CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )
+
+ case 0xDE: // CBNE dp+X,rel
+ data = (uint8_t) (data + x);
+ // fall through
+ case 0x2E:{// CBNE dp,rel
+ int temp;
+ // 61% from timer
+ READ_DP_TIMER( -4, data, temp );
+ CBRANCH( temp != a )
+ }
+
+ case 0x6E: { // DBNZ dp,rel
+ unsigned temp = READ_DP( -4, data ) - 1;
+ WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );
+ CBRANCH( temp )
+ }
+
+ case 0xFE: // DBNZ Y,rel
+ y = (uint8_t) (y - 1);
+ BRANCH( y )
+
+ case 0x1F: // JMP [abs+X]
+ SET_PC( READ_PC16( pc ) + x );
+ // fall through
+ case 0x5F: // JMP abs
+ SET_PC( READ_PC16( pc ) );
+ goto loop;
+
+// 13. SUB-ROUTINE CALL RETURN COMMANDS
+
+ case 0x0F:{// BRK
+ int temp;
+ int ret_addr = GET_PC();
+ SUSPICIOUS_OPCODE( "BRK" );
+ SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified
+ PUSH16( ret_addr );
+ GET_PSW( temp );
+ psw = (psw | b10) & ~i04;
+ PUSH( temp );
+ goto loop;
+ }
+
+ case 0x4F:{// PCALL offset
+ int ret_addr = GET_PC() + 1;
+ SET_PC( 0xFF00 | data );
+ PUSH16( ret_addr );
+ goto loop;
+ }
+
+ case 0x01: // TCALL n
+ case 0x11:
+ case 0x21:
+ case 0x31:
+ case 0x41:
+ case 0x51:
+ case 0x61:
+ case 0x71:
+ case 0x81:
+ case 0x91:
+ case 0xA1:
+ case 0xB1:
+ case 0xC1:
+ case 0xD1:
+ case 0xE1:
+ case 0xF1: {
+ int ret_addr = GET_PC();
+ SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
+ PUSH16( ret_addr );
+ goto loop;
+ }
+
+// 14. STACK OPERATION COMMANDS
+
+ {
+ int temp;
+ case 0x7F: // RET1
+ temp = *sp;
+ SET_PC( GET_LE16( sp + 1 ) );
+ sp += 3;
+ goto set_psw;
+ case 0x8E: // POP PSW
+ POP( temp );
+ set_psw:
+ SET_PSW( temp );
+ goto loop;
+ }
+
+ case 0x0D: { // PUSH PSW
+ int temp;
+ GET_PSW( temp );
+ PUSH( temp );
+ goto loop;
+ }
+
+ case 0x2D: // PUSH A
+ PUSH( a );
+ goto loop;
+
+ case 0x4D: // PUSH X
+ PUSH( x );
+ goto loop;
+
+ case 0x6D: // PUSH Y
+ PUSH( y );
+ goto loop;
+
+ case 0xAE: // POP A
+ POP( a );
+ goto loop;
+
+ case 0xCE: // POP X
+ POP( x );
+ goto loop;
+
+ case 0xEE: // POP Y
+ POP( y );
+ goto loop;
+
+// 15. BIT OPERATION COMMANDS
+
+ case 0x02: // SET1
+ case 0x22:
+ case 0x42:
+ case 0x62:
+ case 0x82:
+ case 0xA2:
+ case 0xC2:
+ case 0xE2:
+ case 0x12: // CLR1
+ case 0x32:
+ case 0x52:
+ case 0x72:
+ case 0x92:
+ case 0xB2:
+ case 0xD2:
+ case 0xF2: {
+ int bit = 1 << (opcode >> 5);
+ int mask = ~bit;
+ if ( opcode & 0x10 )
+ bit = 0;
+ data += dp;
+ WRITE( 0, data, (READ( -1, data ) & mask) | bit );
+ goto inc_pc_loop;
+ }
+
+ case 0x0E: // TSET1 abs
+ case 0x4E: // TCLR1 abs
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -2, data );
+ nz = (uint8_t) (a - temp);
+ temp &= ~a;
+ if ( opcode == 0x0E )
+ temp |= a;
+ WRITE( 0, data, temp );
+ }
+ goto loop;
+
+ case 0x4A: // AND1 C,mem.bit
+ c &= MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+ case 0x6A: // AND1 C,/mem.bit
+ c &= ~MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+ case 0x0A: // OR1 C,mem.bit
+ c |= MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0x2A: // OR1 C,/mem.bit
+ c |= ~MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0x8A: // EOR1 C,mem.bit
+ c ^= MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0xEA: // NOT1 mem.bit
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -1, data & 0x1FFF );
+ temp ^= 1 << (data >> 13);
+ WRITE( 0, data & 0x1FFF, temp );
+ }
+ goto loop;
+
+ case 0xCA: // MOV1 mem.bit,C
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -2, data & 0x1FFF );
+ unsigned bit = data >> 13;
+ temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit);
+ WRITE( 0, data & 0x1FFF, temp + no_read_before_write );
+ }
+ goto loop;
+
+ case 0xAA: // MOV1 C,mem.bit
+ c = MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+// 16. PROGRAM PSW FLAG OPERATION COMMANDS
+
+ case 0x60: // CLRC
+ c = 0;
+ goto loop;
+
+ case 0x80: // SETC
+ c = ~0;
+ goto loop;
+
+ case 0xED: // NOTC
+ c ^= 0x100;
+ goto loop;
+
+ case 0xE0: // CLRV
+ psw &= ~(v40 | h08);
+ goto loop;
+
+ case 0x20: // CLRP
+ dp = 0;
+ goto loop;
+
+ case 0x40: // SETP
+ dp = 0x100;
+ goto loop;
+
+ case 0xA0: // EI
+ SUSPICIOUS_OPCODE( "EI" );
+ psw |= i04;
+ goto loop;
+
+ case 0xC0: // DI
+ SUSPICIOUS_OPCODE( "DI" );
+ psw &= ~i04;
+ goto loop;
+
+// 17. OTHER COMMANDS
+
+ case 0x00: // NOP
+ goto loop;
+
+ case 0xFF:{// STOP
+ // handle PC wrap-around
+ unsigned addr = GET_PC() - 1;
+ if ( addr >= 0x10000 )
+ {
+ addr &= 0xFFFF;
+ SET_PC( addr );
+ dprintf( "SPC: PC wrapped around\n" );
+ goto loop;
+ }
+ }
+ // fall through
+ case 0xEF: // SLEEP
+ SUSPICIOUS_OPCODE( "STOP/SLEEP" );
+ --pc;
+ rel_time = 0;
+ m.cpu_error = "SPC emulation error";
+ goto stop;
+ } // switch
+
+ assert( 0 ); // catch any unhandled instructions
+}
+out_of_time:
+ rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode
+stop:
+
+ // Uncache registers
+ if ( GET_PC() >= 0x10000 )
+ dprintf( "SPC: PC wrapped around\n" );
+ m.cpu_regs.pc = (uint16_t) GET_PC();
+ m.cpu_regs.sp = ( uint8_t) GET_SP();
+ m.cpu_regs.a = ( uint8_t) a;
+ m.cpu_regs.x = ( uint8_t) x;
+ m.cpu_regs.y = ( uint8_t) y;
+ {
+ int temp;
+ GET_PSW( temp );
+ m.cpu_regs.psw = (uint8_t) temp;
+ }
+}
+SPC_CPU_RUN_FUNC_END
diff --git a/snes_spc/snes_spc/SPC_DSP.cpp b/snes_spc/snes_spc/SPC_DSP.cpp
new file mode 100644
index 00000000..96719683
--- /dev/null
+++ b/snes_spc/snes_spc/SPC_DSP.cpp
@@ -0,0 +1,703 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SPC_DSP.h"
+
+#include "blargg_endian.h"
+#include
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+#if INT_MAX < 0x7FFFFFFF
+ #error "Requires that int type have at least 32 bits"
+#endif
+
+
+// TODO: add to blargg_endian.h
+#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))
+#define GET_LE16A( addr ) GET_LE16( addr )
+#define SET_LE16A( addr, data ) SET_LE16( addr, data )
+
+static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
+{
+ 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,
+ 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,
+ 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,
+ 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,
+ 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,
+ 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,
+ 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,
+ 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF
+};
+
+// if ( io < -32768 ) io = -32768;
+// if ( io > 32767 ) io = 32767;
+#define CLAMP16( io )\
+{\
+ if ( (int16_t) io != io )\
+ io = (io >> 31) ^ 0x7FFF;\
+}
+
+// Access global DSP register
+#define REG(n) m.regs [r_##n]
+
+// Access voice DSP register
+#define VREG(r,n) r [v_##n]
+
+#define WRITE_SAMPLES( l, r, out ) \
+{\
+ out [0] = l;\
+ out [1] = r;\
+ out += 2;\
+ if ( out >= m.out_end )\
+ {\
+ check( out == m.out_end );\
+ check( m.out_end != &m.extra [extra_size] || \
+ (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
+ out = m.extra;\
+ m.out_end = &m.extra [extra_size];\
+ }\
+}\
+
+void SPC_DSP::set_output( sample_t* out, int size )
+{
+ require( (size & 1) == 0 ); // must be even
+ if ( !out )
+ {
+ out = m.extra;
+ size = extra_size;
+ }
+ m.out_begin = out;
+ m.out = out;
+ m.out_end = out + size;
+}
+
+// Volume registers and efb are signed! Easy to forget int8_t cast.
+// Prefixes are to avoid accidental use of locals with same names.
+
+// Interleved gauss table (to improve cache coherency)
+// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)]
+static short const interleved_gauss [512] =
+{
+ 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
+ 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
+ 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
+ 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
+ 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
+ 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
+ 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
+ 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
+ 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
+ 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
+ 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
+ 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
+ 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
+ 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
+ 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
+ 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
+ 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
+ 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
+ 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
+ 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
+ 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
+ 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
+ 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
+ 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
+ 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
+ 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
+ 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
+ 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
+ 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
+ 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
+ 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
+ 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
+};
+
+
+//// Counters
+
+#define RATE( rate, div )\
+ (rate >= div ? rate / div * 8 - 1 : rate - 1)
+
+static unsigned const counter_mask [32] =
+{
+ RATE( 2,2), RATE(2048,4), RATE(1536,3),
+ RATE(1280,5), RATE(1024,4), RATE( 768,3),
+ RATE( 640,5), RATE( 512,4), RATE( 384,3),
+ RATE( 320,5), RATE( 256,4), RATE( 192,3),
+ RATE( 160,5), RATE( 128,4), RATE( 96,3),
+ RATE( 80,5), RATE( 64,4), RATE( 48,3),
+ RATE( 40,5), RATE( 32,4), RATE( 24,3),
+ RATE( 20,5), RATE( 16,4), RATE( 12,3),
+ RATE( 10,5), RATE( 8,4), RATE( 6,3),
+ RATE( 5,5), RATE( 4,4), RATE( 3,3),
+ RATE( 2,4),
+ RATE( 1,4)
+};
+#undef RATE
+
+inline void SPC_DSP::init_counter()
+{
+ // counters start out with this synchronization
+ m.counters [0] = 1;
+ m.counters [1] = 0;
+ m.counters [2] = -0x20u;
+ m.counters [3] = 0x0B;
+
+ int n = 2;
+ for ( int i = 1; i < 32; i++ )
+ {
+ m.counter_select [i] = &m.counters [n];
+ if ( !--n )
+ n = 3;
+ }
+ m.counter_select [ 0] = &m.counters [0];
+ m.counter_select [30] = &m.counters [2];
+}
+
+inline void SPC_DSP::run_counter( int i )
+{
+ int n = m.counters [i];
+ if ( !(n-- & 7) )
+ n -= 6 - i;
+ m.counters [i] = n;
+}
+
+#define READ_COUNTER( rate )\
+ (*m.counter_select [rate] & counter_mask [rate])
+
+
+//// Emulation
+
+void SPC_DSP::run( int clock_count )
+{
+ int new_phase = m.phase + clock_count;
+ int count = new_phase >> 5;
+ m.phase = new_phase & 31;
+ if ( !count )
+ return;
+
+ uint8_t* const ram = m.ram;
+ uint8_t const* const dir = &ram [REG(dir) * 0x100];
+ int const slow_gaussian = (REG(pmon) >> 1) | REG(non);
+ int const noise_rate = REG(flg) & 0x1F;
+
+ // Global volume
+ int mvoll = (int8_t) REG(mvoll);
+ int mvolr = (int8_t) REG(mvolr);
+ if ( mvoll * mvolr < m.surround_threshold )
+ mvoll = -mvoll; // eliminate surround
+
+ do
+ {
+ // KON/KOFF reading
+ if ( (m.every_other_sample ^= 1) != 0 )
+ {
+ m.new_kon &= ~m.kon;
+ m.kon = m.new_kon;
+ m.t_koff = REG(koff);
+ }
+
+ run_counter( 1 );
+ run_counter( 2 );
+ run_counter( 3 );
+
+ // Noise
+ if ( !READ_COUNTER( noise_rate ) )
+ {
+ int feedback = (m.noise << 13) ^ (m.noise << 14);
+ m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
+ }
+
+ // Voices
+ int pmon_input = 0;
+ int main_out_l = 0;
+ int main_out_r = 0;
+ int echo_out_l = 0;
+ int echo_out_r = 0;
+ voice_t* v = m.voices;
+ uint8_t* v_regs = m.regs;
+ int vbit = 1;
+ do
+ {
+ #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] )
+
+ int brr_header = ram [v->brr_addr];
+ int kon_delay = v->kon_delay;
+
+ // Pitch
+ int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF;
+ if ( REG(pmon) & vbit )
+ pitch += ((pmon_input >> 5) * pitch) >> 10;
+
+ // KON phases
+ if ( --kon_delay >= 0 )
+ {
+ v->kon_delay = kon_delay;
+
+ // Get ready to start BRR decoding on next sample
+ if ( kon_delay == 4 )
+ {
+ v->brr_addr = SAMPLE_PTR( 0 );
+ v->brr_offset = 1;
+ v->buf_pos = v->buf;
+ brr_header = 0; // header is ignored on this sample
+ }
+
+ // Envelope is never run during KON
+ v->env = 0;
+ v->hidden_env = 0;
+
+ // Disable BRR decoding until last three samples
+ v->interp_pos = (kon_delay & 3 ? 0x4000 : 0);
+
+ // Pitch is never added during KON
+ pitch = 0;
+ }
+
+ int env = v->env;
+
+ // Gaussian interpolation
+ {
+ int output = 0;
+ VREG(v_regs,envx) = (uint8_t) (env >> 4);
+ if ( env )
+ {
+ // Make pointers into gaussian based on fractional position between samples
+ int offset = (unsigned) v->interp_pos >> 3 & 0x1FE;
+ short const* fwd = interleved_gauss + offset;
+ short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian
+
+ int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12];
+
+ if ( !(slow_gaussian & vbit) ) // 99%
+ {
+ // Faster approximation when exact sample value isn't necessary for pitch mod
+ output = (fwd [0] * in [0] +
+ fwd [1] * in [1] +
+ rev [1] * in [2] +
+ rev [0] * in [3]) >> 11;
+ output = (output * env) >> 11;
+ }
+ else
+ {
+ output = (int16_t) (m.noise * 2);
+ if ( !(REG(non) & vbit) )
+ {
+ output = (fwd [0] * in [0]) >> 11;
+ output += (fwd [1] * in [1]) >> 11;
+ output += (rev [1] * in [2]) >> 11;
+ output = (int16_t) output;
+ output += (rev [0] * in [3]) >> 11;
+
+ CLAMP16( output );
+ output &= ~1;
+ }
+ output = (output * env) >> 11 & ~1;
+ }
+
+ // Output
+ int l = output * v->volume [0];
+ int r = output * v->volume [1];
+
+ main_out_l += l;
+ main_out_r += r;
+
+ if ( REG(eon) & vbit )
+ {
+ echo_out_l += l;
+ echo_out_r += r;
+ }
+ }
+
+ pmon_input = output;
+ VREG(v_regs,outx) = (uint8_t) (output >> 8);
+ }
+
+ // Soft reset or end of sample
+ if ( REG(flg) & 0x80 || (brr_header & 3) == 1 )
+ {
+ v->env_mode = env_release;
+ env = 0;
+ }
+
+ if ( m.every_other_sample )
+ {
+ // KOFF
+ if ( m.t_koff & vbit )
+ v->env_mode = env_release;
+
+ // KON
+ if ( m.kon & vbit )
+ {
+ v->kon_delay = 5;
+ v->env_mode = env_attack;
+ REG(endx) &= ~vbit;
+ }
+ }
+
+ // Envelope
+ if ( !v->kon_delay )
+ {
+ if ( v->env_mode == env_release ) // 97%
+ {
+ env -= 0x8;
+ v->env = env;
+ if ( env <= 0 )
+ {
+ v->env = 0;
+ goto skip_brr; // no BRR decoding for you!
+ }
+ }
+ else // 3%
+ {
+ int rate;
+ int const adsr0 = VREG(v_regs,adsr0);
+ int env_data = VREG(v_regs,adsr1);
+ if ( adsr0 >= 0x80 ) // 97% ADSR
+ {
+ if ( v->env_mode > env_decay ) // 89%
+ {
+ env--;
+ env -= env >> 8;
+ rate = env_data & 0x1F;
+
+ // optimized handling
+ v->hidden_env = env;
+ if ( READ_COUNTER( rate ) )
+ goto exit_env;
+ v->env = env;
+ goto exit_env;
+ }
+ else if ( v->env_mode == env_decay )
+ {
+ env--;
+ env -= env >> 8;
+ rate = (adsr0 >> 3 & 0x0E) + 0x10;
+ }
+ else // env_attack
+ {
+ rate = (adsr0 & 0x0F) * 2 + 1;
+ env += rate < 31 ? 0x20 : 0x400;
+ }
+ }
+ else // GAIN
+ {
+ int mode;
+ env_data = VREG(v_regs,gain);
+ mode = env_data >> 5;
+ if ( mode < 4 ) // direct
+ {
+ env = env_data * 0x10;
+ rate = 31;
+ }
+ else
+ {
+ rate = env_data & 0x1F;
+ if ( mode == 4 ) // 4: linear decrease
+ {
+ env -= 0x20;
+ }
+ else if ( mode < 6 ) // 5: exponential decrease
+ {
+ env--;
+ env -= env >> 8;
+ }
+ else // 6,7: linear increase
+ {
+ env += 0x20;
+ if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )
+ env += 0x8 - 0x20; // 7: two-slope linear increase
+ }
+ }
+ }
+
+ // Sustain level
+ if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
+ v->env_mode = env_sustain;
+
+ v->hidden_env = env;
+
+ // unsigned cast because linear decrease going negative also triggers this
+ if ( (unsigned) env > 0x7FF )
+ {
+ env = (env < 0 ? 0 : 0x7FF);
+ if ( v->env_mode == env_attack )
+ v->env_mode = env_decay;
+ }
+
+ if ( !READ_COUNTER( rate ) )
+ v->env = env; // nothing else is controlled by the counter
+ }
+ }
+ exit_env:
+
+ {
+ // Apply pitch
+ int old_pos = v->interp_pos;
+ int interp_pos = (old_pos & 0x3FFF) + pitch;
+ if ( interp_pos > 0x7FFF )
+ interp_pos = 0x7FFF;
+ v->interp_pos = interp_pos;
+
+ // BRR decode if necessary
+ if ( old_pos >= 0x4000 )
+ {
+ // Arrange the four input nybbles in 0xABCD order for easy decoding
+ int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 +
+ ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
+
+ // Advance read position
+ int const brr_block_size = 9;
+ int brr_offset = v->brr_offset;
+ if ( (brr_offset += 2) >= brr_block_size )
+ {
+ // Next BRR block
+ int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
+ assert( brr_offset == brr_block_size );
+ if ( brr_header & 1 )
+ {
+ brr_addr = SAMPLE_PTR( 1 );
+ if ( !v->kon_delay )
+ REG(endx) |= vbit;
+ }
+ v->brr_addr = brr_addr;
+ brr_offset = 1;
+ }
+ v->brr_offset = brr_offset;
+
+ // Decode
+
+ // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11
+ static unsigned char const shifts [16 * 2] = {
+ 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
+ };
+ int const scale = brr_header >> 4;
+ int const right_shift = shifts [scale];
+ int const left_shift = shifts [scale + 16];
+
+ // Write to next four samples in circular buffer
+ int* pos = v->buf_pos;
+ int* end;
+
+ // Decode four samples
+ for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
+ {
+ // Extract upper nybble and scale appropriately
+ int s = ((int16_t) nybbles >> right_shift) << left_shift;
+
+ // Apply IIR filter (8 is the most commonly used)
+ int const filter = brr_header & 0x0C;
+ int const p1 = pos [brr_buf_size - 1];
+ int const p2 = pos [brr_buf_size - 2] >> 1;
+ if ( filter >= 8 )
+ {
+ s += p1;
+ s -= p2;
+ if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875
+ {
+ s += p2 >> 4;
+ s += (p1 * -3) >> 6;
+ }
+ else // s += p1 * 0.8984375 - p2 * 0.40625
+ {
+ s += (p1 * -13) >> 7;
+ s += (p2 * 3) >> 4;
+ }
+ }
+ else if ( filter ) // s += p1 * 0.46875
+ {
+ s += p1 >> 1;
+ s += (-p1) >> 5;
+ }
+
+ // Adjust and write sample
+ CLAMP16( s );
+ s = (int16_t) (s * 2);
+ pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around
+ }
+
+ if ( pos >= &v->buf [brr_buf_size] )
+ pos = v->buf;
+ v->buf_pos = pos;
+ }
+ }
+skip_brr:
+ // Next voice
+ vbit <<= 1;
+ v_regs += 0x10;
+ v++;
+ }
+ while ( vbit < 0x100 );
+
+ // Echo position
+ int echo_offset = m.echo_offset;
+ uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF];
+ if ( !echo_offset )
+ m.echo_length = (REG(edl) & 0x0F) * 0x800;
+ echo_offset += 4;
+ if ( echo_offset >= m.echo_length )
+ echo_offset = 0;
+ m.echo_offset = echo_offset;
+
+ // FIR
+ int echo_in_l = GET_LE16SA( echo_ptr + 0 );
+ int echo_in_r = GET_LE16SA( echo_ptr + 2 );
+
+ int (*echo_hist_pos) [2] = m.echo_hist_pos;
+ if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] )
+ echo_hist_pos = m.echo_hist;
+ m.echo_hist_pos = echo_hist_pos;
+
+ echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l;
+ echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r;
+
+ #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10))
+ echo_in_l = CALC_FIR_( 7, echo_in_l );
+ echo_in_r = CALC_FIR_( 7, echo_in_r );
+
+ #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] )
+ #define DO_FIR( i )\
+ echo_in_l += CALC_FIR( i, 0 );\
+ echo_in_r += CALC_FIR( i, 1 );
+ DO_FIR( 0 );
+ DO_FIR( 1 );
+ DO_FIR( 2 );
+ #if defined (__MWERKS__) && __MWERKS__ < 0x3200
+ __eieio(); // keeps compiler from stupidly "caching" things in memory
+ #endif
+ DO_FIR( 3 );
+ DO_FIR( 4 );
+ DO_FIR( 5 );
+ DO_FIR( 6 );
+
+ // Echo out
+ if ( !(REG(flg) & 0x20) )
+ {
+ int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14);
+ int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14);
+
+ // just to help pass more validation tests
+ #if SPC_MORE_ACCURACY
+ l &= ~1;
+ r &= ~1;
+ #endif
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ SET_LE16A( echo_ptr + 0, l );
+ SET_LE16A( echo_ptr + 2, r );
+ }
+
+ // Sound out
+ int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14;
+ int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14;
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ if ( (REG(flg) & 0x40) )
+ {
+ l = 0;
+ r = 0;
+ }
+
+ sample_t* out = m.out;
+ WRITE_SAMPLES( l, r, out );
+ m.out = out;
+ }
+ while ( --count );
+}
+
+
+//// Setup
+
+void SPC_DSP::mute_voices( int mask )
+{
+ m.mute_mask = mask;
+ for ( int i = 0; i < voice_count; i++ )
+ {
+ m.voices [i].enabled = (mask >> i & 1) - 1;
+ update_voice_vol( i * 0x10 );
+ }
+}
+
+void SPC_DSP::init( void* ram_64k )
+{
+ m.ram = (uint8_t*) ram_64k;
+ mute_voices( 0 );
+ disable_surround( false );
+ set_output( 0, 0 );
+ reset();
+
+ #ifndef NDEBUG
+ // be sure this sign-extends
+ assert( (int16_t) 0x8000 == -0x8000 );
+
+ // be sure right shift preserves sign
+ assert( (-1 >> 1) == -1 );
+
+ // check clamp macro
+ int i;
+ i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
+ i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
+
+ blargg_verify_byte_order();
+ #endif
+}
+
+void SPC_DSP::soft_reset_common()
+{
+ require( m.ram ); // init() must have been called already
+
+ m.noise = 0x4000;
+ m.echo_hist_pos = m.echo_hist;
+ m.every_other_sample = 1;
+ m.echo_offset = 0;
+ m.phase = 0;
+
+ init_counter();
+}
+
+void SPC_DSP::soft_reset()
+{
+ REG(flg) = 0xE0;
+ soft_reset_common();
+}
+
+void SPC_DSP::load( uint8_t const regs [register_count] )
+{
+ memcpy( m.regs, regs, sizeof m.regs );
+ memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
+
+ // Internal state
+ int i;
+ for ( i = voice_count; --i >= 0; )
+ {
+ voice_t& v = m.voices [i];
+ v.brr_offset = 1;
+ v.buf_pos = v.buf;
+ }
+ m.new_kon = REG(kon);
+
+ mute_voices( m.mute_mask );
+ soft_reset_common();
+}
+
+void SPC_DSP::reset() { load( initial_regs ); }
diff --git a/snes_spc/snes_spc/SPC_DSP.h b/snes_spc/snes_spc/SPC_DSP.h
new file mode 100644
index 00000000..045ce3c2
--- /dev/null
+++ b/snes_spc/snes_spc/SPC_DSP.h
@@ -0,0 +1,212 @@
+// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one)
+
+// snes_spc 0.9.0
+#ifndef SPC_DSP_H
+#define SPC_DSP_H
+
+#include "blargg_common.h"
+
+struct SPC_DSP {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+// Setup
+
+ // Initializes DSP and has it use the 64K RAM provided
+ void init( void* ram_64k );
+
+ // Sets destination for output samples. If out is NULL or out_size is 0,
+ // doesn't generate any.
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since it was last set, always
+ // a multiple of 2. Undefined if more samples were generated than
+ // output buffer could hold.
+ int sample_count() const;
+
+// Emulation
+
+ // Resets DSP to power-on state
+ void reset();
+
+ // Emulates pressing reset switch on SNES
+ void soft_reset();
+
+ // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp()
+ // to catch the DSP up to present.
+ int read ( int addr ) const;
+ void write( int addr, int data );
+
+ // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
+ // a pair of samples is be generated.
+ void run( int clock_count );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+ // If true, prevents channels and global volumes from being phase-negated
+ void disable_surround( bool disable = true );
+
+// State
+
+ // Resets DSP and uses supplied values to initialize registers
+ enum { register_count = 128 };
+ void load( uint8_t const regs [register_count] );
+
+// DSP register addresses
+
+ // Global registers
+ enum {
+ r_mvoll = 0x0C, r_mvolr = 0x1C,
+ r_evoll = 0x2C, r_evolr = 0x3C,
+ r_kon = 0x4C, r_koff = 0x5C,
+ r_flg = 0x6C, r_endx = 0x7C,
+ r_efb = 0x0D, r_pmon = 0x2D,
+ r_non = 0x3D, r_eon = 0x4D,
+ r_dir = 0x5D, r_esa = 0x6D,
+ r_edl = 0x7D,
+ r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
+ };
+
+ // Voice registers
+ enum {
+ v_voll = 0x00, v_volr = 0x01,
+ v_pitchl = 0x02, v_pitchh = 0x03,
+ v_srcn = 0x04, v_adsr0 = 0x05,
+ v_adsr1 = 0x06, v_gain = 0x07,
+ v_envx = 0x08, v_outx = 0x09
+ };
+
+public:
+ enum { extra_size = 16 };
+ sample_t* extra() { return m.extra; }
+ sample_t const* out_pos() const { return m.out; }
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::int8_t int8_t;
+ typedef BOOST::int16_t int16_t;
+
+ enum { echo_hist_size = 8 };
+
+ enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
+ enum { brr_buf_size = 12 };
+ struct voice_t
+ {
+ int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
+ int* buf_pos; // place in buffer where next samples will be decoded
+ int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
+ int brr_addr; // address of current BRR block
+ int brr_offset; // current decoding offset in BRR block
+ int kon_delay; // KON delay/current setup phase
+ env_mode_t env_mode;
+ int env; // current envelope level
+ int hidden_env; // used by GAIN mode 7, very obscure quirk
+ int volume [2]; // copy of volume from DSP registers, with surround disabled
+ int enabled; // -1 if enabled, 0 if muted
+ };
+private:
+ struct state_t
+ {
+ uint8_t regs [register_count];
+
+ // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
+ int echo_hist [echo_hist_size * 2] [2];
+ int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
+
+ int every_other_sample; // toggles every sample
+ int kon; // KON value when last checked
+ int noise;
+ int echo_offset; // offset from ESA in echo buffer
+ int echo_length; // number of bytes that echo_offset will stop at
+ int phase; // next clock cycle to run (0-31)
+ unsigned counters [4];
+
+ int new_kon;
+ int t_koff;
+
+ voice_t voices [voice_count];
+
+ unsigned* counter_select [32];
+
+ // non-emulation state
+ uint8_t* ram; // 64K shared RAM between DSP and SMP
+ int mute_mask;
+ int surround_threshold;
+ sample_t* out;
+ sample_t* out_end;
+ sample_t* out_begin;
+ sample_t extra [extra_size];
+ };
+ state_t m;
+
+ void init_counter();
+ void run_counter( int );
+ void soft_reset_common();
+ void write_outline( int addr, int data );
+ void update_voice_vol( int addr );
+};
+
+#include
+
+inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); }
+
+inline int SPC_DSP::read( int addr ) const
+{
+ assert( (unsigned) addr < register_count );
+ return m.regs [addr];
+}
+
+inline void SPC_DSP::update_voice_vol( int addr )
+{
+ int l = (int8_t) m.regs [addr + v_voll];
+ int r = (int8_t) m.regs [addr + v_volr];
+
+ if ( l * r < m.surround_threshold )
+ {
+ // signs differ, so negate those that are negative
+ l ^= l >> 7;
+ r ^= r >> 7;
+ }
+
+ voice_t& v = m.voices [addr >> 4];
+ int enabled = v.enabled;
+ v.volume [0] = l & enabled;
+ v.volume [1] = r & enabled;
+}
+
+inline void SPC_DSP::write( int addr, int data )
+{
+ assert( (unsigned) addr < register_count );
+
+ m.regs [addr] = (uint8_t) data;
+ int low = addr & 0x0F;
+ if ( low < 0x2 ) // voice volumes
+ {
+ update_voice_vol( low ^ addr );
+ }
+ else if ( low == 0xC )
+ {
+ if ( addr == r_kon )
+ m.new_kon = (uint8_t) data;
+
+ if ( addr == r_endx ) // always cleared, regardless of data written
+ m.regs [r_endx] = 0;
+ }
+}
+
+inline void SPC_DSP::disable_surround( bool disable )
+{
+ m.surround_threshold = disable ? 0 : -0x4000;
+}
+
+#define SPC_NO_COPY_STATE_FUNCS 1
+
+#define SPC_LESS_ACCURATE 1
+
+#endif
diff --git a/snes_spc/snes_spc/SPC_Filter.cpp b/snes_spc/snes_spc/SPC_Filter.cpp
new file mode 100644
index 00000000..b3d57708
--- /dev/null
+++ b/snes_spc/snes_spc/SPC_Filter.cpp
@@ -0,0 +1,68 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SPC_Filter.h"
+
+#include
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); }
+
+SPC_Filter::SPC_Filter()
+{
+ gain = gain_unit;
+ bass = bass_norm;
+ clear();
+}
+
+void SPC_Filter::run( short* io, int count )
+{
+ require( (count & 1) == 0 ); // must be even
+
+ int const gain = this->gain;
+ int const bass = this->bass;
+ chan_t* c = &ch [2];
+ do
+ {
+ // cache in registers
+ int sum = (--c)->sum;
+ int pp1 = c->pp1;
+ int p1 = c->p1;
+
+ for ( int i = 0; i < count; i += 2 )
+ {
+ // Low-pass filter (two point FIR with coeffs 0.25, 0.75)
+ int f = io [i] + p1;
+ p1 = io [i] * 3;
+
+ // High-pass filter ("leaky integrator")
+ int delta = f - pp1;
+ pp1 = f;
+ int s = sum >> (gain_bits + 2);
+ sum += (delta * gain) - (sum >> bass);
+
+ // Clamp to 16 bits
+ if ( (short) s != s )
+ s = (s >> 31) ^ 0x7FFF;
+
+ io [i] = (short) s;
+ }
+
+ c->p1 = p1;
+ c->pp1 = pp1;
+ c->sum = sum;
+ ++io;
+ }
+ while ( c != ch );
+}
diff --git a/snes_spc/snes_spc/SPC_Filter.h b/snes_spc/snes_spc/SPC_Filter.h
new file mode 100644
index 00000000..d5c83cb8
--- /dev/null
+++ b/snes_spc/snes_spc/SPC_Filter.h
@@ -0,0 +1,47 @@
+// Simple low-pass and high-pass filter to better match sound output of a SNES
+
+// snes_spc 0.9.0
+#ifndef SPC_FILTER_H
+#define SPC_FILTER_H
+
+#include "blargg_common.h"
+
+struct SPC_Filter {
+public:
+
+ // Filters count samples of stereo sound in place. Count must be a multiple of 2.
+ typedef short sample_t;
+ void run( sample_t* io, int count );
+
+// Optional features
+
+ // Clears filter to silence
+ void clear();
+
+ // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit
+ // are fine, since output is clamped to 16-bit sample range.
+ enum { gain_unit = 0x100 };
+ void set_gain( int gain );
+
+ // Sets amount of bass (logarithmic scale)
+ enum { bass_none = 0 };
+ enum { bass_norm = 8 }; // normal amount
+ enum { bass_max = 31 };
+ void set_bass( int bass );
+
+public:
+ SPC_Filter();
+ BLARGG_DISABLE_NOTHROW
+private:
+ enum { gain_bits = 8 };
+ int gain;
+ int bass;
+ struct chan_t { int p1, pp1, sum; };
+ chan_t ch [2];
+};
+
+inline void SPC_Filter::set_gain( int g ) { gain = g; }
+
+inline void SPC_Filter::set_bass( int b ) { bass = b; }
+
+#endif
diff --git a/snes_spc/snes_spc/blargg_common.h b/snes_spc/snes_spc/blargg_common.h
new file mode 100644
index 00000000..75edff39
--- /dev/null
+++ b/snes_spc/snes_spc/blargg_common.h
@@ -0,0 +1,186 @@
+// Sets up common environment for Shay Green's libraries.
+// To change configuration options, modify blargg_config.h, not this file.
+
+// snes_spc 0.9.0
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#include
+#include
+#include
+#include
+
+#undef BLARGG_COMMON_H
+// allow blargg_config.h to #include blargg_common.h
+#include "blargg_config.h"
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+// BLARGG_RESTRICT: equivalent to restrict, where supported
+#if defined (__GNUC__) || _MSC_VER >= 1100
+ #define BLARGG_RESTRICT __restrict
+#else
+ #define BLARGG_RESTRICT
+#endif
+
+// STATIC_CAST(T,expr): Used in place of static_cast (expr)
+#ifndef STATIC_CAST
+ #define STATIC_CAST(T,expr) ((T) (expr))
+#endif
+
+// blargg_err_t (0 on success, otherwise error string)
+#ifndef blargg_err_t
+ typedef const char* blargg_err_t;
+#endif
+
+// blargg_vector - very lightweight vector of POD types (no constructor/destructor)
+template
+class blargg_vector {
+ T* begin_;
+ size_t size_;
+public:
+ blargg_vector() : begin_( 0 ), size_( 0 ) { }
+ ~blargg_vector() { free( begin_ ); }
+ size_t size() const { return size_; }
+ T* begin() const { return begin_; }
+ T* end() const { return begin_ + size_; }
+ blargg_err_t resize( size_t n )
+ {
+ // TODO: blargg_common.cpp to hold this as an outline function, ugh
+ void* p = realloc( begin_, n * sizeof (T) );
+ if ( p )
+ begin_ = (T*) p;
+ else if ( n > size_ ) // realloc failure only a problem if expanding
+ return "Out of memory";
+ size_ = n;
+ return 0;
+ }
+ void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
+ T& operator [] ( size_t n ) const
+ {
+ assert( n <= size_ ); // <= to allow past-the-end value
+ return begin_ [n];
+ }
+};
+
+#ifndef BLARGG_DISABLE_NOTHROW
+ // throw spec mandatory in ISO C++ if operator new can return NULL
+ #if __cplusplus >= 199711 || defined (__GNUC__)
+ #define BLARGG_THROWS( spec ) throw spec
+ #else
+ #define BLARGG_THROWS( spec )
+ #endif
+ #define BLARGG_DISABLE_NOTHROW \
+ void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\
+ void operator delete ( void* p ) { free( p ); }
+ #define BLARGG_NEW new
+#else
+ #include
+ #define BLARGG_NEW new (std::nothrow)
+#endif
+
+// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
+#define BLARGG_4CHAR( a, b, c, d ) \
+ ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
+
+// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
+#ifndef BOOST_STATIC_ASSERT
+ #ifdef _MSC_VER
+ // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
+ #define BOOST_STATIC_ASSERT( expr ) \
+ void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
+ #else
+ // Some other compilers fail when declaring same function multiple times in class,
+ // so differentiate them by line
+ #define BOOST_STATIC_ASSERT( expr ) \
+ void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
+ #endif
+#endif
+
+// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
+// compiler is assumed to support bool. If undefined, availability is determined.
+#ifndef BLARGG_COMPILER_HAS_BOOL
+ #if defined (__MWERKS__)
+ #if !__option(bool)
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+ #elif defined (_MSC_VER)
+ #if _MSC_VER < 1100
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+ #elif defined (__GNUC__)
+ // supports bool
+ #elif __cplusplus < 199711
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+#endif
+#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
+ // If you get errors here, modify your blargg_config.h file
+ typedef int bool;
+ const bool true = 1;
+ const bool false = 0;
+#endif
+
+// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
+
+#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
+ typedef long blargg_long;
+#else
+ typedef int blargg_long;
+#endif
+
+#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
+ typedef unsigned long blargg_ulong;
+#else
+ typedef unsigned blargg_ulong;
+#endif
+
+// BOOST::int8_t etc.
+
+// HAVE_STDINT_H: If defined, use for int8_t etc.
+#if defined (HAVE_STDINT_H)
+ #include
+ #define BOOST
+
+// HAVE_INTTYPES_H: If defined, use for int8_t etc.
+#elif defined (HAVE_INTTYPES_H)
+ #include
+ #define BOOST
+
+#else
+ struct BOOST
+ {
+ #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+ #else
+ // No suitable 8-bit type available
+ typedef struct see_blargg_common_h int8_t;
+ typedef struct see_blargg_common_h uint8_t;
+ #endif
+
+ #if USHRT_MAX == 0xFFFF
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ #else
+ // No suitable 16-bit type available
+ typedef struct see_blargg_common_h int16_t;
+ typedef struct see_blargg_common_h uint16_t;
+ #endif
+
+ #if ULONG_MAX == 0xFFFFFFFF
+ typedef long int32_t;
+ typedef unsigned long uint32_t;
+ #elif UINT_MAX == 0xFFFFFFFF
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+ #else
+ // No suitable 32-bit type available
+ typedef struct see_blargg_common_h int32_t;
+ typedef struct see_blargg_common_h uint32_t;
+ #endif
+ };
+#endif
+
+#endif
+#endif
diff --git a/snes_spc/snes_spc/blargg_config.h b/snes_spc/snes_spc/blargg_config.h
new file mode 100644
index 00000000..9dc69db8
--- /dev/null
+++ b/snes_spc/snes_spc/blargg_config.h
@@ -0,0 +1,24 @@
+// snes_spc 0.9.0 user configuration file. Don't replace when updating library.
+
+// snes_spc 0.9.0
+#ifndef BLARGG_CONFIG_H
+#define BLARGG_CONFIG_H
+
+// Uncomment to disable debugging checks
+//#define NDEBUG 1
+
+// Uncomment to enable platform-specific (and possibly non-portable) optimizations
+//#define BLARGG_NONPORTABLE 1
+
+// Uncomment if automatic byte-order determination doesn't work
+//#define BLARGG_BIG_ENDIAN 1
+
+// Uncomment if you get errors in the bool section of blargg_common.h
+//#define BLARGG_COMPILER_HAS_BOOL 1
+
+// Use standard config.h if present
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#endif
diff --git a/snes_spc/snes_spc/blargg_endian.h b/snes_spc/snes_spc/blargg_endian.h
new file mode 100644
index 00000000..f2daca64
--- /dev/null
+++ b/snes_spc/snes_spc/blargg_endian.h
@@ -0,0 +1,185 @@
+// CPU Byte Order Utilities
+
+// snes_spc 0.9.0
+#ifndef BLARGG_ENDIAN
+#define BLARGG_ENDIAN
+
+#include "blargg_common.h"
+
+// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLARGG_CPU_X86 1
+ #define BLARGG_CPU_CISC 1
+#endif
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
+ #define BLARGG_CPU_POWERPC 1
+ #define BLARGG_CPU_RISC 1
+#endif
+
+// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
+// one may be #defined to 1. Only needed if something actually depends on byte order.
+#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
+#ifdef __GLIBC__
+ // GCC handles this for us
+ #include
+ #if __BYTE_ORDER == __LITTLE_ENDIAN
+ #define BLARGG_LITTLE_ENDIAN 1
+ #elif __BYTE_ORDER == __BIG_ENDIAN
+ #define BLARGG_BIG_ENDIAN 1
+ #endif
+#else
+
+#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \
+ (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
+ #define BLARGG_LITTLE_ENDIAN 1
+#endif
+
+#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
+ defined (__sparc__) || BLARGG_CPU_POWERPC || \
+ (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
+ #define BLARGG_BIG_ENDIAN 1
+#elif !defined (__mips__)
+ // No endian specified; assume little-endian, since it's most common
+ #define BLARGG_LITTLE_ENDIAN 1
+#endif
+#endif
+#endif
+
+#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN
+ #undef BLARGG_LITTLE_ENDIAN
+ #undef BLARGG_BIG_ENDIAN
+#endif
+
+inline void blargg_verify_byte_order()
+{
+ #ifndef NDEBUG
+ #if BLARGG_BIG_ENDIAN
+ volatile int i = 1;
+ assert( *(volatile char*) &i == 0 );
+ #elif BLARGG_LITTLE_ENDIAN
+ volatile int i = 1;
+ assert( *(volatile char*) &i != 0 );
+ #endif
+ #endif
+}
+
+inline unsigned get_le16( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
+}
+
+inline unsigned get_be16( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [0] << 8 |
+ (unsigned) ((unsigned char const*) p) [1];
+}
+
+inline blargg_ulong get_le32( void const* p )
+{
+ return (blargg_ulong) ((unsigned char const*) p) [3] << 24 |
+ (blargg_ulong) ((unsigned char const*) p) [2] << 16 |
+ (blargg_ulong) ((unsigned char const*) p) [1] << 8 |
+ (blargg_ulong) ((unsigned char const*) p) [0];
+}
+
+inline blargg_ulong get_be32( void const* p )
+{
+ return (blargg_ulong) ((unsigned char const*) p) [0] << 24 |
+ (blargg_ulong) ((unsigned char const*) p) [1] << 16 |
+ (blargg_ulong) ((unsigned char const*) p) [2] << 8 |
+ (blargg_ulong) ((unsigned char const*) p) [3];
+}
+
+inline void set_le16( void* p, unsigned n )
+{
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [0] = (unsigned char) n;
+}
+
+inline void set_be16( void* p, unsigned n )
+{
+ ((unsigned char*) p) [0] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [1] = (unsigned char) n;
+}
+
+inline void set_le32( void* p, blargg_ulong n )
+{
+ ((unsigned char*) p) [0] = (unsigned char) n;
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+}
+
+inline void set_be32( void* p, blargg_ulong n )
+{
+ ((unsigned char*) p) [3] = (unsigned char) n;
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [0] = (unsigned char) (n >> 24);
+}
+
+#if BLARGG_NONPORTABLE
+ // Optimized implementation if byte order is known
+ #if BLARGG_LITTLE_ENDIAN
+ #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr))
+ #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr))
+ #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
+ #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
+ #elif BLARGG_BIG_ENDIAN
+ #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr))
+ #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr))
+ #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
+ #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
+
+ #if BLARGG_CPU_POWERPC
+ // PowerPC has special byte-reversed instructions
+ #if defined (__MWERKS__)
+ #define GET_LE16( addr ) (__lhbrx( addr, 0 ))
+ #define GET_LE32( addr ) (__lwbrx( addr, 0 ))
+ #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 ))
+ #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 ))
+ #elif defined (__GNUC__)
+ #define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;})
+ #define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;})
+ #define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );})
+ #define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );})
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef GET_LE16
+ #define GET_LE16( addr ) get_le16( addr )
+ #define SET_LE16( addr, data ) set_le16( addr, data )
+#endif
+
+#ifndef GET_LE32
+ #define GET_LE32( addr ) get_le32( addr )
+ #define SET_LE32( addr, data ) set_le32( addr, data )
+#endif
+
+#ifndef GET_BE16
+ #define GET_BE16( addr ) get_be16( addr )
+ #define SET_BE16( addr, data ) set_be16( addr, data )
+#endif
+
+#ifndef GET_BE32
+ #define GET_BE32( addr ) get_be32( addr )
+ #define SET_BE32( addr, data ) set_be32( addr, data )
+#endif
+
+// auto-selecting versions
+
+inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); }
+inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); }
+inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); }
+inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); }
+inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); }
+inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); }
+inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); }
+inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); }
+
+#endif
diff --git a/snes_spc/snes_spc/blargg_source.h b/snes_spc/snes_spc/blargg_source.h
new file mode 100644
index 00000000..5e45c4fb
--- /dev/null
+++ b/snes_spc/snes_spc/blargg_source.h
@@ -0,0 +1,100 @@
+/* Included at the beginning of library source files, after all other #include lines.
+Sets up helpful macros and services used in my source code. They don't need
+module an annoying module prefix on their names since they are defined after
+all other #include lines. */
+
+// snes_spc 0.9.0
+#ifndef BLARGG_SOURCE_H
+#define BLARGG_SOURCE_H
+
+// If debugging is enabled, abort program if expr is false. Meant for checking
+// internal state and consistency. A failed assertion indicates a bug in the module.
+// void assert( bool expr );
+#include
+
+// If debugging is enabled and expr is false, abort program. Meant for checking
+// caller-supplied parameters and operations that are outside the control of the
+// module. A failed requirement indicates a bug outside the module.
+// void require( bool expr );
+#undef require
+#define require( expr ) assert( expr )
+
+// Like printf() except output goes to debug log file. Might be defined to do
+// nothing (not even evaluate its arguments).
+// void dprintf( const char* format, ... );
+static inline void blargg_dprintf_( const char*, ... ) { }
+#undef dprintf
+#define dprintf (1) ? (void) 0 : blargg_dprintf_
+
+// If enabled, evaluate expr and if false, make debug log entry with source file
+// and line. Meant for finding situations that should be examined further, but that
+// don't indicate a problem. In all cases, execution continues normally.
+#undef check
+#define check( expr ) ((void) 0)
+
+// If expr yields error string, return it from current function, otherwise continue.
+#undef RETURN_ERR
+#define RETURN_ERR( expr ) do { \
+ blargg_err_t blargg_return_err_ = (expr); \
+ if ( blargg_return_err_ ) return blargg_return_err_; \
+ } while ( 0 )
+
+// If ptr is 0, return out of memory error string.
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
+
+// Avoid any macros which evaluate their arguments multiple times
+#undef min
+#undef max
+
+#define DEF_MIN_MAX( type ) \
+ static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\
+ static inline type max( type x, type y ) { if ( y < x ) return x; return y; }
+
+DEF_MIN_MAX( int )
+DEF_MIN_MAX( unsigned )
+DEF_MIN_MAX( long )
+DEF_MIN_MAX( unsigned long )
+DEF_MIN_MAX( float )
+DEF_MIN_MAX( double )
+
+#undef DEF_MIN_MAX
+
+/*
+// using const references generates crappy code, and I am currenly only using these
+// for built-in types, so they take arguments by value
+
+// TODO: remove
+inline int min( int x, int y )
+template
+inline T min( T x, T y )
+{
+ if ( x < y )
+ return x;
+ return y;
+}
+
+template
+inline T max( T x, T y )
+{
+ if ( x < y )
+ return y;
+ return x;
+}
+*/
+
+// TODO: good idea? bad idea?
+#undef byte
+#define byte byte_
+typedef unsigned char byte;
+
+// deprecated
+#define BLARGG_CHECK_ALLOC CHECK_ALLOC
+#define BLARGG_RETURN_ERR RETURN_ERR
+
+// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
+#ifdef BLARGG_SOURCE_BEGIN
+ #include BLARGG_SOURCE_BEGIN
+#endif
+
+#endif
diff --git a/snes_spc/snes_spc/dsp.cpp b/snes_spc/snes_spc/dsp.cpp
new file mode 100644
index 00000000..58919e90
--- /dev/null
+++ b/snes_spc/snes_spc/dsp.cpp
@@ -0,0 +1,48 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "dsp.h"
+
+#include "SPC_DSP.h"
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+SPC_DSP* spc_dsp_new( void )
+{
+ // be sure constants match
+ assert( spc_dsp_voice_count == (int) SPC_DSP::voice_count );
+ assert( spc_dsp_register_count == (int) SPC_DSP::register_count );
+ #if !SPC_NO_COPY_STATE_FUNCS
+ assert( spc_dsp_state_size == (int) SPC_DSP::state_size );
+ #endif
+
+ return new SPC_DSP;
+}
+
+void spc_dsp_delete ( SPC_DSP* s ) { delete s; }
+void spc_dsp_init ( SPC_DSP* s, void* ram_64k ) { s->init( ram_64k ); }
+void spc_dsp_set_output ( SPC_DSP* s, spc_dsp_sample_t* p, int n ) { s->set_output( p, n ); }
+int spc_dsp_sample_count( SPC_DSP const* s ) { return s->sample_count(); }
+void spc_dsp_reset ( SPC_DSP* s ) { s->reset(); }
+void spc_dsp_soft_reset ( SPC_DSP* s ) { s->soft_reset(); }
+int spc_dsp_read ( SPC_DSP const* s, int addr ) { return s->read( addr ); }
+void spc_dsp_write ( SPC_DSP* s, int addr, int data ) { s->write( addr, data ); }
+void spc_dsp_run ( SPC_DSP* s, int clock_count ) { s->run( clock_count ); }
+void spc_dsp_mute_voices ( SPC_DSP* s, int mask ) { s->mute_voices( mask ); }
+void spc_dsp_disable_surround( SPC_DSP* s, int disable ) { s->disable_surround( !!disable ); }
+void spc_dsp_load ( SPC_DSP* s, unsigned char const regs [spc_dsp_register_count] ) { s->load( regs ); }
+
+#if !SPC_NO_COPY_STATE_FUNCS
+void spc_dsp_copy_state ( SPC_DSP* s, unsigned char** p, spc_dsp_copy_func_t f ) { s->copy_state( p, f ); }
+int spc_dsp_check_kon ( SPC_DSP* s ) { return s->check_kon(); }
+#endif
diff --git a/snes_spc/snes_spc/dsp.h b/snes_spc/snes_spc/dsp.h
new file mode 100644
index 00000000..59867d92
--- /dev/null
+++ b/snes_spc/snes_spc/dsp.h
@@ -0,0 +1,83 @@
+/* SNES SPC-700 DSP emulator C interface (also usable from C++) */
+
+/* snes_spc 0.9.0 */
+#ifndef DSP_H
+#define DSP_H
+
+#include
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct SPC_DSP SPC_DSP;
+
+/* Creates new DSP emulator. NULL if out of memory. */
+SPC_DSP* spc_dsp_new( void );
+
+/* Frees DSP emulator */
+void spc_dsp_delete( SPC_DSP* );
+
+/* Initializes DSP and has it use the 64K RAM provided */
+void spc_dsp_init( SPC_DSP*, void* ram_64k );
+
+/* Sets destination for output samples. If out is NULL or out_size is 0,
+doesn't generate any. */
+typedef short spc_dsp_sample_t;
+void spc_dsp_set_output( SPC_DSP*, spc_dsp_sample_t* out, int out_size );
+
+/* Number of samples written to output since it was last set, always
+a multiple of 2. Undefined if more samples were generated than
+output buffer could hold. */
+int spc_dsp_sample_count( SPC_DSP const* );
+
+
+/**** Emulation *****/
+
+/* Resets DSP to power-on state */
+void spc_dsp_reset( SPC_DSP* );
+
+/* Emulates pressing reset switch on SNES */
+void spc_dsp_soft_reset( SPC_DSP* );
+
+/* Reads/writes DSP registers. For accuracy, you must first call spc_dsp_run() */
+/* to catch the DSP up to present. */
+int spc_dsp_read ( SPC_DSP const*, int addr );
+void spc_dsp_write( SPC_DSP*, int addr, int data );
+
+/* Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks */
+/* a pair of samples is be generated. */
+void spc_dsp_run( SPC_DSP*, int clock_count );
+
+
+/**** Sound control *****/
+
+/* Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */
+enum { spc_dsp_voice_count = 8 };
+void spc_dsp_mute_voices( SPC_DSP*, int mask );
+
+/* If true, prevents channels and global volumes from being phase-negated.
+Only supported by fast DSP; has no effect on accurate DSP. */
+void spc_dsp_disable_surround( SPC_DSP*, int disable );
+
+
+/**** State save/load *****/
+
+/* Resets DSP and uses supplied values to initialize registers */
+enum { spc_dsp_register_count = 128 };
+void spc_dsp_load( SPC_DSP*, unsigned char const regs [spc_dsp_register_count] );
+
+/* Saves/loads exact emulator state (accurate DSP only) */
+enum { spc_dsp_state_size = 640 }; /* maximum space needed when saving */
+typedef void (*spc_dsp_copy_func_t)( unsigned char** io, void* state, size_t );
+void spc_dsp_copy_state( SPC_DSP*, unsigned char** io, spc_dsp_copy_func_t );
+
+/* Returns non-zero if new key-on events occurred since last call (accurate DSP only) */
+int spc_dsp_check_kon( SPC_DSP* );
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/snes_spc/snes_spc/spc.cpp b/snes_spc/snes_spc/spc.cpp
new file mode 100644
index 00000000..0aeb26f2
--- /dev/null
+++ b/snes_spc/snes_spc/spc.cpp
@@ -0,0 +1,73 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "spc.h"
+
+#include "SNES_SPC.h"
+#include "SPC_Filter.h"
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module 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 Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+SNES_SPC* spc_new( void )
+{
+ // be sure constants match
+ assert( spc_sample_rate == (int) SNES_SPC::sample_rate );
+ assert( spc_rom_size == (int) SNES_SPC::rom_size );
+ assert( spc_clock_rate == (int) SNES_SPC::clock_rate );
+ assert( spc_clocks_per_sample == (int) SNES_SPC::clocks_per_sample );
+ assert( spc_port_count == (int) SNES_SPC::port_count );
+ assert( spc_voice_count == (int) SNES_SPC::voice_count );
+ assert( spc_tempo_unit == (int) SNES_SPC::tempo_unit );
+ assert( spc_file_size == (int) SNES_SPC::spc_file_size );
+ #if !SPC_NO_COPY_STATE_FUNCS
+ assert( spc_state_size == (int) SNES_SPC::state_size );
+ #endif
+
+ SNES_SPC* s = new SNES_SPC;
+ if ( s && s->init() )
+ {
+ delete s;
+ s = 0;
+ }
+ return s;
+}
+
+void spc_delete ( SNES_SPC* s ) { delete s; }
+void spc_init_rom ( SNES_SPC* s, unsigned char const r [64] ) { s->init_rom( r ); }
+void spc_set_output ( SNES_SPC* s, spc_sample_t* p, int n ) { s->set_output( p, n ); }
+int spc_sample_count ( SNES_SPC const* s ) { return s->sample_count(); }
+void spc_reset ( SNES_SPC* s ) { s->reset(); }
+void spc_soft_reset ( SNES_SPC* s ) { s->soft_reset(); }
+int spc_read_port ( SNES_SPC* s, spc_time_t t, int p ) { return s->read_port( t, p ); }
+void spc_write_port ( SNES_SPC* s, spc_time_t t, int p, int d ) { s->write_port( t, p, d ); }
+void spc_end_frame ( SNES_SPC* s, spc_time_t t ) { s->end_frame( t ); }
+void spc_mute_voices ( SNES_SPC* s, int mask ) { s->mute_voices( mask ); }
+void spc_disable_surround( SNES_SPC* s, int disable ) { s->disable_surround( !!disable ); }
+void spc_set_tempo ( SNES_SPC* s, int tempo ) { s->set_tempo( tempo ); }
+spc_err_t spc_load_spc ( SNES_SPC* s, void const* p, long n ) { return s->load_spc( p, n ); }
+void spc_clear_echo ( SNES_SPC* s ) { s->clear_echo(); }
+spc_err_t spc_play ( SNES_SPC* s, int count, short* out ) { return s->play( count, out ); }
+spc_err_t spc_skip ( SNES_SPC* s, int count ) { return s->skip( count ); }
+#if !SPC_NO_COPY_STATE_FUNCS
+void spc_copy_state ( SNES_SPC* s, unsigned char** p, spc_copy_func_t f ) { s->copy_state( p, f ); }
+void spc_init_header ( void* spc_out ) { SNES_SPC::init_header( spc_out ); }
+void spc_save_spc ( SNES_SPC* s, void* spc_out ) { s->save_spc( spc_out ); }
+int spc_check_kon ( SNES_SPC* s ) { return s->check_kon(); }
+#endif
+
+SPC_Filter* spc_filter_new( void ) { return new SPC_Filter; }
+void spc_filter_delete( SPC_Filter* f ) { delete f; }
+void spc_filter_run( SPC_Filter* f, spc_sample_t* p, int s ) { f->run( p, s ); }
+void spc_filter_clear( SPC_Filter* f ) { f->clear(); }
+void spc_filter_set_gain( SPC_Filter* f, int gain ) { f->set_gain( gain ); }
+void spc_filter_set_bass( SPC_Filter* f, int bass ) { f->set_bass( bass ); }
diff --git a/snes_spc/snes_spc/spc.h b/snes_spc/snes_spc/spc.h
new file mode 100644
index 00000000..9ffdedce
--- /dev/null
+++ b/snes_spc/snes_spc/spc.h
@@ -0,0 +1,147 @@
+/* SNES SPC-700 APU emulator C interface (also usable from C++) */
+
+/* snes_spc 0.9.0 */
+#ifndef SPC_H
+#define SPC_H
+
+#include
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Error string return. NULL if success, otherwise error message. */
+typedef const char* spc_err_t;
+
+typedef struct SNES_SPC SNES_SPC;
+
+/* Creates new SPC emulator. NULL if out of memory. */
+SNES_SPC* spc_new( void );
+
+/* Frees SPC emulator */
+void spc_delete( SNES_SPC* );
+
+/* Sample pairs generated per second */
+enum { spc_sample_rate = 32000 };
+
+
+/**** Emulator use ****/
+
+/* Sets IPL ROM data. Library does not include ROM data. Most SPC music files
+don't need ROM, but a full emulator must provide this. */
+enum { spc_rom_size = 0x40 };
+void spc_init_rom( SNES_SPC*, unsigned char const rom [spc_rom_size] );
+
+/* Sets destination for output samples */
+typedef short spc_sample_t;
+void spc_set_output( SNES_SPC*, spc_sample_t* out, int out_size );
+
+/* Number of samples written to output since last set */
+int spc_sample_count( SNES_SPC const* );
+
+/* Resets SPC to power-on state. This resets your output buffer, so you must
+call spc_set_output() after this. */
+void spc_reset( SNES_SPC* );
+
+/* Emulates pressing reset switch on SNES. This resets your output buffer, so
+you must call spc_set_output() after this. */
+void spc_soft_reset( SNES_SPC* );
+
+/* 1024000 SPC clocks per second, sample pair every 32 clocks */
+typedef int spc_time_t;
+enum { spc_clock_rate = 1024000 };
+enum { spc_clocks_per_sample = 32 };
+
+/* Reads/writes port at specified time */
+enum { spc_port_count = 4 };
+int spc_read_port ( SNES_SPC*, spc_time_t, int port );
+void spc_write_port( SNES_SPC*, spc_time_t, int port, int data );
+
+/* Runs SPC to end_time and starts a new time frame at 0 */
+void spc_end_frame( SNES_SPC*, spc_time_t end_time );
+
+
+/**** Sound control ****/
+
+/*Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */
+enum { spc_voice_count = 8 };
+void spc_mute_voices( SNES_SPC*, int mask );
+
+/* If true, prevents channels and global volumes from being phase-negated.
+Only supported by fast DSP; has no effect on accurate DSP. */
+void spc_disable_surround( SNES_SPC*, int disable );
+
+/* Sets tempo, where spc_tempo_unit = normal, spc_tempo_unit / 2 = half speed, etc. */
+enum { spc_tempo_unit = 0x100 };
+void spc_set_tempo( SNES_SPC*, int );
+
+
+/**** SPC music playback *****/
+
+/* Loads SPC data into emulator. Returns NULL on success, otherwise error string. */
+spc_err_t spc_load_spc( SNES_SPC*, void const* spc_in, long size );
+
+/* Clears echo region. Useful after loading an SPC as many have garbage in echo. */
+void spc_clear_echo( SNES_SPC* );
+
+/* Plays for count samples and write samples to out. Discards samples if out
+is NULL. Count must be a multiple of 2 since output is stereo. */
+spc_err_t spc_play( SNES_SPC*, int count, short* out );
+
+/* Skips count samples. Several times faster than spc_play(). */
+spc_err_t spc_skip( SNES_SPC*, int count );
+
+
+/**** State save/load (only available with accurate DSP) ****/
+
+/* Saves/loads exact emulator state */
+enum { spc_state_size = 67 * 1024L }; /* maximum space needed when saving */
+typedef void (*spc_copy_func_t)( unsigned char** io, void* state, size_t );
+void spc_copy_state( SNES_SPC*, unsigned char** io, spc_copy_func_t );
+
+/* Writes minimal SPC file header to spc_out */
+void spc_init_header( void* spc_out );
+
+/* Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
+Does not set up SPC header; use spc_init_header() for that. */
+enum { spc_file_size = 0x10200 }; /* spc_out must have this many bytes allocated */
+void spc_save_spc( SNES_SPC*, void* spc_out );
+
+/* Returns non-zero if new key-on events occurred since last check. Useful for
+trimming silence while saving an SPC. */
+int spc_check_kon( SNES_SPC* );
+
+
+/**** SPC_Filter ****/
+
+typedef struct SPC_Filter SPC_Filter;
+
+/* Creates new filter. NULL if out of memory. */
+SPC_Filter* spc_filter_new( void );
+
+/* Frees filter */
+void spc_filter_delete( SPC_Filter* );
+
+/* Filters count samples of stereo sound in place. Count must be a multiple of 2. */
+void spc_filter_run( SPC_Filter*, spc_sample_t* io, int count );
+
+/* Clears filter to silence */
+void spc_filter_clear( SPC_Filter* );
+
+/* Sets gain (volume), where spc_filter_gain_unit is normal. Gains greater than
+spc_filter_gain_unit are fine, since output is clamped to 16-bit sample range. */
+enum { spc_filter_gain_unit = 0x100 };
+void spc_filter_set_gain( SPC_Filter*, int gain );
+
+/* Sets amount of bass (logarithmic scale) */
+enum { spc_filter_bass_none = 0 };
+enum { spc_filter_bass_norm = 8 }; /* normal amount */
+enum { spc_filter_bass_max = 31 };
+void spc_filter_set_bass( SPC_Filter*, int bass );
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/actor.h b/src/actor.h
index cfa37cb0..29339343 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -545,9 +545,9 @@ public:
bool CheckLocalView (int playernum) const;
// Finds the first item of a particular type.
- AInventory *FindInventory (const PClass *type) const;
- AInventory *FindInventory (FName type) const;
- template T *FindInventory () const
+ AInventory *FindInventory (const PClass *type);
+ AInventory *FindInventory (FName type);
+ template T *FindInventory ()
{
return static_cast (FindInventory (RUNTIME_CLASS(T)));
}
@@ -556,7 +556,7 @@ public:
AInventory *GiveInventoryType (const PClass *type);
// Returns the first item held with IF_INVBAR set.
- AInventory *FirstInv () const;
+ AInventory *FirstInv ();
// Tries to give the actor some ammo.
bool GiveAmmo (const PClass *type, int amount);
@@ -572,7 +572,7 @@ public:
void ConversationAnimation (int animnum);
// Make this actor hate the same things as another actor
- void CopyFriendliness (const AActor *other, bool changeTarget);
+ void CopyFriendliness (AActor *other, bool changeTarget);
// Moves the other actor's inventory to this one
void ObtainInventory (AActor *other);
@@ -647,35 +647,32 @@ public:
BYTE movedir; // 0-7
SBYTE visdir;
SWORD movecount; // when 0, select a new dir
- AActor *target; // thing being chased/attacked (or NULL)
+ TObjPtr target; // thing being chased/attacked (or NULL)
// also the originator for missiles
- AActor *lastenemy; // Last known enemy -- killogh 2/15/98
- AActor *LastHeard; // [RH] Last actor this one heard
+ TObjPtr lastenemy; // Last known enemy -- killogh 2/15/98
+ TObjPtr LastHeard; // [RH] Last actor this one heard
SDWORD reactiontime; // if non 0, don't attack yet; used by
// player to freeze a bit after teleporting
SDWORD threshold; // if > 0, the target will be chased
// no matter what (even if shot)
player_s *player; // only valid if type of APlayerPawn
- union
- {
- AActor *Actor; // Actor last looked for (if TIDtoHate != 0)
- SDWORD PlayerNumber; // Player number last looked for
- } LastLook;
+ TObjPtr LastLookActor; // Actor last looked for (if TIDtoHate != 0)
WORD SpawnPoint[3]; // For nightmare respawn
WORD SpawnAngle;
int skillrespawncount;
- AActor *tracer; // Thing being chased/attacked for tracers
- AActor *master; // Thing which spawned this one (prevents mutual attacks)
+ TObjPtr tracer; // Thing being chased/attacked for tracers
+ TObjPtr master; // Thing which spawned this one (prevents mutual attacks)
fixed_t floorclip; // value to use for floor clipping
SWORD tid; // thing identifier
BYTE special; // special
int args[5]; // special arguments
AActor *inext, **iprev;// Links to other mobjs in same bucket
- AActor *goal; // Monster's goal if not chasing anything
+ TObjPtr goal; // Monster's goal if not chasing anything
BYTE waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes
BYTE boomwaterlevel; // splash information for non-swimmable water sectors
BYTE MinMissileChance;// [RH] If a random # is > than this, then missile attack.
+ SBYTE LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0)
WORD SpawnFlags;
fixed_t meleerange; // specifies how far a melee attack reaches.
fixed_t meleethreshold; // Distance below which a monster doesn't try to shoot missiles anynore
@@ -686,9 +683,10 @@ public:
int bouncecount; // Strife's grenades only bounce twice before exploding
fixed_t gravity; // [GRB] Gravity factor
int FastChaseStrafeCount;
- // [KS] These temporary-use properties are needed to allow A_LookEx to pass it's parameters to LookFor*InBlock in
- // P_BlockmapSearch so that friendly enemies and monsters that look for other monsters can find their targets properly.
- // If there's a cleaner way of doing this, feel free to remove these and use that method instead.
+ // [KS] These temporary-use properties are needed to allow A_LookEx to pass its parameters to
+ // LookFor*InBlock in P_BlockmapSearch so that friendly enemies and monsters that look for
+ // other monsters can find their targets properly. If there's a cleaner way of doing this,
+ // feel free to remove these and use that method instead.
fixed_t LookExMinDist; // Minimum sight distance
fixed_t LookExMaxDist; // Maximum sight distance
angle_t LookExFOV; // Field of Vision
@@ -696,7 +694,7 @@ public:
// a linked list of sectors where this object appears
struct msecnode_s *touching_sectorlist; // phares 3/14/98
- AInventory *Inventory; // [RH] This actor's inventory
+ TObjPtr Inventory; // [RH] This actor's inventory
DWORD InventoryID; // A unique ID to keep track of inventory items
//Added by MC:
@@ -769,10 +767,12 @@ public:
enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 };
- TArray dynamiclights;
+ TArray> dynamiclights;
void * lightassociations;
bool hasmodel;
subsector_s * subsector;
+
+ size_t PropagateMark();
};
class FActorIterator
diff --git a/src/b_bot.h b/src/b_bot.h
index 1dd48864..607fd01f 100644
--- a/src/b_bot.h
+++ b/src/b_bot.h
@@ -117,7 +117,7 @@ public:
botinfo_t *botinfo;
int spawn_tries;
int wanted_botnum;
- AActor *firstthing;
+ TObjPtr firstthing;
bool m_Thinking;
@@ -141,8 +141,8 @@ protected:
bool ctf;
int loaded_bots;
int t_join;
- AActor *body1;
- AActor *body2;
+ TObjPtr body1;
+ TObjPtr body2;
bool observer; //Consoleplayer is observer.
};
diff --git a/src/b_func.cpp b/src/b_func.cpp
index e77761bc..8e4fe03d 100644
--- a/src/b_func.cpp
+++ b/src/b_func.cpp
@@ -453,16 +453,26 @@ void DCajunMaster::SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum)
if (hostnum == 1)
{
if (body1)
+ {
body1->SetOrigin (x, y, z);
+ }
else
+ {
body1 = Spawn ("CajunBodyNode", x, y, z, NO_REPLACE);
+ GC::WriteBarrier(this, body1);
+ }
}
else if (hostnum == 2)
{
if (body2)
+ {
body2->SetOrigin (x, y, z);
+ }
else
+ {
body2 = Spawn ("CajunBodyNode", x, y, z, NO_REPLACE);
+ GC::WriteBarrier(this, body2);
+ }
}
}
diff --git a/src/b_game.cpp b/src/b_game.cpp
index d8dcccf1..cdb2e52e 100644
--- a/src/b_game.cpp
+++ b/src/b_game.cpp
@@ -94,9 +94,11 @@ DCajunMaster::~DCajunMaster()
ForgetBots();
if (getspawned != NULL)
{
- delete getspawned;
+ getspawned->Destroy();
getspawned = NULL;
}
+ // FIXME: Make this object proper
+ ObjectFlags |= OF_Cleanup | OF_YesReallyDelete;
}
//This function is called every tick (from g_game.c),
diff --git a/src/c_console.cpp b/src/c_console.cpp
index 9a3920a1..9ea68023 100644
--- a/src/c_console.cpp
+++ b/src/c_console.cpp
@@ -229,7 +229,7 @@ CUSTOM_CVAR (Int, msgmidcolor2, 4, CVAR_ARCHIVE)
static void maybedrawnow (bool tick, bool force)
{
// FIXME: Does not work right with hw2d
- if (ConsoleDrawing || !gotconback || screen->IsLocked () || screen->Accel2D)
+ if (ConsoleDrawing || !gotconback || screen == NULL || screen->IsLocked () || screen->Accel2D)
{
return;
}
diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp
index fb43ec27..2d5abf05 100644
--- a/src/c_dispatch.cpp
+++ b/src/c_dispatch.cpp
@@ -1250,23 +1250,23 @@ CCMD (key)
// These all begin with '+' as opposed to '-'.
void C_ExecCmdLineParams ()
{
- for (int currArg = 1; currArg < Args.NumArgs(); )
+ for (int currArg = 1; currArg < Args->NumArgs(); )
{
- if (*Args.GetArg (currArg++) == '+')
+ if (*Args->GetArg (currArg++) == '+')
{
FString cmdString;
int cmdlen = 1;
int argstart = currArg - 1;
- while (currArg < Args.NumArgs())
+ while (currArg < Args->NumArgs())
{
- if (*Args.GetArg (currArg) == '-' || *Args.GetArg (currArg) == '+')
+ if (*Args->GetArg (currArg) == '-' || *Args->GetArg (currArg) == '+')
break;
currArg++;
cmdlen++;
}
- cmdString = BuildString (cmdlen, Args.GetArgList (argstart));
+ cmdString = BuildString (cmdlen, Args->GetArgList (argstart));
if (!cmdString.IsEmpty())
{
C_DoCommand (&cmdString[1]);
diff --git a/src/cmdlib.h b/src/cmdlib.h
index a6a45097..af31bb26 100644
--- a/src/cmdlib.h
+++ b/src/cmdlib.h
@@ -23,7 +23,7 @@
#include
// the dec offsetof macro doesnt work very well...
-#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+#define myoffsetof(type,identifier) ((size_t)&((type *)1)->identifier - 1)
int Q_filelength (FILE *f);
bool FileExists (const char *filename);
diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp
index 9a728287..071e2a5e 100644
--- a/src/d_dehacked.cpp
+++ b/src/d_dehacked.cpp
@@ -161,7 +161,7 @@ public:
void DoPickupSpecial (AActor *toucher);
private:
const PClass *DetermineType ();
- AInventory *RealPickup;
+ TObjPtr RealPickup;
};
IMPLEMENT_POINTY_CLASS (ADehackedPickup)
diff --git a/src/d_main.cpp b/src/d_main.cpp
index dae6892d..0a614962 100644
--- a/src/d_main.cpp
+++ b/src/d_main.cpp
@@ -745,7 +745,6 @@ void D_DoomLoop ()
if (singletics)
{
I_StartTic ();
- DObject::BeginFrame ();
D_ProcessEvents ();
G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
//Added by MC: For some of that bot stuff. The main bot function.
@@ -774,7 +773,7 @@ void D_DoomLoop ()
G_Ticker ();
gametic++;
maketic++;
- DObject::EndFrame ();
+ GC::CheckGC ();
Net_NewMakeTic ();
}
else
@@ -1644,7 +1643,7 @@ static EIWADType IdentifyVersion (const char *zdoom_wad)
{
WadStuff wads[sizeof(IWADNames)/sizeof(char *)];
size_t foundwads[NUM_IWAD_TYPES] = { 0 };
- const char *iwadparm = Args.CheckValue ("-iwad");
+ const char *iwadparm = Args->CheckValue ("-iwad");
size_t numwads;
int pickwad;
size_t i;
@@ -1914,7 +1913,7 @@ static const char *BaseFileSearch (const char *file, const char *ext, bool lookf
bool ConsiderPatches (const char *arg, const char *ext)
{
bool noDef = false;
- DArgs *files = Args.GatherFiles (arg, ext, false);
+ DArgs *files = Args->GatherFiles (arg, ext, false);
if (files->NumArgs() > 0)
{
@@ -1930,7 +1929,7 @@ bool ConsiderPatches (const char *arg, const char *ext)
}
noDef = true;
}
- delete files;
+ files->Destroy();
return noDef;
}
@@ -2038,8 +2037,7 @@ void D_DoomMain (void)
const IWADInfo *iwad_info;
srand(I_MSTime());
-
- atterm (DObject::StaticShutdown);
+
PClass::StaticInit ();
atterm (C_DeinitConsole);
@@ -2121,19 +2119,19 @@ void D_DoomMain (void)
execFiles = new DArgs;
GameConfig->AddAutoexec (execFiles, GameNames[gameinfo.gametype]);
D_MultiExec (execFiles, true);
- delete execFiles;
+ execFiles->Destroy();
// Run .cfg files at the start of the command line.
- execFiles = Args.GatherFiles (NULL, ".cfg", false);
+ execFiles = Args->GatherFiles (NULL, ".cfg", false);
D_MultiExec (execFiles, true);
- delete execFiles;
+ execFiles->Destroy();
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
- DArgs *files = Args.GatherFiles ("-file", ".wad", true);
- DArgs *files1 = Args.GatherFiles (NULL, ".zip", false);
- DArgs *files2 = Args.GatherFiles (NULL, ".pk3", false);
- DArgs *files3 = Args.GatherFiles (NULL, ".txt", false);
+ DArgs *files = Args->GatherFiles ("-file", ".wad", true);
+ DArgs *files1 = Args->GatherFiles (NULL, ".zip", false);
+ DArgs *files2 = Args->GatherFiles (NULL, ".pk3", false);
+ DArgs *files3 = Args->GatherFiles (NULL, ".txt", false);
if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0 || files3->NumArgs() > 0)
{
// Check for -file in shareware
@@ -2160,10 +2158,10 @@ void D_DoomMain (void)
D_AddWildFile (files3->GetArg (i));
}
}
- delete files;
- delete files1;
- delete files2;
- delete files3;
+ files->Destroy();
+ files1->Destroy();
+ files2->Destroy();
+ files3->Destroy();
Printf ("W_Init: Init WADfiles.\n");
Wads.InitMultipleFiles (&wadfiles);
@@ -2193,18 +2191,18 @@ void D_DoomMain (void)
Printf ("P_Init: Checking cmd-line parameters...\n");
flags = dmflags;
- if (Args.CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS;
- if (Args.CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN;
- if (Args.CheckParm ("-fast")) flags |= DF_FAST_MONSTERS;
+ if (Args->CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS;
+ if (Args->CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN;
+ if (Args->CheckParm ("-fast")) flags |= DF_FAST_MONSTERS;
- devparm = !!Args.CheckParm ("-devparm");
+ devparm = !!Args->CheckParm ("-devparm");
- if (Args.CheckParm ("-altdeath"))
+ if (Args->CheckParm ("-altdeath"))
{
deathmatch = 1;
flags |= DF_ITEMS_RESPAWN;
}
- else if (Args.CheckParm ("-deathmatch"))
+ else if (Args->CheckParm ("-deathmatch"))
{
deathmatch = 1;
flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN;
@@ -2223,27 +2221,27 @@ void D_DoomMain (void)
}
autostart = false;
- const char *val = Args.CheckValue ("-skill");
+ const char *val = Args->CheckValue ("-skill");
if (val)
{
gameskill = val[0] - '1';
autostart = true;
}
- p = Args.CheckParm ("-warp");
- if (p && p < Args.NumArgs() - 1)
+ p = Args->CheckParm ("-warp");
+ if (p && p < Args->NumArgs() - 1)
{
int ep, map;
if (gameinfo.flags & GI_MAPxx)
{
ep = 1;
- map = atoi (Args.GetArg(p+1));
+ map = atoi (Args->GetArg(p+1));
}
else
{
- ep = atoi (Args.GetArg(p+1));
- map = p < Args.NumArgs() - 2 ? atoi (Args.GetArg(p+2)) : 10;
+ ep = atoi (Args->GetArg(p+1));
+ map = p < Args->NumArgs() - 2 ? atoi (Args->GetArg(p+2)) : 10;
if (map < 1 || map > 9)
{
map = ep;
@@ -2256,19 +2254,19 @@ void D_DoomMain (void)
}
// [RH] Hack to handle +map
- p = Args.CheckParm ("+map");
- if (p && p < Args.NumArgs()-1)
+ p = Args->CheckParm ("+map");
+ if (p && p < Args->NumArgs()-1)
{
- MapData * map = P_OpenMapData(Args.GetArg (p+1));
+ MapData * map = P_OpenMapData(Args->GetArg (p+1));
if (map == NULL)
{
- Printf ("Can't find map %s\n", Args.GetArg (p+1));
+ Printf ("Can't find map %s\n", Args->GetArg (p+1));
}
else
{
delete map;
- strncpy (startmap, Args.GetArg (p+1), 8);
- Args.GetArg (p)[0] = '-';
+ strncpy (startmap, Args->GetArg (p+1), 8);
+ Args->GetArg (p)[0] = '-';
autostart = true;
}
}
@@ -2281,7 +2279,7 @@ void D_DoomMain (void)
// We do not need to support -cdrom under Unix, because all the files
// that would go to c:\\zdoomdat are already stored in .zdoom inside
// the user's home directory.
- if (Args.CheckParm("-cdrom"))
+ if (Args->CheckParm("-cdrom"))
{
Printf (GStrings("D_CDROM"));
mkdir (CDROM_DIR, 0);
@@ -2293,7 +2291,7 @@ void D_DoomMain (void)
UCVarValue value;
static char one_hundred[] = "100";
- value.String = Args.CheckValue ("-turbo");
+ value.String = Args->CheckValue ("-turbo");
if (value.String == NULL)
value.String = one_hundred;
else
@@ -2302,7 +2300,7 @@ void D_DoomMain (void)
turbo.SetGenericRepDefault (value, CVAR_String);
}
- v = Args.CheckValue ("-timer");
+ v = Args->CheckValue ("-timer");
if (v)
{
double time = strtod (v, NULL);
@@ -2310,7 +2308,7 @@ void D_DoomMain (void)
timelimit = (float)time;
}
- v = Args.CheckValue ("-avg");
+ v = Args->CheckValue ("-avg");
if (v)
{
Printf ("Austin Virtual Gaming: Levels will end after 20 minutes\n");
@@ -2420,10 +2418,10 @@ void D_DoomMain (void)
}
//Added by MC:
- bglobal.getspawned = Args.GatherFiles ("-bots", "", false);
+ bglobal.getspawned = Args->GatherFiles ("-bots", "", false);
if (bglobal.getspawned->NumArgs() == 0)
{
- delete bglobal.getspawned;
+ bglobal.getspawned->Destroy();
bglobal.getspawned = NULL;
}
else
@@ -2459,13 +2457,11 @@ void D_DoomMain (void)
// [RH] Run any saved commands from the command line or autoexec.cfg now.
gamestate = GS_FULLCONSOLE;
Net_NewMakeTic ();
- DObject::BeginFrame ();
DThinker::RunThinkers ();
- DObject::EndFrame ();
gamestate = GS_STARTUP;
// start the apropriate game based on parms
- v = Args.CheckValue ("-record");
+ v = Args->CheckValue ("-record");
if (v)
{
@@ -2477,24 +2473,23 @@ void D_DoomMain (void)
StartScreen = NULL;
V_Init2();
- files = Args.GatherFiles ("-playdemo", ".lmp", false);
+ files = Args->GatherFiles ("-playdemo", ".lmp", false);
if (files->NumArgs() > 0)
{
singledemo = true; // quit after one demo
G_DeferedPlayDemo (files->GetArg (0));
- delete files;
D_DoomLoop (); // never returns
}
- delete files;
+ files->Destroy();
- v = Args.CheckValue ("-timedemo");
+ v = Args->CheckValue ("-timedemo");
if (v)
{
G_TimeDemo (v);
D_DoomLoop (); // never returns
}
- v = Args.CheckValue ("-loadgame");
+ v = Args->CheckValue ("-loadgame");
if (v)
{
file = v;
diff --git a/src/d_net.cpp b/src/d_net.cpp
index bbf79f5d..1bb32254 100644
--- a/src/d_net.cpp
+++ b/src/d_net.cpp
@@ -921,6 +921,8 @@ void NetUpdate (void)
BYTE *cmddata;
bool resendOnly;
+ GC::CheckGC();
+
if (ticdup == 0)
{
return;
@@ -1586,7 +1588,7 @@ void D_CheckNetGame (void)
consoleplayer = doomcom.consoleplayer;
- v = Args.CheckValue ("-netmode");
+ v = Args->CheckValue ("-netmode");
if (v != NULL)
{
NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer;
@@ -1595,7 +1597,7 @@ void D_CheckNetGame (void)
// [RH] Setup user info
D_SetupUserInfo ();
- if (Args.CheckParm ("-debugfile"))
+ if (Args->CheckParm ("-debugfile"))
{
char filename[20];
sprintf (filename,"debug%i.txt",consoleplayer);
@@ -1834,12 +1836,11 @@ void TryRunTics (void)
D_DoAdvanceDemo ();
}
if (debugfile) fprintf (debugfile, "run tic %d\n", gametic);
- DObject::BeginFrame ();
C_Ticker ();
M_Ticker ();
I_GetTime (true);
G_Ticker ();
- DObject::EndFrame ();
+ GC::CheckGC ();
gametic++;
NetUpdate (); // check for new console commands
diff --git a/src/d_player.h b/src/d_player.h
index 4da112a4..a2f13c65 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -105,8 +105,8 @@ public:
int crouchsprite;
int MaxHealth;
int RunHealth;
- AInventory *InvFirst; // first inventory item displayed on inventory bar
- AInventory *InvSel; // selected inventory item
+ TObjPtr InvFirst; // first inventory item displayed on inventory bar
+ TObjPtr InvSel; // selected inventory item
// [GRB] Player class properties
fixed_t JumpZ;
@@ -192,6 +192,7 @@ public:
void Serialize (FArchive &arc);
void FixPointers (const DObject *obj, DObject *replacement);
+ size_t PropagateMark();
void SetLogNumber (int num);
void SetLogText (const char *text);
diff --git a/src/decallib.cpp b/src/decallib.cpp
index e157cf2b..1a0e193e 100644
--- a/src/decallib.cpp
+++ b/src/decallib.cpp
@@ -110,7 +110,7 @@ struct DDecalThinker : public DThinker
public:
DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {}
void Serialize (FArchive &arc);
- DBaseDecal *TheDecal;
+ TObjPtr TheDecal;
protected:
DDecalThinker () : DThinker (STAT_DECALTHINKER) {}
};
@@ -1143,6 +1143,7 @@ void DDecalFader::Tick ()
{
TheDecal->Destroy (); // remove the decal
Destroy (); // remove myself
+ return;
}
if (StartTrans == -1)
{
diff --git a/src/dobject.cpp b/src/dobject.cpp
index ebce1083..4bd7d4a2 100644
--- a/src/dobject.cpp
+++ b/src/dobject.cpp
@@ -47,6 +47,7 @@
#include "r_state.h"
#include "stats.h"
#include "a_sharedglobal.h"
+#include "dsectoreffect.h"
#include "autosegs.h"
@@ -343,148 +344,120 @@ CCMD (dumpclasses)
Printf ("%d classes shown, %d omitted\n", shown, omitted);
}
-TArray DObject::Objects (TArray::NoInit);
-TArray DObject::FreeIndices (TArray::NoInit);
-TArray DObject::ToDestroy (TArray::NoInit);
-bool DObject::Inactive;
-
void DObject::InPlaceConstructor (void *mem)
{
new ((EInPlace *)mem) DObject;
}
DObject::DObject ()
-: ObjectFlags(0), Class(0)
+: Class(0), ObjectFlags(0)
{
- if (FreeIndices.Pop (Index))
- Objects[Index] = this;
- else
- Index = Objects.Push (this);
+ ObjectFlags = GC::CurrentWhite & OF_WhiteBits;
+ ObjNext = GC::Root;
+ GC::Root = this;
}
DObject::DObject (PClass *inClass)
-: ObjectFlags(0), Class(inClass)
+: Class(inClass), ObjectFlags(0)
{
- if (FreeIndices.Pop (Index))
- Objects[Index] = this;
- else
- Index = Objects.Push (this);
+ ObjectFlags = GC::CurrentWhite & OF_WhiteBits;
+ ObjNext = GC::Root;
+ GC::Root = this;
}
DObject::~DObject ()
{
- if (!Inactive)
+ if (!(ObjectFlags & OF_Cleanup))
{
- if (!(ObjectFlags & OF_MassDestruction))
- {
- RemoveFromArray ();
- DestroyScan (this);
- }
- else if (!(ObjectFlags & OF_Cleanup))
- {
- // object is queued for deletion, but is not being deleted
- // by the destruction process, so remove it from the
- // ToDestroy array and do other necessary stuff.
- unsigned int i;
+ DObject **probe;
+ PClass *type = GetClass();
- for (i = ToDestroy.Size() - 1; i-- > 0; )
+ if (!(ObjectFlags & OF_YesReallyDelete))
+ {
+ Printf ("Warning: '%s' is freed outside the GC process.\n",
+ type != NULL ? type->TypeName.GetChars() : "==some object==");
+ }
+
+ // Find all pointers that reference this object and NULL them.
+ PointerSubstitution(this, NULL);
+
+ // Now unlink this object from the GC list.
+ for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext))
+ {
+ if (*probe == this)
{
- if (ToDestroy[i] == this)
+ *probe = ObjNext;
+ if (&ObjNext == GC::SweepPos)
{
- ToDestroy[i] = NULL;
+ GC::SweepPos = probe;
+ }
+ break;
+ }
+ }
+
+ // If it's gray, also unlink it from the gray list.
+ if (this->IsGray())
+ {
+ for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext))
+ {
+ if (*probe == this)
+ {
+ *probe = GCNext;
break;
}
}
- DestroyScan (this);
}
}
}
void DObject::Destroy ()
{
- if (!Inactive)
- {
- if (!(ObjectFlags & OF_MassDestruction))
- {
- RemoveFromArray ();
- ObjectFlags |= OF_MassDestruction;
- ToDestroy.Push (this);
- }
- }
- else
- delete this;
+ ObjectFlags |= OF_EuthanizeMe;
}
-void DObject::BeginFrame ()
+size_t DObject::PropagateMark()
{
- StaleCycles = 0;
- StaleCount = 0;
-}
-
-void DObject::EndFrame ()
-{
- clock (StaleCycles);
- if (ToDestroy.Size ())
+ const PClass *info = GetClass();
+ const size_t *offsets = info->FlatPointers;
+ if (offsets == NULL)
{
- StaleCount += (int)ToDestroy.Size ();
- DestroyScan ();
- //Printf ("Destroyed %d objects\n", ToDestroy.Size());
-
- DObject *obj;
- while (ToDestroy.Pop (obj))
- {
- if (obj)
- {
- obj->ObjectFlags |= OF_Cleanup;
- delete obj;
- }
- }
+ const_cast(info)->BuildFlatPointers();
+ offsets = info->FlatPointers;
}
- unclock (StaleCycles);
-}
-
-void DObject::RemoveFromArray ()
-{
- if (Objects.Size() == Index + 1)
+ while (*offsets != ~(size_t)0)
{
- DObject *dummy;
- Objects.Pop (dummy);
- }
- else if (Objects.Size() > Index)
- {
- Objects[Index] = NULL;
- FreeIndices.Push (Index);
+ GC::Mark((DObject **)((BYTE *)this + *offsets));
+ offsets++;
}
+ return info->Size;
}
void DObject::PointerSubstitution (DObject *old, DObject *notOld)
{
- unsigned int i, highest;
- highest = Objects.Size ();
+ DObject *probe;
+ int i;
- for (i = 0; i <= highest; i++)
+ // Go through all objects.
+ for (probe = GC::Root; probe != NULL; probe = probe->ObjNext)
{
- DObject *current = i < highest ? Objects[i] : &bglobal;
- if (current)
+ const PClass *info = probe->GetClass();
+ const size_t *offsets = info->FlatPointers;
+ if (offsets == NULL)
{
- const PClass *info = current->GetClass();
- const size_t *offsets = info->FlatPointers;
- if (offsets == NULL)
+ const_cast(info)->BuildFlatPointers();
+ offsets = info->FlatPointers;
+ }
+ while (*offsets != ~(size_t)0)
+ {
+ if (*(DObject **)((BYTE *)probe + *offsets) == old)
{
- const_cast(info)->BuildFlatPointers();
- offsets = info->FlatPointers;
- }
- while (*offsets != ~(size_t)0)
- {
- if (*(DObject **)((BYTE *)current + *offsets) == old)
- {
- *(DObject **)((BYTE *)current + *offsets) = notOld;
- }
- offsets++;
+ *(DObject **)((BYTE *)probe + *offsets) = notOld;
}
+ offsets++;
}
}
+ // Go through the bodyque.
for (i = 0; i < BODYQUESIZE; ++i)
{
if (bodyque[i] == old)
@@ -493,125 +466,32 @@ void DObject::PointerSubstitution (DObject *old, DObject *notOld)
}
}
- // This is an ugly hack, but it's the best I can do for now.
+ // Go through players.
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
players[i].FixPointers (old, notOld);
}
+ // Go through sectors.
if (sectors != NULL)
{
- for (i = 0; i < (unsigned int)numsectors; ++i)
+ for (i = 0; i < numsectors; ++i)
{
- if (sectors[i].SoundTarget == old)
- {
- sectors[i].SoundTarget = static_cast(notOld);
- }
- if (sectors[i].CeilingSkyBox == old)
- {
- sectors[i].CeilingSkyBox = static_cast(notOld);
- }
- if (sectors[i].FloorSkyBox == old)
- {
- sectors[i].FloorSkyBox = static_cast(notOld);
- }
+#define SECTOR_CHECK(f,t) \
+ if (sectors[i].f == static_cast(old)) { sectors[i].f = static_cast(notOld); }
+ SECTOR_CHECK( SoundTarget, AActor );
+ SECTOR_CHECK( CeilingSkyBox, ASkyViewpoint );
+ SECTOR_CHECK( FloorSkyBox, ASkyViewpoint );
+ SECTOR_CHECK( SecActTarget, ASectorAction );
+ SECTOR_CHECK( floordata, DSectorEffect );
+ SECTOR_CHECK( ceilingdata, DSectorEffect );
+ SECTOR_CHECK( lightingdata, DSectorEffect );
+#undef SECTOR_CHECK
}
}
}
-// Search for references to a single object and NULL them.
-// It should not be listed in ToDestroy.
-void DObject::DestroyScan (DObject *obj)
-{
- PointerSubstitution (obj, NULL);
-}
-
-// Search for references to all objects scheduled for
-// destruction and NULL them.
-void DObject::DestroyScan ()
-{
- unsigned int i, highest;
- int j, destroycount;
- DObject **destroybase;
- destroycount = (int)ToDestroy.Size ();
- if (destroycount == 0)
- return;
- destroybase = &ToDestroy[0] + destroycount;
- destroycount = -destroycount;
- highest = Objects.Size ();
-
- for (i = 0; i <= highest; i++)
- {
- DObject *current = i < highest ? Objects[i] : &bglobal;
- if (current)
- {
- const PClass *info = current->GetClass();
- const size_t *offsets = info->FlatPointers;
- if (offsets == NULL)
- {
- const_cast(info)->BuildFlatPointers();
- offsets = info->FlatPointers;
- }
- while (*offsets != ~(size_t)0)
- {
- j = destroycount;
- do
- {
- if (*(DObject **)((BYTE *)current + *offsets) == *(destroybase + j))
- {
- *(DObject **)((BYTE *)current + *offsets) = NULL;
- }
- } while (++j);
- offsets++;
- }
- }
- }
-
- j = destroycount;
- do
- {
- for (i = 0; i < BODYQUESIZE; ++i)
- {
- if (bodyque[i] == *(destroybase + j))
- {
- bodyque[i] = NULL;
- }
- }
-
- } while (++j);
-
- // This is an ugly hack, but it's the best I can do for now.
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i])
- {
- j = destroycount;
- do
- {
- players[i].FixPointers (*(destroybase + j), NULL);
- } while (++j);
- }
- }
-
- for (i = 0; i < (unsigned int)numsectors; ++i)
- {
- j = destroycount;
- do
- {
- if (sectors[i].SoundTarget == *(destroybase + j))
- {
- sectors[i].SoundTarget = NULL;
- }
- } while (++j);
- }
-}
-
-void DObject::StaticShutdown ()
-{
- Inactive = true;
-}
-
void DObject::Serialize (FArchive &arc)
{
ObjectFlags |= OF_SerialSuccess;
diff --git a/src/dobject.h b/src/dobject.h
index f18f1e03..b0ef5d70 100644
--- a/src/dobject.h
+++ b/src/dobject.h
@@ -2,7 +2,7 @@
** dobject.h
**
**---------------------------------------------------------------------------
-** Copyright 1998-2006 Randy Heit
+** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@@ -48,7 +48,6 @@ class FArchive;
class DObject;
class DArgs;
-class DBoundingBox;
class DCanvas;
class DConsoleCommand;
class DConsoleAlias;
@@ -211,12 +210,205 @@ private: \
enum EObjectFlags
{
- OF_MassDestruction = 0x00000001, // Object is queued for deletion
- OF_Cleanup = 0x00000002, // Object is being deconstructed as a result of a queued deletion
- OF_JustSpawned = 0x00000004, // Thinker was spawned this tic
- OF_SerialSuccess = 0x10000000 // For debugging Serialize() calls
+ // GC flags
+ OF_White0 = 1 << 0, // Object is white (type 0)
+ OF_White1 = 1 << 1, // Object is white (type 1)
+ OF_Black = 1 << 2, // Object is black
+ OF_Fixed = 1 << 3, // Object is fixed (should not be collected)
+ OF_Rooted = 1 << 4, // Object is soft-rooted
+ OF_EuthanizeMe = 1 << 5, // Object wants to die
+ OF_Cleanup = 1 << 6, // Object is now being deleted by the collector
+ OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning
+
+ OF_WhiteBits = OF_White0 | OF_White1,
+ OF_MarkBits = OF_WhiteBits | OF_Black,
+
+ // Other flags
+ OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
+ OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
};
+template class TObjPtr;
+
+namespace GC
+{
+ enum EGCState
+ {
+ GCS_Pause,
+ GCS_Propagate,
+ GCS_Sweep,
+ GCS_Finalize
+ };
+
+ // Number of bytes currently allocated through M_Malloc/M_Realloc.
+ extern size_t AllocBytes;
+
+ // Amount of memory to allocate before triggering a collection.
+ extern size_t Threshold;
+
+ // List of gray objects.
+ extern DObject *Gray;
+
+ // List of every object.
+ extern DObject *Root;
+
+ // Current white value for potentially-live objects.
+ extern DWORD CurrentWhite;
+
+ // Current collector state.
+ extern EGCState State;
+
+ // Position of GC sweep in the list of objects.
+ extern DObject **SweepPos;
+
+ // Size of GC pause.
+ extern int Pause;
+
+ // Size of GC steps.
+ extern int StepMul;
+
+ // Current white value for known-dead objects.
+ static inline DWORD OtherWhite()
+ {
+ return CurrentWhite ^ OF_WhiteBits;
+ }
+
+ // Frees all objects, whether they're dead or not.
+ void FreeAll();
+
+ // Does one collection step.
+ void Step();
+
+ // Does a complete collection.
+ void FullGC();
+
+ // Handles the grunt work for a write barrier.
+ void Barrier(DObject *pointing, DObject *pointed);
+
+ // Handles a write barrier.
+ static inline void WriteBarrier(DObject *pointing, DObject *pointed);
+
+ // Handles a write barrier for a pointer that isn't inside an object.
+ static inline void WriteBarrier(DObject *pointed);
+
+ // Handles a read barrier.
+ template inline T *ReadBarrier(T *&obj)
+ {
+ if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe))
+ {
+ return obj;
+ }
+ return obj = NULL;
+ }
+
+ // Check if it's time to collect, and do a collection step if it is.
+ static inline void CheckGC()
+ {
+ if (AllocBytes >= Threshold)
+ Step();
+ }
+
+ // Marks a white object gray. If the object wants to die, the pointer
+ // is NULLed instead.
+ void Mark(DObject **obj);
+
+ // Soft-roots an object.
+ void AddSoftRoot(DObject *obj);
+
+ // Unroots an object.
+ void DelSoftRoot(DObject *obj);
+
+ template void Mark(T *&obj) { Mark((DObject **)&obj); }
+ template void Mark(TObjPtr &obj);
+}
+
+// A template class to help with handling read barriers. It does not
+// handle write barriers, because those can be handled more efficiently
+// with knowledge of the object that holds the pointer.
+template
+class TObjPtr
+{
+ T *p;
+public:
+ TObjPtr() throw()
+ {
+ }
+ TObjPtr(T *q) throw()
+ : p(q)
+ {
+ }
+ TObjPtr(const TObjPtr &q) throw()
+ : p(q.p)
+ {
+ }
+ T *operator=(T *q) throw()
+ {
+ return p = q;
+ // The caller must now perform a write barrier.
+ }
+ operator T*() throw()
+ {
+ return GC::ReadBarrier(p);
+ }
+ T &operator*()
+ {
+ T *q = GC::ReadBarrier(p);
+ assert(q != NULL);
+ return *q;
+ }
+ T **operator&() throw()
+ {
+ // Does not perform a read barrier. The only real use for this is with
+ // the DECLARE_POINTER macro, where a read barrier would be a very bad
+ // thing.
+ return &p;
+ }
+ T *operator->() throw()
+ {
+ return GC::ReadBarrier(p);
+ }
+ bool operator<(T *u) throw()
+ {
+ return GC::ReadBarrier(p) < u;
+ }
+ bool operator<=(T *u) throw()
+ {
+ return GC::ReadBarrier(p) <= u;
+ }
+ bool operator>(T *u) throw()
+ {
+ return GC::ReadBarrier(p) > u;
+ }
+ bool operator>=(T *u) throw()
+ {
+ return GC::ReadBarrier(p) >= u;
+ }
+ bool operator!=(T *u) throw()
+ {
+ return GC::ReadBarrier(p) != u;
+ }
+ bool operator==(T *u) throw()
+ {
+ return GC::ReadBarrier(p) == u;
+ }
+
+ template friend inline FArchive &operator<<(FArchive &arc, TObjPtr &o);
+};
+
+template inline FArchive &operator<<(FArchive &arc, TObjPtr &o)
+{
+ return arc << o.p;
+}
+
+// Use barrier_cast instead of static_cast when you need to cast
+// the contents of a TObjPtr to a related type.
+template inline T barrier_cast(TObjPtr &o)
+{
+ return static_cast(static_cast(o));
+}
+
+template void GC::Mark(TObjPtr &obj) { GC::Mark((DObject **)&obj); }
+
class DObject
{
public:
@@ -227,12 +419,13 @@ public:
private:
typedef DObject ThisClass;
- // Per-instance variables. There are three.
-public:
- DWORD ObjectFlags; // Flags for this object
+ // Per-instance variables. There are four.
private:
PClass *Class; // This object's type
- unsigned int Index; // This object's index in the global object table
+public:
+ DObject *ObjNext; // Keep track of all allocated objects
+ DObject *GCNext; // Next object in this collection list
+ DWORD ObjectFlags; // Flags for this object
public:
DObject ();
@@ -250,16 +443,11 @@ public:
virtual void Destroy ();
- static void BeginFrame ();
- static void EndFrame ();
-
// If you need to replace one object with another and want to
// change any pointers from the old object to the new object,
// use this method.
static void PointerSubstitution (DObject *old, DObject *notOld);
- static void StaticShutdown ();
-
PClass *GetClass() const
{
if (Class == NULL)
@@ -286,6 +474,60 @@ public:
M_Free(mem);
}
+ // GC fiddling
+
+ // An object is white if either white bit is set.
+ bool IsWhite() const
+ {
+ return !!(ObjectFlags & OF_WhiteBits);
+ }
+
+ bool IsBlack() const
+ {
+ return !!(ObjectFlags & OF_Black);
+ }
+
+ // An object is gray if it isn't white or black.
+ bool IsGray() const
+ {
+ return !(ObjectFlags & OF_MarkBits);
+ }
+
+ // An object is dead if it's the other white.
+ bool IsDead() const
+ {
+ return !!(ObjectFlags & GC::OtherWhite() & OF_WhiteBits);
+ }
+
+ void ChangeWhite()
+ {
+ ObjectFlags ^= OF_WhiteBits;
+ }
+
+ void MakeWhite()
+ {
+ ObjectFlags = (ObjectFlags & ~OF_MarkBits) | (GC::CurrentWhite & OF_WhiteBits);
+ }
+
+ void White2Gray()
+ {
+ ObjectFlags &= ~OF_WhiteBits;
+ }
+
+ void Black2Gray()
+ {
+ ObjectFlags &= ~OF_Black;
+ }
+
+ void Gray2Black()
+ {
+ ObjectFlags |= OF_Black;
+ }
+
+ // Marks all objects pointed to by this one. Returns the (approximate)
+ // amount of memory used by this object.
+ virtual size_t PropagateMark();
+
protected:
// This form of placement new and delete is for use *only* by PClass's
// CreateNew() method. Do not use them for some other purpose.
@@ -296,22 +538,26 @@ protected:
void operator delete (void *mem, EInPlace *)
{
- free (mem);
+ M_Free (mem);
}
-
-private:
- static TArray Objects;
- static TArray FreeIndices;
- static TArray ToDestroy;
-
- static void DestroyScan (DObject *obj);
- static void DestroyScan ();
-
- void RemoveFromArray ();
-
- static bool Inactive;
};
+static inline void GC::WriteBarrier(DObject *pointing, DObject *pointed)
+{
+ if (pointed != NULL && pointed->IsWhite() && pointing->IsBlack())
+ {
+ Barrier(pointing, pointed);
+ }
+}
+
+static inline void GC::WriteBarrier(DObject *pointed)
+{
+ if (pointed != NULL && State == GCS_Propagate && pointed->IsWhite())
+ {
+ Barrier(NULL, pointed);
+ }
+}
+
#include "dobjtype.h"
inline bool DObject::IsKindOf (const PClass *base) const
diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp
new file mode 100644
index 00000000..ec0ede4b
--- /dev/null
+++ b/src/dobjgc.cpp
@@ -0,0 +1,623 @@
+/*
+** dobjgc.cpp
+** The garbage collector. Based largely on Lua's.
+**
+**---------------------------------------------------------------------------
+** Copyright 2008 Randy Heit
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+// HEADER FILES ------------------------------------------------------------
+
+#include "dobject.h"
+#include "templates.h"
+#include "b_bot.h"
+#include "p_local.h"
+#include "g_game.h"
+#include "r_data.h"
+#include "a_sharedglobal.h"
+#include "sbar.h"
+#include "stats.h"
+#include "c_dispatch.h"
+#include "p_acs.h"
+
+// MACROS ------------------------------------------------------------------
+
+/*
+@@ DEFAULT_GCPAUSE defines the default pause between garbage-collector cycles
+@* as a percentage.
+** CHANGE it if you want the GC to run faster or slower (higher values
+** mean larger pauses which mean slower collection.) You can also change
+** this value dynamically.
+*/
+#define DEFAULT_GCPAUSE 150 // 150% (wait for memory to increase by half before next GC)
+
+/*
+@@ DEFAULT_GCMUL defines the default speed of garbage collection relative to
+@* memory allocation as a percentage.
+** CHANGE it if you want to change the granularity of the garbage
+** collection. (Higher values mean coarser collections. 0 represents
+** infinity, where each step performs a full collection.) You can also
+** change this value dynamically.
+*/
+#define DEFAULT_GCMUL 400 // GC runs 'quadruple the speed' of memory allocation
+
+
+#define GCSTEPSIZE 1024u
+#define GCSWEEPMAX 40
+#define GCSWEEPCOST 10
+#define GCFINALIZECOST 100
+
+// TYPES -------------------------------------------------------------------
+
+// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
+
+// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
+
+// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+// EXTERNAL DATA DECLARATIONS ----------------------------------------------
+
+// PUBLIC DATA DEFINITIONS -------------------------------------------------
+
+namespace GC
+{
+size_t AllocBytes;
+size_t Threshold;
+size_t Estimate;
+DObject *Gray;
+DObject *Root;
+DObject *SoftRoots;
+DObject **SweepPos;
+DWORD CurrentWhite = OF_White0 | OF_Fixed;
+EGCState State = GCS_Pause;
+int Pause = DEFAULT_GCPAUSE;
+int StepMul = DEFAULT_GCMUL;
+int StepCount;
+size_t Dept;
+
+// PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+// CODE --------------------------------------------------------------------
+
+//==========================================================================
+//
+// SetThreshold
+//
+// Sets the new threshold after a collection is finished.
+//
+//==========================================================================
+
+void SetThreshold()
+{
+ Threshold = (Estimate / 100) * Pause;
+}
+
+//==========================================================================
+//
+// PropagateMark
+//
+// Marks the top-most gray object black and marks all objects it points to
+// gray.
+//
+//==========================================================================
+
+size_t PropagateMark()
+{
+ DObject *obj = Gray;
+ assert(obj->IsGray());
+ obj->Gray2Black();
+ Gray = obj->GCNext;
+ return obj->PropagateMark();
+}
+
+//==========================================================================
+//
+// PropagateAll
+//
+// Empties the gray list by propagating every single object in it.
+//
+//==========================================================================
+
+static size_t PropagateAll()
+{
+ size_t m = 0;
+ while (Gray != NULL)
+ {
+ m += PropagateMark();
+ }
+ return m;
+}
+
+//==========================================================================
+//
+// SweepList
+//
+// Runs a limited sweep on a list, returning the location where to resume
+// the sweep at next time.
+//
+//==========================================================================
+
+static DObject **SweepList(DObject **p, size_t count)
+{
+ static int scount;
+ DObject *curr;
+ int deadmask = OtherWhite();
+
+ while ((curr = *p) != NULL && count-- > 0)
+ {
+ if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
+ {
+ assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
+ curr->MakeWhite(); // make it white (for next cycle)
+ p = &curr->ObjNext;
+ }
+ else // must erase 'curr'
+ {
+ assert(curr->IsDead());
+ *p = curr->ObjNext;
+ if (!(curr->ObjectFlags & OF_EuthanizeMe))
+ { // The object must be destroyed before it can be finalized.
+ assert(!curr->IsKindOf(RUNTIME_CLASS(DThinker)));
+ curr->Destroy();
+ }
+ curr->ObjectFlags |= OF_Cleanup;
+ delete curr;
+ }
+ }
+ return p;
+}
+
+//==========================================================================
+//
+// Mark
+//
+// Mark a single object gray.
+//
+//==========================================================================
+
+void Mark(DObject **obj)
+{
+ DObject *lobj = *obj;
+ if (lobj != NULL)
+ {
+ if (lobj->ObjectFlags & OF_EuthanizeMe)
+ {
+ *obj = NULL;
+ }
+ else if (lobj->IsWhite())
+ {
+ lobj->White2Gray();
+ lobj->GCNext = Gray;
+ Gray = lobj;
+ }
+ }
+}
+
+//==========================================================================
+//
+// MarkRoot
+//
+// Mark the root set of objects.
+//
+//==========================================================================
+
+static void MarkRoot()
+{
+ int i;
+
+ Gray = NULL;
+ Mark(Args);
+ Mark(screen);
+ Mark(StatusBar);
+ DThinker::MarkRoots();
+ Mark(DACSThinker::ActiveThinker);
+ for (i = 0; i < BODYQUESIZE; ++i)
+ {
+ Mark(bodyque[i]);
+ }
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ players[i].PropagateMark();
+ }
+ if (sectors != NULL)
+ {
+ for (i = 0; i < numsectors; ++i)
+ {
+ Mark(sectors[i].SoundTarget);
+ Mark(sectors[i].CeilingSkyBox);
+ Mark(sectors[i].FloorSkyBox);
+ Mark(sectors[i].SecActTarget);
+ Mark(sectors[i].floordata);
+ Mark(sectors[i].ceilingdata);
+ Mark(sectors[i].lightingdata);
+ }
+ }
+ { // Silly bots
+ DObject *foo = &bglobal;
+ Mark(foo);
+ }
+ // Add soft roots
+ if (SoftRoots != NULL)
+ {
+ DObject **probe = &SoftRoots->ObjNext;
+ while (*probe != NULL)
+ {
+ DObject *soft = *probe;
+ probe = &soft->ObjNext;
+ if ((soft->ObjectFlags & (OF_Rooted | OF_EuthanizeMe)) == OF_Rooted)
+ {
+ Mark(soft);
+ }
+ }
+ }
+ State = GCS_Propagate;
+ StepCount = 0;
+}
+
+//==========================================================================
+//
+// Atomic
+//
+// If their were any propagations that needed to be done atomicly, they
+// would go here. It also sets things up for the sweep state.
+//
+//==========================================================================
+
+static void Atomic()
+{
+ // Flip current white
+ CurrentWhite = OtherWhite();
+ SweepPos = &Root;
+ State = GCS_Sweep;
+ Estimate = AllocBytes;
+}
+
+//==========================================================================
+//
+// SingleStep
+//
+// Performs one step of the collector.
+//
+//==========================================================================
+
+static size_t SingleStep()
+{
+ switch (State)
+ {
+ case GCS_Pause:
+ MarkRoot(); // Start a new collection
+ return 0;
+
+ case GCS_Propagate:
+ if (Gray != NULL)
+ {
+ return PropagateMark();
+ }
+ else
+ { // no more gray objects
+ Atomic(); // finish mark phase
+ return 0;
+ }
+
+ case GCS_Sweep: {
+ size_t old = AllocBytes;
+ SweepPos = SweepList(SweepPos, GCSWEEPMAX);
+ if (*SweepPos == NULL)
+ { // Nothing more to sweep?
+ State = GCS_Finalize;
+ }
+ assert(old >= AllocBytes);
+ Estimate -= old - AllocBytes;
+ return GCSWEEPMAX * GCSWEEPCOST;
+ }
+
+ case GCS_Finalize:
+ State = GCS_Pause; // end collection
+ Dept = 0;
+ return 0;
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+//==========================================================================
+//
+// Step
+//
+// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of
+// memory.
+//
+//==========================================================================
+
+void Step()
+{
+ size_t lim = (GCSTEPSIZE/100) * StepMul;
+ size_t olim;
+ if (lim == 0)
+ {
+ lim = (~(size_t)0) / 2; // no limit
+ }
+ Dept += AllocBytes - Threshold;
+ do
+ {
+ olim = lim;
+ lim -= SingleStep();
+ } while (olim > lim && State != GCS_Pause);
+ if (State != GCS_Pause)
+ {
+ if (Dept < GCSTEPSIZE)
+ {
+ Threshold = AllocBytes + GCSTEPSIZE; // - lim/StepMul
+ }
+ else
+ {
+ Dept -= GCSTEPSIZE;
+ Threshold = AllocBytes;
+ }
+ }
+ else
+ {
+ assert(AllocBytes >= Estimate);
+ SetThreshold();
+ }
+ StepCount++;
+}
+
+//==========================================================================
+//
+// FullGC
+//
+// Collects everything in one fell swoop.
+//
+//==========================================================================
+
+void FullGC()
+{
+ if (State <= GCS_Propagate)
+ {
+ // Reset sweep mark to sweep all elements (returning them to white)
+ SweepPos = &Root;
+ // Reset other collector lists
+ Gray = NULL;
+ State = GCS_Sweep;
+ }
+ // Finish any pending sweep phase
+ while (State != GCS_Finalize)
+ {
+ SingleStep();
+ }
+ MarkRoot();
+ while (State != GCS_Pause)
+ {
+ SingleStep();
+ }
+ SetThreshold();
+}
+
+//==========================================================================
+//
+// Barrier
+//
+// Implements a write barrier to maintain the invariant that a black node
+// never points to a white node by making the node pointed at gray.
+//
+//==========================================================================
+
+void Barrier(DObject *pointing, DObject *pointed)
+{
+ assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
+ assert(pointed->IsWhite() && !pointed->IsDead());
+ assert(State != GCS_Finalize && State != GCS_Pause);
+ // The invariant only needs to be maintained in the propagate state.
+ if (State == GCS_Propagate)
+ {
+ pointed->White2Gray();
+ pointed->GCNext = Gray;
+ Gray = pointed;
+ }
+ // In other states, we can mark the pointing object white so this
+ // barrier won't be triggered again, saving a few cycles in the future.
+ else if (pointing != NULL)
+ {
+ pointing->MakeWhite();
+ }
+}
+
+//==========================================================================
+//
+// AddSoftRoot
+//
+// Marks an object as a soft root. A soft root behaves exactly like a root
+// in MarkRoot, except it can be added at run-time.
+//
+//==========================================================================
+
+void AddSoftRoot(DObject *obj)
+{
+ DObject **probe;
+
+ // Are there any soft roots yet?
+ if (SoftRoots == NULL)
+ {
+ // Create a new object to root the soft roots off of, and stick
+ // it at the end of the object list, so we know that anything
+ // before it is not a soft root.
+ SoftRoots = new DObject;
+ SoftRoots->ObjectFlags |= OF_Fixed;
+ probe = &Root;
+ while (*probe != NULL)
+ {
+ probe = &(*probe)->ObjNext;
+ }
+ Root = SoftRoots->ObjNext;
+ SoftRoots->ObjNext = NULL;
+ *probe = SoftRoots;
+ }
+ // Mark this object as rooted and move it after the SoftRoots marker.
+ probe = &Root;
+ while (*probe != NULL && *probe != obj)
+ {
+ probe = &(*probe)->ObjNext;
+ }
+ *probe = (*probe)->ObjNext;
+ obj->ObjNext = SoftRoots->ObjNext;
+ SoftRoots->ObjNext = obj;
+ obj->ObjectFlags |= OF_Rooted;
+ WriteBarrier(obj);
+}
+
+//==========================================================================
+//
+// DelSoftRoot
+//
+// Unroots an object so that it must be reachable or it will get collected.
+//
+//==========================================================================
+
+void DelSoftRoot(DObject *obj)
+{
+ DObject **probe;
+
+ if (!(obj->ObjectFlags & OF_Rooted))
+ { // Not rooted, so nothing to do.
+ return;
+ }
+ obj->ObjectFlags &= ~OF_Rooted;
+ // Move object out of the soft roots part of the list.
+ probe = &SoftRoots;
+ while (*probe != NULL && *probe != obj)
+ {
+ probe = &(*probe)->ObjNext;
+ }
+ if (*probe == obj)
+ {
+ *probe = obj->ObjNext;
+ obj->ObjNext = Root;
+ Root = obj;
+ }
+}
+
+}
+
+ADD_STAT(gc)
+{
+ static const char *StateStrings[] = {
+ " Pause ",
+ "Propagate",
+ " Sweep ",
+ "Finalize " };
+ FString out;
+ out.Format("[%s] Alloc:%6uK Thresh:%6uK Est:%6uK Steps: %d",
+ StateStrings[GC::State],
+ (GC::AllocBytes + 1023) >> 10,
+ (GC::Threshold + 1023) >> 10,
+ (GC::Estimate + 1023) >> 10,
+ GC::StepCount);
+ if (GC::State != GC::GCS_Pause)
+ {
+ out.AppendFormat(" %uK", (GC::Dept + 1023) >> 10);
+ }
+ return out;
+}
+
+//==========================================================================
+//
+// CCMD gc
+//
+// Controls various aspects of the collector.
+//
+//==========================================================================
+
+CCMD(gc)
+{
+ if (argv.argc() == 1)
+ {
+ Printf ("Usage: gc stop|now|full|pause [size]|stepmul [size]\n");
+ return;
+ }
+ if (stricmp(argv[1], "stop") == 0)
+ {
+ GC::Threshold = ~(size_t)0 - 2;
+ }
+ else if (stricmp(argv[1], "now") == 0)
+ {
+ GC::Threshold = GC::AllocBytes;
+ }
+ else if (stricmp(argv[1], "full") == 0)
+ {
+ GC::FullGC();
+ }
+ else if (stricmp(argv[1], "pause") == 0)
+ {
+ if (argv.argc() == 2)
+ {
+ Printf ("Current GC pause is %d\n", GC::Pause);
+ }
+ else
+ {
+ GC::Pause = MAX(1,atoi(argv[2]));
+ }
+ }
+ else if (stricmp(argv[1], "stepmul") == 0)
+ {
+ if (argv.argc() == 2)
+ {
+ Printf ("Current GC stepmul is %d\n", GC::StepMul);
+ }
+ else
+ {
+ GC::StepMul = MAX(100, atoi(argv[2]));
+ }
+ }
+}
diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp
index 5aa97540..3a4a4c43 100644
--- a/src/dsectoreffect.cpp
+++ b/src/dsectoreffect.cpp
@@ -33,7 +33,7 @@ DSectorEffect::DSectorEffect ()
m_Sector = NULL;
}
-DSectorEffect::~DSectorEffect ()
+void DSectorEffect::Destroy()
{
if (m_Sector)
{
@@ -52,6 +52,7 @@ DSectorEffect::~DSectorEffect ()
m_Sector->lightingdata = NULL;
}
}
+ Super::Destroy();
}
DSectorEffect::DSectorEffect (sector_t *sector)
diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h
index 1c35bbd7..64e1e658 100644
--- a/src/dsectoreffect.h
+++ b/src/dsectoreffect.h
@@ -10,9 +10,9 @@ class DSectorEffect : public DThinker
DECLARE_CLASS (DSectorEffect, DThinker)
public:
DSectorEffect (sector_t *sector);
- ~DSectorEffect ();
void Serialize (FArchive &arc);
+ void Destroy();
sector_t *GetSector() const { return m_Sector; }
diff --git a/src/dthinker.cpp b/src/dthinker.cpp
index fa6bfe05..bd8990bb 100644
--- a/src/dthinker.cpp
+++ b/src/dthinker.cpp
@@ -155,6 +155,14 @@ DThinker::DThinker (int statnum) throw()
{
statnum = MAX_STATNUM;
}
+ if (FreshThinkers[statnum].TailPred->Pred != NULL)
+ {
+ GC::WriteBarrier(static_cast(FreshThinkers[statnum].Tail, this));
+ }
+ else
+ {
+ GC::WriteBarrier(this);
+ }
FreshThinkers[statnum].AddTail (this);
}
@@ -179,6 +187,16 @@ void DThinker::Destroy ()
Super::Destroy ();
}
+void DThinker::Remove()
+{
+ if (Pred->Pred != NULL && Succ->Succ != NULL)
+ {
+ GC::WriteBarrier(static_cast(Pred), static_cast(Succ));
+ GC::WriteBarrier(static_cast(Succ), static_cast(Pred));
+ }
+ Node::Remove();
+}
+
void DThinker::PostBeginPlay ()
{
}
@@ -205,6 +223,8 @@ DThinker *DThinker::FirstThinker (int statnum)
void DThinker::ChangeStatNum (int statnum)
{
+ List *list;
+
if ((unsigned)statnum > MAX_STATNUM)
{
statnum = MAX_STATNUM;
@@ -212,12 +232,44 @@ void DThinker::ChangeStatNum (int statnum)
Remove ();
if ((ObjectFlags & OF_JustSpawned) && statnum >= STAT_FIRST_THINKING)
{
- FreshThinkers[statnum].AddTail (this);
+ list = &FreshThinkers[statnum];
}
else
{
- Thinkers[statnum].AddTail (this);
+ list = &Thinkers[statnum];
}
+ if (list->TailPred->Pred != NULL)
+ {
+ GC::WriteBarrier(static_cast(list->Tail, this));
+ }
+ else
+ {
+ GC::WriteBarrier(this);
+ }
+ list->AddTail(this);
+}
+
+// Mark the first thinker of each list
+void DThinker::MarkRoots()
+{
+ for (int i = 0; i <= MAX_STATNUM; ++i)
+ {
+ DThinker *t = static_cast(Thinkers[i].Head);
+ GC::Mark(t);
+ t = static_cast(FreshThinkers[i].Head);
+ GC::Mark(t);
+ }
+}
+
+size_t DThinker::PropagateMark()
+{
+ // Mark the next thinker in my list
+ if (Succ != NULL && Succ->Succ != NULL)
+ {
+ DThinker *t = static_cast(Succ);
+ GC::Mark(t);
+ }
+ return Super::PropagateMark();
}
// Destroy every thinker
@@ -225,7 +277,6 @@ void DThinker::DestroyAllThinkers ()
{
int i;
- DObject::BeginFrame ();
for (i = 0; i <= MAX_STATNUM; i++)
{
if (i != STAT_TRAVELLING)
@@ -234,7 +285,7 @@ void DThinker::DestroyAllThinkers ()
DestroyThinkersInList (FreshThinkers[i].Head);
}
}
- DObject::EndFrame ();
+ GC::FullGC ();
}
// Destroy all thinkers except for player-controlled actors
@@ -244,7 +295,6 @@ void DThinker::DestroyMostThinkers ()
{
int i;
- DObject::BeginFrame ();
for (i = 0; i <= MAX_STATNUM; i++)
{
if (i != STAT_TRAVELLING)
@@ -253,7 +303,7 @@ void DThinker::DestroyMostThinkers ()
DestroyMostThinkersInList (FreshThinkers[i], i);
}
}
- DObject::EndFrame ();
+ GC::FullGC ();
}
void DThinker::DestroyThinkersInList (Node *node)
@@ -327,6 +377,14 @@ int DThinker::TickThinkers (List *list, List *dest)
if (dest != NULL)
{ // Move thinker from this list to the destination list
node->Remove ();
+ if (dest->TailPred->Pred != NULL)
+ {
+ GC::WriteBarrier(static_cast(dest->Tail, thinker));
+ }
+ else
+ {
+ GC::WriteBarrier(thinker);
+ }
dest->AddTail (node);
}
thinker->PostBeginPlay ();
@@ -336,7 +394,7 @@ int DThinker::TickThinkers (List *list, List *dest)
I_Error ("There is a thinker in the fresh list that has already ticked.\n");
}
- if (!(thinker->ObjectFlags & OF_MassDestruction))
+ if (!(thinker->ObjectFlags & OF_EuthanizeMe))
{ // Only tick thinkers not scheduled for destruction
thinker->Tick ();
}
diff --git a/src/dthinker.h b/src/dthinker.h
index f7905c5b..9cfd9791 100644
--- a/src/dthinker.h
+++ b/src/dthinker.h
@@ -49,13 +49,14 @@ class FThinkerIterator;
enum { MAX_STATNUM = 127 };
// Doubly linked list of thinkers
-class DThinker : public DObject, public Node
+class DThinker : public DObject, protected Node
{
DECLARE_CLASS (DThinker, DObject)
public:
DThinker (int statnum = MAX_STATNUM) throw();
void Destroy ();
+ size_t PropagateMark();
virtual ~DThinker ();
virtual void Tick ();
virtual void PostBeginPlay (); // Called just before the first tick
@@ -67,6 +68,7 @@ public:
static void DestroyAllThinkers ();
static void DestroyMostThinkers ();
static void SerializeAll (FArchive &arc, bool keepPlayers);
+ static void MarkRoots();
static DThinker *FirstThinker (int statnum);
@@ -75,6 +77,7 @@ private:
static void DestroyMostThinkersInList (List &list, int stat);
static int TickThinkers (List *list, List *dest); // Returns: # of thinkers ticked
static void SaveList(FArchive &arc, Node *node);
+ void Remove();
static List Thinkers[MAX_STATNUM+1]; // Current thinkers
static List FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers
diff --git a/src/farchive.cpp b/src/farchive.cpp
index 7d17c8d4..2087b7be 100644
--- a/src/farchive.cpp
+++ b/src/farchive.cpp
@@ -1004,9 +1004,17 @@ FArchive &FArchive::SerializePointer (void *ptrbase, BYTE **ptr, DWORD elemSize)
FArchive &FArchive::SerializeObject (DObject *&object, PClass *type)
{
if (IsStoring ())
+ {
+ if (object != (DObject*)~0)
+ {
+ GC::ReadBarrier(object);
+ }
return WriteObject (object);
+ }
else
+ {
return ReadObject (object, type);
+ }
}
FArchive &FArchive::WriteObject (DObject *obj)
diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp
index 33f482ea..b370fb57 100644
--- a/src/fragglescript/t_func.cpp
+++ b/src/fragglescript/t_func.cpp
@@ -1925,6 +1925,11 @@ void FParser::SF_FadeLight(void)
}
}
+//==========================================================================
+//
+//
+//
+//==========================================================================
void FParser::SF_FloorTexture(void)
{
int tagnum, secnum;
@@ -2066,12 +2071,13 @@ void FParser::SF_StartSkill(void)
I_Error("startskill is not supported by this implementation!\n");
}
-//////////////////////////////////////////////////////////////////////////
+//==========================================================================
//
// Doors
//
-
// opendoor(sectag, [delay], [speed])
+//
+//==========================================================================
void FParser::SF_OpenDoor(void)
{
@@ -2204,13 +2210,13 @@ inline line_t * P_FindLine(int tag,int * searchPosition)
return *searchPosition>=0? &lines[*searchPosition]:NULL;
}
-/*
-FParser::SF_SetLineBlocking()
-
- Sets a line blocking or unblocking
-
- setlineblocking(tag, [1|0]);
-*/
+//==========================================================================
+//
+// FParser::SF_SetLineBlocking()
+//
+// Sets a line blocking or unblocking
+//
+//==========================================================================
void FParser::SF_SetLineBlocking(void)
{
line_t *line;
@@ -2236,12 +2242,10 @@ void FParser::SF_SetLineBlocking(void)
//==========================================================================
//
-//
+// similar, but monster blocking
//
//==========================================================================
-// similar, but monster blocking
-
void FParser::SF_SetLineMonsterBlocking(void)
{
line_t *line;
@@ -2261,17 +2265,19 @@ void FParser::SF_SetLineMonsterBlocking(void)
}
}
-/*
-FParser::SF_SetLineTexture
- #2 in a not-so-long line of ACS-inspired functions
- This one is *much* needed, IMO
-
- Eternity: setlinetexture(tag, side, position, texture)
- Legacy: setlinetexture(tag, texture, side, sections)
-
-*/
+//==========================================================================
+//
+//FParser::SF_SetLineTexture
+//
+// #2 in a not-so-long line of ACS-inspired functions
+// This one is *much* needed, IMO
+//
+// Eternity: setlinetexture(tag, side, position, texture)
+// Legacy: setlinetexture(tag, texture, side, sections)
+//
+//==========================================================================
void FParser::SF_SetLineTexture(void)
{
@@ -2357,11 +2363,10 @@ void FParser::SF_SetLineTexture(void)
//==========================================================================
//
-//
+// SoM: Max, Min, Abs math functions.
//
//==========================================================================
-// SoM: Max, Min, Abs math functions.
void FParser::SF_Max(void)
{
fixed_t n1, n2;
@@ -2417,13 +2422,15 @@ void FParser::SF_Abs(void)
}
}
-/*
-FParser::SF_Gameskill, FParser::SF_Gamemode
-
- Access functions are more elegant for these than variables,
- especially for the game mode, which doesn't exist as a numeric
- variable already.
-*/
+//==========================================================================
+//
+// FParser::SF_Gameskill, FParser::SF_Gamemode
+//
+// Access functions are more elegant for these than variables,
+// especially for the game mode, which doesn't exist as a numeric
+// variable already.
+//
+//==========================================================================
void FParser::SF_Gameskill(void)
{
@@ -2452,13 +2459,16 @@ void FParser::SF_Gamemode(void)
t_return.value.i = 2; // deathmatch
}
-/*
-FParser::SF_IsPlayerObj()
+//==========================================================================
+//
+// FParser::SF_IsPlayerObj()
+//
+// A function suggested by SoM to help the script coder prevent
+// exceptions related to calling player functions on non-player
+// objects.
+//
+//==========================================================================
- A function suggested by SoM to help the script coder prevent
- exceptions related to calling player functions on non-player
- objects.
-*/
void FParser::SF_IsPlayerObj(void)
{
AActor *mo;
@@ -2476,6 +2486,11 @@ void FParser::SF_IsPlayerObj(void)
//============================================================================
//
+// Inventory stuff - mostly new to GZDoom
+//
+// all the original functions are still supported but they have not
+// been expanded from their original and are limited as a result
+//
// Since FraggleScript is rather hard coded to the original inventory
// handling of Doom this is rather messy.
//
@@ -2614,14 +2629,14 @@ static int FS_CheckInventory (AActor *activator, const char *type)
//==========================================================================
//
-//
+// This function is just kept for backwards compatibility
+// and intentionally limited to thr standard keys!
+// Use Give/Take/CheckInventory instead!
//
//==========================================================================
void FParser::SF_PlayerKeys(void)
{
- // This function is just kept for backwards compatibility and intentionally limited to thr standard keys!
- // Use Give/Take/CheckInventory instead!
static const char * const DoomKeys[]={"BlueCard", "YellowCard", "RedCard", "BlueSkull", "YellowSkull", "RedSkull"};
int playernum, keynum, givetake;
const char * keyname;
@@ -2658,14 +2673,13 @@ void FParser::SF_PlayerKeys(void)
//==========================================================================
//
-//
+// This function is just kept for backwards compatibility and intentionally limited!
+// Use Give/Take/CheckInventory instead!
//
//==========================================================================
void FParser::SF_PlayerAmmo(void)
{
- // This function is just kept for backwards compatibility and intentionally limited!
- // Use Give/Take/CheckInventory instead!
int playernum, amount;
const PClass * ammotype;
@@ -2751,7 +2765,9 @@ void FParser::SF_MaxPlayerAmmo()
//==========================================================================
//
-//
+// This function is just kept for backwards compatibility and
+// intentionally limited to the standard weapons!
+// Use Give/Take/CheckInventory instead!
//
//==========================================================================
@@ -2762,8 +2778,6 @@ void FParser::SF_PlayerWeapon()
"PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" };
- // This function is just kept for backwards compatibility and intentionally limited to the standard weapons!
- // Use Give/Take/CheckInventory instead!
int playernum;
int weaponnum;
int newweapon;
@@ -2830,7 +2844,8 @@ void FParser::SF_PlayerWeapon()
//==========================================================================
//
-//
+// This function is just kept for backwards compatibility and
+// intentionally limited to the standard weapons!
//
//==========================================================================
@@ -2839,7 +2854,6 @@ void FParser::SF_PlayerSelectedWeapon()
int playernum;
int weaponnum;
- // This function is just kept for backwards compatibility and intentionally limited to the standard weapons!
static const char * const WeaponNames[]={
"Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher",
@@ -2991,13 +3005,11 @@ void FParser::SF_SetWeapon()
}
}
-// removed FParser::SF_PlayerMaxAmmo
-
-
-
+//==========================================================================
//
// movecamera(camera, targetobj, targetheight, movespeed, targetangle, anglespeed)
//
+//==========================================================================
void FParser::SF_MoveCamera(void)
{
@@ -3219,12 +3231,10 @@ void FParser::SF_ExitSecret(void)
//==========================================================================
//
-//
+// Type forcing functions -- useful with arrays et al
//
//==========================================================================
-// Type forcing functions -- useful with arrays et al
-
void FParser::SF_MobjValue(void)
{
if (CheckArgs(1))
@@ -3448,7 +3458,7 @@ void FParser::SF_SpawnMissile()
void FParser::SF_MapThingNumExist()
{
- TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
+ TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
int intval;
@@ -3456,7 +3466,7 @@ void FParser::SF_MapThingNumExist()
{
intval = intvalue(t_argv[0]);
- if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval]->actor)
+ if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval])
{
t_return.type = svt_int;
t_return.value.i = 0;
@@ -3477,7 +3487,7 @@ void FParser::SF_MapThingNumExist()
void FParser::SF_MapThings()
{
- TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
+ TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
t_return.type = svt_int;
t_return.value.i = SpawnedThings.Size();
@@ -4330,7 +4340,7 @@ void FParser::SF_WallGlow()
//==========================================================================
//
-// Spawns a projectile at a map spot
+//
//
//==========================================================================
@@ -4354,8 +4364,12 @@ DRunningScript *FParser::SaveCurrentScript()
runscr->next = th->RunningScripts->next;
runscr->prev = th->RunningScripts;
runscr->prev->next = runscr;
+ GC::WriteBarrier(runscr->prev, runscr);
if(runscr->next)
+ {
runscr->next->prev = runscr;
+ GC::WriteBarrier(runscr->next, runscr);
+ }
// save the script variables
for(i=0; inext = th->RunningScripts->next;
runscr->prev = th->RunningScripts;
runscr->prev->next = runscr;
+ GC::WriteBarrier(runscr->prev, runscr);
if(runscr->next)
+ {
runscr->next->prev = runscr;
+ GC::WriteBarrier(runscr->next, runscr);
+ }
// save the script variables
for(i=0; iSpawnedThings.Push(acp);
+ DFraggleThinker::ActiveThinker->SpawnedThings.Push(NULL);
}
}
@@ -291,7 +288,7 @@ void T_RegisterSpawnThing(AActor * ac)
{
if (DFraggleThinker::ActiveThinker)
{
- TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
- SpawnedThings[SpawnedThings.Size()-1]->actor=ac;
+ TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
+ SpawnedThings[SpawnedThings.Size()-1] = ac;
}
}
diff --git a/src/fragglescript/t_oper.cpp b/src/fragglescript/t_oper.cpp
index 47f08a84..b807ac0d 100644
--- a/src/fragglescript/t_oper.cpp
+++ b/src/fragglescript/t_oper.cpp
@@ -51,6 +51,12 @@
EvaluateExpression(right, (b)+1, (c)); }\
+//-----------------------------------------------------------------------------
+//
+//
+//
+//-----------------------------------------------------------------------------
+
FParser::operator_t FParser::operators[]=
{
{"=", &FParser::OPequals, backward},
@@ -81,11 +87,11 @@ int FParser::num_operators = sizeof(FParser::operators) / sizeof(FParser::operat
/***************** logic *********************/
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPequals(svalue_t &result, int start, int n, int stop)
{
@@ -105,11 +111,11 @@ void FParser::OPequals(svalue_t &result, int start, int n, int stop)
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPor(svalue_t &result, int start, int n, int stop)
{
@@ -132,11 +138,11 @@ void FParser::OPor(svalue_t &result, int start, int n, int stop)
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPand(svalue_t &result, int start, int n, int stop)
{
@@ -157,11 +163,11 @@ void FParser::OPand(svalue_t &result, int start, int n, int stop)
result.value.i = exprtrue;
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPcmp(svalue_t &result, int start, int n, int stop)
{
@@ -196,11 +202,11 @@ void FParser::OPcmp(svalue_t &result, int start, int n, int stop)
result.value.i = (intvalue(left) == intvalue(right));
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPnotcmp(svalue_t &result, int start, int n, int stop)
{
@@ -209,11 +215,11 @@ void FParser::OPnotcmp(svalue_t &result, int start, int n, int stop)
result.value.i = !result.value.i;
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPlessthan(svalue_t &result, int start, int n, int stop)
{
@@ -230,11 +236,11 @@ void FParser::OPlessthan(svalue_t &result, int start, int n, int stop)
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPgreaterthan(svalue_t &result, int start, int n, int stop)
{
@@ -250,11 +256,11 @@ void FParser::OPgreaterthan(svalue_t &result, int start, int n, int stop)
result.value.i = (intvalue(left) > intvalue(right));
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPnot(svalue_t &result, int start, int n, int stop)
{
@@ -264,13 +270,11 @@ void FParser::OPnot(svalue_t &result, int start, int n, int stop)
result.type = svt_int;
}
-/************** simple math ***************/
-
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPplus(svalue_t &result, int start, int n, int stop)
{
@@ -307,11 +311,11 @@ void FParser::OPplus(svalue_t &result, int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPminus(svalue_t &result, int start, int n, int stop)
{
@@ -341,11 +345,11 @@ void FParser::OPminus(svalue_t &result, int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPmultiply(svalue_t &result,int start, int n, int stop)
{
@@ -366,11 +370,11 @@ void FParser::OPmultiply(svalue_t &result,int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPdivide(svalue_t &result, int start, int n, int stop)
{
@@ -405,11 +409,11 @@ void FParser::OPdivide(svalue_t &result, int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPremainder(svalue_t &result, int start, int n, int stop)
{
@@ -429,13 +433,11 @@ void FParser::OPremainder(svalue_t &result, int start, int n, int stop)
/********** binary operators **************/
-// binary or |
-
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPor_bin(svalue_t &result, int start, int n, int stop)
{
@@ -448,13 +450,11 @@ void FParser::OPor_bin(svalue_t &result, int start, int n, int stop)
}
-// binary and &
-
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPand_bin(svalue_t &result, int start, int n, int stop)
{
@@ -466,11 +466,11 @@ void FParser::OPand_bin(svalue_t &result, int start, int n, int stop)
result.value.i = intvalue(left) & intvalue(right);
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPnot_bin(svalue_t &result, int start, int n, int stop)
{
@@ -481,11 +481,11 @@ void FParser::OPnot_bin(svalue_t &result, int start, int n, int stop)
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPincrement(svalue_t &result, int start, int n, int stop)
{
@@ -546,11 +546,11 @@ void FParser::OPincrement(svalue_t &result, int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPdecrement(svalue_t &result, int start, int n, int stop)
{
@@ -611,11 +611,11 @@ void FParser::OPdecrement(svalue_t &result, int start, int n, int stop)
}
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPlessthanorequal(svalue_t &result, int start, int n, int stop)
{
@@ -631,11 +631,11 @@ void FParser::OPlessthanorequal(svalue_t &result, int start, int n, int stop)
result.value.i = (intvalue(left) <= intvalue(right));
}
-//==========================================================================
+//-----------------------------------------------------------------------------
//
//
//
-//==========================================================================
+//-----------------------------------------------------------------------------
void FParser::OPgreaterthanorequal(svalue_t &result, int start, int n, int stop)
{
diff --git a/src/fragglescript/t_prepro.cpp b/src/fragglescript/t_prepro.cpp
index 8e019b62..4430ad29 100644
--- a/src/fragglescript/t_prepro.cpp
+++ b/src/fragglescript/t_prepro.cpp
@@ -154,6 +154,7 @@ DFsSection *DFsScript::NewSection(const char *brace)
newsec->start_index = MakeIndex(brace);
newsec->next = sections[n];
sections[n] = newsec;
+ GC::WriteBarrier(this, newsec);
return newsec;
}
diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp
index e481ca52..23643614 100644
--- a/src/fragglescript/t_script.cpp
+++ b/src/fragglescript/t_script.cpp
@@ -320,10 +320,6 @@ void DRunningScript::Serialize(FArchive &arc)
// The main thinker
//
//==========================================================================
-IMPLEMENT_POINTY_CLASS(DActorPointer)
- DECLARE_POINTER(actor)
-END_POINTERS
-
IMPLEMENT_POINTY_CLASS(DFraggleThinker)
DECLARE_POINTER(RunningScripts)
DECLARE_POINTER(LevelScript)
@@ -373,10 +369,6 @@ void DFraggleThinker::Destroy()
LevelScript->Destroy();
LevelScript = NULL;
- for(unsigned i=0; iDestroy();
- }
SpawnedThings.Clear();
ActiveThinker = NULL;
Super::Destroy();
@@ -488,7 +480,12 @@ void DFraggleThinker::Tick()
// unhook from chain
current->prev->next = current->next;
- if(current->next) current->next->prev = current->prev;
+ GC::WriteBarrier(current->prev, current->next);
+ if(current->next)
+ {
+ current->next->prev = current->prev;
+ GC::WriteBarrier(current->next, current->prev);
+ }
next = current->next; // save before freeing
// continue the script
@@ -503,6 +500,23 @@ void DFraggleThinker::Tick()
}
}
+
+//==========================================================================
+//
+// We have to mark the SpawnedThings array manually because it's not
+// in the list of declared pointers.
+//
+//==========================================================================
+
+size_t DFraggleThinker::PropagateMark()
+{
+ for(unsigned i=0;inext = th->RunningScripts->next;
runscr->prev = th->RunningScripts;
runscr->prev->next = runscr;
+ GC::WriteBarrier(runscr->prev, runscr);
if(runscr->next)
+ {
runscr->next->prev = runscr;
+ GC::WriteBarrier(runscr->next, runscr);
+ }
// save the script variables
for(i=0; inext; // save for after freeing
- //current->ObjectFlags |= OF_YESREALLYDELETE;
+ current->ObjectFlags |= OF_YesReallyDelete;
delete current;
current = next; // go to next in chain
}
}
+ GC::DelSoftRoot(global_script);
+ global_script->ObjectFlags |= OF_YesReallyDelete;
delete global_script;
}
@@ -627,6 +647,7 @@ AT_GAME_SET(FS_Init)
// I'd rather link the special here than make another source file depend on FS!
LineSpecials[54]=LS_FS_Execute;
global_script = new DFsScript;
+ GC::AddSoftRoot(global_script);
init_functions();
atterm(FS_Close);
}
diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h
index 3842b37e..622a21c4 100644
--- a/src/fragglescript/t_script.h
+++ b/src/fragglescript/t_script.h
@@ -59,33 +59,6 @@ inline bool isop(int c)
//
//==========================================================================
-class DActorPointer : public DObject
-{
- DECLARE_CLASS(DActorPointer, DObject)
- HAS_OBJECT_POINTERS
-
-public:
-
- AActor * actor;
-
- DActorPointer()
- {
- actor=NULL;
- }
-
- void Serialize(FArchive & ar)
- {
- Super::Serialize(ar);
- ar << actor;
- }
-};
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
enum
{
svt_string,
@@ -168,11 +141,11 @@ struct DFsVariable : public DObject
public:
FString Name;
- DFsVariable *next; // for hashing
+ TObjPtr next; // for hashing
int type; // vt_string or vt_int: same as in svalue_t
FString string;
- AActor *actor;
+ TObjPtr actor;
union value_t
{
@@ -190,7 +163,7 @@ public:
DFsVariable(const char *_name = "");
- svalue_t GetValue() const;
+ svalue_t GetValue();
void SetValue(const svalue_t &newvalue);
void Serialize(FArchive &ar);
};
@@ -234,7 +207,7 @@ public:
int start_index;
int end_index;
int loop_index;
- DFsSection *next; // for hashing
+ TObjPtr next; // for hashing
DFsSection()
{
@@ -313,27 +286,27 @@ public:
// {} sections
- DFsSection *sections[SECTIONSLOTS];
+ TObjPtr sections[SECTIONSLOTS];
// variables:
- DFsVariable *variables[VARIABLESLOTS];
+ TObjPtr variables[VARIABLESLOTS];
// ptr to the parent script
// the parent script is the script above this level
// eg. individual linetrigger scripts are children
// of the levelscript, which is a child of the
// global_script
- DFsScript *parent;
+ TObjPtr parent;
// haleyjd: 8-17
// child scripts.
// levelscript holds ptrs to all of the level's scripts
// here.
- DFsScript *children[MAXSCRIPTS];
+ TObjPtr children[MAXSCRIPTS];
- AActor *trigger; // object which triggered this script
+ TObjPtr trigger; // object which triggered this script
bool lastiftrue; // haleyjd: whether last "if" statement was
// true or false
@@ -654,7 +627,7 @@ public:
void Destroy();
void Serialize(FArchive &arc);
- DFsScript *script;
+ TObjPtr script;
// where we are
int save_point;
@@ -663,10 +636,10 @@ public:
int wait_data; // data for wait: tagnum, counter, script number etc
// saved variables
- DFsVariable *variables[VARIABLESLOTS];
+ TObjPtr variables[VARIABLESLOTS];
- DRunningScript *prev, *next; // for chain
- AActor *trigger;
+ TObjPtr prev, next; // for chain
+ TObjPtr trigger;
};
//-----------------------------------------------------------------------------
@@ -680,9 +653,9 @@ class DFraggleThinker : public DThinker
HAS_OBJECT_POINTERS
public:
- DFsScript *LevelScript;
- DRunningScript *RunningScripts;
- TArray SpawnedThings;
+ TObjPtr LevelScript;
+ TObjPtr RunningScripts;
+ TArray> SpawnedThings;
DFraggleThinker();
void Destroy();
@@ -690,6 +663,7 @@ public:
void Serialize(FArchive & arc);
void Tick();
+ size_t PropagateMark();
bool wait_finished(DRunningScript *script);
static DFraggleThinker *ActiveThinker;
diff --git a/src/fragglescript/t_spec.cpp b/src/fragglescript/t_spec.cpp
index f1ba3593..7b36c7e3 100644
--- a/src/fragglescript/t_spec.cpp
+++ b/src/fragglescript/t_spec.cpp
@@ -421,6 +421,7 @@ void FParser::spec_script()
// add to scripts list of parent
Script->children[scriptnum] = newscript;
+ GC::WriteBarrier(Script, newscript);
// copy newscript data
// workout newscript size: -2 to ignore { and }
diff --git a/src/fragglescript/t_variable.cpp b/src/fragglescript/t_variable.cpp
index 8562e5da..3988e92c 100644
--- a/src/fragglescript/t_variable.cpp
+++ b/src/fragglescript/t_variable.cpp
@@ -151,7 +151,7 @@ AActor *actorvalue(const svalue_t &svalue)
}
else
{
- TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
+ TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
// this requires some creativity. We use the intvalue
// as the thing number of a thing in the level.
intval = intvalue(svalue);
@@ -161,14 +161,14 @@ AActor *actorvalue(const svalue_t &svalue)
return NULL;
}
// Inventory items in the player's inventory have to be considered non-present.
- if (SpawnedThings[intval]->actor != NULL &&
- SpawnedThings[intval]->actor->IsKindOf(RUNTIME_CLASS(AInventory)) &&
- static_cast(SpawnedThings[intval]->actor)->Owner != NULL)
+ if (SpawnedThings[intval] != NULL &&
+ SpawnedThings[intval]->IsKindOf(RUNTIME_CLASS(AInventory)) &&
+ barrier_cast(SpawnedThings[intval])->Owner != NULL)
{
return NULL;
}
- return SpawnedThings[intval]->actor;
+ return SpawnedThings[intval];
}
}
@@ -203,7 +203,7 @@ DFsVariable::DFsVariable(const char * _name)
//
//==========================================================================
-svalue_t DFsVariable::GetValue() const
+svalue_t DFsVariable::GetValue()
{
svalue_t returnvar;
@@ -323,6 +323,7 @@ DFsVariable *DFsScript::NewVariable(const char *name, int vtype)
int n = variable_hash(name);
newvar->next = variables[n];
variables[n] = newvar;
+ GC::WriteBarrier(this, newvar);
return newvar;
}
diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp
index 16a64af6..4ad3ae21 100644
--- a/src/g_doom/a_bossbrain.cpp
+++ b/src/g_doom/a_bossbrain.cpp
@@ -315,7 +315,7 @@ void A_SpawnFly (AActor *self)
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true))
newmobj->SetState (newmobj->SeeState);
- if (!(newmobj->ObjectFlags & OF_MassDestruction))
+ if (!(newmobj->ObjectFlags & OF_EuthanizeMe))
{
// telefrag anything in this spot
P_TeleportMove (newmobj, newmobj->x, newmobj->y, newmobj->z, true);
diff --git a/src/g_doom/doom_sbar.cpp b/src/g_doom/doom_sbar.cpp
index 31538f7b..c97b3961 100644
--- a/src/g_doom/doom_sbar.cpp
+++ b/src/g_doom/doom_sbar.cpp
@@ -27,10 +27,11 @@
EXTERN_CVAR (Bool, vid_fps)
-class FDoomStatusBar : public FBaseStatusBar
+class DDoomStatusBar : public DBaseStatusBar
{
+ DECLARE_CLASS(DDoomStatusBar, DBaseStatusBar)
public:
- FDoomStatusBar () : FBaseStatusBar (32)
+ DDoomStatusBar () : DBaseStatusBar (32)
{
static const char *sharedLumpNames[] =
{
@@ -44,8 +45,8 @@ public:
};
FTexture *tex;
- FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
- tex = FBaseStatusBar::Images[imgBNumbers];
+ DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
+ tex = DBaseStatusBar::Images[imgBNumbers];
BigWidth = tex->GetWidth();
BigHeight = tex->GetHeight();
@@ -53,7 +54,7 @@ public:
bEvilGrin = false;
}
- ~FDoomStatusBar ()
+ ~DDoomStatusBar ()
{
}
@@ -126,7 +127,7 @@ public:
void MultiplayerChanged ()
{
- FBaseStatusBar::MultiplayerChanged ();
+ DBaseStatusBar::MultiplayerChanged ();
if (multiplayer)
{
// set face background color
@@ -138,7 +139,7 @@ public:
{
player_t *oldplayer = CPlayer;
- FBaseStatusBar::AttachToPlayer (player);
+ DBaseStatusBar::AttachToPlayer (player);
if (oldplayer != CPlayer)
{
SetFace (&skins[CPlayer->userinfo.skin]);
@@ -153,14 +154,14 @@ public:
void Tick ()
{
- FBaseStatusBar::Tick ();
+ DBaseStatusBar::Tick ();
RandomNumber = M_Random ();
UpdateFace ();
}
void Draw (EHudState state)
{
- FBaseStatusBar::Draw (state);
+ DBaseStatusBar::Draw (state);
if (state == HUD_Fullscreen)
{
@@ -375,7 +376,7 @@ private:
void DrawArm (int on, int picnum, int x, int y, bool drawBackground)
{
int w;
- FTexture *pic = on ? FBaseStatusBar::Images[imgSmNumbers + 2 + picnum] : Images[imgGNUM2 + picnum];
+ FTexture *pic = on ? DBaseStatusBar::Images[imgSmNumbers + 2 + picnum] : Images[imgGNUM2 + picnum];
if (pic != NULL)
{
@@ -555,7 +556,7 @@ private:
void DrawInventoryBar ()
{
- const AInventory *item;
+ AInventory *item;
int i;
// If the player has no artifacts, don't draw the bar
@@ -602,7 +603,7 @@ private:
void DrawFullScreenStuff ()
{
- const AInventory *item;
+ AInventory *item;
int i;
int ammotop;
@@ -1041,7 +1042,9 @@ private:
bool bEvilGrin;
};
-FDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture ()
+IMPLEMENT_CLASS(DDoomStatusBar)
+
+DDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture ()
{
BaseTexture = TexMan["STBAR"];
if (BaseTexture==NULL)
@@ -1057,7 +1060,7 @@ FDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture ()
STBFremap = NULL;
}
-const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int column, const Span **spans_out)
+const BYTE *DDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int column, const Span **spans_out)
{
if (Pixels == NULL)
{
@@ -1068,7 +1071,7 @@ const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int colum
return Pixels + column*Height;
}
-const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetPixels ()
+const BYTE *DDoomStatusBar::FDoomStatusBarTexture::GetPixels ()
{
if (Pixels == NULL)
{
@@ -1077,7 +1080,7 @@ const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetPixels ()
return Pixels;
}
-void FDoomStatusBar::FDoomStatusBarTexture::Unload ()
+void DDoomStatusBar::FDoomStatusBarTexture::Unload ()
{
if (Pixels != NULL)
{
@@ -1086,13 +1089,13 @@ void FDoomStatusBar::FDoomStatusBarTexture::Unload ()
}
}
-FDoomStatusBar::FDoomStatusBarTexture::~FDoomStatusBarTexture ()
+DDoomStatusBar::FDoomStatusBarTexture::~FDoomStatusBarTexture ()
{
Unload ();
}
-void FDoomStatusBar::FDoomStatusBarTexture::MakeTexture ()
+void DDoomStatusBar::FDoomStatusBarTexture::MakeTexture ()
{
Pixels = new BYTE[Width*Height];
const BYTE *pix = BaseTexture->GetPixels();
@@ -1103,7 +1106,7 @@ void FDoomStatusBar::FDoomStatusBarTexture::MakeTexture ()
if (multiplayer) DrawToBar("STFBANY", 143, 1, STBFremap? STBFremap->Remap : NULL);
}
-int FDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int buf_pitch, int buf_height, int x, int y)
+int DDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int buf_pitch, int buf_height, int x, int y)
{
FTexture *tex;
@@ -1136,7 +1139,7 @@ int FDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int
-void FDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x, int y, const BYTE *colormap_in)
+void DDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x, int y, const BYTE *colormap_in)
{
FTexture *pic;
BYTE colormap[256];
@@ -1165,14 +1168,14 @@ void FDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x,
}
}
-void FDoomStatusBar::FDoomStatusBarTexture::SetPlayerRemap(FRemapTable *remap)
+void DDoomStatusBar::FDoomStatusBarTexture::SetPlayerRemap(FRemapTable *remap)
{
Unload();
KillNative();
STBFremap = remap;
}
-FBaseStatusBar *CreateDoomStatusBar ()
+DBaseStatusBar *CreateDoomStatusBar ()
{
- return new FDoomStatusBar;
+ return new DDoomStatusBar;
}
diff --git a/src/g_game.cpp b/src/g_game.cpp
index a0d3ec1c..53aa2906 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -1850,7 +1850,7 @@ FString G_BuildSaveName (const char *prefix, int slot)
const char *leader;
const char *slash = "";
- if (NULL != (leader = Args.CheckValue ("-savedir")))
+ if (NULL != (leader = Args->CheckValue ("-savedir")))
{
size_t len = strlen (leader);
if (leader[len-1] != '\\' && leader[len-1] != '/')
@@ -1859,7 +1859,7 @@ FString G_BuildSaveName (const char *prefix, int slot)
}
}
#ifndef unix
- else if (Args.CheckParm ("-cdrom"))
+ else if (Args->CheckParm ("-cdrom"))
{
leader = CDROM_DIR "/";
}
@@ -2205,7 +2205,7 @@ void G_RecordDemo (char* name)
strcpy (demoname, name);
FixPathSeperator (demoname);
DefaultExtension (demoname, ".lmp");
- v = Args.CheckValue ("-maxdemo");
+ v = Args->CheckValue ("-maxdemo");
maxdemosize = 0x20000;
demobuffer = (BYTE *)M_Malloc (maxdemosize);
@@ -2510,8 +2510,8 @@ void G_DoPlayDemo (void)
//
void G_TimeDemo (char* name)
{
- nodrawers = !!Args.CheckParm ("-nodraw");
- noblit = !!Args.CheckParm ("-noblit");
+ nodrawers = !!Args->CheckParm ("-nodraw");
+ noblit = !!Args->CheckParm ("-noblit");
timingdemo = true;
singletics = true;
diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp
index 773ad6ca..c0f44e9c 100644
--- a/src/g_heretic/a_hereticmisc.cpp
+++ b/src/g_heretic/a_hereticmisc.cpp
@@ -32,7 +32,7 @@ class APod : public AActor
HAS_OBJECT_POINTERS
public:
void BeginPlay ();
- AActor *Generator;
+ TObjPtr Generator;
void Serialize (FArchive &arc);
};
diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp
index ec4abd3f..8874729b 100644
--- a/src/g_heretic/a_hereticweaps.cpp
+++ b/src/g_heretic/a_hereticweaps.cpp
@@ -803,7 +803,7 @@ public:
protected:
bool DoRespawn ();
int NumMaceSpots;
- AActor *FirstSpot;
+ TObjPtr FirstSpot;
private:
friend void A_SpawnMace (AActor *self);
diff --git a/src/g_heretic/heretic_sbar.cpp b/src/g_heretic/heretic_sbar.cpp
index d2b02d6e..058779c9 100644
--- a/src/g_heretic/heretic_sbar.cpp
+++ b/src/g_heretic/heretic_sbar.cpp
@@ -84,10 +84,12 @@ const BYTE *FHereticShader::GetPixels ()
}
-class FHereticStatusBar : public FBaseStatusBar
+class DHereticStatusBar : public DBaseStatusBar
{
+ DECLARE_CLASS(DHereticStatusBar, DBaseStatusBar)
+ HAS_OBJECT_POINTERS
public:
- FHereticStatusBar () : FBaseStatusBar (42)
+ DHereticStatusBar () : DBaseStatusBar (42)
{
static const char *hereticLumpNames[NUM_HERETICSB_IMAGES] =
{
@@ -118,7 +120,7 @@ public:
hereticLumpNames[5] = "LIFEBAR";
}
- FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
+ DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
Images.Init (hereticLumpNames, NUM_HERETICSB_IMAGES);
oldarti = NULL;
@@ -136,7 +138,7 @@ public:
ArtifactFlash = 0;
}
- ~FHereticStatusBar ()
+ ~DHereticStatusBar ()
{
}
@@ -144,7 +146,7 @@ public:
{
int curHealth;
- FBaseStatusBar::Tick ();
+ DBaseStatusBar::Tick ();
if (level.time & 1)
{
ChainWiggle = pr_chainwiggle() & 1;
@@ -174,7 +176,7 @@ public:
void Draw (EHudState state)
{
- FBaseStatusBar::Draw (state);
+ DBaseStatusBar::Draw (state);
if (state == HUD_Fullscreen)
{
@@ -316,6 +318,7 @@ private:
|| (oldarti != NULL && oldartiCount != oldarti->Amount))
{
oldarti = CPlayer->mo->InvSel;
+ GC::WriteBarrier(this, oldarti);
oldartiCount = oldarti != NULL ? oldarti->Amount : 0;
ArtiRefresh = screen->GetPageCount ();
}
@@ -422,6 +425,8 @@ private:
oldammo2 = ammo2;
oldammocount1 = ammocount1;
oldammocount2 = ammocount2;
+ GC::WriteBarrier(this, ammo1);
+ GC::WriteBarrier(this, ammo2);
AmmoRefresh = screen->GetPageCount ();
}
if (AmmoRefresh)
@@ -475,7 +480,7 @@ private:
void DrawInventoryBar ()
{
- const AInventory *item;
+ AInventory *item;
int i;
DrawImage (Images[imgINVBAR], 34, 2);
@@ -517,7 +522,7 @@ private:
void DrawFullScreenStuff ()
{
- const AInventory *item;
+ AInventory *item;
FTexture *pic;
int i;
@@ -713,8 +718,8 @@ private:
static const char patcharti[][10];
static const char ammopic[][10];
- AInventory *oldarti;
- AAmmo *oldammo1, *oldammo2;
+ TObjPtr oldarti;
+ TObjPtr oldammo1, oldammo2;
int oldammocount1, oldammocount2;
int oldartiCount;
int oldfrags;
@@ -773,7 +778,13 @@ private:
char ArmorRefresh;
};
-FBaseStatusBar *CreateHereticStatusBar ()
+IMPLEMENT_POINTY_CLASS(DHereticStatusBar)
+ DECLARE_POINTER(oldarti)
+ DECLARE_POINTER(oldammo1)
+ DECLARE_POINTER(oldammo2)
+END_POINTERS
+
+DBaseStatusBar *CreateHereticStatusBar ()
{
- return new FHereticStatusBar;
+ return new DHereticStatusBar;
}
diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp
index e9b5c6ad..177bbde2 100644
--- a/src/g_hexen/a_clericholy.cpp
+++ b/src/g_hexen/a_clericholy.cpp
@@ -386,6 +386,7 @@ bool AHolySpirit::SpecialBlastHandling (AActor *source, fixed_t strength)
{
tracer = target;
target = source;
+ GC::WriteBarrier(this, source);
}
return true;
}
diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp
index 7a17c986..e0d969cf 100644
--- a/src/g_hexen/a_heresiarch.cpp
+++ b/src/g_hexen/a_heresiarch.cpp
@@ -706,7 +706,7 @@ void A_SorcBallOrbit(AActor *ball)
int x,y;
angle_t angle, baseangle;
int mode = ball->target->args[3];
- AHeresiarch *parent = static_cast (ball->target);
+ AHeresiarch *parent = barrier_cast(ball->target);
int dist = parent->radius - (ball->radius<<1);
angle_t prevangle = ball->special1;
diff --git a/src/g_hexen/a_hexenglobal.h b/src/g_hexen/a_hexenglobal.h
index 9720ffdf..dbc16d2a 100644
--- a/src/g_hexen/a_hexenglobal.h
+++ b/src/g_hexen/a_hexenglobal.h
@@ -66,7 +66,7 @@ protected:
virtual bool MatchPlayerClass (AActor *toucher);
const PClass *FourthWeaponClass;
int PieceValue;
- AInventory *TempFourthWeapon;
+ TObjPtr TempFourthWeapon;
bool PrivateShouldStay ();
};
diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp
index 775e0af7..fa4ff486 100644
--- a/src/g_hexen/a_spike.cpp
+++ b/src/g_hexen/a_spike.cpp
@@ -103,7 +103,7 @@ public:
void Activate (AActor *activator);
void Deactivate (AActor *activator);
- ADirtClump *DirtClump;
+ TObjPtr DirtClump;
};
IMPLEMENT_POINTY_CLASS (AThrustFloor)
diff --git a/src/g_hexen/a_teleportother.cpp b/src/g_hexen/a_teleportother.cpp
index 75610b3b..2a0cd8b5 100644
--- a/src/g_hexen/a_teleportother.cpp
+++ b/src/g_hexen/a_teleportother.cpp
@@ -249,7 +249,7 @@ int ATelOtherFX1::DoSpecialDamage (AActor *target, int damage)
{
target->RemoveFromHash ();
LineSpecials[target->special] (NULL, level.flags & LEVEL_ACTOWNSPECIAL
- ? target : this->target, false, target->args[0], target->args[1],
+ ? target : (AActor *)(this->target), false, target->args[0], target->args[1],
target->args[2], target->args[3], target->args[4]);
target->special = 0;
}
diff --git a/src/g_hexen/hexen_sbar.cpp b/src/g_hexen/hexen_sbar.cpp
index ebff79bb..1a12a82a 100644
--- a/src/g_hexen/hexen_sbar.cpp
+++ b/src/g_hexen/hexen_sbar.cpp
@@ -119,10 +119,12 @@ void FManaBar::MakeTexture ()
memset (Pixels + 25+24+24, color0, run);
}
-class FHexenStatusBar : public FBaseStatusBar
+class DHexenStatusBar : public DBaseStatusBar
{
+ DECLARE_CLASS(DHexenStatusBar, DBaseStatusBar)
+ HAS_OBJECT_POINTERS
public:
- FHexenStatusBar () : FBaseStatusBar (38)
+ DHexenStatusBar () : DBaseStatusBar (38)
{
static const char *hexenLumpNames[NUM_HEXENSB_IMAGES] =
{
@@ -166,7 +168,7 @@ public:
"INRED5", "INRED6", "INRED7", "INRED8", "INRED9"
};
- FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES + 10);
+ DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES + 10);
Images.Init (hexenLumpNames, NUM_HEXENSB_IMAGES);
ClassImages[0].Init (classLumpNames[0], NUM_HEXENCLASSSB_IMAGES);
ClassImages[1].Init (classLumpNames[1], NUM_HEXENCLASSSB_IMAGES);
@@ -201,7 +203,7 @@ public:
AmmoRefresh = 0;
}
- ~FHexenStatusBar ()
+ ~DHexenStatusBar ()
{
}
@@ -209,7 +211,7 @@ public:
{
int curHealth;
- FBaseStatusBar::Tick ();
+ DBaseStatusBar::Tick ();
if (CPlayer->mo == NULL)
{
curHealth = 0;
@@ -242,7 +244,7 @@ public:
void Draw (EHudState state)
{
- FBaseStatusBar::Draw (state);
+ DBaseStatusBar::Draw (state);
if (state == HUD_Fullscreen)
{
@@ -309,7 +311,7 @@ public:
void AttachToPlayer (player_s *player)
{
- FBaseStatusBar::AttachToPlayer (player);
+ DBaseStatusBar::AttachToPlayer (player);
if (player->mo != NULL)
{
if (player->mo->IsKindOf (PClass::FindClass(NAME_MagePlayer)))
@@ -395,6 +397,7 @@ private:
|| (oldarti != NULL && oldartiCount != oldarti->Amount))
{
oldarti = CPlayer->mo->InvSel;
+ GC::WriteBarrier(this, oldarti);
oldartiCount = oldarti != NULL ? oldarti->Amount : 0;
ArtiRefresh = screen->GetPageCount ();
}
@@ -521,12 +524,14 @@ private:
AmmoRefresh = screen->GetPageCount ();
oldammo1 = ammo1;
oldammocount1 = ammocount1;
+ GC::WriteBarrier(this, ammo1);
}
if (ammo2 != oldammo2 || ammocount2 != oldammocount2)
{
AmmoRefresh = screen->GetPageCount ();
oldammo2 = ammo2;
oldammocount2 = ammocount2;
+ GC::WriteBarrier(this, ammo2);
}
if (AmmoRefresh)
@@ -705,7 +710,7 @@ private:
void DrawInventoryBar ()
{
- const AInventory *item;
+ AInventory *item;
int i;
DrawImage (Images[imgINVBAR], 38, 0);
@@ -772,6 +777,7 @@ private:
if (keys[i] != oldkeys[i])
{
oldkeys[i] = keys[i];
+ GC::WriteBarrier(this, keys[i]);
different = true;
}
}
@@ -880,7 +886,7 @@ private:
void DrawFullScreenStuff ()
{
- const AInventory *item;
+ AInventory *item;
int i;
// Health
@@ -1051,9 +1057,9 @@ private:
static const char patcharti[][10];
static const char ammopic[][10];
- AInventory *oldarti;
- AAmmo *oldammo1, *oldammo2;
- AKey *oldkeys[5];
+ TObjPtr oldarti;
+ TObjPtr oldammo1, oldammo2;
+ TObjPtr oldkeys[5];
int oldammocount1, oldammocount2;
int oldartiCount;
int oldfrags;
@@ -1155,7 +1161,18 @@ private:
FManaBar ManaVial2Pic;
};
-FBaseStatusBar *CreateHexenStatusBar ()
+IMPLEMENT_POINTY_CLASS(DHexenStatusBar)
+ DECLARE_POINTER(oldarti)
+ DECLARE_POINTER(oldammo1)
+ DECLARE_POINTER(oldammo2)
+ DECLARE_POINTER(oldkeys[0])
+ DECLARE_POINTER(oldkeys[1])
+ DECLARE_POINTER(oldkeys[2])
+ DECLARE_POINTER(oldkeys[3])
+ DECLARE_POINTER(oldkeys[4])
+END_POINTERS
+
+DBaseStatusBar *CreateHexenStatusBar ()
{
- return new FHexenStatusBar;
+ return new DHexenStatusBar;
}
diff --git a/src/g_level.cpp b/src/g_level.cpp
index 965e1d4f..caeaeb94 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -1540,12 +1540,12 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
if (StatusBar != NULL)
{
- delete StatusBar;
+ StatusBar->Destroy();
StatusBar = NULL;
}
if (bTitleLevel)
{
- StatusBar = new FBaseStatusBar (0);
+ StatusBar = new DBaseStatusBar (0);
}
else if (SBarInfoScript != NULL)
{
@@ -1592,9 +1592,10 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
}
else
{
- StatusBar = new FBaseStatusBar (0);
+ StatusBar = new DBaseStatusBar (0);
}
}
+ GC::WriteBarrier(StatusBar);
StatusBar->AttachToPlayer (&players[consoleplayer]);
StatusBar->NewGame ();
setsizeneeded = true;
diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp
index e708e5a3..d043389b 100644
--- a/src/g_shared/a_action.cpp
+++ b/src/g_shared/a_action.cpp
@@ -332,7 +332,7 @@ public:
DCorpsePointer (AActor *ptr);
void Destroy ();
void Serialize (FArchive &arc);
- AActor *Corpse;
+ TObjPtr Corpse;
DWORD Count; // Only the first corpse pointer's count is valid.
private:
DCorpsePointer () {}
diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp
index 72b1db45..9b692087 100644
--- a/src/g_shared/a_decals.cpp
+++ b/src/g_shared/a_decals.cpp
@@ -52,10 +52,10 @@ static int ImpactCount;
CVAR (Bool, cl_spreaddecals, true, CVAR_ARCHIVE)
-// They also overload floorclip to be the fractional distance from the
-// left edge of the side. This distance is stored as a 2.30 fixed pt number.
+IMPLEMENT_POINTY_CLASS (DBaseDecal)
+ DECLARE_POINTER(WallNext)
+END_POINTERS
-IMPLEMENT_CLASS (DBaseDecal)
IMPLEMENT_CLASS (DImpactDecal)
DBaseDecal::DBaseDecal ()
diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp
index 3837a0af..c091c513 100644
--- a/src/g_shared/a_morph.cpp
+++ b/src/g_shared/a_morph.cpp
@@ -137,7 +137,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
{
return false;
}
- mo = static_cast(pmo->tracer);
+ mo = barrier_cast(pmo->tracer);
mo->SetOrigin (pmo->x, pmo->y, pmo->z);
mo->flags |= MF_SOLID;
pmo->flags &= ~MF_SOLID;
diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp
index 775260b9..50015f34 100644
--- a/src/g_shared/a_movingcamera.cpp
+++ b/src/g_shared/a_movingcamera.cpp
@@ -61,7 +61,7 @@ public:
void Serialize (FArchive &arc);
- AInterpolationPoint *Next;
+ TObjPtr Next;
};
IMPLEMENT_POINTY_CLASS (AInterpolationPoint)
@@ -181,7 +181,7 @@ protected:
void Serialize (FArchive &arc);
bool bActive, bJustStepped;
- AInterpolationPoint *PrevNode, *CurrNode;
+ TObjPtr PrevNode, CurrNode;
float Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode->Next
int HoldTime;
};
@@ -289,8 +289,8 @@ void APathFollower::Activate (AActor *activator)
{
if (!bActive)
{
- CurrNode = static_cast(target);
- PrevNode = static_cast(lastenemy);
+ CurrNode = barrier_cast(target);
+ PrevNode = barrier_cast(lastenemy);
if (CurrNode != NULL)
{
@@ -638,7 +638,7 @@ public:
protected:
bool Interpolate ();
- AActor *Activator;
+ TObjPtr Activator;
};
IMPLEMENT_POINTY_CLASS (AMovingCamera)
diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp
index a7660bdf..a85f022b 100644
--- a/src/g_shared/a_pickups.cpp
+++ b/src/g_shared/a_pickups.cpp
@@ -101,7 +101,7 @@ bool AAmmo::HandlePickup (AInventory *item)
(Owner->player->ReadyWeapon == NULL ||
(Owner->player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)))
{
- AWeapon *best = static_cast(Owner)->BestWeapon (GetClass());
+ AWeapon *best = barrier_cast(Owner)->BestWeapon (GetClass());
if (best != NULL && (Owner->player->ReadyWeapon == NULL ||
best->SelectionOrder < Owner->player->ReadyWeapon->SelectionOrder))
{
@@ -1052,7 +1052,7 @@ PalEntry AInventory::GetBlend ()
//
//===========================================================================
-AInventory *AInventory::PrevItem () const
+AInventory *AInventory::PrevItem ()
{
AInventory *item = Owner->Inventory;
@@ -1071,7 +1071,7 @@ AInventory *AInventory::PrevItem () const
//
//===========================================================================
-AInventory *AInventory::PrevInv () const
+AInventory *AInventory::PrevInv ()
{
AInventory *lastgood = NULL;
AInventory *item = Owner->Inventory;
@@ -1094,7 +1094,7 @@ AInventory *AInventory::PrevInv () const
//
//===========================================================================
-AInventory *AInventory::NextInv () const
+AInventory *AInventory::NextInv ()
{
AInventory *item = Inventory;
diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h
index 3ceaa9a0..ad0e3a65 100644
--- a/src/g_shared/a_pickups.h
+++ b/src/g_shared/a_pickups.h
@@ -124,11 +124,11 @@ public:
virtual const char *PickupMessage ();
virtual void PlayPickupSound (AActor *toucher);
- AInventory *PrevItem () const; // Returns the item preceding this one in the list.
- AInventory *PrevInv () const; // Returns the previous item with IF_INVBAR set.
- AInventory *NextInv () const; // Returns the next item with IF_INVBAR set.
+ AInventory *PrevItem(); // Returns the item preceding this one in the list.
+ AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set.
+ AInventory *NextInv(); // Returns the next item with IF_INVBAR set.
- AActor *Owner; // Who owns this item? NULL if it's still a pickup.
+ TObjPtr Owner; // Who owns this item? NULL if it's still a pickup.
int Amount; // Amount of item this instance has
int MaxAmount; // Max amount of item this instance can have
int RespawnTics; // Tics from pickup time to respawn time
@@ -217,8 +217,8 @@ public:
fixed_t MoveCombatDist; // Used by bots, but do they *really* need it?
// In-inventory instance variables
- AAmmo *Ammo1, *Ammo2;
- AWeapon *SisterWeapon;
+ TObjPtr Ammo1, Ammo2;
+ TObjPtr SisterWeapon;
bool bAltFire; // Set when this weapon's alternate fire is used.
diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp
index bddc0f15..3b82aff0 100644
--- a/src/g_shared/a_sectoraction.cpp
+++ b/src/g_shared/a_sectoraction.cpp
@@ -88,7 +88,7 @@ void ASectorAction::Deactivate (AActor *source)
bool ASectorAction::TriggerAction (AActor *triggerer, int activationType)
{
if (tracer != NULL)
- return static_cast(tracer)->TriggerAction (triggerer, activationType);
+ return barrier_cast(tracer)->TriggerAction (triggerer, activationType);
else
return false;
}
diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h
index 8f5e7898..0e27e223 100644
--- a/src/g_shared/a_sharedglobal.h
+++ b/src/g_shared/a_sharedglobal.h
@@ -24,6 +24,7 @@ struct F3DFloor;
class DBaseDecal : public DThinker
{
DECLARE_CLASS (DBaseDecal, DThinker)
+ HAS_OBJECT_POINTERS
public:
DBaseDecal ();
DBaseDecal (fixed_t z);
@@ -149,7 +150,7 @@ protected:
float Blends[2][4];
int TotalTics;
int StartTic;
- AActor *ForWho;
+ TObjPtr ForWho;
void SetBlend (float time);
DFlashFader ();
@@ -165,7 +166,7 @@ public:
void Serialize (FArchive &arc);
void Tick ();
- AActor *m_Spot;
+ TObjPtr m_Spot;
fixed_t m_TremorRadius, m_DamageRadius;
int m_Intensity;
int m_Countdown;
@@ -197,7 +198,7 @@ public:
void Die (AActor *source, AActor *inflictor);
void Destroy ();
- AActor *UnmorphedMe;
+ TObjPtr UnmorphedMe;
int UnmorphTime;
DWORD FlagsSave;
};
diff --git a/src/g_shared/a_weaponpiece.cpp b/src/g_shared/a_weaponpiece.cpp
index 2e987c33..aa47874e 100644
--- a/src/g_shared/a_weaponpiece.cpp
+++ b/src/g_shared/a_weaponpiece.cpp
@@ -27,7 +27,11 @@ IMPLEMENT_STATELESS_ACTOR (AWeaponHolder, Any, -1, 0)
PROP_Inventory_FlagsSet (IF_UNDROPPABLE)
END_DEFAULTS
-IMPLEMENT_STATELESS_ACTOR (AWeaponPiece, Any, -1, 0)
+
+IMPLEMENT_POINTY_CLASS (AWeaponPiece)
+ DECLARE_POINTER (FullWeapon)
+END_POINTERS
+BEGIN_STATELESS_DEFAULTS (AWeaponPiece, Any, -1, 0)
END_DEFAULTS
diff --git a/src/g_shared/a_weaponpiece.h b/src/g_shared/a_weaponpiece.h
index f0d46577..d6df4a71 100644
--- a/src/g_shared/a_weaponpiece.h
+++ b/src/g_shared/a_weaponpiece.h
@@ -2,6 +2,7 @@
class AWeaponPiece : public AInventory
{
DECLARE_CLASS (AWeaponPiece, AInventory)
+ HAS_OBJECT_POINTERS
protected:
bool PrivateShouldStay ();
public:
@@ -13,5 +14,5 @@ public:
int PieceValue;
const PClass * WeaponClass;
- AWeapon * FullWeapon;
+ TObjPtr FullWeapon;
};
diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp
index 6cdcab3f..8f35819f 100644
--- a/src/g_shared/a_weapons.cpp
+++ b/src/g_shared/a_weapons.cpp
@@ -370,7 +370,7 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo)
bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false);
if (!gotSome && autoSwitch)
{
- static_cast (Owner)->PickNewWeapon (NULL);
+ barrier_cast(Owner)->PickNewWeapon (NULL);
}
return gotSome;
}
@@ -402,7 +402,7 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo)
// out of ammo, pick a weapon to change to
if (autoSwitch)
{
- static_cast (Owner)->PickNewWeapon (NULL);
+ barrier_cast(Owner)->PickNewWeapon (NULL);
}
return false;
}
diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp
index 0f17da02..5bb9be2b 100644
--- a/src/g_shared/hudmessages.cpp
+++ b/src/g_shared/hudmessages.cpp
@@ -41,7 +41,10 @@
EXTERN_CVAR (Int, con_scaletext)
-IMPLEMENT_CLASS (DHUDMessage)
+IMPLEMENT_POINTY_CLASS (DHUDMessage)
+ DECLARE_POINTER(Next)
+END_POINTERS
+
IMPLEMENT_CLASS (DHUDMessageFadeOut)
IMPLEMENT_CLASS (DHUDMessageFadeInOut)
IMPLEMENT_CLASS (DHUDMessageTypeOnFadeOut)
diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h
index d6c3ee70..f7101d97 100644
--- a/src/g_shared/sbar.h
+++ b/src/g_shared/sbar.h
@@ -55,6 +55,7 @@ class AWeapon;
class DHUDMessage : public DObject
{
DECLARE_CLASS (DHUDMessage, DObject)
+ HAS_OBJECT_POINTERS
public:
DHUDMessage (const char *text, float x, float y, int hudwidth, int hudheight,
EColorRange textColor, float holdTime);
@@ -84,11 +85,11 @@ protected:
DHUDMessage () : SourceText(NULL) {}
private:
- DHUDMessage *Next;
+ TObjPtr Next;
DWORD SBarID;
char *SourceText;
- friend class FBaseStatusBar;
+ friend class DBaseStatusBar;
};
class DHUDMessageFadeOut : public DHUDMessage
@@ -149,8 +150,10 @@ protected:
class FTexture;
class AAmmo;
-class FBaseStatusBar
+class DBaseStatusBar : public DObject
{
+ DECLARE_CLASS (DBaseStatusBar, DObject)
+ HAS_OBJECT_POINTERS
public:
// Popup screens for Strife's status bar
enum
@@ -162,8 +165,8 @@ public:
POP_Status
};
- FBaseStatusBar (int reltop);
- virtual ~FBaseStatusBar ();
+ DBaseStatusBar (int reltop);
+ void Destroy ();
void SetScaled (bool scale);
@@ -246,20 +249,21 @@ public:
player_s *CPlayer;
private:
+ DBaseStatusBar() {}
bool RepositionCoords (int &x, int &y, int xo, int yo, const int w, const int h) const;
- void DrawMessages (int bottom) const;
+ void DrawMessages (int bottom);
void DrawConsistancy () const;
static BYTE DamageToAlpha[114];
- DHUDMessage *Messages;
+ TObjPtr Messages;
bool ShowLog;
};
-extern FBaseStatusBar *StatusBar;
+extern DBaseStatusBar *StatusBar;
-FBaseStatusBar *CreateDoomStatusBar ();
-FBaseStatusBar *CreateHereticStatusBar ();
-FBaseStatusBar *CreateHexenStatusBar ();
-FBaseStatusBar *CreateStrifeStatusBar ();
-FBaseStatusBar *CreateCustomStatusBar ();
+DBaseStatusBar *CreateDoomStatusBar ();
+DBaseStatusBar *CreateHereticStatusBar ();
+DBaseStatusBar *CreateHexenStatusBar ();
+DBaseStatusBar *CreateStrifeStatusBar ();
+DBaseStatusBar *CreateCustomStatusBar ();
diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp
index 4f546004..31e38ae4 100644
--- a/src/g_shared/sbarinfo.cpp
+++ b/src/g_shared/sbarinfo.cpp
@@ -1238,10 +1238,11 @@ SBarInfoBlock::SBarInfoBlock()
}
//SBarInfo Display
-class FSBarInfo : public FBaseStatusBar
+class DSBarInfo : public DBaseStatusBar
{
+ DECLARE_CLASS(DSBarInfo, DBaseStatusBar)
public:
- FSBarInfo () : FBaseStatusBar (SBarInfoScript->height),
+ DSBarInfo () : DBaseStatusBar (SBarInfoScript->height),
shader_horz_normal(false, false),
shader_horz_reverse(false, true),
shader_vert_normal(true, false),
@@ -1279,7 +1280,7 @@ public:
artiflash = 4;
}
- ~FSBarInfo ()
+ ~DSBarInfo ()
{
Images.Uninit();
Faces.Uninit();
@@ -1287,7 +1288,7 @@ public:
void Draw (EHudState state)
{
- FBaseStatusBar::Draw(state);
+ DBaseStatusBar::Draw(state);
int hud = 2;
if(state == HUD_StatusBar)
{
@@ -1341,7 +1342,7 @@ public:
void AttachToPlayer (player_t *player)
{
player_t *oldplayer = CPlayer;
- FBaseStatusBar::AttachToPlayer(player);
+ DBaseStatusBar::AttachToPlayer(player);
if (oldplayer != CPlayer)
{
SetFace(&skins[CPlayer->userinfo.skin], "STF");
@@ -1350,7 +1351,7 @@ public:
void Tick ()
{
- FBaseStatusBar::Tick();
+ DBaseStatusBar::Tick();
if(level.time & 1)
chainWiggle = pr_chainwiggle() & 1;
getNewFace(M_Random());
@@ -2310,7 +2311,7 @@ private:
void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow,
int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter)
{ //yes, there is some Copy & Paste here too
- const AInventory *item;
+ AInventory *item;
int i;
// If the player has no artifacts, don't draw the bar
@@ -2408,7 +2409,9 @@ private:
FBarShader shader_vert_reverse;
};
-FBaseStatusBar *CreateCustomStatusBar ()
+IMPLEMENT_CLASS(DSBarInfo);
+
+DBaseStatusBar *CreateCustomStatusBar ()
{
- return new FSBarInfo;
+ return new DSBarInfo;
}
diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp
index 52d5299f..c8209eb4 100644
--- a/src/g_shared/shared_sbar.cpp
+++ b/src/g_shared/shared_sbar.cpp
@@ -53,6 +53,10 @@
#define XHAIRPICKUPSIZE (FRACUNIT*2+XHAIRSHRINKSIZE)
#define POWERUPICONSIZE 32
+IMPLEMENT_POINTY_CLASS(DBaseStatusBar)
+ DECLARE_POINTER(Messages)
+END_POINTERS
+
EXTERN_CVAR (Bool, am_showmonsters)
EXTERN_CVAR (Bool, am_showsecrets)
EXTERN_CVAR (Bool, am_showitems)
@@ -62,7 +66,7 @@ EXTERN_CVAR (Bool, noisedebug)
EXTERN_CVAR (Bool, hud_scale)
EXTERN_CVAR (Int, con_scaletext)
-FBaseStatusBar *StatusBar;
+DBaseStatusBar *StatusBar;
extern int setblocks;
@@ -126,7 +130,7 @@ CVAR (Bool, idmypos, false, 0);
// [RH] Amount of red flash for up to 114 damage points. Calculated by hand
// using a logarithmic scale and my trusty HP48G.
-BYTE FBaseStatusBar::DamageToAlpha[114] =
+BYTE DBaseStatusBar::DamageToAlpha[114] =
{
0, 8, 16, 23, 30, 36, 42, 47, 53, 58, 62, 67, 71, 75, 79,
83, 87, 90, 94, 97, 100, 103, 107, 109, 112, 115, 118, 120, 123, 125,
@@ -144,7 +148,7 @@ BYTE FBaseStatusBar::DamageToAlpha[114] =
//
//---------------------------------------------------------------------------
-FBaseStatusBar::FBaseStatusBar (int reltop)
+DBaseStatusBar::DBaseStatusBar (int reltop)
{
Centering = false;
FixedOrigin = false;
@@ -160,11 +164,11 @@ FBaseStatusBar::FBaseStatusBar (int reltop)
//---------------------------------------------------------------------------
//
-// Destructor
+// PROP Destroy
//
//---------------------------------------------------------------------------
-FBaseStatusBar::~FBaseStatusBar ()
+void DBaseStatusBar::Destroy ()
{
DHUDMessage *msg;
@@ -172,9 +176,10 @@ FBaseStatusBar::~FBaseStatusBar ()
while (msg)
{
DHUDMessage *next = msg->Next;
- delete msg;
+ msg->Destroy();
msg = next;
}
+ Super::Destroy();
}
//---------------------------------------------------------------------------
@@ -183,7 +188,7 @@ FBaseStatusBar::~FBaseStatusBar ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::SetScaled (bool scale)
+void DBaseStatusBar::SetScaled (bool scale)
{
Scaled = RelTop != 0 && (SCREENWIDTH != 320 && scale);
if (!Scaled)
@@ -225,7 +230,7 @@ void FBaseStatusBar::SetScaled (bool scale)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::AttachToPlayer (player_s *player)
+void DBaseStatusBar::AttachToPlayer (player_s *player)
{
CPlayer = player;
SB_state = screen->GetPageCount ();
@@ -237,7 +242,7 @@ void FBaseStatusBar::AttachToPlayer (player_s *player)
//
//---------------------------------------------------------------------------
-int FBaseStatusBar::GetPlayer ()
+int DBaseStatusBar::GetPlayer ()
{
return int(CPlayer - players);
}
@@ -248,7 +253,7 @@ int FBaseStatusBar::GetPlayer ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::MultiplayerChanged ()
+void DBaseStatusBar::MultiplayerChanged ()
{
SB_state = screen->GetPageCount ();
}
@@ -259,7 +264,7 @@ void FBaseStatusBar::MultiplayerChanged ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::Tick ()
+void DBaseStatusBar::Tick ()
{
DHUDMessage *msg = Messages;
DHUDMessage **prev = &Messages;
@@ -271,7 +276,7 @@ void FBaseStatusBar::Tick ()
if (msg->Tick ())
{
*prev = next;
- delete msg;
+ msg->Destroy();
}
else
{
@@ -297,15 +302,16 @@ void FBaseStatusBar::Tick ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id)
+void DBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id)
{
DHUDMessage *old = NULL;
DHUDMessage **prev;
+ DObject *container = this;
old = (id == 0 || id == 0xFFFFFFFF) ? NULL : DetachMessage (id);
if (old != NULL)
{
- delete old;
+ old->Destroy();
}
prev = &Messages;
@@ -315,12 +321,14 @@ void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id)
// it gets drawn back to front.)
while (*prev != NULL && (*prev)->SBarID > id)
{
+ container = *prev;
prev = &(*prev)->Next;
}
msg->Next = *prev;
msg->SBarID = id;
*prev = msg;
+ GC::WriteBarrier(container, msg);
}
//---------------------------------------------------------------------------
@@ -329,7 +337,7 @@ void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id)
//
//---------------------------------------------------------------------------
-DHUDMessage *FBaseStatusBar::DetachMessage (DHUDMessage *msg)
+DHUDMessage *DBaseStatusBar::DetachMessage (DHUDMessage *msg)
{
DHUDMessage *probe = Messages;
DHUDMessage **prev = &Messages;
@@ -349,7 +357,7 @@ DHUDMessage *FBaseStatusBar::DetachMessage (DHUDMessage *msg)
return probe;
}
-DHUDMessage *FBaseStatusBar::DetachMessage (DWORD id)
+DHUDMessage *DBaseStatusBar::DetachMessage (DWORD id)
{
DHUDMessage *probe = Messages;
DHUDMessage **prev = &Messages;
@@ -375,7 +383,7 @@ DHUDMessage *FBaseStatusBar::DetachMessage (DWORD id)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DetachAllMessages ()
+void DBaseStatusBar::DetachAllMessages ()
{
DHUDMessage *probe = Messages;
@@ -383,7 +391,7 @@ void FBaseStatusBar::DetachAllMessages ()
while (probe != NULL)
{
DHUDMessage *next = probe->Next;
- delete probe;
+ probe->Destroy();
probe = next;
}
}
@@ -394,7 +402,7 @@ void FBaseStatusBar::DetachAllMessages ()
//
//---------------------------------------------------------------------------
-bool FBaseStatusBar::CheckMessage (DHUDMessage *msg)
+bool DBaseStatusBar::CheckMessage (DHUDMessage *msg)
{
DHUDMessage *probe = Messages;
while (probe && probe != msg)
@@ -410,7 +418,7 @@ bool FBaseStatusBar::CheckMessage (DHUDMessage *msg)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::ShowPlayerName ()
+void DBaseStatusBar::ShowPlayerName ()
{
EColorRange color;
@@ -427,7 +435,7 @@ void FBaseStatusBar::ShowPlayerName ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawImage (FTexture *img,
+void DBaseStatusBar::DrawImage (FTexture *img,
int x, int y, FRemapTable *translation) const
{
if (img != NULL)
@@ -448,7 +456,7 @@ void FBaseStatusBar::DrawImage (FTexture *img,
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawDimImage (FTexture *img,
+void DBaseStatusBar::DrawDimImage (FTexture *img,
int x, int y, bool dimmed) const
{
if (img != NULL)
@@ -469,7 +477,7 @@ void FBaseStatusBar::DrawDimImage (FTexture *img,
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawFadedImage (FTexture *img,
+void DBaseStatusBar::DrawFadedImage (FTexture *img,
int x, int y, fixed_t shade) const
{
if (img != NULL)
@@ -491,7 +499,7 @@ void FBaseStatusBar::DrawFadedImage (FTexture *img,
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const
+void DBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const
{
if (img != NULL)
{
@@ -511,7 +519,7 @@ void FBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const
+void DBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const
{
int oldval;
@@ -551,7 +559,7 @@ void FBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const
+void DBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const
{
bool neg;
int i, w;
@@ -611,7 +619,7 @@ void FBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrSmallNumber (int val, int x, int y) const
+void DBaseStatusBar::DrSmallNumber (int val, int x, int y) const
{
int digit = 0;
@@ -642,7 +650,7 @@ void FBaseStatusBar::DrSmallNumber (int val, int x, int y) const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, int w) const
+void DBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, int w) const
{
bool negative = false;
@@ -706,7 +714,7 @@ void FBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center,
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) const
+void DBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) const
{
int xpos;
int w;
@@ -813,7 +821,7 @@ void FBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) con
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) const
+void DBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) const
{
int xpos;
int w, v;
@@ -914,7 +922,7 @@ void FBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) const
+void DBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) const
{
int digit = 0;
@@ -946,7 +954,7 @@ void FBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) con
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::RefreshBackground () const
+void DBaseStatusBar::RefreshBackground () const
{
int x, x2, y, ratio;
@@ -981,7 +989,7 @@ void FBaseStatusBar::RefreshBackground () const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawCrosshair ()
+void DBaseStatusBar::DrawCrosshair ()
{
static DWORD prevcolor = 0xffffffff;
static int palettecolor = 0;
@@ -1072,7 +1080,7 @@ void FBaseStatusBar::DrawCrosshair ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::FlashCrosshair ()
+void DBaseStatusBar::FlashCrosshair ()
{
CrosshairSize = XHAIRPICKUPSIZE;
}
@@ -1083,7 +1091,7 @@ void FBaseStatusBar::FlashCrosshair ()
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawMessages (int bottom) const
+void DBaseStatusBar::DrawMessages (int bottom)
{
DHUDMessage *msg = Messages;
while (msg)
@@ -1100,7 +1108,7 @@ void FBaseStatusBar::DrawMessages (int bottom) const
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::Draw (EHudState state)
+void DBaseStatusBar::Draw (EHudState state)
{
char line[64+10];
@@ -1254,7 +1262,7 @@ void FBaseStatusBar::Draw (EHudState state)
}
-void FBaseStatusBar::DrawLog ()
+void DBaseStatusBar::DrawLog ()
{
int hudwidth, hudheight;
@@ -1318,7 +1326,7 @@ void FBaseStatusBar::DrawLog ()
}
}
-bool FBaseStatusBar::MustDrawLog(EHudState)
+bool DBaseStatusBar::MustDrawLog(EHudState)
{
return true;
}
@@ -1329,7 +1337,7 @@ bool FBaseStatusBar::MustDrawLog(EHudState)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawTopStuff (EHudState state)
+void DBaseStatusBar::DrawTopStuff (EHudState state)
{
if (demoplayback && demover != DEMOGAMEVERSION)
{
@@ -1360,7 +1368,7 @@ void FBaseStatusBar::DrawTopStuff (EHudState state)
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::DrawPowerups ()
+void DBaseStatusBar::DrawPowerups ()
{
// Each icon gets a 32x32 block to draw itself in.
int x, y;
@@ -1388,7 +1396,7 @@ SV_AddBlend
[RH] This is from Q2.
=============
*/
-void FBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend[4])
+void DBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend[4])
{
float a2, a3;
@@ -1409,7 +1417,7 @@ void FBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend
//
//---------------------------------------------------------------------------
-void FBaseStatusBar::BlendView (float blend[4])
+void DBaseStatusBar::BlendView (float blend[4])
{
int cnt;
@@ -1480,7 +1488,7 @@ void FBaseStatusBar::BlendView (float blend[4])
(int)(blend[2] * 255.0f), (int)(blend[3] * 256.0f));
}
-void FBaseStatusBar::DrawConsistancy () const
+void DBaseStatusBar::DrawConsistancy () const
{
static bool firsttime = true;
int i;
@@ -1524,37 +1532,37 @@ void FBaseStatusBar::DrawConsistancy () const
}
}
-void FBaseStatusBar::FlashItem (const PClass *itemtype)
+void DBaseStatusBar::FlashItem (const PClass *itemtype)
{
}
-void FBaseStatusBar::SetFace (void *)
+void DBaseStatusBar::SetFace (void *)
{
}
-void FBaseStatusBar::NewGame ()
+void DBaseStatusBar::NewGame ()
{
}
-void FBaseStatusBar::SetInteger (int pname, int param)
+void DBaseStatusBar::SetInteger (int pname, int param)
{
}
-void FBaseStatusBar::ShowPop (int popnum)
+void DBaseStatusBar::ShowPop (int popnum)
{
ShowLog = (popnum == POP_Log && !ShowLog);
}
-void FBaseStatusBar::ReceivedWeapon (AWeapon *weapon)
+void DBaseStatusBar::ReceivedWeapon (AWeapon *weapon)
{
}
-void FBaseStatusBar::Serialize (FArchive &arc)
+void DBaseStatusBar::Serialize (FArchive &arc)
{
arc << Messages;
}
-void FBaseStatusBar::ScreenSizeChanged ()
+void DBaseStatusBar::ScreenSizeChanged ()
{
st_scale.Callback ();
SB_state = screen->GetPageCount ();
@@ -1576,7 +1584,7 @@ void FBaseStatusBar::ScreenSizeChanged ()
//
//---------------------------------------------------------------------------
-AInventory *FBaseStatusBar::ValidateInvFirst (int numVisible) const
+AInventory *DBaseStatusBar::ValidateInvFirst (int numVisible) const
{
AInventory *item;
int i;
@@ -1660,14 +1668,14 @@ AInventory *FBaseStatusBar::ValidateInvFirst (int numVisible) const
//============================================================================
//
-// FBaseStatusBar :: GetCurrentAmmo
+// DBaseStatusBar :: GetCurrentAmmo
//
// Returns the types and amounts of ammo used by the current weapon. If the
// weapon only uses one type of ammo, it is always returned as ammo1.
//
//============================================================================
-void FBaseStatusBar::GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const
+void DBaseStatusBar::GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const
{
if (CPlayer->ReadyWeapon != NULL)
{
diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp
index 2583e36a..83cf9a37 100644
--- a/src/g_strife/strife_sbar.cpp
+++ b/src/g_strife/strife_sbar.cpp
@@ -180,10 +180,11 @@ void FHealthBar::FillBar (int min, int max, BYTE light, BYTE dark)
}
}
-class FStrifeStatusBar : public FBaseStatusBar
+class DStrifeStatusBar : public DBaseStatusBar
{
+ DECLARE_CLASS(DStrifeStatusBar, DBaseStatusBar)
public:
- FStrifeStatusBar () : FBaseStatusBar (32)
+ DStrifeStatusBar () : DBaseStatusBar (32)
{
static const char *sharedLumpNames[] =
{
@@ -196,11 +197,11 @@ public:
"INVFONG7", "INVFONG8", "INVFONG9"
};
- FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
+ DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
DoCommonInit ();
}
- ~FStrifeStatusBar ()
+ ~DStrifeStatusBar ()
{
}
@@ -216,7 +217,7 @@ public:
void Draw (EHudState state)
{
- FBaseStatusBar::Draw (state);
+ DBaseStatusBar::Draw (state);
if (state == HUD_Fullscreen)
{
@@ -235,7 +236,7 @@ public:
void ShowPop (int popnum)
{
- FBaseStatusBar::ShowPop(popnum);
+ DBaseStatusBar::ShowPop(popnum);
if (popnum == CurrentPop)
{
if (popnum == POP_Keys)
@@ -310,7 +311,7 @@ private:
void Tick ()
{
- FBaseStatusBar::Tick ();
+ DBaseStatusBar::Tick ();
if (ItemFlash > 0)
{
@@ -845,7 +846,9 @@ private:
fixed_t ItemFlash;
};
-FBaseStatusBar *CreateStrifeStatusBar ()
+IMPLEMENT_CLASS(DStrifeStatusBar);
+
+DBaseStatusBar *CreateStrifeStatusBar ()
{
- return new FStrifeStatusBar;
+ return new DStrifeStatusBar;
}
diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp
index b29e0873..7db13ed7 100644
--- a/src/gameconfigfile.cpp
+++ b/src/gameconfigfile.cpp
@@ -295,6 +295,16 @@ void FGameConfigFile::DoGlobalSetup ()
vsync->ResetToDefault ();
}
}
+ if (last < 206)
+ { // spc_amp is now a float, not an int.
+ FBaseCVar *amp = FindCVar ("spc_amp", NULL);
+ if (amp != NULL)
+ {
+ UCVarValue val = amp->GetGenericRep(CVAR_Float);
+ val.Float /= 16.f;
+ amp->SetGenericRep(val, CVAR_Float);
+ }
+ }
}
}
}
@@ -515,7 +525,7 @@ FString FGameConfigFile::GetConfigPath (bool tryProg)
char *pathval;
FString path;
- pathval = Args.CheckValue ("-config");
+ pathval = Args->CheckValue ("-config");
if (pathval != NULL)
return FString(pathval);
@@ -568,7 +578,7 @@ FString FGameConfigFile::GetConfigPath (bool tryProg)
if (path.IsEmpty())
{
- if (Args.CheckParm ("-cdrom"))
+ if (Args->CheckParm ("-cdrom"))
return CDROM_DIR "\\zdoom.ini";
path = progdir;
@@ -615,7 +625,7 @@ void FGameConfigFile::AddAutoexec (DArgs *list, const char *game)
FString path;
#ifndef unix
- if (Args.CheckParm ("-cdrom"))
+ if (Args->CheckParm ("-cdrom"))
{
path = CDROM_DIR "\\autoexec.cfg";
}
diff --git a/src/gl/gl_data.cpp b/src/gl/gl_data.cpp
index e9eed115..561e0f69 100644
--- a/src/gl/gl_data.cpp
+++ b/src/gl/gl_data.cpp
@@ -162,7 +162,7 @@ static void PrepareSectorData()
{
int i;
int j;
- DBoundingBox bbox;
+ FBoundingBox bbox;
size_t /*ii,*/ jj;
TArray undetermined;
subsector_t * ss;
diff --git a/src/gl/gl_dynlight.cpp b/src/gl/gl_dynlight.cpp
index 8d545efe..1bcd058c 100644
--- a/src/gl/gl_dynlight.cpp
+++ b/src/gl/gl_dynlight.cpp
@@ -1022,7 +1022,7 @@ void gl_SetActorLights(AActor *actor)
(LightAssociations[i]->Frame()==frame || LightAssociations[i]->Frame()==-1))
{
// I'm skipping the single rotations because that really doesn't make sense!
- if (count < actor->dynamiclights.Size()) light = (ADynamicLight*)actor->dynamiclights[count];
+ if (count < actor->dynamiclights.Size()) light = barrier_cast(actor->dynamiclights[count]);
else
{
light = Spawn(actor->x, actor->y, actor->z, NO_REPLACE);
diff --git a/src/gl/gl_lights.h b/src/gl/gl_lights.h
index 63d886c3..226e7f32 100644
--- a/src/gl/gl_lights.h
+++ b/src/gl/gl_lights.h
@@ -114,7 +114,7 @@ public:
BYTE lighttype;
bool owned;
bool halo;
- AActor *Owner; // NOTE: This is *NOT* subject to pointer cleanup!!!
+ AActor *Owner; // NOTE: This is *NOT* subject to pointer cleanup or garbage collection!!!
// intermediate texture coordinate data
// this is stored in the light object to avoid recalculating it
@@ -249,9 +249,6 @@ enum
STAT_DLIGHT=64
};
-extern TArray PointLights;
-
-
//
// Light helper methods
diff --git a/src/i_net.cpp b/src/i_net.cpp
index 7c4b38d9..6e080df8 100644
--- a/src/i_net.cpp
+++ b/src/i_net.cpp
@@ -564,7 +564,7 @@ void HostGame (int i)
int node;
int gotack[MAXNETNODES+1];
- if ((i == Args.NumArgs() - 1) || !(numplayers = atoi (Args.GetArg(i+1))))
+ if ((i == Args->NumArgs() - 1) || !(numplayers = atoi (Args->GetArg(i+1))))
{ // No player count specified, assume 2
numplayers = 2;
}
@@ -742,15 +742,15 @@ bool Guest_WaitForOthers (void *userdata)
void JoinGame (int i)
{
- if ((i == Args.NumArgs() - 1) ||
- (Args.GetArg(i+1)[0] == '-') ||
- (Args.GetArg(i+1)[0] == '+'))
+ if ((i == Args->NumArgs() - 1) ||
+ (Args->GetArg(i+1)[0] == '-') ||
+ (Args->GetArg(i+1)[0] == '+'))
I_FatalError ("You need to specify the host machine's address");
StartNetwork (true);
// Host is always node 1
- BuildAddress (&sendaddress[1], Args.GetArg(i+1));
+ BuildAddress (&sendaddress[1], Args->GetArg(i+1));
sendplayer[1] = 0;
doomcom.numnodes = 2;
@@ -789,7 +789,7 @@ void I_InitNetwork (void)
memset (&doomcom, 0, sizeof(doomcom));
// set up for network
- v = Args.CheckValue ("-dup");
+ v = Args->CheckValue ("-dup");
if (v)
{
doomcom.ticdup = clamp (atoi (v), 1, MAXTICDUP);
@@ -799,12 +799,12 @@ void I_InitNetwork (void)
doomcom.ticdup = 1;
}
- if (Args.CheckParm ("-extratic"))
+ if (Args->CheckParm ("-extratic"))
doomcom.extratics = 1;
else
doomcom.extratics = 0;
- v = Args.CheckValue ("-port");
+ v = Args->CheckValue ("-port");
if (v)
{
DOOMPORT = atoi (v);
@@ -814,11 +814,11 @@ void I_InitNetwork (void)
// parse network game options,
// player 1: -host
// player x: -join
- if ( (i = Args.CheckParm ("-host")) )
+ if ( (i = Args->CheckParm ("-host")) )
{
HostGame (i);
}
- else if ( (i = Args.CheckParm ("-join")) )
+ else if ( (i = Args->CheckParm ("-join")) )
{
JoinGame (i);
}
diff --git a/src/m_alloc.cpp b/src/m_alloc.cpp
index 897f2e54..4d9e0fbf 100644
--- a/src/m_alloc.cpp
+++ b/src/m_alloc.cpp
@@ -33,18 +33,8 @@
*/
#include "i_system.h"
-#include "stats.h"
#include
-ADD_STAT(mem)
-{
- FString out;
- out.Format("Alloc: %uKB", (AllocBytes + 1023) >> 10);
- return out;
-}
-
-size_t AllocBytes;
-
#ifndef _MSC_VER
#define _NORMAL_BLOCK 0
#define _malloc_dbg(s,b,f,l) malloc(s)
@@ -62,7 +52,7 @@ void *M_Malloc(size_t size)
if (block == NULL)
I_FatalError("Could not malloc %u bytes", size);
- AllocBytes += _msize(block);
+ GC::AllocBytes += _msize(block);
return block;
}
@@ -70,14 +60,14 @@ void *M_Realloc(void *memblock, size_t size)
{
if (memblock != NULL)
{
- AllocBytes -= _msize(memblock);
+ GC::AllocBytes -= _msize(memblock);
}
void *block = realloc(memblock, size);
if (block == NULL)
{
I_FatalError("Could not realloc %u bytes", size);
}
- AllocBytes += _msize(block);
+ GC::AllocBytes += _msize(block);
return block;
}
#else
@@ -92,7 +82,7 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno)
if (block == NULL)
I_FatalError("Could not malloc %u bytes", size);
- AllocBytes += _msize(block);
+ GC::AllocBytes += _msize(block);
return block;
}
@@ -100,14 +90,14 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
{
if (memblock != NULL)
{
- AllocBytes -= _msize(memblock);
+ GC::AllocBytes -= _msize(memblock);
}
void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno);
if (block == NULL)
{
I_FatalError("Could not realloc %u bytes", size);
}
- AllocBytes += _msize(block);
+ GC::AllocBytes += _msize(block);
return block;
}
#endif
@@ -116,7 +106,7 @@ void M_Free (void *block)
{
if (block != NULL)
{
- AllocBytes -= _msize(block);
+ GC::AllocBytes -= _msize(block);
free(block);
}
}
diff --git a/src/m_alloc.h b/src/m_alloc.h
index 8d7866a7..17a6adeb 100644
--- a/src/m_alloc.h
+++ b/src/m_alloc.h
@@ -53,7 +53,4 @@ void *M_Realloc (void *memblock, size_t size);
void M_Free (void *memblock);
-// Number of bytes currently allocated through M_Malloc/M_Realloc
-extern size_t AllocBytes;
-
#endif //__M_ALLOC_H__
diff --git a/src/m_argv.h b/src/m_argv.h
index 65973de9..b0d44076 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -71,6 +71,6 @@ private:
void CopyArgs (int argc, char **argv);
};
-extern DArgs Args;
+extern DArgs *Args;
#endif //__M_ARGV_H__
diff --git a/src/m_bbox.cpp b/src/m_bbox.cpp
index 42b54361..7be4e90c 100644
--- a/src/m_bbox.cpp
+++ b/src/m_bbox.cpp
@@ -26,20 +26,18 @@
#include "m_bbox.h"
-IMPLEMENT_CLASS (DBoundingBox)
-
-DBoundingBox::DBoundingBox ()
+FBoundingBox::FBoundingBox ()
{
ClearBox ();
}
-void DBoundingBox::ClearBox ()
+void FBoundingBox::ClearBox ()
{
m_Box[BOXTOP] = m_Box[BOXRIGHT] = FIXED_MIN;
m_Box[BOXBOTTOM] = m_Box[BOXLEFT] = FIXED_MAX;
}
-void DBoundingBox::AddToBox (fixed_t x, fixed_t y)
+void FBoundingBox::AddToBox (fixed_t x, fixed_t y)
{
if (x < m_Box[BOXLEFT])
m_Box[BOXLEFT] = x;
diff --git a/src/m_bbox.h b/src/m_bbox.h
index 0ef2853a..7a813ffa 100644
--- a/src/m_bbox.h
+++ b/src/m_bbox.h
@@ -23,7 +23,6 @@
#define __M_BBOX_H__
#include "doomtype.h"
-#include "dobject.h"
#include "m_fixed.h"
@@ -37,11 +36,10 @@ enum
}; // bbox coordinates
-class DBoundingBox : public DObject
+class FBoundingBox
{
- DECLARE_CLASS (DBoundingBox, DObject)
public:
- DBoundingBox();
+ FBoundingBox();
void ClearBox ();
void AddToBox (fixed_t x, fixed_t y);
diff --git a/src/m_misc.cpp b/src/m_misc.cpp
index 3909d32e..7246fcdf 100644
--- a/src/m_misc.cpp
+++ b/src/m_misc.cpp
@@ -150,9 +150,9 @@ void M_FindResponseFile (void)
{
int i;
- for (i = 1; i < Args.NumArgs(); i++)
+ for (i = 1; i < Args->NumArgs(); i++)
{
- if (Args.GetArg(i)[0] == '@')
+ if (Args->GetArg(i)[0] == '@')
{
char **argv;
char *file;
@@ -165,14 +165,14 @@ void M_FindResponseFile (void)
int index;
// READ THE RESPONSE FILE INTO MEMORY
- handle = fopen (Args.GetArg(i) + 1,"rb");
+ handle = fopen (Args->GetArg(i) + 1,"rb");
if (!handle)
{ // [RH] Make this a warning, not an error.
- Printf ("No such response file (%s)!", Args.GetArg(i) + 1);
+ Printf ("No such response file (%s)!", Args->GetArg(i) + 1);
continue;
}
- Printf ("Found response file %s!\n", Args.GetArg(i) + 1);
+ Printf ("Found response file %s!\n", Args->GetArg(i) + 1);
fseek (handle, 0, SEEK_END);
size = ftell (handle);
fseek (handle, 0, SEEK_SET);
@@ -182,7 +182,7 @@ void M_FindResponseFile (void)
fclose (handle);
argsize = ParseCommandLine (file, &argcinresp, NULL);
- argc = argcinresp + Args.NumArgs() - 1;
+ argc = argcinresp + Args->NumArgs() - 1;
if (argc != 0)
{
@@ -191,21 +191,21 @@ void M_FindResponseFile (void)
ParseCommandLine (file, NULL, argv+i);
for (index = 0; index < i; ++index)
- argv[index] = Args.GetArg (index);
+ argv[index] = Args->GetArg (index);
- for (index = i + 1, i += argcinresp; index < Args.NumArgs (); ++index)
- argv[i++] = Args.GetArg (index);
+ for (index = i + 1, i += argcinresp; index < Args->NumArgs (); ++index)
+ argv[i++] = Args->GetArg (index);
- DArgs newargs (i, argv);
- Args = newargs;
+ Args->Destroy();
+ Args = new DArgs(i, argv);
}
delete[] file;
// DISPLAY ARGS
- Printf ("%d command-line args:\n", Args.NumArgs ());
- for (k = 1; k < Args.NumArgs (); k++)
- Printf ("%s\n", Args.GetArg (k));
+ Printf ("%d command-line args:\n", Args->NumArgs ());
+ for (k = 1; k < Args->NumArgs (); k++)
+ Printf ("%s\n", Args->GetArg (k));
break;
}
@@ -652,7 +652,7 @@ void M_ScreenShot (const char *filename)
if (filename == NULL || filename[0] == '\0')
{
#ifndef unix
- if (Args.CheckParm ("-cdrom"))
+ if (Args->CheckParm ("-cdrom"))
{
autoname = CDROM_DIR "\\";
}
diff --git a/src/modplug/load_669.cpp b/src/modplug/load_669.cpp
deleted file mode 100644
index a0dbfabd..00000000
--- a/src/modplug/load_669.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * This source code is public domain.
- *
- * Authors: Olivier Lapicque ,
- * Adam Goode (endian and char fixes for PPC)
-*/
-
-////////////////////////////////////////////////////////////
-// 669 Composer / UNIS 669 module loader
-////////////////////////////////////////////////////////////
-
-#include "stdafx.h"
-#include "sndfile.h"
-
-//#pragma warning(disable:4244)
-
-typedef struct tagFILEHEADER669
-{
- WORD sig; // 'if' or 'JN'
- signed char songmessage[108]; // Song Message
- BYTE samples; // number of samples (1-64)
- BYTE patterns; // number of patterns (1-128)
- BYTE restartpos;
- BYTE orders[128];
- BYTE tempolist[128];
- BYTE breaks[128];
-} FILEHEADER669;
-
-
-typedef struct tagSAMPLE669
-{
- BYTE filename[13];
- BYTE length[4]; // when will somebody think about DWORD align ???
- BYTE loopstart[4];
- BYTE loopend[4];
-} SAMPLE669;
-
-
-BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength)
-//---------------------------------------------------------------
-{
- BOOL b669Ext;
- const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
- const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
- DWORD dwMemPos = 0;
-
- if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE;
- if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE;
- b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
- if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
- || (!pfh->patterns) || (pfh->patterns > 128)) return FALSE;
- DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
- if (dontfuckwithme > dwMemLength) return FALSE;
- for (UINT ichk=0; ichksamples; ichk++)
- {
- DWORD len = bswapLE32(*((DWORD *)(&psmp[ichk].length)));
- dontfuckwithme += len;
- }
- if (dontfuckwithme > dwMemLength) return FALSE;
- // That should be enough checking: this must be a 669 module.
- m_nType = MOD_TYPE_669;
- m_dwSongFlags |= SONG_LINEARSLIDES;
- m_nMinPeriod = 28 << 2;
- m_nMaxPeriod = 1712 << 3;
- m_nDefaultTempo = 125;
- m_nDefaultSpeed = 6;
- m_nChannels = 8;
- memcpy(m_szNames[0], pfh->songmessage, 16);
- m_nSamples = pfh->samples;
- for (UINT nins=1; nins<=m_nSamples; nins++, psmp++)
- {
- DWORD len = bswapLE32(*((DWORD *)(&psmp->length)));
- DWORD loopstart = bswapLE32(*((DWORD *)(&psmp->loopstart)));
- DWORD loopend = bswapLE32(*((DWORD *)(&psmp->loopend)));
- if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
- if ((loopend > len) && (!loopstart)) loopend = 0;
- if (loopend > len) loopend = len;
- if (loopstart + 4 >= loopend) loopstart = loopend = 0;
- Ins[nins].nLength = len;
- Ins[nins].nLoopStart = loopstart;
- Ins[nins].nLoopEnd = loopend;
- if (loopend) Ins[nins].uFlags |= CHN_LOOP;
- memcpy(m_szNames[nins], psmp->filename, 13);
- Ins[nins].nVolume = 256;
- Ins[nins].nGlobalVol = 64;
- Ins[nins].nPan = 128;
- }
- // Song Message
- m_lpszSongComments = new char[109];
- memcpy(m_lpszSongComments, pfh->songmessage, 108);
- m_lpszSongComments[108] = 0;
- // Reading Orders
- memcpy(Order, pfh->orders, 128);
- m_nRestartPos = pfh->restartpos;
- if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
- // Reading Pattern Break Locations
- for (UINT npan=0; npan<8; npan++)
- {
- ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0;
- ChnSettings[npan].nVolume = 64;
- }
- // Reading Patterns
- dwMemPos = 0x1F1 + pfh->samples * 25;
- for (UINT npat=0; npatpatterns; npat++)
- {
- Patterns[npat] = AllocatePattern(64, m_nChannels);
- if (!Patterns[npat]) break;
- PatternSize[npat] = 64;
- MODCOMMAND *m = Patterns[npat];
- const BYTE *p = lpStream + dwMemPos;
- for (UINT row=0; row<64; row++)
- {
- MODCOMMAND *mspeed = m;
- if ((row == pfh->breaks[npat]) && (row != 63))
- {
- for (UINT i=0; i<8; i++)
- {
- m[i].command = CMD_PATTERNBREAK;
- m[i].param = 0;
- }
- }
- for (UINT n=0; n<8; n++, m++, p+=3)
- {
- UINT note = p[0] >> 2;
- UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
- UINT vol = p[1] & 0x0F;
- if (p[0] < 0xFE)
- {
- m->note = note + 37;
- m->instr = instr + 1;
- }
- if (p[0] <= 0xFE)
- {
- m->volcmd = VOLCMD_VOLUME;
- m->vol = (vol << 2) + 2;
- }
- if (p[2] != 0xFF)
- {
- UINT command = p[2] >> 4;
- UINT param = p[2] & 0x0F;
- switch(command)
- {
- case 0x00: command = CMD_PORTAMENTOUP; break;
- case 0x01: command = CMD_PORTAMENTODOWN; break;
- case 0x02: command = CMD_TONEPORTAMENTO; break;
- case 0x03: command = CMD_MODCMDEX; param |= 0x50; break;
- case 0x04: command = CMD_VIBRATO; param |= 0x40; break;
- case 0x05: if (param) command = CMD_SPEED; else command = 0; param += 2; break;
- case 0x06: if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; } else
- if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; } else
- command = 0;
- break;
- default: command = 0;
- }
- if (command)
- {
- if (command == CMD_SPEED) mspeed = NULL;
- m->command = command;
- m->param = param;
- }
- }
- }
- if ((!row) && (mspeed))
- {
- for (UINT i=0; i<8; i++) if (!mspeed[i].command)
- {
- mspeed[i].command = CMD_SPEED;
- mspeed[i].param = pfh->tempolist[npat] + 2;
- break;
- }
- }
- }
- dwMemPos += 0x600;
- }
- // Reading Samples
- for (UINT n=1; n<=m_nSamples; n++)
- {
- UINT len = Ins[n].nLength;
- if (dwMemPos >= dwMemLength) break;
- if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
- dwMemPos += len;
- }
- return TRUE;
-}
-
-
diff --git a/src/modplug/load_abc.cpp b/src/modplug/load_abc.cpp
deleted file mode 100644
index 00c31767..00000000
--- a/src/modplug/load_abc.cpp
+++ /dev/null
@@ -1,5114 +0,0 @@
-/*
-
- MikMod Sound System
-
- By Jake Stine of Divine Entertainment (1996-2000)
-
- Support:
- If you find problems with this code, send mail to:
- air@divent.org
-
- Distribution / Code rights:
- Use this source code in any fashion you see fit. Giving me credit where
- credit is due is optional, depending on your own levels of integrity and
- honesty.
-
- -----------------------------------------
- Module: LOAD_ABC
-
- ABC module loader.
- by Peter Grootswagers (2006)
-
-
- Portability:
- All systems - all compilers (hopefully)
-*/
-
-#include
-#include
-#include
-#include
-#include
-//#include // for sleep
-
-#ifdef NEWMIKMOD
-#include "mikmod.h"
-#include "uniform.h"
-typedef UBYTE BYTE;
-typedef UWORD WORD;
-#else
-#include "stdafx.h"
-#include "sndfile.h"
-#endif
-
-#include "load_pat.h"
-
-#define MAXABCINCLUDES 8
-#define MAXCHORDNAMES 80
-#define ABC_ENV_DUMPTRACKS "MMABC_DUMPTRACKS"
-#define ABC_ENV_NORANDOMPICK "MMABC_NO_RANDOM_PICK"
-
-// gchords use tracks with vpos 1 thru 7
-// drums use track with vpos 8
-// voice chords use vpos 0 and vpos from 11 up
-#define GCHORDBPOS 1
-#define GCHORDFPOS 2
-#define GCHORDCPOS 3
-#define DRUMPOS 8
-#define DRONEPOS1 9
-#define DRONEPOS2 10
-
-// in the patterns a whole note at unmodified tempo is 16 rows
-#define ROWSPERNOTE 16
-// a 1/64-th note played in triool equals a 1/96-th note, to be able
-// to play them and also to play the 1/64-th we need a resolution of 192
-// because 2/192 = 1/96 and 3/192 = 1/64
-#define RESOLUTION 192
-
-#pragma pack(1)
-
-/**************************************************************************
-**************************************************************************/
-#ifdef NEWMIKMOD
-static char ABC_Version[] = "ABC+2.0 (draft IV)";
-#endif
-
-typedef enum {
- note,
- octave,
- smpno,
- volume,
- effect,
- effoper
-} ABCEVENT_X_NOTE;
-
-typedef enum {
- none,
- trill,
- bow,
- accent
-} ABCEVENT_X_EFFECT;
-
-typedef enum {
- cmdflag,
- command,
- chordnum,
- chordnote,
- chordbase,
- jumptype
-} ABCEVENT_X_CMD;
-
-typedef enum {
- cmdsegno = '$',
- cmdcapo = 'B',
- cmdchord = 'C',
- cmdfine = 'F',
- cmdhide = 'H',
- cmdjump = 'J',
- cmdloop = 'L',
- cmdcoda = 'O',
- cmdpartbrk = 'P',
- cmdsync = 'S',
- cmdtempo = 'T',
- cmdvariant = 'V',
- cmdtocoda = 'X'
-} ABCEVENT_CMD;
-
-typedef enum {
- jumpnormal,
- jumpfade,
- jumpdacapo,
- jumpdcfade,
- jumpdasegno,
- jumpdsfade,
- jumpfine,
- jumptocoda,
- jumpvariant,
- jumpnot
-} ABCEVENT_JUMPTYPE;
-
-typedef struct _ABCEVENT
-{
- struct _ABCEVENT *next;
- ULONG tracktick;
- union {
- BYTE par[6];
- struct {
- BYTE flg;
- BYTE cmd;
- ULONG lpar; // for variant selections, bit pattern
- };
- };
- BYTE part;
- BYTE tiednote;
-} ABCEVENT;
-
-typedef struct _ABCTRACK
-{
- struct _ABCTRACK *next;
- ABCEVENT *head;
- ABCEVENT *tail;
- ABCEVENT *capostart;
- ABCEVENT *tienote;
- int transpose;
- int octave_shift;
- ULONG slidevoltime; // for crescendo and diminuendo
- int slidevol; // -2:fade away, -1:diminuendo, 0:none, +1:crescendo
- BYTE vno; // 0 is track is free for use, from previous song in multi-songbook
- BYTE vpos; // 0 is main voice, other is subtrack for gchords, gchords or drumnotes
- BYTE tiedvpos;
- BYTE mute;
- BYTE chan; // 10 is percussion channel, any other is melodic channel
- BYTE volume;
- BYTE instr; // current instrument for this track
- BYTE legato;
- char v[22]; // first twenty characters are significant
-} ABCTRACK;
-
-typedef struct _ABCMACRO
-{
- struct _ABCMACRO *next;
- char *name;
- char *subst;
- char *n;
-} ABCMACRO;
-
-/**************************************************************************
-**************************************************************************/
-
-typedef struct _ABCHANDLE
-{
-#ifdef NEWMIKMOD
- MM_ALLOC *allochandle;
- MM_ALLOC *macrohandle;
- MM_ALLOC *trackhandle;
- MM_ALLOC *ho;
-#endif
- ABCMACRO *macro;
- ABCMACRO *umacro;
- ABCTRACK *track;
- long int pickrandom;
- unsigned int len;
- int speed;
- char *line;
- char *beatstring;
- BYTE beat[4]; // a:first note, b:strong notes, c:weak notes, n:strong note every n
- char gchord[80]; // last setting for gchord
- char drum[80]; // last setting for drum
- char drumins[80]; // last setting for drum
- char drumvol[80]; // last setting for drum
- ULONG barticks;
- // parse variables, declared here to avoid parameter pollution
- int abcchordvol, abcchordprog, abcbassvol, abcbassprog;
- int ktrans;
- int drumon, gchordon, droneon;
- int dronegm, dronepitch[2], dronevol[2];
- ABCTRACK *tp, *tpc, *tpr;
- ULONG tracktime;
-} ABCHANDLE;
-
-static int global_voiceno, global_octave_shift, global_tempo_factor, global_tempo_divider;
-static char global_part;
-static ULONG global_songstart;
-/* Named guitar chords */
-static char chordname[MAXCHORDNAMES][8];
-static int chordnotes[MAXCHORDNAMES][6];
-static int chordlen[MAXCHORDNAMES];
-static int chordsnamed = 0;
-
-static char *sig[] = {
- " C D EF G A Bc d ef g a b", // 7 sharps C#
- " C D EF G AB c d ef g ab ", // 6 sharps F#
- " C DE F G AB c de f g ab ", // 5 sharps B
- " C DE F GA B c de f ga b ", // 4 sharps E
- " CD E F GA B cd e f ga b ", // 3 sharps A
- " CD E FG A B cd e fg a b ", // 2 sharps D
- " C D E FG A Bc d e fg a b", // 1 sharps G
- " C D EF G A Bc d ef g a b", // 0 sharps C
- " C D EF G AB c d ef g ab ", // 1 flats F
- " C DE F G AB c de f g ab ", // 2 flats Bb
- " C DE F GA B c de f ga b ", // 3 flats Eb
- " CD E F GA B cd e f ga b ", // 4 flats Ab
- " CD E FG A B cd e fg a b ", // 5 flats Db
- "C D E FG A Bc d e fg a b ", // 6 flats Gb
- "C D EF G A Bc d ef g a b ", // 7 flats Cb
-// 0123456789012345678901234
-};
-
-static char *keySigs[] = {
-/* 0....:....1....:....2....:....3....:....4....:....5. */
- "7 sharps: C# A#m G#Mix D#Dor E#Phr F#Lyd B#Loc ",
- "6 sharps: F# D#m C#Mix G#Dor A#Phr BLyd E#Loc ",
- "5 sharps: B G#m F#Mix C#Dor D#Phr ELyd A#Loc ",
- "4 sharps: E C#m BMix F#Dor G#Phr ALyd D#Loc ",
- "3 sharps: A F#m EMix BDor C#Phr DLyd G#Loc ",
- "2 sharps: D Bm AMix EDor F#Phr GLyd C#Loc ",
- "1 sharp : G Em DMix ADor BPhr CLyd F#Loc ",
- "0 sharps: C Am GMix DDor EPhr FLyd BLoc ",
- "1 flat : F Dm CMix GDor APhr BbLyd ELoc ",
- "2 flats : Bb Gm FMix CDor DPhr EbLyd ALoc ",
- "3 flats : Eb Cm BbMix FDor GPhr AbLyd DLoc ",
- "4 flats : Ab Fm EbMix BbDor CPhr DbLyd GLoc ",
- "5 flats : Db Bbm AbMix EbDor FPhr GbLyd CLoc ",
- "6 flats : Gb Ebm DbMix AbDor BbPhr CbLyd FLoc ",
- "7 flats : Cb Abm GbMix DbDor EbPhr FbLyd BbLoc ",
- 0
-};
-
-// local prototypes
-static int abc_getnumber(const char *p, int *number);
-static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos);
-static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e);
-static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime);
-static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j);
-static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime);
-static int abc_patno(ABCHANDLE *h, ULONG tracktime);
-
-static void abc_message(const char *s1, const char *s2)
-{
- char txt[256];
- if( strlen(s1) + strlen(s2) > 255 ) return;
- sprintf(txt, s1, s2);
-#ifdef NEWMIKMOD
- _mmlog(txt);
-#else
- fprintf(stderr, "load_abc > %s\n", txt);
-#endif
-}
-
-static ULONG modticks(ULONG abcticks)
-{
- return abcticks / RESOLUTION;
-}
-
-static ULONG abcticks(ULONG modticks)
-{
- return modticks * RESOLUTION;
-}
-
-static ULONG notelen_notediv_to_ticks(int speed, int len, int div)
-{
- ULONG u;
- u = (ROWSPERNOTE * RESOLUTION * speed * len * global_tempo_factor) / (div * global_tempo_divider);
- return u;
-}
-
-static void abc_dumptracks(ABCHANDLE *h, const char *p)
-{
- ABCTRACK *t;
- ABCEVENT *e;
- int n,pat,row,tck;
- char nn[3];
- if( !h ) return;
- for( t=h->track; t; t=t->next ) {
- printf("track %d.%d chan=%d %s\n", (int)(t->vno), (int)(t->vpos),
- (int)(t->chan), (char *)(t->v));
- if( strcmp(p,"nonotes") )
- n = 1;
- else
- n = 0;
- for( e=t->head; e; e=e->next ) {
- tck = modticks(e->tracktick);
- row = tck / h->speed;
- pat = row / 64;
- tck = tck % h->speed;
- row = row % 64;
- nn[0] = ( e->tracktick % abcticks(h->speed * 64) ) ? ' ': '-';
- if( e->flg == 1 ) {
- printf(" %6d.%02d.%d%c%c %d.%d %s ",
- pat, row, tck, nn[0], (int)(e->part), (int)(t->vno),
- (int)(t->vpos), (char *)(t->v));
- if( e->cmd == cmdchord ) {
- nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordnote]];
- nn[1] = "b # # # # # # # # # # #"[e->par[chordnote]];
- nn[2] = '\0';
- if( isspace(nn[1]) ) nn[1] = '\0';
- printf("CMD %c: gchord %s%s",
- (char)(e->cmd), nn, chordname[e->par[chordnum]]);
- if( e->par[chordbase] != e->par[chordnote] ) {
- nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordbase]];
- nn[1] = "b # # # # # # # # # # #"[e->par[chordbase]];
- nn[2] = '\0';
- printf("/%s", nn);
- }
- printf("\n");
- }
- else
- printf("CMD %c @%p 0x%08lX\n",
- (char)(e->cmd), e,
- (unsigned long)(e->lpar));
- if( strcmp(p,"nonotes") )
- n = 1;
- else
- n = 0;
- }
- else if( n ) {
- printf(" %6d.%02d.%d%c%c %d.%d %s ", pat, row, tck, nn[0], e->part, t->vno, t->vpos, t->v);
- if( e->par[note] ) {
- nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[note]-23];
- nn[1] = "b # # # # # # # # # # #"[e->par[note]-23];
- nn[2] = '\0';
- }
- else strcpy(nn,"--");
- printf("NOTE %s octave %d inst %s vol %03d\n",
- nn, e->par[octave], pat_gm_name(pat_smptogm(e->par[smpno])),e->par[volume]);
- if( strcmp(p,"all") )
- n = 0;
- }
- }
- }
-}
-
-#ifdef NEWMIKMOD
-
-#define MMFILE MMSTREAM
-#define mmfgetc(x) _mm_read_SBYTE(x)
-#define mmfeof(x) _mm_feof(x)
-#define mmfgets(buf,sz,f) _mm_fgets(f,buf,sz)
-#define mmftell(x) _mm_ftell(x)
-#define mmfseek(f,p,w) _mm_fseek(f,p,w)
-#define mmfopen(s,m) _mm_fopen(s,m)
-#define mmfclose(f) _mm_fclose(f)
-
-#else
-
-#define MMSTREAM FILE
-#define _mm_fopen(name,mode) fopen(name,mode)
-#define _mm_fgets(f,buf,sz) fgets(buf,sz,f)
-#define _mm_fseek(f,pos,whence) fseek(f,pos,whence)
-#define _mm_ftell(f) ftell(f)
-#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f)
-#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f)
-#define _mm_feof(f) feof(f)
-#define _mm_fclose(f) fclose(f)
-#define DupStr(h,buf,sz) strdup(buf)
-#define _mm_calloc(h,n,sz) calloc(n,sz)
-#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz)
-#define _mm_free(h,p) free(p)
-
-typedef struct {
- char *mm;
- int sz;
- int pos;
-} MMFILE;
-
-static MMFILE *mmfopen(const char *name, const char *mode)
-{
- FILE *fp;
- MMFILE *mmfile;
- long len;
- if( *mode != 'r' ) return NULL;
- fp = fopen(name, mode);
- if( !fp ) return NULL;
- fseek(fp, 0, SEEK_END);
- len = ftell(fp);
- mmfile = (MMFILE *)malloc(len+sizeof(MMFILE));
- if( !mmfile ) return NULL;
- fseek(fp, 0, SEEK_SET);
- fread(&mmfile[1],1,len,fp);
- fclose(fp);
- mmfile->mm = (char *)&mmfile[1];
- mmfile->sz = len;
- mmfile->pos = 0;
- return mmfile;
-}
-
-static void mmfclose(MMFILE *mmfile)
-{
- free(mmfile);
-}
-
-static bool mmfeof(MMFILE *mmfile)
-{
- if( mmfile->pos < 0 ) return TRUE;
- if( mmfile->pos < mmfile->sz ) return FALSE;
- return TRUE;
-}
-
-static int mmfgetc(MMFILE *mmfile)
-{
- int b;
- if( mmfeof(mmfile) ) return EOF;
- b = mmfile->mm[mmfile->pos];
- mmfile->pos++;
- if( b=='\r' && mmfile->mm[mmfile->pos] == '\n' ) {
- b = '\n';
- mmfile->pos++;
- }
- return b;
-}
-
-static void mmfgets(char buf[], unsigned int bufsz, MMFILE *mmfile)
-{
- int i,b;
- for( i=0; i<(int)bufsz-1; i++ ) {
- b = mmfgetc(mmfile);
- if( b==EOF ) break;
- buf[i] = b;
- if( b == '\n' ) break;
- }
- buf[i] = '\0';
-}
-
-static long mmftell(MMFILE *mmfile)
-{
- return mmfile->pos;
-}
-
-static void mmfseek(MMFILE *mmfile, long p, int whence)
-{
- switch(whence) {
- case SEEK_SET:
- mmfile->pos = p;
- break;
- case SEEK_CUR:
- mmfile->pos += p;
- break;
- case SEEK_END:
- mmfile->pos = mmfile->sz + p;
- break;
- }
-}
-#endif
-
-// =====================================================================================
-static ABCEVENT *abc_new_event(ABCHANDLE *h, ULONG abctick, const char data[])
-// =====================================================================================
-{
- ABCEVENT *retval;
- int i;
-
- retval = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT));
- retval->next = NULL;
- retval->tracktick = abctick;
- for( i=0; i<6; i++ )
- retval->par[i] = data[i];
- retval->part = global_part;
- retval->tiednote = 0;
- return retval;
-}
-
-// =============================================================================
-static ABCEVENT *abc_copy_event(ABCHANDLE *h, ABCEVENT *se)
-// =============================================================================
-{
- ABCEVENT *e;
- e = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT));
- e->next = NULL;
- e->tracktick = se->tracktick;
- e->flg = se->flg;
- e->cmd = se->cmd;
- e->lpar = se->lpar;
- e->part = se->part;
- return e;
-}
-
-// =============================================================================
-static void abc_new_macro(ABCHANDLE *h, const char *m)
-// =============================================================================
-{
- ABCMACRO *retval;
- const char *p;
- char buf[256],*q;
- for( p=m; *p && isspace(*p); p++ ) ;
- for( q=buf; *p && *p != '='; p++ )
- *q++ = *p;
- if( q != buf )
- while( isspace(q[-1]) ) q--;
- *q = '\0';
- retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK));
- retval->name = DupStr(h->macrohandle, buf,strlen(buf));
- retval->n = strrchr(retval->name, 'n'); // for transposing macro's
- for( p++; *p && isspace(*p); p++ ) ;
- strncpy(buf,p,200);
- for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0';
- retval->subst = DupStr(h->macrohandle, buf, strlen(buf));
- retval->next = h->macro;
- h->macro = retval;
-}
-
-// =============================================================================
-static void abc_new_umacro(ABCHANDLE *h, const char *m)
-// =============================================================================
-{
- ABCMACRO *retval, *mp;
- const char *p;
- char buf[256], let[2], *q;
- for( p=m; *p && isspace(*p); p++ ) ;
- for( q=buf; *p && *p != '='; p++ )
- *q++ = *p;
- if( q != buf )
- while( isspace(q[-1]) ) q--;
- *q = '\0';
- if( strlen(buf) > 1 || strchr("~HIJKLMNOPQRSTUVWXY",toupper(buf[0])) == 0 || strchr("xy",buf[0]) ) return;
- strcpy(let,buf);
- for( p++; *p && isspace(*p); p++ ) ;
- strncpy(buf,p,200);
- for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0';
- for( q=buf; *q; q++ ) if( *q == '!' ) *q = '+'; // translate oldstyle to newstyle
- if( !strcmp(buf,"+nil+") ) { // delete a macro
- mp = NULL;
- for( retval=h->umacro; retval; retval = retval->next ) {
- if( retval->name[0] == let[0] ) { // delete this one
- if( mp ) mp->next = retval->next;
- else h->umacro = retval->next;
- _mm_free(h->macrohandle, retval);
- return;
- }
- mp = retval;
- }
- return;
- }
- retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK));
- retval->name = DupStr(h->macrohandle, let,1);
- retval->subst = DupStr(h->macrohandle, buf, strlen(buf));
- retval->n = 0;
- retval->next = h->umacro; // by placing it up front we mask out the old macro until we +nil+ it
- h->umacro = retval;
-}
-
-// =============================================================================
-static ABCTRACK *abc_new_track(ABCHANDLE *h, const char *voice, int pos)
-// =============================================================================
-{
- ABCTRACK *retval;
- if( !pos ) global_voiceno++;
- retval = (ABCTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(ABCTRACK));
- retval->next = NULL;
- retval->vno = global_voiceno;
- retval->vpos = pos;
- retval->tiedvpos = pos;
- retval->instr = 1;
- strncpy(retval->v, voice, 20);
- retval->v[20] = '\0';
- retval->head = NULL;
- retval->tail = NULL;
- retval->capostart = NULL;
- retval->tienote = NULL;
- retval->mute = 0;
- retval->chan = 0;
- retval->transpose = 0;
- retval->volume = h->track? h->track->volume: 120;
- retval->slidevoltime = 0;
- retval->slidevol = 0;
- retval->legato = 0;
- return retval;
-}
-
-static int abc_numtracks(ABCHANDLE *h)
-{
- int n;
- ABCTRACK *t;
- n=0;
- for( t = h->track; t; t=t->next )
- n++;
- return n;
-}
-
-static int abc_interval(const char *s, const char *d)
-{
- const char *p;
- int i,j,k;
- int n,oct,m[2];
- for( j=0; j<2; j++ ) {
- if( j ) p = d;
- else p = s;
- switch(p[0]) {
- case '^':
- n = p[1];
- i = 2;
- break;
- case '_':
- n = p[1];
- i = 2;
- break;
- case '=':
- n = p[1];
- i = 2;
- break;
- default:
- n = p[0];
- i = 1;
- break;
- }
- for( k=0; k<25; k++ )
- if( n == sig[7][k] )
- break;
- oct = 4; // ABC note pitch C is C4 and pitch c is C5
- if( k > 12 ) {
- oct++;
- k -= 12;
- }
- while( p[i] == ',' || p[i] == '\'' ) {
- if( p[i] == ',' )
- oct--;
- else
- oct++;
- i++;
- }
- m[j] = k + 12 * oct;
- }
- return m[0] - m[1];
-}
-
-static int abc_transpose(const char *v)
-{
- int i,j,t;
- const char *m = "B", *mv = "";
- t = 0;
- global_octave_shift = 99;
- for( ; *v && *v != ']'; v++ ) {
- if( !strnicmp(v,"t=",2) ) {
- v+=2;
- if( *v=='-' ) {
- j = -1;
- v++;
- }
- else j = 1;
- v+=abc_getnumber(v,&i);
- t += i * j;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"octave=",7) ) {
- v+=7;
- if( *v=='-' ) {
- j = -1;
- v++;
- }
- else j = 1;
- v+=abc_getnumber(v,&i);
- t += i * j * 12;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"transpose=",10) ) {
- v+=10;
- if( *v=='-' ) {
- j = -1;
- v++;
- }
- else j = 1;
- v+=abc_getnumber(v,&i);
- t += i * j;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"octave=",7) ) { // used in kv304*.abc
- v+=7;
- if( *v=='-' ) {
- j = -1;
- v++;
- }
- else j = 1;
- v+=abc_getnumber(v,&i);
- t += i * j * 12;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"m=",2) ) {
- v += 2;
- mv = v; // get the pitch for the middle staff line
- while( *v && *v != ' ' && *v != ']' ) v++;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"middle=",7) ) {
- v += 7;
- mv = v; // get the pitch for the middle staff line
- while( *v && *v != ' ' && *v != ']' ) v++;
- global_octave_shift = 0;
- }
- if( !strnicmp(v,"clef=",5) )
- v += 5;
- j = 1;
- if( !strnicmp(v,"treble",6) ) {
- j = 0;
- v += 6;
- switch( *v ) {
- case '1': v++; m = "d"; break;
- case '2': v++;
- default: m = "B"; break;
- case '3': v++; m = "G"; break;
- case '4': v++; m = "E"; break;
- case '5': v++; m = "C"; break;
- }
- global_octave_shift = 0;
- }
- if( j && !strnicmp(v,"bass",4) ) {
- m = "D,";
- j = 0;
- v += 4;
- switch( *v ) {
- case '1': v++; m = "C"; break;
- case '2': v++; m = "A,"; break;
- case '3': v++; m = "F,"; break;
- case '4': v++;
- default: m = "D,"; break;
- case '5': v++; m = "B,,"; break;
- }
- if( global_octave_shift == 99 )
- global_octave_shift = -2;
- }
- if( j && !strnicmp(v,"tenor",5) ) {
- j = 0;
- v += 5;
- switch( *v ) {
- case '1': v++; m = "G"; break;
- case '2': v++; m = "E"; break;
- case '3': v++; m = "C"; break;
- case '4': v++;
- default: m = "A,"; break;
- case '5': v++; m = "F,"; break;
- }
- if( global_octave_shift == 99 )
- global_octave_shift = 1;
- }
- if( j && !strnicmp(v,"alto",4) ) {
- j = 0;
- v += 4;
- switch( *v ) {
- case '1': v++; m = "G"; break;
- case '2': v++; m = "E"; break;
- case '3': v++;
- default: m = "C"; break;
- case '4': v++; m = "A,"; break;
- case '5': v++; m = "F,"; break;
- }
- if( global_octave_shift == 99 )
- global_octave_shift = 1;
- }
- if( j && strchr("+-",*v) && *v && v[1]=='8' ) {
- switch(*v) {
- case '+':
- t += 12;
- break;
- case '-':
- t -= 12;
- break;
- }
- v += 2;
- if( !strnicmp(v,"va",2) ) v += 2;
- global_octave_shift = 0;
- j = 0;
- }
- if( j ) {
- while( *v && *v != ' ' && *v != ']' ) v++;
- }
- }
- if( strlen(mv) > 0 ) // someone set the middle note
- t += abc_interval(mv, m);
- if( global_octave_shift == 99 )
- global_octave_shift = 0;
- return t;
-}
-
-// =============================================================================
-static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos)
-// =============================================================================
-{
- ABCTRACK *tr, *prev, *trunused;
- char vc[21];
- int i, trans=0, voiceno=0, instrno = 1, channo = 0;
- for( ; *voice == ' '; voice++ ) ; // skip leading spaces
- for( i=0; *voice && *voice != ']' && *voice != '%' && !isspace(*voice); voice++ ) // can work with inline voice instructions
- vc[i++] = *voice;
- vc[i] = '\0';
- prev = NULL;
- trunused = NULL;
- if( !pos ) trans = abc_transpose(voice);
- for( tr=h->track; tr; tr=tr->next ) {
- if( tr->vno == 0 ) {
- if( !trunused ) trunused = tr; // must reuse mastertrack (h->track) as first
- }
- else {
- if( !strnicmp(tr->v, vc, 20) ) {
- if( tr->vpos == pos )
- return tr;
- trans = tr->transpose;
- global_octave_shift = tr->octave_shift;
- voiceno = tr->vno;
- instrno = tr->instr;
- channo = tr->chan;
- }
- }
- prev = tr;
- }
- if( trunused ) {
- tr = trunused;
- if( pos ) {
- tr->vno = voiceno;
- tr->instr = instrno;
- tr->chan = channo;
- }
- else {
- global_voiceno++;
- tr->vno = global_voiceno;
- tr->instr = 1;
- tr->chan = 0;
- }
- tr->vpos = pos;
- tr->tiedvpos = pos;
- strncpy(tr->v, vc, 20);
- tr->v[20] = '\0';
- tr->mute = 0;
- tr->transpose = trans;
- tr->octave_shift = global_octave_shift;
- tr->volume = h->track->volume;
- tr->tienote = NULL;
- tr->legato = 0;
- return tr;
- }
- tr = abc_new_track(h, vc, pos);
- if( pos ) {
- tr->vno = voiceno;
- tr->instr = instrno;
- tr->chan = channo;
- }
- tr->transpose = trans;
- tr->octave_shift = global_octave_shift;
- if( prev ) prev->next = tr;
- else h->track = tr;
- return tr;
-}
-
-// =============================================================================
-static ABCTRACK *abc_check_track(ABCHANDLE *h, ABCTRACK *tp)
-// =============================================================================
-{
- if( !tp ) {
- tp = abc_locate_track(h, "", 0); // must work for voiceless abc too...
- tp->transpose = h->ktrans;
- }
- return tp;
-}
-
-static void abc_add_capo(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdcapo;
- e = abc_new_event(h, tracktime, d);
- tp->capostart = e;
- abc_add_event(h, tp, e); // do this last (recursion danger)
-}
-
-static void abc_add_segno(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdsegno;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_coda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdcoda;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_fine(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdfine;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_tocoda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdtocoda;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-// first track is dirigent, remove all control events from other tracks
-// to keep the information where the events should be relative to note events
-// in the same tick the ticks are octated and four added for note events
-// the control events that come before the note events get a decremented tick,
-// those that come after get an incremented tick, for example:
-// ctrl ctrl note ctrl ctrl note
-// original: t t t t t+1 t+1
-// recoded: 8t+1 8t+2 8t+4 8t+5 8t+11 8t+12
-static void abc_remove_unnecessary_events(ABCHANDLE *h)
-{
- ABCTRACK *tp,*ptp;
- ABCEVENT *ep, *el;
- ULONG ct, et;
- int d;
- ptp = NULL;
- for( tp=h->track; tp; tp=tp->next ) {
- el = NULL;
- ep = tp->head;
- ct = 0;
- d = -3;
- while( ep ) {
- et = ep->tracktick;
- ep->tracktick <<= 3;
- ep->tracktick += 4;
- if( ep->flg == 1 ) {
- ep->tracktick += d;
- d++;
- if( d == 0 ) d = -1;
- if( d == 4 ) d = 3;
- if( tp!=h->track ) ep->cmd = cmdhide;
- switch( ep->cmd ) {
- case cmdhide:
- case cmdsync:
- if( el ) {
- el->next = ep->next;
- _mm_free(h->trackhandle,ep);
- ep = el->next;
- }
- else {
- tp->head = ep->next;
- _mm_free(h->trackhandle,ep);
- ep = tp->head;
- }
- break;
- default:
- el = ep;
- ep = ep->next;
- break;
- }
- }
- else {
- el = ep;
- ep = ep->next;
- d = 1;
- }
- if( et > ct )
- d = -3;
- ct = et;
- }
- if( !tp->head ) { // no need to keep empty tracks...
- if( ptp ) {
- ptp->next = tp->next;
- _mm_free(h->trackhandle,tp);
- tp = ptp;
- }
- else {
- h->track = tp->next;
- _mm_free(h->trackhandle,tp);
- tp = h->track;
- }
- }
- ptp = tp; // remember previous track
- }
-}
-
-// set ticks back, and handle partbreaks
-static void abc_retick_events(ABCHANDLE *h)
-{
- ABCTRACK *tp;
- ABCEVENT *ep;
- ULONG et, tt=0, at = abcticks(64 * h->speed);
- for( tp=h->track; tp; tp=tp->next ) {
- // make ticks relative
- tt = 0;
- for( ep=tp->head; ep; ep=ep->next ) {
- et = ep->tracktick >> 3;
- ep->tracktick = et - tt;
- tt = et;
- }
- // make ticks absolute again, skipping no-op partbreaks
- tt = 0;
- for( ep=tp->head; ep; ep=ep->next ) {
- ep->tracktick += tt;
- tt = ep->tracktick;
- if( ep->flg == 1 && ep->cmd == cmdpartbrk ) {
- if( tt % at ) {
- tt += at;
- tt /= at;
- tt *= at;
- ep->tracktick -= abcticks(h->speed); // break plays current row
- }
- else ep->cmd = cmdhide;
- }
- }
- }
-}
-
-// make sure every track has the control events it needs, this way it is not
-// necessary to have redundant +segno+ +D.C.+ etc in the voices, the first voice
-// is the master, it is pointed to by the member 'track' in the ABCHANDLE
-static void abc_synchronise_tracks(ABCHANDLE *h)
-{
- ABCTRACK *tp;
- ULONG tm; // tracktime in master
- ABCEVENT *em, *es, *et, *ec; // events in master, slave, slave temporary and copied event
- if( !h || !h->track ) return;
- abc_remove_unnecessary_events(h);
- for( tp = h->track->next; tp; tp = tp->next ) {
- for( em=h->track->head; em; em=em->next ) {
- if( em->flg == 1 ) { // some kind of control event
- switch( em->cmd ) {
- case cmdchord:
- case cmdhide:
- case cmdtempo:
- case cmdsync:
- break;
- default: // check to see if copy is necessary
- ec = abc_copy_event(h, em);
- tm = em->tracktick;
- es = tp->head; // allways search from the begin...
- for( et=es; et && et->tracktick <= tm; et=et->next )
- es = et;
- if( es == NULL || es->tracktick > tm ) { // special case: head of track
- ec->next = es;
- tp->head = ec;
- }
- else {
- ec->next = es->next;
- es->next = ec;
- }
- break;
- }
- }
- }
- }
- abc_retick_events(h);
-}
-
-static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e)
-{
- if( !tp->capostart ) abc_add_capo(h, tp, global_songstart);
- if( tp->tail ) {
- tp->tail->next = e;
- tp->tail = e;
- }
- else {
- tp->head = e;
- tp->tail = e;
- }
-}
-
-static void abc_add_partbreak(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdpartbrk;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_tempo_event(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int tempo)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdtempo;
- e = abc_new_event(h, tracktime, d);
- e->lpar = tempo;
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_noteoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- d[note] = 0;
- d[octave] = 0;
- d[smpno] = pat_gmtosmp(tp->instr);
- d[volume] = 0;
- d[effect] = 0;
- d[effoper] = 0;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static int abc_dynamic_volume(ABCTRACK *tp, ULONG tracktime, int vol)
-{
- ULONG slidetime;
- int voldelta;
- if( tp->mute ) return 0;
- if( tp->slidevol == 0 ) return vol;
- if( tracktime < tp->slidevoltime ) return vol;
- slidetime = modticks(tracktime - tp->slidevoltime);
- voldelta = (slidetime * 15) / 64 / 6; // slide from say mf up to f in one pattern's time
- if( tp->slidevol > -2 && voldelta > 15 ) voldelta = 15; // never to much dynamics
- if( tp->slidevol > 0 ) vol += voldelta;
- else vol -= voldelta;
- if( vol < 2 ) vol = 2; // xmms divides this by 2....
- if( vol > 127 ) vol = 127;
- return vol;
-}
-
-static void abc_track_untie_short_chordnotes(ABCHANDLE *h)
-{
- ABCTRACK *tp;
- int vn;
- tp = h->tp;
- vn = tp->vno;
- for( tp = h->track; tp; tp = tp->next )
- if( tp != h->tp && tp->vno == vn && tp->tienote ) {
- abc_message("short notes in chord can not be tied:\n%s", h->line);
- tp->tienote = 0;
- }
-}
-
-static void abc_track_clear_tiednote(ABCHANDLE *h)
-{
- ABCTRACK *tp;
- int vn;
- tp = h->tp;
- vn = tp->vno;
- for( tp = h->track; tp; tp = tp->next )
- if( tp->vno == vn ) tp->tienote = 0;
-}
-
-static void abc_track_clear_tiedvpos(ABCHANDLE *h)
-{
- ABCTRACK *tp;
- int vn;
- tp = h->tp;
- vn = tp->vno;
- for( tp = h->track; tp; tp = tp->next )
- if( tp->vno == vn ) tp->tiedvpos = tp->vpos;
-}
-
-static ABCTRACK *abc_track_with_note_tied(ABCHANDLE *h, ULONG tracktime, int n, int oct)
-{
- int vn, vp;
- ABCTRACK *tp;
- ABCEVENT *e;
- tp = h->tp;
- vn = tp->vno;
- vp = tp->vpos;
- for( tp = h->track; tp; tp = tp->next ) {
- if( tp->vno == vn ) {
- e = tp->tienote;
- if( e && e->tracktick < tracktime
- && e->par[octave] == oct && abs(e->par[note] - n) < 3 ) {
- if( tp->vpos != vp ) tp->tiedvpos = vp;
- h->tp = tp;
- return tp;
- }
- }
- }
- tp = h->tp;
- vp = tp->tiedvpos;
- if( tp->vpos != vp ) {
- // chord note track allready returned in previous call
- for( tp = h->track; tp; tp = tp->next ) {
- if( tp->vno == vn && tp->vpos == vp ) {
- tp->tiedvpos = h->tp->vpos;
- h->tp = tp;
- return tp;
- }
- }
- }
- return h->tp;
-}
-
-static int abc_add_noteon(ABCHANDLE *h, int ch, const char *p, ULONG tracktime, char *barkey, int vol, ABCEVENT_X_EFFECT fx, int fxop)
-{
- ABCEVENT *e;
- ABCTRACK *tp;
- int i,j,k;
- int n,oct;
- char d[6];
- tp = h->tp;
- switch(ch) {
- case '^':
- if( p[0] == '^' ) {
- n = p[1];
- i = 2;
- ch = 'x';
- }
- else {
- n = p[0];
- i = 1;
- }
- break;
- case '_':
- if( p[0] == '_' ) {
- n = p[1];
- i = 2;
- ch = 'b';
- }
- else {
- n = p[0];
- i = 1;
- }
- break;
- case '=':
- n = p[0];
- i = 1;
- break;
- default:
- n = ch;
- i = 0;
- break;
- }
- for( k=0; k<51; k++ ) {
- if( n == barkey[k] )
- break;
- }
- j = k;
- if( k > 24 )
- k -= 25; // had something like A# over Bb key F signature....
- if( i ) {
- // propagate accidentals if necessary
- // DON'T do redundant accidentals they're always relative to C-scale
- for( k=0; k<25; k++ ) {
- if( n == sig[7][k] )
- break;
- }
- if( k < 25 ) { // only do real notes...
- switch(ch) {
- case 'x':
- k++;
- case '^':
- k++;
- break;
- case 'b':
- k--;
- case '_':
- k--;
- break;
- case '=':
- break;
- }
- if( j < 25 ) // was it not A# over Bb?
- barkey[j] = ' ';
- barkey[k] = n;
- }
- }
- oct = 3; // ABC note pitch C is C4 and pitch c is C5
- if( k < 25 ) {
- k += tp->transpose;
- while( k > 12 ) {
- oct++;
- k -= 12;
- }
- while( k < 0 ) {
- oct--;
- k += 12;
- }
- d[note] = 23 + k; // C0 is midi notenumber 24
- }
- else
- d[note] = 0; // someone has doen ^X3 or something like it...
- while( p[i] && strchr(",'",p[i]) ) {
- if( p[i]==',' ) oct--;
- else oct++;
- i++;
- tp->octave_shift = 0; // forget we ever had to look at it
- }
- if( tp->octave_shift )
- tp->transpose += 12 * tp->octave_shift;
- oct += tp->octave_shift;
- tp->octave_shift = 0; // after the first note we never have to look at it again
- if( oct < 0 ) oct = 0;
- if( oct > 9 ) oct = 9;
- d[octave] = oct;
- d[smpno] = pat_gmtosmp(tp->instr);
- d[volume] = abc_dynamic_volume(tp, tracktime, vol);
- d[effect] = fx; // effect
- d[effoper] = fxop;
- tp = abc_track_with_note_tied(h, tracktime, d[note], oct);
- if( tp->tienote ) {
- if( tp->tienote->par[note] != d[note] ) {
- if( abs(tp->tienote->par[note] - d[note]) < 3 ) {
- // may be tied over bar symbol, recover local accidental to barkey
- k = tp->tienote->par[note] - 23 - tp->transpose;
- while( k < 0 ) k += 12;
- while( k > 12 ) k -= 12;
- if( (isupper(n) && barkey[k+12] == ' ') || (islower(n) && barkey[k] == ' ') ) {
- barkey[j] = ' ';
- if( isupper(n) )
- barkey[k] = n;
- else
- barkey[k+12] = n;
- d[note] = tp->tienote->par[note];
- d[octave] = tp->tienote->par[octave];
- }
- }
- }
- }
- if( tp->tienote
- && tp->tienote->par[note] == d[note]
- && tp->tienote->par[octave] == d[octave] ) {
- for( e = tp->tienote; e; e = e->next ) {
- if( e->par[note] == 0 && e->par[octave] == 0 ) { // undo noteoff
- e->flg = 1;
- e->cmd = cmdhide;
- e->lpar = 0;
- break;
- }
- }
- tp->tienote->tiednote = 1; // mark him for the pattern writers
- for( j=i; isdigit(p[j]) || p[j]=='/'; j++ ) ; // look ahead to see if this one is tied too
- if( p[j] != '-' ) // is this note tied too?
- tp->tienote = NULL; // if not the tie ends here...
- return i;
- }
- tp->tienote = NULL;
- if( tp->tail
- && tp->tail->tracktick == tracktime
- && tp->tail->par[note] == 0
- && tp->tail->par[octave] == 0 ) {
- for( j=0; j<6; j++ )
- tp->tail->par[j] = d[j];
- }
- else {
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
- }
- if( i > 0 && p[i-1] == '"' ) {
- i--; // someone coded a weird note like ^"E"
- abc_message("strange note encountered scanning %s", h->line);
- }
- return i;
-}
-
-static void abc_add_dronenote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
-{
- ABCEVENT *e;
- int j,k;
- int oct;
- char d[6];
- oct = -1; // ABC note pitch C is C4 and pitch c is C5
- k = nnum + 1;
- while( k > 12 ) {
- oct++;
- k -= 12;
- }
- while( k < 0 ) {
- oct--;
- k += 12;
- }
- if( oct < 0 ) oct = 0;
- d[note] = 23 + k; // C0 is midi notenumber 24
- d[octave] = oct;
- d[smpno] = pat_gmtosmp(tp->instr);
- d[volume] = abc_dynamic_volume(tp, tracktime, vol);
- d[effect] = 0; // effect
- d[effoper] = 0;
- if( tp->tail
- && tp->tail->tracktick == tracktime
- && tp->tail->par[note] == 0
- && tp->tail->par[octave] == 0 ) {
- for( j=0; j<6; j++ )
- tp->tail->par[j] = d[j];
- }
- else {
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
- }
-}
-
-static void abc_add_chordnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
-{
- abc_add_dronenote(h, tp, tracktime, nnum + 23, tp->mute? 0: vol);
-}
-
-static void abc_add_drumnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
-{
- abc_add_dronenote(h, tp, tracktime, nnum, tp->mute? 0: vol);
-}
-
-static void abc_add_variant_start(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int n)
-{
- ABCEVENT *e;
- char d[6];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdvariant;
- e = abc_new_event(h, tracktime, d);
- e->lpar = 1<tail->lpar |= 1<flg != 1 && e->par[note] != 0 )
- e->par[volume] = abc_dynamic_volume(tp, e->tracktick, e->par[volume]);
- e = e->next;
- }
-}
-
-static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j)
-{
- ABCEVENT *e;
- char d[8];
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdjump;
- d[jumptype] = j;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_sync(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
-{
- ABCEVENT *e;
- char d[6];
- e = tp->tail;
- if( e && e->tracktick == tracktime ) return;
- if( e && e->flg == 1 && e->cmd == cmdsync ) {
- e->tracktick = tracktime;
- return;
- }
- d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0;
- d[cmdflag] = 1;
- d[command] = cmdsync;
- e = abc_new_event(h, tracktime, d);
- abc_add_event(h, tp, e);
-}
-
-static void abc_add_gchord_syncs(ABCHANDLE *h, ABCTRACK *tpc, ULONG tracktime)
-{
- ABCTRACK *tp;
- int i;
- for( i = GCHORDBPOS; i < DRUMPOS; i++ ) {
- tp = abc_locate_track(h, tpc->v, i);
- abc_add_sync(h,tp,tracktime);
- }
-}
-
-static void abc_add_drum_sync(ABCHANDLE *h, ABCTRACK *tpr, ULONG tracktime)
-{
- ABCTRACK *tp;
- tp = abc_locate_track(h, tpr->v, DRUMPOS);
- abc_add_sync(h,tp,tracktime);
-}
-
-static int abc_getnumber(const char *p, int *number)
-{
- int i,h;
- i = 0;
- h = 0;
- while( isdigit(p[i]) ) {
- h = 10 * h + p[i] - '0';
- i++;
- }
- if( i==0 )
- *number = 1;
- else
- *number = h;
- return i;
-}
-
-static int abc_getexpr(const char *p, int *number)
-{
- int i, term, total;
- i = 0;
- while( isspace(p[i]) )
- i++;
- if( p[i] == '(' ) {
- i += abc_getexpr(p+i+1, number);
- while( p[i] && (p[i] != ')') )
- i++;
- return i;
- }
- i += abc_getnumber(p+i, &total);
- while( isspace(p[i]) )
- i++;
- while( p[i] == '+' ) {
- i += abc_getexpr(p+i+1, &term);
- total += term;
- while( isspace(p[i]) )
- i++;
- }
- *number = total;
- return i;
-}
-
-static int abc_notelen(const char *p, int *len, int *div)
-{
- int i,h,k;
- i = abc_getnumber(p,len);
- h = 1;
- while( p[i] == '/' ) {
- h *= 2;
- i++;
- }
- if( isdigit(p[i]) ) {
- h /= 2;
- i += abc_getnumber(p+i,&k);
- }
- else k = 1;
- *div = h * k;
- return i;
-}
-
-static int abc_brokenrithm(const char *p, int *nl, int *nd, int *b, int hornpipe)
-{
- switch( *b ) {
- case '<':
- *nl *= 3;
- *nd *= 2;
- hornpipe = 0;
- break;
- case '>':
- *nd *= 2;
- hornpipe = 0;
- break;
- }
- *b = *p;
- switch( *b ) {
- case '>':
- *nl *= 3;
- *nd *= 2;
- return 1;
- case '<':
- *nd *= 2;
- return 1;
- default:
- *b = 0;
- break;
- }
- if( hornpipe ) { // still true then make 1/8 notes broken rithme
- if( *nl == 1 && *nd == 1 ) {
- *b = '>';
- *nl = 3;
- *nd = 2;
- }
- }
- return 0;
-}
-
-// put p notes in the time q for the next r notes
-static int abc_tuplet(int *nl, int *nd, int p, int q, int r)
-{
- if( !r ) return 0;
- *nl *= q;
- *nd *= p;
- return r - 1;
-}
-
-// evaluate [Q:"string" n1/m1 n2/m2 n3/m3 n4/m4=bpm "string"]
-// minimal form [Q:"string"]
-// most used form [Q: 1/4=120]
-static int abc_extract_tempo(const char *p, int invoice)
-{
- int nl, nd, ns, in, tempo;
- int nl1=0, nd1, notes, state;
- const char *q;
- in = 0;
- nl = 0;
- nd = 1;
- ns = 120;
- notes = 0;
- state = 0;
- for( q=p; *q; q++ ) {
- if( in ) {
- if( *q=='"' )
- in = 0;
- }
- else {
- if( *q == ']' ) break;
- switch( *q ) {
- case '"':
- in = 1;
- break;
- case '/':
- notes++;
- state = 1;
- nl1 = ns;
- break;
- case '=':
- break;
- default:
- if( isdigit(*q) ) {
- if( state ) {
- q+=abc_getnumber(q,&nd1)-1;
- state = 0;
- nl = nl * nd1 + nl1 * nd;
- nd = nd * nd1;
- }
- else
- q+=abc_getnumber(q,&ns)-1;
- }
- break;
- }
- }
- }
- if( !notes ) {
- nl = 1;
- nd = 4;
- }
- if( !nd ) tempo = 120;
- else tempo = ns * nl * 4 / nd; // mod tempo is really BPM where one B is equal to a quartnote
- if( invoice ) {
- nl = global_tempo_factor;
- nd = global_tempo_divider;
- }
- global_tempo_factor = 1;
- global_tempo_divider = 1;
- while( tempo/global_tempo_divider > 255 )
- global_tempo_divider++;
- tempo /= global_tempo_divider;
- while( tempo * global_tempo_factor < 256 )
- global_tempo_factor++;
- global_tempo_factor--;
- tempo *= global_tempo_factor;
- if( tempo * 3 < 512 ) {
- global_tempo_factor *= 3;
- global_tempo_divider *= 2;
- tempo = (tempo * 3) / 2;
- }
- if( invoice ) {
- if( nl != global_tempo_factor || nd != global_tempo_divider ) {
- ns = (tempo * nl * global_tempo_divider) / (nd * global_tempo_factor);
- if( ns > 31 && ns < 256 ) {
- tempo = ns;
- global_tempo_factor = nl;
- global_tempo_divider = nd;
- }
- else
- abc_message("Failure: inconvenient tempo change in middle of voice (%s)", p);
- }
- }
- return tempo;
-}
-
-static void abc_set_parts(char **d, char *p)
-{
- int i,j,k,m,n;
- char *q;
-#ifdef NEWMIKMOD
- static MM_ALLOC *h;
- if( *d ) _mmalloc_close(h);
-#else
- if( *d ) free(*d);
-#endif
- *d = 0;
- if( !p ) return;
- for( i=0; p[i] && p[i] != '%'; i++ ) {
- if( !strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ().0123456789 ",p[i]) ) {
- abc_message("invalid characters in part string scanning P:%s", p);
- return;
- }
- }
-#ifdef NEWMIKMOD
- h = _mmalloc_create("Load_ABC_parts", NULL);
-#endif
- // decode constructs like "((AB)2.(CD)2)3.(AB)E2" to "ABABCDCDABABCDCDABABCDCDABEE"
- // first compute needed storage...
- j=0;
- k=0;
- for( i=0; p[i] && p[i] != '%'; i++ ) {
- if( isupper(p[i]) ) {
- j++;
- }
- if( isdigit(p[i]) ) {
- n=abc_getnumber(p+i,&k);
- if( p[i-1] == ')' )
- j *= k; // never mind multiple parens, just take the worst case
- else
- j += k-1;
- i += n-1;
- }
- }
- q = (char *)_mm_calloc(h, j+1, sizeof(char)); // enough storage for the worst case
- // now copy bytes from p to *d, taking parens and digits in account
- j = 0;
- for( i=0; p[i] && p[i] != '%'; i++ ) {
- if( isdigit(p[i]) || isupper(p[i]) || p[i] == '(' || p[i] == ')' ) {
- if( p[i] == ')' ) {
- for( n=j; n > 0 && q[n-1] != '('; n-- ) ; // find open paren in q
- // q[n+1] to q[j] contains the substring that must be repeated
- if( n > 0 ) {
- for( k = n; k 1 ) {
- for( m=0; m 1 ) {
- q[j] = q[j-1];
- j++;
- }
- continue;
- }
- q[j] = p[i];
- j++;
- }
- }
- q[j] = '\0';
- // remove any left over parens
- for( i=0; itail->tracktick - pt1;
- for( e=tp->head; e && e->tracktick <= pt2; e=e->next ) {
- if( e->tracktick >= pt1 ) {
- if( e->flg != 1 || e->cmd == cmdsync || e->cmd == cmdchord ) {
- if( e != tp->tail ) {
- // copy this event at tail
- ec = abc_copy_event(h,e);
- ec->tracktick += dt;
- ec->part = '*';
- tp->tail->next = ec;
- tp->tail = ec;
- }
- }
- }
- }
- abc_add_sync(h, tp, pt2 + dt); // make sure there is progression...
-}
-
-static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime)
-{
- ABCEVENT *e;
- ULONG dt,et,pt=abcticks(64 * h->speed);
- if(!h || !h->track || !h->track->head ) return 0;
- dt = 0;
- for( e=h->track->head; e && e->tracktick <= tracktime; e=e->next ) {
- if( e->flg == 1 && e->cmd == cmdpartbrk ) {
- et = e->tracktick + dt;
- if( et % pt ) {
- et += pt;
- et /= pt;
- et *= pt;
- dt = et - e->tracktick;
- }
- }
- }
- return (tracktime + dt);
-}
-
-static int abc_patno(ABCHANDLE *h, ULONG tracktime)
-{
- return modticks(abc_pattracktime(h, tracktime)) / 64 / h->speed;
-}
-
-static void abc_stripoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tt)
-{
- ABCEVENT *e1, *e2;
- e2 = NULL;
- for( e1 = tp->head; e1 && e1->tracktick <= tt; e1=e1->next )
- e2 = e1;
- if( e2 ) {
- e1 = e2->next;
- tp->tail = e2;
- e2->next = NULL;
- }
- else {
- e1 = tp->tail;
- tp->head = NULL;
- tp->tail = NULL;
- }
- while( e1 ) {
- e2 = e1->next;
- _mm_free(h->trackhandle,e1);
- e1 = e2;
- }
-}
-
-static void abc_keeptiednotes(ABCHANDLE *h, ULONG fromtime, ULONG totime) {
- ABCTRACK *tp;
- ABCEVENT *e,*n,*f;
- if( totime <= fromtime ) return;
- for( tp=h->track; tp; tp=tp->next ) {
- if( tp->vno ) { // if track is in use...
- n = NULL;
- for( e=tp->head; e && e->tracktick < fromtime; e = e->next )
- if( e->flg != 1 ) n = e; // remember it when it is a note event
- if( n && n->tiednote ) { // we've a candidate to tie over the break
- while( e && e->tracktick < totime ) e=e->next; // skip to other part
- if( e && e->tracktick == totime ) { // if this is on begin row of this part
- f = NULL;
- while( !f && e && e->tracktick == totime ) {
- if( e->flg != 1 ) f = e;
- e = e->next;
- }
- if( f && f->par[note] ) { // pfoeie, we've found a candidate
- if( abs(n->par[note] - f->par[note]) < 3 ) { // undo the note on
- f->flg = 1;
- f->cmd = cmdhide;
- f->lpar = 0;
- }
- }
- }
- }
- }
- }
-}
-
-static ULONG abc_fade_tracks(ABCHANDLE *h, char *abcparts, ULONG ptt[27])
-{
- ABCTRACK *tp;
- ABCEVENT *e0;
- char *p;
- int vol;
- ULONG pt1,pt2;
- ULONG tt;
- tt = h->track->tail->tracktick;
- for( tp=h->track->next; tp; tp=tp->next ) {
- if( !tp->tail ) abc_add_sync(h, tp, tt); // no empty tracks please...
- if( tp->tail->tracktick > tt ) abc_stripoff(h, tp, tt); // should not happen....
- if( tp->tail->tracktick < tt ) abc_add_sync(h, tp, tt);
- }
- for( tp=h->track; tp; tp=tp->next ) {
- vol = 127;
- e0 = tp->tail;
- if( tp->slidevol != -2 ) {
- tp->slidevol = -2;
- tp->slidevoltime = e0->tracktick;
- }
- tp->mute = 0; // unmute track for safety, notes in a muted track already have zero volume...
- while( vol > 5 ) {
- for( p=abcparts; *p && vol > 5; p++ ) {
- pt1 = ptt[*p-'A'];
- pt2 = ptt[*p-'A'+1];
- abc_appendpart(h, tp, pt1, pt2);
- vol = abc_dynamic_volume(tp, tp->tail->tracktick, 127);
- }
- }
- abc_fade_track(tp,e0);
- }
- return h->track->tail->tracktick;
-}
-
-static void abc_song_to_parts(ABCHANDLE *h, char **abcparts, BYTE partp[27][2])
-{
- ULONG starttick;
- ABCEVENT *e;
- int i, fading, loop, normal, partno, partsegno, partloop, partcoda, parttocoda, partfine, skip, x, y;
- int vmask[27],nextp[27];
- ULONG ptt[27];
- char buf[256]; // must be enough, mod's cannot handle more than 240 patterns
- char *pfade;
- if( !h || !h->track || !h->track->capostart ) return;
- strcpy(buf,"A"); // initialize our temporary array
- i = 1;
- loop = 1;
- partno = 0;
- partsegno = 0;
- partloop = 0;
- partcoda = -1;
- parttocoda = -1;
- partfine = -1;
- starttick = h->track->capostart->tracktick;
- ptt[0] = starttick;
- vmask[0] = -1;
- nextp[0] = 1;
- for( e=h->track->capostart; e; e=e->next ) {
- if( e->flg == 1 ) {
- switch( e->cmd ) {
- case cmdpartbrk:
- if( e->tracktick > starttick) {
- starttick = e->tracktick; // do not make empty parts
- if( partno < 26 ) {
- partno++;
- ptt[partno] = starttick;
- }
- if( i < 255 ) buf[i++] = partno+'A';
- vmask[partno] = -1;
- nextp[partno] = partno+1;
- }
- break;
- case cmdloop:
- partloop = partno;
- loop = 1; // start counting anew...
- break;
- case cmdvariant:
- vmask[partno] = e->lpar;
- break;
- case cmdjump:
- x = 0;
- fading = 0;
- normal = 0;
- skip = 0;
- pfade = &buf[i];
- switch( e->par[jumptype] ) {
- case jumpfade:
- fading = 1;
- case jumpnormal:
- normal = 1;
- x = partloop;
- loop++;
- break;
- case jumpdsfade:
- fading = 1;
- case jumpdasegno:
- x = partsegno;
- break;
- case jumpdcfade:
- fading = 1;
- case jumpdacapo:
- x = 0;
- break;
- default:
- x = 0;
- break;
- }
- if( vmask[partno] != -1 ) nextp[partno] = x;
- if( partno < 26 ) ptt[partno+1] = e->tracktick; // for handling ties over breaks
- while( x <= partno ) {
- if( skip == 1 && x == partcoda ) skip = 0;
- y = !skip;
- if( y ) {
- if( !normal ) {
- if( x == partfine ) skip = 2;
- if( x == parttocoda ) skip = 1;
- y = !skip;
- }
- if( !(vmask[x] & (1<tracktick;
- buf[i] = '\0'; // close up pfade with zero byte
- starttick = abc_fade_tracks(h, pfade, ptt);
- buf[i++] = partno+'A';
- partno++;
- ptt[partno] = starttick;
- buf[i++] = partno+'A'; // one extra to throw away...
- e = h->track->tail; // this is the edge of the world captain...
- }
- break;
- case cmdtocoda:
- parttocoda = partno;
- break;
- case cmdcoda:
- partcoda = partno;
- break;
- case cmdfine:
- partfine = partno;
- break;
- case cmdsegno:
- partsegno = partno;
- break;
- }
- }
- e->part = partno+'a'; // small caps for generated parts...
- }
- i--; // strip off last partno
- if( partno > 0 ) partno--;
- buf[i] = '\0';
- if( i > 1 ) {
- for( i=1; buf[i]; i++ ) {
- if( buf[i] != buf[i-1] + 1 ) {
- x = buf[i-1] - 'A';
- y = buf[i] - 'A';
- abc_keeptiednotes(h, ptt[x+1], ptt[y]);
- }
- }
- }
- starttick = h->track->tail->tracktick;
- ptt[partno+1] = starttick;
- for( i=0; i<=partno; i++ ) {
- partp[i][0] = abc_patno(h, ptt[i]);
- partp[i][1] = abc_patno(h, ptt[i+1]);
- }
- // calculate end point of last part
- starttick = abc_pattracktime(h, starttick);
- if( starttick % abcticks(64 * h->speed) )
- partp[partno][1]++;
- abc_set_parts(abcparts, buf);
-}
-
-// =====================================================================================
-static char *abc_fgets(MMFILE *mmfile, char buf[], unsigned int bufsz)
-// =====================================================================================
-{
- if( mmfeof(mmfile) ) return NULL;
- mmfgets(buf,bufsz,mmfile);
- return buf;
-}
-
-// =====================================================================================
-static char *abc_fgetbytes(MMFILE *mmfile, char buf[], unsigned int bufsz)
-// =====================================================================================
-{
- unsigned int i;
- long pos;
- if( mmfeof(mmfile) ) return NULL;
- for( i=0; iline, target)) ) {
- if( (i=strlen(h->line)) + n - l >= (int)h->len ) {
- h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char));
- h->len <<= 1;
- p=strstr(h->line, target);
- }
- if( n > l ) {
- for( q=&h->line[i]; q>p; q-- ) q[n-l] = q[0];
- for( q=s; *q; q++ ) *p++ = *q;
- }
- else {
- strcpy(p,s);
- strcat(p,p+l);
- }
- }
-}
-
-static void abc_preprocess(ABCHANDLE *h, ABCMACRO *m)
-{
- int i, j, k, l, a, b;
- char t[32];
- char s[200],*p;
- if( m->n ) {
- k = m->n - m->name;
- for( i=0; i<14; i++ ) {
- strncpy(t, m->name, 32);
- t[k] = "CDEFGABcdefgab"[i];
- l = strlen(m->subst);
- p = s;
- for( j=0; jsubst[j];
- if( a > 'g' && islower(a) ) {
- b = a - 'n';
- a = "CDEFGABCDEFGABcdefgabcdefgab"[i+b+7];
- *p++ = a;
- if( i+b < 0 )
- *p++ = ',';
- if( i+b > 13 )
- *p++ = '\'';
- }
- else *p++ = a;
- }
- *p = '\0';
- abc_substitute(h, t, s);
- }
- }
- else
- abc_substitute(h, m->name, m->subst);
-}
-
-static char *abc_gets(ABCHANDLE *h, MMFILE *mmfile)
-{
- int i;
- ABCMACRO *mp;
- if( !h->len ) {
- h->len = 64; // initial line size, adequate for most abc's
- h->line = (char *)_mm_calloc(h->allochandle, h->len, sizeof(char));
- }
- if( abc_fgetbytes(mmfile, h->line, h->len) ) {
- while( (i=strlen(h->line)) > (int)(h->len - 3) ) {
- // line too short, double it
- h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char));
- if( h->line[i-1] != '\n' )
- abc_fgetbytes(mmfile, &h->line[i], h->len);
- h->len <<= 1;
- }
- h->line[i-1] = '\0'; // strip off newline
- for( mp=h->macro; mp; mp=mp->next )
- abc_preprocess(h,mp);
- return h->line;
- }
- return NULL;
-}
-
-static int abc_parse_decorations(ABCHANDLE *h, ABCTRACK *tp, const char *p)
-{
- int vol=0;
- if( !strncmp(p,"mp",2) ) vol = 75;
- if( !strncmp(p,"mf",2) ) vol = 90;
- if( !strncmp(p,"sfz",3) ) vol = 100;
- if( *p == 'p' ) {
- vol = 60;
- while( *p++ == 'p' ) vol -= 15;
- if( vol < 1 ) vol = 1;
- }
- if( *p == 'f' ) {
- vol = 105;
- while( *p++ == 'f' ) vol += 15;
- if( vol > 135 ) vol = 127; // ffff
- if( vol > 127 ) vol = 125; // fff
- }
- if( vol ) {
- tp->volume = vol;
- if( tp == h->track ) { // copy volume over to all voice tracks
- for( ; tp; tp=tp->next ) {
- if( tp->vpos == 0 || tp->vpos > DRONEPOS2 ) tp->volume = vol;
- }
- tp = h->track;
- }
- }
- return tp->volume;
-}
-
-// =====================================================================================
-#ifdef NEWMIKMOD
-BOOL ABC_Test(MMSTREAM *mmfile)
-#else
-BOOL CSoundFile::TestABC(const BYTE *lpStream, DWORD dwMemLength)
-#endif
-// =====================================================================================
-{
- char id[128];
- // scan file for first K: line (last in header)
-#ifdef NEWMIKMOD
- _mm_fseek(mmfile,0,SEEK_SET);
- while(abc_fgets(mmfile,id,128)) {
-#else
- MMFILE mmfile;
- mmfile.mm = (char *)lpStream;
- mmfile.sz = dwMemLength;
- mmfseek(&mmfile,0,SEEK_SET);
- while(abc_fgets(&mmfile,id,128)) {
-#endif
- if(id[0]=='K'
- && id[1]==':'
- && (isalpha(id[2]) || isspace(id[2])) ) return 1;
- }
- return 0;
-}
-
-// =====================================================================================
-static ABCHANDLE *ABC_Init(void)
-{
- ABCHANDLE *retval;
- char *p;
- char buf[10];
-#ifdef NEWMIKMOD
- MM_ALLOC *allochandle;
-
- allochandle = _mmalloc_create("Load_ABC", NULL);
- retval = (ABCHANDLE *)_mm_calloc(allochandle, 1,sizeof(ABCHANDLE));
- if( !retval ) return NULL;
- retval->allochandle = allochandle;
- allochandle = _mmalloc_create("Load_ABC_macros", NULL);
- retval->macrohandle = allochandle;
- allochandle = _mmalloc_create("Load_ABC_tracks", NULL);
- retval->trackhandle = allochandle;
-#else
- retval = (ABCHANDLE *)calloc(1,sizeof(ABCHANDLE));
- if( !retval ) return NULL;
-#endif
- retval->track = NULL;
- retval->macro = NULL;
- retval->umacro = NULL;
- retval->beatstring = NULL;
- retval->pickrandom = 0;
- retval->len = 0;
- retval->line = NULL;
- strcpy(retval->gchord, "");
- retval->barticks = 0;
- p = getenv(ABC_ENV_NORANDOMPICK);
- if( p ) {
- if( isdigit(*p) )
- retval->pickrandom = atoi(p);
- if( *p == '-' ) {
-#ifdef NEWMIKMOD
- retval->pickrandom = atoi(p+1);
- sprintf(buf,"-%ld",retval->pickrandom+1);
-#else
- retval->pickrandom = atoi(p+1)-1; // xmms preloads the file
- sprintf(buf,"-%ld",retval->pickrandom+2);
-#endif
- setenv(ABC_ENV_NORANDOMPICK, buf, 1);
- }
- }
- else {
- srandom(time(0)); // initialize random generator with seed
- retval->pickrandom = 1+(int)(10000.0*random()/(RAND_MAX+1.0));
- // can handle pickin' from songbooks with 10.000 songs
-#ifdef NEWMIKMOD
- sprintf(buf,"-%ld",retval->pickrandom+1); // next in sequence
-#else
- sprintf(buf,"-%ld",retval->pickrandom); // xmms preloads the file
-#endif
- setenv(ABC_ENV_NORANDOMPICK, buf, 1);
- }
- return retval;
-}
-
-#ifndef NEWMIKMOD
-static void ABC_CleanupTrack(ABCTRACK *tp)
-{
- ABCEVENT *ep, *en;
- if( tp ) {
- for( ep=tp->head; ep; ep = en ) {
- en=ep->next;
- free(ep);
- }
- tp->head = NULL;
- }
-}
-
-static void ABC_CleanupMacro(ABCMACRO *m)
-{
- if( m->name )
- free(m->name);
- if( m->subst )
- free(m->subst);
- free(m);
-}
-#endif
-
-// =====================================================================================
-static void ABC_CleanupTracks(ABCHANDLE *handle)
-// =====================================================================================
-{
-#ifdef NEWMIKMOD
- if(handle && handle->trackhandle) {
- _mmalloc_close(handle->trackhandle);
- handle->trackhandle = 0;
- }
-#else
- ABCTRACK *tp, *tn;
- if(handle) {
- for( tp=handle->track; tp; tp = tn ) {
- tn=tp->next;
- ABC_CleanupTrack(tp);
- }
- handle->track = NULL;
- }
-#endif
-}
-
-// =====================================================================================
-static void ABC_CleanupMacros(ABCHANDLE *handle)
-// =====================================================================================
-{
-#ifdef NEWMIKMOD
- if(handle && handle->macrohandle) {
- _mmalloc_close(handle->macrohandle);
- handle->macrohandle = 0;
- }
-#else
- ABCMACRO *mp, *mn;
- if(handle) {
- for( mp=handle->macro; mp; mp = mn ) {
- mn=mp->next;
- ABC_CleanupMacro(mp);
- }
- for( mp=handle->umacro; mp; mp = mn ) {
- mn=mp->next;
- ABC_CleanupMacro(mp);
- }
- handle->macro = NULL;
- handle->umacro = NULL;
- }
-#endif
-}
-
-// =====================================================================================
-static void ABC_Cleanup(ABCHANDLE *handle)
-// =====================================================================================
-{
-#ifdef NEWMIKMOD
- if(handle && handle->allochandle) {
-#else
- if(handle) {
-#endif
- ABC_CleanupMacros(handle);
- ABC_CleanupTracks(handle);
-#ifdef NEWMIKMOD
- _mmalloc_close(handle->allochandle);
- handle->allochandle = 0;
- handle->len = 0;
-#else
- if( handle->line )
- free(handle->line);
- if( handle->beatstring )
- free(handle->beatstring);
- free(handle);
-#endif
- }
-}
-
-static int abc_is_global_event(ABCEVENT *e)
-{
- return e->flg == 1 && (e->cmd == cmdtempo || e->cmd == cmdpartbrk);
-}
-
-static ABCEVENT *abc_next_global(ABCEVENT *e)
-{
- for( ; e && !abc_is_global_event(e); e=e->next ) ;
- return e;
-}
-
-static ABCEVENT *abc_next_note(ABCEVENT *e)
-{
- for( ; e && e->flg == 1; e=e->next ) ;
- return e;
-}
-
-// =============================================================================
-#ifdef NEWMIKMOD
-static void ABC_ReadPatterns(UNIMOD *of, ABCHANDLE *h, int numpat)
-// =============================================================================
-{
- int pat,row,i,ch,trillbits;
- BYTE n,ins,vol;
- ABCTRACK *t;
- ABCEVENT *e, *en, *ef, *el;
- ULONG tt1, tt2;
- UNITRK_EFFECT eff;
-
- // initialize start points of event list in tracks
- for( t = h->track; t; t = t->next ) t->capostart = t->head;
- trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels
- for( pat = 0; pat < numpat; pat++ ) {
- utrk_reset(of->ut);
- for( row = 0; row < 64; row++ ) {
- tt1 = abcticks((pat * 64 + row ) * h->speed);
- tt2 = tt1 + abcticks(h->speed);
- ch = 0;
- for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) {
- if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row
- switch( e->cmd ) {
- case cmdtempo:
- eff.effect = UNI_GLOB_TEMPO;
- eff.param.u = e->lpar;
- eff.framedly = UFD_RUNONCE;
- utrk_write_global(of->ut, &eff, PTMEM_TEMPO);
- break;
- case cmdpartbrk:
- eff.effect = UNI_GLOB_PATBREAK;
- eff.param.u = 0;
- eff.framedly = UFD_RUNONCE;
- utrk_write_global(of->ut, &eff, UNIMEM_NONE);
- break;
- }
- }
- }
- for( t = h->track; t; t = t->next ) {
- for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) )
- t->capostart = e;
- i = 0;
- ef = NULL;
- en = e;
- el = e;
- for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row
- t->capostart = e;
- i++;
- if( e->par[volume] ) {
- if( !ef ) ef = e;
- el = e;
- }
- }
- if( i ) {
- trillbits &= ~(1<ut, ch);
- if( i == 1 || ef == el || !ef ) { // only one event in this row
- if( ef ) e = ef;
- else e = en;
- el = t->capostart;
- i = e->par[note] + ((e->par[octave])*12);
- if( t->chan == 10 ) {
- n = pat_gm_drumnote(i) + 23;
- ins = pat_gmtosmp(pat_gm_drumnr(i));
- }
- else {
- n = pat_modnote(i);
- ins = e->par[smpno];
- }
- eff.framedly = modticks(e->tracktick - tt1);
- vol = e->par[volume];
- if( e->par[effect] == accent ) {
- vol += vol / 10;
- if( vol > 127 ) vol = 127;
- }
- if (vol <= 0) {}
- else if( el->par[volume] == 0 ) {
- eff.framedly = modticks(el->tracktick - tt1);
- eff.param.u = 0;
- eff.param.byte_a = n;
- eff.param.byte_b = ins;
- eff.effect = UNI_NOTEKILL;
- utrk_write_local(of->ut, &eff, UNIMEM_NONE);
- }
- else {
- switch( e->par[effect] ) {
- case trill:
- eff.effect = UNI_VIBRATO_DEPTH;
- eff.param.u = 12; // depth 1.5
- utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH);
- eff.effect = UNI_VIBRATO_SPEED;
- eff.param.u = 48; // speed 12
- utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED);
- trillbits |= (1<speed/2)|UFD_RUNONCE;
- eff.param.s = 2;
- utrk_write_local(of->ut, &eff, (e->par[effoper])? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN);
- break;
- default:
- break;
- }
- if( eff.framedly ) {
- eff.param.u = 0;
- eff.param.byte_a = n;
- eff.param.byte_b = ins;
- eff.effect = UNI_NOTEDELAY;
- utrk_write_local(of->ut, &eff, UNIMEM_NONE);
- }
- }
- utrk_write_inst(of->ut, ins);
- utrk_write_note(of->ut, n); // <- normal note
- pt_write_effect(of->ut, 0xc, vol);
- }
- else {
- // two notes in one row, use FINEPITCHSLIDE runonce effect
- // start first note on first tick and framedly runonce on seconds note tick
- // use volume and instrument of last note
- if( t->chan == 10 ) {
- i = el->par[note] + ((el->par[octave])*12);
- n = pat_gm_drumnote(i) + 23;
- ins = pat_gmtosmp(pat_gm_drumnr(i));
- i = n; // cannot change instrument here..
- }
- else {
- i = ef->par[note] + ((ef->par[octave])*12);
- n = pat_modnote(i);
- ins = el->par[smpno];
- i = pat_modnote(el->par[note] + ((el->par[octave])*12));
- }
- vol = el->par[volume];
- eff.effect = UNI_PITCHSLIDE;
- eff.framedly = modticks(el->tracktick - tt1)|UFD_RUNONCE;
- eff.param.s = ((i > n)?i-n:n-i);
- utrk_write_inst(of->ut, ins);
- utrk_write_note(of->ut, n); // <- normal note
- pt_write_effect(of->ut, 0xc, vol);
- utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN);
- }
- }
- else { // no new notes, keep on trilling...
- if( trillbits & (1<ut, ch);
- eff.effect = UNI_VIBRATO_DEPTH;
- eff.param.u = 12; // depth 1.5
- utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH);
- eff.effect = UNI_VIBRATO_SPEED;
- eff.param.u = 60; // speed 15
- utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED);
- }
- }
- ch++;
- }
- utrk_newline(of->ut);
- }
- if(!utrk_dup_pattern(of->ut,of)) return;
- }
-}
-
-#else
-
-static int ABC_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], ABCHANDLE *h, int numpat, int channels)
-// =====================================================================================
-{
- int pat,row,i,ch,trillbits;
- BYTE n,ins,vol;
- ABCTRACK *t;
- ABCEVENT *e, *en, *ef, *el;
- ULONG tt1, tt2;
- MODCOMMAND *m;
- int patbrk, tempo;
- if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS;
-
- // initialize start points of event list in tracks
- for( t = h->track; t; t = t->next ) t->capostart = t->head;
- trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels
- for( pat = 0; pat < numpat; pat++ ) {
- pattern[pat] = CSoundFile::AllocatePattern(64, channels);
- if( !pattern[pat] ) return 0;
- psize[pat] = 64;
- for( row = 0; row < 64; row++ ) {
- tt1 = abcticks((pat * 64 + row ) * h->speed);
- tt2 = tt1 + abcticks(h->speed);
- ch = 0;
- tempo = 0;
- patbrk = 0;
- for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) {
- if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row
- switch( e->cmd ) {
- case cmdtempo:
- tempo = e->lpar;
- break;
- case cmdpartbrk:
- patbrk = 1;
- break;
- }
- }
- }
- for( t = h->track; t; t = t->next ) {
- for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) ;
- i = 0;
- ef = NULL;
- en = e;
- el = e;
- for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row
- t->capostart = e;
- i++;
- if( e->par[volume] ) {
- if( !ef ) ef = e;
- el = e;
- }
- }
- m = &pattern[pat][row * channels + ch];
- m->param = 0;
- m->command = CMD_NONE;
- if( i ) {
- trillbits &= ~(1<capostart;
- i = e->par[note] + ((e->par[octave])*12);
- if( t->chan == 10 ) {
- n = pat_gm_drumnote(i) + 23;
- ins = pat_gmtosmp(pat_gm_drumnr(i));
- }
- else {
- n = pat_modnote(i);
- ins = e->par[smpno];
- }
- vol = e->par[volume]/2;
- if( e->par[volume] > 0 ) {
- if( e->par[effect] == accent ) vol += vol / 20;
- if( vol > 64 ) vol = 64;
- if( el->par[volume] == 0 ) { // note cut
- m->param = el->tracktick - tt1;
- m->command = CMD_S3MCMDEX;
- m->param |= 0xC0;
- }
- else {
- switch( e->par[effect] ) {
- case trill:
- m->command = CMD_VIBRATO;
- m->param = 0xC2; // speed 12 depth 2
- trillbits |= (1<command = CMD_XFINEPORTAUPDOWN;
- m->param |= (e->par[effoper])? 0x12: 0x22;
- break;
- default:
- m->param = modticks(e->tracktick - tt1);
- if( m->param ) { // note delay
- m->command = CMD_S3MCMDEX;
- m->param |= 0xD0;
- }
- break;
- }
- }
- }
- m->instr = ins;
- m->note = n; // <- normal note
- m->volcmd = VOLCMD_VOLUME;
- m->vol = vol;
- }
- else {
- // two notes in one row, use FINEPITCHSLIDE runonce effect
- // start first note on first tick and framedly runonce on seconds note tick
- // use volume and instrument of last note
- if( t->chan == 10 ) {
- i = el->par[note] + ((el->par[octave])*12);
- n = pat_gm_drumnote(i) + 23;
- ins = pat_gmtosmp(pat_gm_drumnr(i));
- i = n; // cannot change instrument here..
- }
- else {
- i = ef->par[note] + ((ef->par[octave])*12);
- n = pat_modnote(i);
- ins = el->par[smpno];
- i = pat_modnote(el->par[note] + ((el->par[octave])*12));
- }
- vol = el->par[volume]/2;
- if( vol > 64 ) vol = 64;
- m->instr = ins;
- m->note = n; // <- normal note
- m->volcmd = VOLCMD_VOLUME;
- m->vol = vol;
- m->param = ((i > n)?i-n:n-i);
- if( m->param < 16 ) {
- if( m->param ) {
- m->command = CMD_XFINEPORTAUPDOWN;
- m->param |= (i > n)? 0x10: 0x20;
- }
- else { // retrigger same note...
- m->command = CMD_RETRIG;
- m->param = modticks(el->tracktick - tt1);
- }
- }
- else
- m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN;
- }
- }
- else { // no new notes, keep on trilling...
- if( trillbits & (1<command = CMD_VIBRATO;
- m->param = 0; // inherited from first effect
- m->instr = 0;
- m->note = 0;
- m->volcmd = 0;
- m->vol = 0;
- }
- }
- if( m->param == 0 && m->command == CMD_NONE ) {
- if( tempo ) {
- m->command = CMD_TEMPO;
- m->param = tempo;
- tempo = 0;
- }
- else {
- if( patbrk ) {
- m->command = CMD_PATTERNBREAK;
- patbrk = 0;
- }
- }
- }
- ch++;
- }
- if( tempo || patbrk ) return 1;
- }
- }
- return 0;
-}
-
-#endif
-
-static int ABC_Key(const char *p)
-{
- int i,j;
- char c[8];
- const char *q;
- while( isspace(*p) ) p++;
- i = 0;
- q = p;
- for( i=0; i<8 && *p && *p != ']'; p++ ) {
- if( isspace(*p) ) {
- while( isspace(*p) ) p++;
- if( strnicmp(p, "min", 3) && strnicmp(p, "maj", 3) )
- break;
- }
- c[i] = *p;
- i++;
- }
- c[i] = '\0';
- if( !strcmp(c,"Hp") || !strcmp(c,"HP") ) // highland pipes
- strcpy(c,"Bm"); // two sharps at c and f
- if( !strcasecmp(c+1, "minor") ) i=2;
- if( !strcasecmp(c+2, "minor") ) i=3;
- if( !strcasecmp(c+1, "major") ) i=1;
- if( !strcasecmp(c+2, "major") ) i=2;
- if( !strcasecmp(c+1, "min") ) i=2;
- if( !strcasecmp(c+2, "min") ) i=3;
- if( !strcasecmp(c+1, "maj") ) i=1;
- if( !strcasecmp(c+2, "maj") ) i=2;
- for( ; i<6; i++ )
- c[i] = ' ';
- c[i] = '\0';
- for( i=0; keySigs[i]; i++ ) {
- for( j=10; j<46; j+=6 )
- if( !strnicmp(keySigs[i]+j, c, 6) )
- return i;
- }
- abc_message("Failure: Unrecognised K: field %s", q);
- return 7;
-}
-
-static char *abc_skip_word(char *p)
-{
- while( isspace(*p) ) p++;
- while( *p && !isspace(*p) && *p != ']') p++;
- while( isspace(*p) ) p++;
- return p;
-}
-
-static ULONG abc_tracktime(ABCTRACK *tp)
-{
- ULONG tracktime;
- if( tp->tail ) tracktime = tp->tail->tracktick;
- else tracktime = 0;
- if( tracktime < global_songstart )
- tracktime = global_songstart;
- return tracktime;
-}
-
-static void abc_addchordname(char *s, int len, int *notes)
-// adds chord name and note set to list of known chords
-{
- int i, j;
- if(strlen(s) > 7) {
- abc_message("Failure: Chord name cannot exceed 7 characters, %s", s);
- return;
- }
- if(len > 6) {
- abc_message("Failure: Named chord cannot have more than 6 notes, %s", s);
- return;
- }
- for( i=0; i < chordsnamed; i++ ) {
- if(strcmp(s, chordname[i]) == 0) {
- /* change chord */
- chordlen[i] = len;
- for(j = 0; j < len; j++) chordnotes[i][j] = notes[j];
- return;
- }
- }
- if(chordsnamed > MAXCHORDNAMES - 1)
- abc_message("Failure: Too many Guitar Chord Names used, %s", s);
- else {
- strcpy(chordname[chordsnamed], s);
- chordlen[chordsnamed] = len;
- for(j = 0; j < len; j++) chordnotes[chordsnamed][j] = notes[j];
- chordsnamed++;
- }
-}
-
-static void abc_setup_chordnames()
-// set up named guitar chords
-{
- static int list_Maj[3] = { 0, 4, 7 };
- static int list_m[3] = { 0, 3, 7 };
- static int list_7[4] = { 0, 4, 7, 10 };
- static int list_m7[4] = { 0, 3, 7, 10 };
- static int list_maj7[4] = { 0, 4, 7, 11 };
- static int list_M7[4] = { 0, 4, 7, 11 };
- static int list_6[4] = { 0, 4, 7, 9 };
- static int list_m6[4] = { 0, 3, 7, 9 };
- static int list_aug[3] = { 0, 4, 8 };
- static int list_plus[3] = { 0, 4, 8 };
- static int list_aug7[4] = { 0, 4, 8, 10 };
- static int list_dim[3] = { 0, 3, 6 };
- static int list_dim7[4] = { 0, 3, 6, 9 };
- static int list_9[5] = { 0, 4, 7, 10, 2 };
- static int list_m9[5] = { 0, 3, 7, 10, 2 };
- static int list_maj9[5] = { 0, 4, 7, 11, 2 };
- static int list_M9[5] = { 0, 4, 7, 11, 2 };
- static int list_11[6] = { 0, 4, 7, 10, 2, 5 };
- static int list_dim9[5] = { 0, 4, 7, 10, 13 };
- static int list_sus[3] = { 0, 5, 7 };
- static int list_sus9[3] = { 0, 2, 7 };
- static int list_7sus[4] = { 0, 5, 7, 10 };
- static int list_7sus4[4] = { 0, 5, 7, 10 };
- static int list_7sus9[4] = { 0, 2, 7, 10 };
- static int list_9sus4[5] = { 0, 5, 10, 14, 19 };
- static int list_5[2] = { 0, 7 };
- static int list_13[6] = { 0, 4, 7, 10, 16, 21 };
-
- chordsnamed = 0;
- abc_addchordname("", 3, list_Maj);
- abc_addchordname("m", 3, list_m);
- abc_addchordname("7", 4, list_7);
- abc_addchordname("m7", 4, list_m7);
- abc_addchordname("maj7", 4, list_maj7);
- abc_addchordname("M7", 4, list_M7);
- abc_addchordname("6", 4, list_6);
- abc_addchordname("m6", 4, list_m6);
- abc_addchordname("aug", 3, list_aug);
- abc_addchordname("+", 3, list_plus);
- abc_addchordname("aug7", 4, list_aug7);
- abc_addchordname("7+", 4, list_aug7);
- abc_addchordname("dim", 3, list_dim);
- abc_addchordname("dim7", 4, list_dim7);
- abc_addchordname("9", 5, list_9);
- abc_addchordname("m9", 5, list_m9);
- abc_addchordname("maj9", 5, list_maj9);
- abc_addchordname("M9", 5, list_M9);
- abc_addchordname("11", 6, list_11);
- abc_addchordname("dim9", 5, list_dim9);
- abc_addchordname("sus", 3, list_sus);
- abc_addchordname("sus9", 3, list_sus9);
- abc_addchordname("7sus", 4, list_7sus);
- abc_addchordname("7sus4", 4, list_7sus4);
- abc_addchordname("7sus9", 4, list_7sus9);
- abc_addchordname("9sus4", 5, list_9sus4);
- abc_addchordname("5", 2, list_5);
- abc_addchordname("13", 6, list_13);
-}
-
-static int abc_MIDI_getnumber(const char *p)
-{
- int n;
- while( isspace(*p) ) p++;
- abc_getnumber(p, &n);
- if( n < 0 ) n = 0;
- if( n > 127 ) n = 127;
- return n;
-}
-
-static int abc_MIDI_getprog(const char *p)
-{
- int n;
- while( isspace(*p) ) p++;
- abc_getnumber(p, &n);
- if( n < 1 ) n = 1;
- if( n > 128 ) n = 128;
- return n;
-}
-
-// MIDI drone
-static void abc_MIDI_drone(const char *p, int *gm, int *ptch, int *vol)
-{
- int i;
- while( isspace(*p) ) p++;
- p += abc_getnumber(p, &i);
- i++; // adjust for 1..128
- if( i>0 && i < 129 )
- *gm = i;
- else
- *gm = 71; // bassoon
- while( isspace(*p) ) p++;
- p += abc_getnumber(p, &i);
- if( i>0 && i < 127 )
- ptch[0] = i;
- else
- ptch[0] = 45;
- while( isspace(*p) ) p++;
- p += abc_getnumber(p, &i);
- if( i>0 && i < 127 )
- ptch[1] = i;
- else
- ptch[1] = 33;
- while( isspace(*p) ) p++;
- p += abc_getnumber(p, &i);
- if( i>0 && i < 127 )
- vol[0] = i;
- else
- vol[0] = 80;
- while( isspace(*p) ) p++;
- p += abc_getnumber(p, &i);
- if( i>0 && i < 127 )
- vol[1] = i;
- else
- vol[1] = 80;
-}
-
-static void abc_chan_to_tracks(ABCHANDLE *h, int tno, int ch)
-{
- ABCTRACK *tp;
- if( tno>0 && tno<33 ) {
- for( tp=h->track; tp; tp=tp->next ) {
- if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
- tp->chan = ch;
- }
- }
-}
-
-// %%MIDI channel int1
-// channel numbers are 1-16
-static void abc_MIDI_channel(const char *p, ABCTRACK *tp, ABCHANDLE *h)
-{
- int i1, i2;
- i1 = tp? tp->vno: 1;
- for( ; *p && isspace(*p); p++ ) ;
- if( isdigit(*p) ) {
- p += abc_getnumber(p, &i2);
- if( i2 >= 1 && i2 <= 16 )
- abc_chan_to_tracks(h, i1, i2); // we start at 1
- }
-}
-
-static void abc_instr_to_tracks(ABCHANDLE *h, int tno, int gm)
-{
- ABCTRACK *tp;
- if( tno>0 && tno<33 && gm>0 && gm<129 ) {
- for( tp=h->track; tp; tp=tp->next ) {
- if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
- tp->instr = gm;
- }
- }
-}
-
-// %%MIDI program [int1]
-// instrument numbers are 0-127
-static void abc_MIDI_program(const char *p, ABCTRACK *tp, ABCHANDLE *h)
-{
- int i1, i2;
- i1 = tp? tp->vno: 1;
- for( ; *p && isspace(*p); p++ ) ;
- if( isdigit(*p) ) {
- p += abc_getnumber(p, &i2);
- for( ; *p && isspace(*p); p++ ) ;
- if( isdigit(*p) ) {
- i1 = i2;
- p += abc_getnumber(p, &i2);
- }
- abc_instr_to_tracks(h, i1, i2 + 1); // we start at 1
- }
-}
-
-static void abc_mute_voice(ABCHANDLE *h, ABCTRACK *tp, int m)
-{
- ABCTRACK *t;
- for( t=h->track; t; t=t->next ) {
- if( t->vno == tp->vno ) t->mute = m;
- }
-}
-
-// %%MIDI voice [] [instrument= [bank=]] [mute]
-// instrument numbers are 1-128
-static void abc_MIDI_voice(const char *p, ABCTRACK *tp, ABCHANDLE *h)
-{
- int i1, i2;
- for( ; *p && isspace(*p); p++ ) ;
- if( strncmp(p,"instrument=",11) && strncmp(p,"mute",4) ) {
- tp = abc_locate_track(h, p, 0);
- for( ; *p && !isspace(*p); p++ ) ;
- for( ; *p && isspace(*p); p++ ) ;
- }
- i1 = tp? tp->vno: 1;
- i2 = 0;
- if( !strncmp(p,"instrument=",11) && isdigit(p[11]) ) {
- p += 11;
- p += abc_getnumber(p, &i2);
- for( ; *p && isspace(*p); p++ ) ;
- if( !strncmp(p,"bank=",5) && isdigit(p[5]) ) {
- for( ; *p && !isspace(*p); p++ ) ;
- for( ; *p && isspace(*p); p++ ) ;
- }
- }
- if( tp ) abc_mute_voice(h,tp,0);
- if( !strncmp(p,"mute",4) && (p[4]=='\0' || p[4]=='%' || isspace(p[4])) ) {
- if( tp ) abc_mute_voice(h,tp,1);
- }
- abc_instr_to_tracks(h, i1, i2); // starts already at 1 (draft 4.0)
-}
-
-// %%MIDI chordname ...
-static void abc_MIDI_chordname(const char *p)
-{
- char name[20];
- int i, notes[6];
-
- for( ; *p && isspace(*p); p++ ) ;
- i = 0;
- while ((i < 19) && (*p != ' ') && (*p != '\0')) {
- name[i] = *p;
- p = p + 1;
- i = i + 1;
- }
- name[i] = '\0';
- if(*p != ' ') {
- abc_message("Failure: Bad format for chordname command, %s", p);
- }
- else {
- i = 0;
- while ((i <= 6) && isspace(*p)) {
- for( ; *p && isspace(*p); p++ ) ;
- p += abc_getnumber(p, ¬es[i]);
- i = i + 1;
- }
- abc_addchordname(name, i, notes);
- }
-}
-
-// %%MIDI drum ... ...
-// instrument numbers are 0-127
-static int abc_MIDI_drum(const char *p, ABCHANDLE *h)
-{
- char *q;
- int i,n,m;
- while( isspace(*p) ) p++;
- if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
- if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
- n = 0;
- for( q = h->drum; *p && !isspace(*p); p++ ) {
- if( !strchr("dz0123456789",*p) ) break;
- *q++ = *p;
- if( !isdigit(*p) ) {
- if( !isdigit(p[1]) ) *q++ = '1';
- n++; // count the silences too....
- }
- }
- *q = '\0';
- q = h->drumins;
- for( i = 0; idrum[i*2] == 'd' ) {
- while( isspace(*p) ) p++;
- if( !isdigit(*p) ) {
- m = 0;
- while( !isspace(*p) ) p++;
- }
- else
- p += abc_getnumber(p,&m);
- q[i] = m + 1; // we start at 1
- }
- else q[i] = 0;
- }
- q = h->drumvol;
- for( i = 0; idrum[i*2] == 'd' ) {
- while( isspace(*p) ) p++;
- if( !isdigit(*p) ) {
- m = 0;
- while( !isspace(*p) ) p++;
- }
- else
- p += abc_getnumber(p,&m);
- q[i] = m;
- }
- else q[i] = 0;
- }
- return 0;
-}
-
-// %%MIDI gchord
-static int abc_MIDI_gchord(const char *p, ABCHANDLE *h)
-{
- char *q;
- while( isspace(*p) ) p++;
- if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
- if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
- for( q = h->gchord; *p && !isspace(*p); p++ ) {
- if( !strchr("fbcz0123456789ghijGHIJ",*p) ) break;
- *q++ = *p;
- if( !isdigit(*p) && !isdigit(p[1]) ) *q++ = '1';
- }
- *q = '\0';
- return 0;
-}
-
-static void abc_metric_gchord(ABCHANDLE *h, int mlen, int mdiv)
-{
- switch( 16 * mlen + mdiv ) {
- case 0x24:
- case 0x44:
- case 0x22:
- abc_MIDI_gchord("fzczfzcz", h);
- break;
- case 0x64:
- case 0x32:
- abc_MIDI_gchord("fzczczfzczcz", h);
- break;
- case 0x34:
- case 0x38:
- abc_MIDI_gchord("fzczcz", h);
- break;
- case 0x68:
- abc_MIDI_gchord("fzcfzc", h);
- break;
- case 0x98:
- abc_MIDI_gchord("fzcfzcfzc", h);
- break;
- case 0xc8:
- abc_MIDI_gchord("fzcfzcfzcfzc", h);
- break;
- default:
- if( mlen % 3 == 0 )
- abc_MIDI_gchord("fzcfzcfzcfzcfzcfzcfzcfzcfzc", h);
- else
- abc_MIDI_gchord("fzczfzczfzczfzczfzczfzczfzcz", h);
- if( mdiv == 8 ) h->gchord[mlen*2] = '\0';
- else h->gchord[mlen*4] = '\0';
- break;
- }
-}
-
-static void abc_MIDI_legato(const char *p, ABCTRACK *tp)
-{
- for( ; *p && isspace(*p); p++ ) ;
- if( !strncmp(p,"off",3) ) tp->legato = 0;
- else tp->legato = 1;
-}
-
-static void abc_M_field(const char *p, int *mlen, int *mdiv)
-{
- if( !strncmp(p,"none",4) ) {
- *mlen = 1;
- *mdiv = 1;
- return;
- }
- if( !strncmp(p,"C|",2) ) {
- *mlen = 2;
- *mdiv = 2;
- return;
- }
- if( *p == 'C' ) {
- *mlen = 4;
- *mdiv = 4;
- return;
- }
- p += abc_getexpr(p,mlen);
- sscanf(p," / %d", mdiv);
-}
-
-static int abc_drum_steps(const char *dch)
-{
- const char *p;
- int i=0;
- for( p=dch; *p; p++ ) {
- if( isdigit(*p) ) i += *p - '0';;
- }
- return i;
-}
-
-static void abc_add_drum(ABCHANDLE *h, ULONG tracktime, ULONG bartime)
-{
- ABCEVENT *e;
- ABCTRACK *tp;
- ULONG etime, ctime , rtime, stime;
- int i, g, steps, gnote, gsteps, nnum;
- steps = abc_drum_steps(h->drum);
- ctime = h->barticks;
- // look up the last event in tpr drumtrack
- tp = abc_locate_track(h, h->tpr->v, DRUMPOS);
- e = tp->tail;
- etime = e? e->tracktick: bartime;
- if( etime > tracktime ) return;
- if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
- else rtime = (etime - bartime) % h->barticks;
- stime = ctime*steps;
- rtime *= steps;
- rtime += stime;
- gsteps = strlen(h->drum)/2;
- g = 0;
- while( rtime > stime ) {
- rtime -= ctime*(h->drum[g*2+1] - '0');
- if( ++g == gsteps ) g = 0;
- }
- stime = (tracktime - etime) * steps;
- rtime = 0;
- while( rtime < stime ) {
- gnote = h->drum[g*2];
- i = h->drum[g*2+1] - '0';
- if(gnote=='d') {
- tp->instr = pat_gm_drumnr(h->drumins[g]-1);
- nnum = pat_gm_drumnote(h->drumins[g]);
- abc_add_drumnote(h, tp, etime + rtime/steps, nnum, h->drumvol[g]);
- abc_add_noteoff(h,tp,etime + ( rtime + ctime * i )/steps);
- }
- if( ++g == gsteps ) g = 0;
- rtime += ctime * i;
- }
-}
-
-static int abc_gchord_steps(const char *gch)
-{
- const char *p;
- int i=0;
- for( p=gch; *p; p++ )
- if( isdigit(*p) ) i += *p - '0';
- return i;
-}
-
-static void abc_add_gchord(ABCHANDLE *h, ULONG tracktime, ULONG bartime)
-{
- ABCEVENT *e, *c;
- ABCTRACK *tp;
- ULONG etime, ctime , rtime, stime;
- int i, g, steps, gnote, gcnum, gsteps, nnum, glen;
- // look up the last chord event in tpc
- c = 0;
- for( e = h->tpc->head; e; e = e->next )
- if( e->flg == 1 && e->cmd == cmdchord )
- c = e;
- if( !c ) return;
- gcnum = c->par[chordnum];
- steps = abc_gchord_steps(h->gchord);
- ctime = h->barticks;
- etime = 0;
- for( i = GCHORDBPOS; i < DRUMPOS; i++ ) {
- tp = abc_locate_track(h, h->tpc->v, i);
- e = tp->tail;
- if( !e ) e = c;
- stime = e->tracktick;
- if( stime > etime ) etime = stime;
- }
- if( etime > tracktime ) return;
- if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
- else rtime = (etime - bartime) % h->barticks;
- stime = ctime * steps;
- rtime *= steps;
- rtime += stime;
- gsteps = strlen(h->gchord);
- g = 0;
- while( rtime > stime ) {
- glen = h->gchord[2*g+1] - '0';
- rtime -= ctime * glen;
- if( ++g == gsteps ) g = 0;
- }
- stime = (tracktime - etime) * steps;
- rtime = 0;
- while( rtime < stime ) {
- gnote = h->gchord[2*g];
- glen = h->gchord[2*g+1] - '0';
- if( ++g == gsteps ) g = 0;
- nnum = 0;
- switch(gnote) {
- case 'b':
- tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
- tp->instr = h->abcbassprog;
- nnum = c->par[chordnote]+chordnotes[gcnum][0]+24;
- abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
- abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
- case 'c':
- for( i = 1; i < chordlen[gcnum]; i++ ) {
- tp = abc_locate_track(h, h->tpc->v, i+GCHORDFPOS);
- tp->instr = h->abcchordprog;
- nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
- abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
- abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
- }
- rtime += ctime * glen;
- break;
- case 'f':
- tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
- tp->instr = h->abcbassprog;
- nnum = c->par[chordbase]+12;
- abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
- rtime += ctime * glen;
- abc_add_noteoff(h,tp,etime + rtime/steps);
- break;
- case 'g':
- case 'h':
- case 'i':
- case 'j':
- case 'G':
- case 'H':
- case 'I':
- case 'J':
- i = toupper(gnote) - 'G';
- nnum = 0;
- if( i < chordlen[gcnum] ) {
- tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS+i+1);
- tp->instr = h->abcchordprog;
- nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
- if( isupper(gnote) ) nnum -= 12;
- abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
- }
- rtime += ctime * glen;
- if( nnum ) abc_add_noteoff(h,tp,etime + rtime/steps);
- break;
- case 'z':
- rtime += ctime * glen;
- break;
- }
- }
-}
-
-// %%MIDI beat a b c n
-//
-// controls the way note velocities are selected. The first note in a bar has
-// velocity a. Other "strong" notes have velocity b and all the rest have velocity
-// c. a, b and c must be in the range 0-128. The parameter n determines which
-// notes are "strong". If the time signature is x/y, then each note is given
-// a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for
-// n are not the unit note length. If k is a multiple of n, then the note is
-// "strong". The volume specifiers !ppp! to !fff! are equivalent to the
-// following :
-//
-// !ppp! = %%MIDI beat 30 20 10 1
-// !pp! = %%MIDI beat 45 35 20 1
-// !p! = %%MIDI beat 60 50 35 1
-// !mp! = %%MIDI beat 75 65 50 1
-// !mf! = %%MIDI beat 90 80 65 1
-// !f! = %%MIDI beat 105 95 80 1
-// !ff! = %%MIDI beat 120 110 95 1
-// !fff! = %%MIDI beat 127 125 110 1
-static void abc_MIDI_beat(ABCHANDLE *h, const char *p)
-{
- int i,j;
- h->beat[0] = 127;
- h->beat[1] = 125;
- h->beat[2] = 110;
- h->beat[3] = 1;
- for( j=0; j<4; j++ ) {
- while( isspace(*p) ) p++;
- if( *p ) {
- p += abc_getnumber(p, &i);
- if( i < 0 ) i = 0;
- if( i > 127 ) i = 127;
- h->beat[j] = i;
- }
- }
- if( h->beat[3] == 0 ) h->beat[3] = 1; // BB Ruud says: do not let you make mad
-}
-
-//
-// %%MIDI beatstring
-//
-// This provides an alternative way of specifying where the strong and weak
-// stresses fall within a bar. 'f' means velocity a (normally strong), 'm'
-// means velocity b (medium velocity) and 'p' means velocity c (soft velocity).
-// For example, if the time signature is 7/8 with stresses on the first, fourth
-// and sixth notes in the bar, we could use the following
-//
-// %%MIDI beatstring fppmpmp
-static void abc_MIDI_beatstring(ABCHANDLE *h, char *p)
-{
- while( isspace(*p) ) p++;
- if( h->beatstring ) _mm_free(h->allochandle, h->beatstring);
- if( strlen(p) )
- h->beatstring = DupStr(h->allochandle,p,strlen(p)+1);
- else
- h->beatstring = NULL;
-}
-
-static int abc_beat_vol(ABCHANDLE *h, int abcvol, int barpos)
-{
- int vol;
- if( h->beatstring ) {
- vol = (h->beat[2] * 9) / 10;
- if( barpos < (int)strlen(h->beatstring) ) {
- switch(h->beatstring[barpos]) {
- case 'f':
- vol = h->beat[0];
- break;
- case 'm':
- vol = h->beat[1];
- break;
- case 'p':
- vol = h->beat[2];
- break;
- default:
- break;
- }
- }
- }
- else {
- if( (barpos % h->beat[3]) == 0 ) {
- if( barpos )
- vol = h->beat[1];
- else
- vol = h->beat[0];
- }
- else
- vol = h->beat[2];
- }
- vol *= abcvol;
- vol /= 128;
- return vol;
-}
-
-static void abc_init_partpat(BYTE partp[27][2])
-{
- int i;
- for( i=0; i<27; i++ ) {
- partp[i][0] = 0xff;
- partp[i][1] = 0;
- }
-}
-
-static int abc_partpat_to_orderlist(BYTE partp[27][2], const char *abcparts, ABCHANDLE *h, BYTE **list, int orderlen)
-{
- int t, partsused;
- const char *p;
- BYTE *orderlist = *list;
- static int ordersize = 0;
- if( *list == NULL ) {
- ordersize = 128;
- orderlist = (BYTE *)_mm_calloc(h->ho, ordersize, sizeof(BYTE));
- *list = orderlist;
- }
- if( abcparts ) {
- partsused = 0;
- for( p = abcparts; *p; p++ ) {
- for( t = partp[*p - 'A'][0]; t < partp[*p - 'A'][1]; t++ ) {
- if( orderlen == ordersize ) {
- ordersize <<= 1;
- orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
- *list = orderlist;
- }
- orderlist[orderlen] = t;
- orderlen++;
- partsused++;
- }
- }
- if( partsused ) return orderlen;
- }
- // some fool wrote a P: string in the header but didn't use P: in the body
- for( t = partp[26][0]; t < partp[26][1]; t++ ) {
- if( orderlen == ordersize ) {
- ordersize <<= 1;
- orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
- *list = orderlist;
- }
- orderlist[orderlen] = t;
- orderlen++;
- }
- return orderlen;
-}
-
-static void abc_globalslide(ABCHANDLE *h, ULONG tracktime, int slide)
-{
- ABCTRACK *tp;
- ABCEVENT *e;
- int hslide;
- hslide = h->track? h->track->slidevol: slide;
- for( tp=h->track; tp; tp = tp->next ) {
- if( slide ) {
- tp->slidevoltime = tracktime;
- if( slide == 2 )
- tp->slidevol = 0;
- }
- if( tp->slidevol > -2 && slide < 2 )
- tp->slidevol = slide;
- }
- if( h->track && h->track->tail
- && hslide != slide && slide == -2
- && h->track->tail->tracktick >= tracktime ) {
- // need to update jumptypes in mastertrack from tracktime on...
- for( e=h->track->head; e; e=e->next ) {
- if( e->flg == 1 && e->cmd == cmdjump && e->tracktick >= tracktime ) {
- switch( e->par[jumptype] ) {
- case jumpnormal:
- case jumpfade:
- e->par[jumptype] = jumpfade;
- break;
- case jumpdacapo:
- case jumpdcfade:
- e->par[jumptype] = jumpdcfade;
- break;
- case jumpdasegno:
- case jumpdsfade:
- e->par[jumptype] = jumpdsfade;
- break;
- }
- }
- }
- }
-}
-
-static void abc_recalculate_tracktime(ABCHANDLE *h) {
- ABCTRACK *ttp;
- h->tracktime = 0;
- for( ttp=h->track; ttp; ttp=ttp->next )
- if( ttp->tail && ttp->tail->tracktick > h->tracktime )
- h->tracktime = ttp->tail->tracktick;
-}
-
-static void abc_MIDI_command(ABCHANDLE *h, char *p, char delim) {
- int t;
- // interpret some of the possibilitys
- if( !strncmp(p,"bassprog",8) && isspace(p[8]) ) h->abcbassprog = abc_MIDI_getprog(p+8)+1;
- if( !strncmp(p,"bassvol",7) && isspace(p[7]) ) h->abcbassvol = abc_MIDI_getnumber(p+7);
- if( !strncmp(p,"beat",4) && isspace(p[4]) ) abc_MIDI_beat(h, p+4);
- if( !strncmp(p,"beatstring",10) && isspace(p[10]) ) abc_MIDI_beatstring(h, p+4);
- if( !strncmp(p,"chordname",9) && isspace(p[9]) ) abc_MIDI_chordname(p+9);
- if( !strncmp(p,"chordprog",9) && isspace(p[9]) ) h->abcchordprog = abc_MIDI_getprog(p+9)+1;
- if( !strncmp(p,"chordvol",8) && isspace(p[8]) ) h->abcchordvol = abc_MIDI_getnumber(p+8);
- if( !strncmp(p,"drone",5) && isspace(p[5]) ) abc_MIDI_drone(p+5, &h->dronegm, h->dronepitch, h->dronevol);
- if( !strncmp(p,"droneoff",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->droneon = 0;
- if( !strncmp(p,"droneon",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->droneon = 1;
- t = h->drumon;
- if( !strncmp(p,"drum",4) && isspace(p[4]) ) {
- h->drumon = abc_MIDI_drum(p+4, h);
- if( h->drumon ) --h->drumon;
- else h->drumon = t;
- }
- if( !strncmp(p,"drumoff",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->drumon = 0;
- if( !strncmp(p,"drumon",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) h->drumon = 1;
- if( t != h->drumon ) {
- if( h->drumon && !h->tpr ) h->tpr = h->track;
- if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time!
- if( h->tpr && !h->drumon ) h->tpr = NULL;
- }
- t = h->gchordon;
- if( !strncmp(p,"gchord",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) {
- h->gchordon = abc_MIDI_gchord(p+6, h);
- if( h->gchordon ) --h->gchordon;
- else h->gchordon = t;
- }
- if( !strncmp(p,"gchordoff",9) && (p[9]=='\0' || p[9]==delim || isspace(p[9])) ) h->gchordon = 0;
- if( !strncmp(p,"gchordon",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->gchordon = 1;
- if( t != h->gchordon ) {
- if( h->tpc ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
- }
- if( !strncmp(p,"channel",7) && isspace(p[7]) )
- abc_MIDI_channel(p+8, h->tp = abc_check_track(h, h->tp), h);
- if( !strncmp(p,"program",7) && isspace(p[7]) )
- abc_MIDI_program(p+8, h->tp = abc_check_track(h, h->tp), h);
- if( !strncmp(p,"voice",5) && isspace(p[5]) )
- abc_MIDI_voice(p+6, h->tp = abc_check_track(h, h->tp), h);
- if( !strncmp(p,"legato",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) )
- abc_MIDI_legato(p+6, h->tp = abc_check_track(h, h->tp));
-}
-
-// continuate line that ends with a backslash, can't do this in abc_gets because voice lines
-// can have comment lines in between that must be parsed properly, for example:
-// [V:1] cdef gabc' |\ << continuation backslash
-// %%MIDI program 25
-// c'bag fedc |
-// informational lines can have this too, so it is rather convoluted code...
-static char *abc_continuated(ABCHANDLE *h, MMFILE *mmf, char *p) {
- char *pm, *p1, *p2 = 0;
- int continued;
- pm = p;
- while( pm[strlen(pm)-1]=='\\' ) {
- p1 = strdup(pm);
- if( p2 ) free(p2);
- continued = 1;
- while( continued ) {
- continued = 0;
- pm = abc_gets(h, mmf);
- if( !pm ) {
- abc_message("line not properly continued\n%s", p1);
- return p1;
- }
- while( *pm && isspace(*pm) ) ++pm;
- if( !strncmp(pm,"%%",2) ) {
- for( p2 = pm+2; *p2 && isspace(*p2); p2++ ) ;
- if( !strncmp(p2,"MIDI",4) && (p2[4]=='=' || isspace(p2[4])) ) {
- for( p2+=5; *p2 && isspace(*p2); p2++ ) ;
- if( *p2 == '=' )
- for( p2+=1; *p2 && isspace(*p2); p2++ ) ;
- abc_MIDI_command(h,p2,'%');
- }
- continued = 1;
- }
- }
- p2 = (char *)malloc(strlen(p1)+strlen(pm));
- if( !p2 ) {
- abc_message("macro line too long\n%s", p1);
- return p1;
- }
- p1[strlen(p1)-1] = '\0'; // strip off the backslash
- strcpy(p2,p1);
- strcat(p2,pm);
- pm = p2;
- free(p1);
- }
- return pm;
-}
-
-// =====================================================================================
-#ifdef NEWMIKMOD
-BOOL ABC_Load(ABCHANDLE *h, UNIMOD *of, MMSTREAM *mmfile)
-#else
-BOOL CSoundFile::ReadABC(const BYTE *lpStream, DWORD dwMemLength)
-#endif
-{
- static int avoid_reentry = 0;
-#ifdef NEWMIKMOD
-#define m_nDefaultTempo of->inittempo
-#else
- ABCHANDLE *h;
- uint numpat;
- MMFILE mm, *mmfile;
-#endif
- uint t;
- char *line, *p, *pp, ch, ch0=0;
- char barsig[52]; // for propagated accidental key signature within bar
- char *abcparts;
- BYTE partpat[27][2], *orderlist;
- int orderlen;
- enum { NOWHERE, INBETWEEN, INHEAD, INBODY, INSKIPFORX, INSKIPFORQUOTE } abcstate;
- ABCEVENT_JUMPTYPE j;
- ABCEVENT_X_EFFECT abceffect;
- int abceffoper;
- int abcxcount=0, abcxwanted=0, abcxnumber=1;
- int abckey, abcrate, abcchord, abcvol, abcbeatvol, abcnoslurs, abcnolegato, abcfermata, abcarpeggio, abcto;
- int abctempo;
- int cnotelen=0, cnotediv=0, snotelen, snotediv, mnotelen, mnotediv, notelen, notediv;
- // c for chords, s for standard L: setting, m for M: barlength
- int abchornpipe, brokenrithm, tupletp, tupletq, tupletr;
- int ktempo;
- ULONG abcgrace=0, bartime, thistime=0;
- ABCTRACK *tpd, *ttp;
- ABCMACRO *mp;
- int mmsp;
-#ifdef NEWMIKMOD
- MMSTREAM *mmstack[MAXABCINCLUDES];
- h->ho = _mmalloc_create("Load_ABC_ORDERLIST", NULL);
-#else
- MMFILE *mmstack[MAXABCINCLUDES];
- if( !TestABC(lpStream, dwMemLength) ) return FALSE;
- h = ABC_Init();
- if( !h ) return FALSE;
- mmfile = &mm;
- mm.mm = (char *)lpStream;
- mm.sz = dwMemLength;
- mm.pos = 0;
-#endif
- while( avoid_reentry ) sleep(1);
- avoid_reentry = 1;
- pat_resetsmp();
- pat_init_patnames();
- m_nDefaultTempo = 0;
- global_voiceno = 0;
- abckey = 0;
- h->tracktime = 0;
- global_songstart = 0;
- h->speed = 6;
- abcrate = 240;
- global_tempo_factor = 2;
- global_tempo_divider = 1;
- abctempo = 0;
- ktempo = 0;
- abceffect = none;
- abceffoper = 0;
- abcvol = 120;
- h->abcchordvol = abcvol;
- h->abcbassvol = abcvol;
- h->abcchordprog = 25; // acoustic guitar
- h->abcbassprog = 33; // acoustic bass
- abcparts = 0;
- abcnoslurs = 1;
- abcnolegato = 1;
- abcfermata = 0;
- abcarpeggio = 0;
- abcto = 0;
- snotelen = 0;
- snotediv = 0;
- mnotelen = 1;
- mnotediv = 1;
- abchornpipe = 0;
- brokenrithm = 0;
- tupletp = 0;
- tupletq = 0;
- tupletr = 0;
- h->ktrans = 0;
- h->drumon = 0;
- h->gchordon = 1;
- h->droneon = 0;
- h->tracktime = 0;
- bartime = 0;
- h->tp = NULL;
- h->tpc = NULL;
- h->tpr = NULL;
- tpd = NULL;
- h->dronegm = 71;
- h->dronepitch[0] = 45;
- h->dronepitch[1] = 33;
- h->dronevol[0] = 80;
- h->dronevol[1] = 80;
- abc_new_umacro(h, "v = +downbow+");
- abc_new_umacro(h, "u = +upbow+");
- abc_new_umacro(h, "O = +coda+");
- abc_new_umacro(h, "S = +segno+");
- abc_new_umacro(h, "P = +uppermordent+");
- abc_new_umacro(h, "M = +lowermordent+");
- abc_new_umacro(h, "L = +emphasis+");
- abc_new_umacro(h, "H = +fermata+");
- abc_new_umacro(h, "T = +trill+");
- abc_new_umacro(h, "~ = +roll+");
- abc_setup_chordnames();
- abc_init_partpat(partpat);
- abc_MIDI_beat(h, ""); // reset beat array
- abc_MIDI_beatstring(h, ""); // reset beatstring
- orderlist = NULL;
- orderlen = 0;
- mmsp = 1;
- mmstack[0] = mmfile;
- mmfseek(mmfile,0,SEEK_SET);
- abcstate = NOWHERE;
- if( h->pickrandom ) {
- abcstate = INSKIPFORX;
- abcxcount = 0;
- mmfseek(mmfile,0,SEEK_SET);
- while( (line=abc_gets(h, mmfile)) ) {
- for( p=line; isspace(*p); p++ ) ;
- if( !strncmp(p,"X:",2) ) abcxcount++;
- }
- if( abcxcount == 0 )
- abcstate = NOWHERE;
- else
- abcxwanted = (h->pickrandom - 1) % abcxcount;
- abcxcount = 0;
- mmfseek(mmfile,0,SEEK_SET);
- }
- while( mmsp > 0 ) {
- mmsp--;
- while((line=abc_gets(h, mmstack[mmsp]))) {
- for( p=line; isspace(*p); p++ ) ;
- switch(abcstate) {
- case INSKIPFORX:
- if( !strncmp(p,"X:",2) ) {
- if( abcxcount++ != abcxwanted )
- break;
- }
- // fall through
- case INBETWEEN:
- if( !strncmp(p,"X:",2) ) {
- abcstate = INHEAD;
-#ifdef NEWMIKMOD
- of->songname = NULL;
-#else
- memset(m_szNames[0], 0, 32);
-#endif
- for( p+=2; isspace(*p); p++ ) ;
- abcxnumber = atoi(p);
- abchornpipe = 0;
- h->droneon = 0;
- h->dronegm = 71;
- h->dronepitch[0] = 45;
- h->dronepitch[1] = 33;
- h->dronevol[0] = 80;
- h->dronevol[1] = 80;
- for( ttp = h->track; ttp; ttp=ttp->next ) {
- ttp->vno = 0; // mark track unused
- ttp->capostart = NULL;
- }
- h->tp = NULL; // forget old voices
- h->tpc = NULL;
- h->tpr = NULL;
- global_voiceno = 0;
- abc_set_parts(&abcparts, 0);
- abcgrace = 0;
- h->ktrans = 0;
- ktempo = 0;
- h->gchordon = 1;
- h->drumon = 0;
- global_songstart = h->tracktime;
- abc_MIDI_beat(h, ""); // reset beat array
- abc_MIDI_beatstring(h, ""); // reset beatstring
- strcpy(h->gchord, ""); // reset gchord string
- abcnolegato = 1; // reset legato switch
- }
- break;
- case NOWHERE:
- if( p[0] != '\0' && p[1] == ':' ) {
- abcstate = INHEAD;
- abc_set_parts(&abcparts, 0);
- strcpy(h->gchord, "");
- if( h->drumon && h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime);
- if( h->tpc && !h->gchordon ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
- h->gchordon = 1;
- h->drumon = 0;
- }
- else
- break;
- case INHEAD:
- if( !strncmp(p,"L:",2) ) {
- sscanf(p+2," %d / %d", &snotelen, &snotediv);
- break;
- }
- if( !strncmp(p,"M:",2) ) {
- abc_M_field(p+2, &mnotelen, &mnotediv);
- break;
- }
- if( !strncmp(p,"P:",2) ) {
- abc_set_parts(&abcparts, p+2);
- break;
- }
- if( !strncmp(p,"Q:",2) ) {
- abctempo = abc_extract_tempo(p+2,0);
- ktempo = 1;
- if( h->track ) {
- // make h->tracktime start of a new age...
- abc_add_partbreak(h, h->track, h->tracktime);
- abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
- }
- if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo;
- break;
- }
- if( !strncmp(p,"T:",2) ) {
- char buf[200];
- if( strchr(p,'%') ) *strchr(p,'%') = '\0';
- for( t=strlen(p)-1; isspace(p[t]); t-- )
- p[t]='\0';
- for( t=2; isspace(p[t]); t++ ) ;
-#ifdef NEWMIKMOD
- if( of->songname )
- strcpy(buf,of->songname);
- else
- strcpy(buf,"");
-#else
- strcpy(buf,m_szNames[0]);
-#endif
- if( strlen(buf) + strlen(p+t) > 199 ) p[t+199-strlen(buf)] = '\0'; // chop it of
- if( strlen(buf) ) strcat(buf," "); // add a space
- strcat(buf, p+t);
-#ifdef NEWMIKMOD
- of->songname = DupStr(of->allochandle, buf, strlen(buf));
-#else
- if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
- strcpy(m_szNames[0], buf);
-#endif
- break;
- }
- if( !strncmp(p,"R:",2) ) {
- for( p+=2; isspace(*p); p++ ) ;
- if( !strncmp(p,"hornpipe",8) && (isspace(p[8]) || p[8]=='\0') ) abchornpipe = 1;
- else abchornpipe = 0;
- break;
- }
- if( !strncmp(p,"V:",2) ) {
- for( t=2; p[t]==' '; t++ ) ;
- h->tp = abc_locate_track(h, p+t, 0);
- abcvol = h->tp->volume;
- abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- break;
- }
- if( !strncmp(p,"K:",2) ) {
- abcstate = INBODY;
- abckey = ABC_Key(p+2);
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- p = abc_skip_word(p+2);
- h->ktrans = abc_transpose(p);
- *p = '%'; // force skip rest of line
- if( snotelen == 0 ) { // calculate default notelen from meter M:
- if( mnotediv == 0 ) mnotediv = mnotelen = 1; // do'nt get nuked
- snotelen = 100 * mnotelen / mnotediv;
- if( snotelen > 74 )
- snotediv = 8;
- else
- snotediv = 16;
- snotelen = 1;
- }
- abceffect = none;
- abceffoper = 0;
- if( !(snotelen == 1 && snotediv == 8) ) abchornpipe = 0; // no matter what they said at R:
- brokenrithm = 0;
- global_part = ' ';
- abcgrace = 0;
- abcnoslurs = abcnolegato;
- abcto = 0;
- h->tpc = NULL; // reset chord track
- tpd = NULL; // reset drone track
- h->tpr = NULL; // reset drum track
- if( !strlen(h->gchord) ) abc_metric_gchord(h, mnotelen, mnotediv);
- h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
- if( abctempo && !ktempo ) { // did not set tempo in this songpiece so reset to abcrate
- abctempo = 0;
- global_tempo_factor = 2;
- global_tempo_divider = 1;
- if( h->track ) {
- // make h->tracktime start of a new age...
- abc_add_partbreak(h, h->track, h->tracktime);
- abc_add_tempo_event(h, h->track, h->tracktime, abcrate);
- }
- if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abcrate;
- }
- abc_init_partpat(partpat);
- partpat[26][0] = abc_patno(h, h->tracktime);
- partpat[26][1] = 0;
- abc_globalslide(h, h->tracktime, 2); // reset all volumeslides
- break;
- }
- if( !strlen(p) )
- abcstate = INBETWEEN;
- break;
- case INSKIPFORQUOTE:
- while( (ch=*p++) && (ch != '"') )
- ;
- if( !ch ) break;
- abcstate = INBODY;
- // fall through
- case INBODY:
- if( !strlen(p) && h->track ) { // end of this song
- abcstate = h->pickrandom? INSKIPFORX: INBETWEEN;
- // last but not least shut off all pending events
- abc_recalculate_tracktime(h);
- for( ttp=h->track; ttp; ttp=ttp->next )
- abc_add_noteoff(h,ttp,h->tracktime);
- abc_add_partbreak(h, h->track, h->tracktime);
- t = abc_patno(h, h->tracktime);
- if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++;
- if( global_part == ' ' ) {
- partpat[26][1] = t;
- if( abcparts ) {
- for( t=0; t<26; t++ )
- if( partpat[t][0] < partpat[t][1] ) break;
- if( t == 26 ) {
- abc_message("parts (%s) set but not used", abcparts);
- abc_set_parts(&abcparts, 0); // forget the parts array
- }
- }
- }
- else
- partpat[global_part - 'A'][1] = t;
- if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat);
- orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen);
- }
- if( !strncmp(p,"V:",2) ) {
- for( t=2; p[t]==' '; t++ ) ;
- h->tp = abc_locate_track(h, p+t, 0);
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- abcgrace = 0;
- brokenrithm = 0;
- h->tracktime = abc_tracktime(h->tp);
- bartime = h->tracktime; // it is not friendly to break voices in the middle of a track...
- abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- *p = '%'; // make me skip the rest of the line....
- }
- if( !strncmp(p,"K:",2) ) {
- abckey = ABC_Key(p+2);
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- p = abc_skip_word(p+2);
- h->ktrans = abc_transpose(p);
- *p = '%'; // make me skip the rest of the line....
- }
- if( !strncmp(p,"L:",2) ) {
- sscanf(p+2," %d / %d", &snotelen, &snotediv);
- *p = '%'; // make me skip the rest of the line....
- }
- if( !strncmp(p,"M:",2) ) {
- abc_M_field(p+2, &mnotelen, &mnotediv);
- h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
- *p = '%'; // make me skip the rest of the line....
- }
- if( !strncmp(p,"Q:",2) ) {
- abctempo = abc_extract_tempo(p+2,ch0=='\\');
- if( !h->track ) {
- h->tp = abc_check_track(h, h->track);
- h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up)
- }
- abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
- *p = '%'; // make me skip the rest of the line....
- }
- if( !strncmp(p,"T:",2) ) {
- char buf[200];
- if( strchr(p,'%') ) *strchr(p,'%') = '\0';
- for( t=strlen(p)-1; isspace(p[t]); t-- )
- p[t]='\0';
- for( t=2; isspace(p[t]); t++ ) ;
-#ifdef NEWMIKMOD
- if( of->songname )
- strcpy(buf,of->songname);
- else
- strcpy(buf,"");
-#else
- strcpy(buf,m_szNames[0]);
-#endif
- if( strlen(buf) + strlen(p+t) > 198 ) p[t+198-strlen(buf)] = '\0'; // chop it of
- if( strlen(buf) ) strcat(buf," "); // add a space
- strcat(buf, p+t);
-#ifdef NEWMIKMOD
- of->songname = DupStr(of->allochandle, buf, strlen(buf));
-#else
- if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
- strcpy(m_szNames[0], buf);
-#endif
- *p = '%'; // make me skip the rest of the line....
- }
- break;
- }
- if( !strncmp(p,"m:",2) ) {
- if( abcstate != INSKIPFORX ) {
- char *pm;
- pm = abc_continuated(h, mmstack[mmsp], p);
- abc_new_macro(h, pm+2);
- if( pm != p ) {
- free(pm);
- if( h->tp ) abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- }
- }
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"U:",2) ) {
- abc_new_umacro(h, p+2);
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"w:",2) ) { // inline lyrics
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"W:",2) ) { // lyrics at end of song body
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"d:",2) ) { // oldstyle decorations
- abc_message("warning: old style decorations not handled\n%s", p);
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"s:",2) ) { // newstyle decorations (symbols)
- abc_message("warning: new style decorations not handled\n%s", p);
- *p = '%'; // skip rest of line
- }
- if( !strncmp(p,"I:",2) && abcstate != INSKIPFORX ) { // handle like oldstyle '%%command' lines
- p[0]= '%';
- p[1]= '%';
- }
- if( !strncmp(p,"%%",2) ) {
- for( p+=2; *p && isspace(*p); p++ ) ;
- if( !strncmp(p,"abc-include",11) && isspace(p[11]) ) {
- for( t=12; isspace(p[t]); t++ ) ;
- if( p[t] ) {
- mmsp++;
- if( mmsp == MAXABCINCLUDES ) {
- mmsp--;
- abc_message("failure: too many abc-include's, %s", &p[t]);
- } else {
- mmstack[mmsp] = mmfopen(&p[t], "r");
- if( !mmstack[mmsp] ) {
- mmsp--;
- abc_message("failure: abc-include file %s not found", &p[t]);
- }
- }
- }
- else abc_message("failure: abc-include missing file name, %s", p);
- }
- if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) && abcstate != INSKIPFORX ) {
- for( p+=5; *p && isspace(*p); p++ ) ;
- if( *p == '=' )
- for( p+=1; *p && isspace(*p); p++ ) ;
- abc_MIDI_command(h,p,'%');
- if( h->tp ) abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- }
- if(*p) *p = '%'; // skip rest of line
- }
- if( abcstate == INBODY ) {
- if( *p == 'P' && p[1] == ':' ) { // a line with a part indication
- if( abcparts != NULL ) {
- // make h->tracktime start of a new age...
- if( !h->track ) {
- h->tp = abc_check_track(h, h->track);
- h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up)
- }
- h->tracktime = h->track? abc_tracktime(h->track): 0; // global parts are voice independent
- abc_add_partbreak(h, h->track, h->tracktime);
- t = abc_patno(h, h->tracktime);
- if( global_part == ' ' ) {
- partpat[26][1] = t;
- if( abcparts ) {
- for( t=0; t<26; t++ )
- if( partpat[t][0] < partpat[t][1] ) break;
- if( t == 26 ) {
- abc_message("parts (%s) set but not used", abcparts);
- abc_set_parts(&abcparts, 0); // forget the parts array
- }
- }
- }
- else
- partpat[global_part - 'A'][1] = t;
- // give every new coming abcevent the desired part indication
- while( p[2]==' ' || p[2]=='.' ) p++; // skip blancs and dots
- if( isupper(p[2]) )
- global_part = p[2];
- else
- global_part = ' ';
- if( global_part == ' ' )
- partpat[26][0] = t;
- else
- partpat[global_part - 'A'][0] = t;
- }
- *p = '%'; // make me skip the rest of the line....
- }
- if( h->droneon && !tpd ) {
- tpd = h->track;
- if( tpd ) {
- tpd = abc_locate_track(h, tpd->v, DRONEPOS1);
- tpd->instr = h->dronegm;
- abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[0], h->dronevol[0]);
- tpd = abc_locate_track(h, tpd->v, DRONEPOS2);
- tpd->instr = h->dronegm;
- abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[1], h->dronevol[1]);
- }
- }
- if( tpd && !h->droneon ) {
- tpd = abc_locate_track(h, tpd->v, DRONEPOS1);
- abc_add_noteoff(h, tpd, h->tracktime);
- tpd = abc_locate_track(h, tpd->v, DRONEPOS2);
- abc_add_noteoff(h, tpd, h->tracktime);
- tpd = NULL;
- }
- if( h->drumon && !h->tpr ) {
- h->tpr = h->track;
- if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time!
- }
- if( h->tpr && !h->drumon ) h->tpr = NULL;
- if( *p != '%' ) { // skip uninteresting lines
- // plough thru the songline gathering mos....
- ch0 = ' ';
- pp = 0;
- while( (ch = *p++) ) {
- if( isalpha(ch) && *p != ':' ) { // maybe a macro
- for( mp=h->umacro; mp; mp=mp->next ) {
- if( ch == mp->name[0] ) {
- pp = p;
- p = mp->subst;
- ch = *p++;
- break;
- }
- }
- }
- switch(ch) {
- case '%':
- abcto = 0;
- while( *p ) p++;
- break;
- case '[': // chord follows or some inline field
- abcto = 0;
- if( *p=='|' ) break; // [| a thick-thin bar line, loop around and let case '|' handle it
- if( !strncmp(p,"V:",2) ) { // inline voice change
- for( t=2; isspace(p[t]); t++ ) ;
- h->tp = abc_locate_track(h, p+t, 0);
- for( ; *p && *p != ']'; p++ ) ;
- abcgrace = 0;
- brokenrithm = 0;
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- h->tracktime = abc_tracktime(h->tp);
- bartime = h->tracktime; // it is not wise to break voices in the middle of a track...
- abcvol = h->tp->volume;
- abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- break;
- }
- if( !strncmp(p,"K:",2) ) {
- abckey = ABC_Key(p+2);
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- p = abc_skip_word(p+2);
- h->ktrans = abc_transpose(p);
- for( ; *p && *p != ']'; p++ ) ;
- break;
- }
- if( !strncmp(p,"M:",2) ) {
- abc_M_field(p+2, &mnotelen, &mnotediv);
- for( ; *p && *p != ']'; p++ ) ;
- h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
- break;
- }
- if( !strncmp(p,"P:",2) ) { // a [P:X] field inline
- if( abcparts != NULL ) {
- // make h->tracktime start of a new age...
- abc_add_partbreak(h, h->track, h->tracktime);
- t = abc_patno(h, h->tracktime);
- if( global_part == ' ' )
- partpat[26][1] = t;
- else
- partpat[global_part - 'A'][1] = t;
- // give every new coming abcevent the desired part indication
- while( isspace(p[2]) || p[2]=='.' ) p++; // skip blancs and dots
- if( isupper(p[2]) )
- global_part = p[2];
- else
- global_part = ' ';
- if( global_part == ' ' )
- partpat[26][0] = t;
- else
- partpat[global_part - 'A'][0] = t;
- }
- for( ; *p && *p != ']'; p++ ) ;
- break;
- }
- if( !strncmp(p,"Q:",2) ) {
- abctempo = abc_extract_tempo(p+2,1);
- for( ; *p && *p != ']'; p++ ) ;
- abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
- break;
- }
- if( !strncmp(p,"I:",2) ) { // interpret some of the possibilitys
- for( p += 2; isspace(*p); p++ ) ;
- if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) ) { // interpret some of the possibilitys
- for( p += 4; isspace(*p); p++ ) ;
- if( *p == '=' )
- for( p += 1; isspace(*p); p++ ) ;
- abc_MIDI_command(h, p, ']');
- if( h->tp ) abcnolegato = !h->tp->legato;
- if( !abcnolegato ) abcnoslurs = 0;
- }
- for( ; *p && *p != ']'; p++ ) ; // skip rest of inline field
- }
- if( *p && p[1] == ':' ) { // some other kind of inline field
- for( ; *p && *p != ']'; p++ ) ;
- break;
- }
- if( *p && strchr("abcdefgABCDEFG^_=",*p) ) {
- int cnl[8],cnd[8],vnl,nl0=0,nd0=0; // for chords with notes of varying length
- abcchord = 0;
- vnl = 0;
- h->tp = abc_check_track(h, h->tp);
- abc_track_clear_tiedvpos(h);
- abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
- while( (ch=*p++) && (ch != ']') ) {
- h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0);
- p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper);
- p += abc_notelen(p, ¬elen, ¬ediv);
- if( *p == '-' ) {
- p++;
- if( h->tp->tail->flg != 1 )
- h->tp->tienote = h->tp->tail;
- }
- if( abcchord<8 ) {
- cnl[abcchord] = notelen;
- cnd[abcchord] = notediv;
- }
- if( abcchord==0 ) {
- cnotelen = notelen;
- cnotediv = notediv;
- nl0 = notelen;
- nd0 = notediv;
- }
- else {
- if( cnotelen != notelen || cnotediv != notediv ) {
- vnl = 1;
- // update to longest duration
- if( cnotelen * notediv < notelen * cnotediv ) {
- cnotelen = notelen;
- cnotediv = notediv;
- abc_track_untie_short_chordnotes(h);
- }
- if( cnotelen * notediv > notelen * cnotediv ) {
- if( h->tp->tienote ) {
- abc_message("short notes in chord can not be tied:\n%s", h->line);
- h->tp->tienote = 0; // short chord notes cannot be tied...
- }
- }
- // update to shortest duration
- if( nl0 * notediv > notelen * nd0 ) {
- nl0 = notelen;
- nd0 = notediv;
- }
- }
- }
- abcchord++;
- }
- p += abc_notelen(p, ¬elen, ¬ediv);
- if( (ch = *p) == '-' ) p++; // tied chord...
- if( abcarpeggio ) { // update starttime in the noteon events...
- thistime = notelen_notediv_to_ticks(h->speed, nl0*notelen*snotelen, nd0*notediv*snotediv)/abcchord;
- if( thistime > abcticks(h->speed) ) thistime = abcticks(h->speed);
- for( nl0=1; nl0tp = abc_locate_track(h, h->tp->v, nl0+DRONEPOS2);
- h->tp->tail->tracktick = h->tracktime + thistime * nl0;
- }
- }
- notelen *= cnotelen;
- notediv *= cnotediv;
- tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr);
- while( isspace(*p) ) p++; // allow spacing in broken rithm notation
- p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe);
- thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
- if( abcfermata ) {
- thistime <<= 1;
- abcfermata = 0;
- }
- if( thistime > abcgrace ) {
- thistime -= abcgrace;
- abcgrace = 0;
- }
- else {
- abcgrace -= thistime;
- thistime = abcticks(h->speed);
- abcgrace += abcticks(h->speed);
- }
- h->tracktime += thistime;
- while( abcchord>0 ) {
- abcchord--;
- h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0);
- if( vnl && (abcchord < 8) && (cnl[abcchord] != cnotelen || cnd[abcchord] != cnotediv) ) {
- abc_add_noteoff(h, h->tp,
- h->tracktime - thistime
- + (thistime * cnl[abcchord] * cnotediv)/(cnd[abcchord] * cnotelen) );
- }
- else {
- if( ch=='-' && h->tp->tail->flg != 1 )
- h->tp->tienote = h->tp->tail; // copy noteon event to tienote in track
- if( thistime > abcticks(h->speed) )
- abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs);
- else
- abc_add_noteoff(h, h->tp, h->tracktime);
- }
- }
- if( h->gchordon && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- abcarpeggio = 0;
- if( abceffoper != 255 ) abceffect = none;
- break;
- }
- if( isdigit(*p) ) { // different endings in repeats [i,j,n-r,s,...
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += abc_getnumber(p, ¬elen);
- abc_add_variant_start(h, h->tp, h->tracktime, notelen);
- while( *p==',' || *p=='-' ) {
- if( *p==',' ) {
- p++;
- p += abc_getnumber(p, ¬elen);
- abc_add_variant_choise(h->tp, notelen);
- }
- else {
- p++;
- p += abc_getnumber(p, ¬ediv);
- while( notelen < notediv ) {
- notelen++;
- abc_add_variant_choise(h->tp, notelen);
- }
- }
- }
- break;
- }
- // collect the notes in the chord
- break;
- case '(': // slurs follow or some tuplet (duplet, triplet etc.)
- abcto = 0;
- if( isdigit(*p) ) {
- p += abc_getnumber(p,&tupletp);
- tupletr = tupletp; // ABC draft 2.0 (4.13): if r is not given it defaults to p
- switch( tupletp ) { // ABC draft 2.0 (4.13): q defaults depending on p and time signature
- case 2: case 4: case 8:
- tupletq = 3;
- break;
- case 3: case 6:
- tupletq = 2;
- break;
- default:
- if( snotediv == 8 )
- tupletq = 3;
- else
- tupletq = 2;
- break;
- }
- if( *p==':' ) {
- p++;
- if( isdigit(*p) ) p += abc_getnumber(p,&tupletq);
- if( *p==':' ) {
- p++;
- if( isdigit(*p) ) p += abc_getnumber(p,&tupletr);
- }
- }
- }
- else
- abcnoslurs=0;
- break;
- case ')': // end of slurs
- abcto = 0;
- abcnoslurs = abcnolegato;
- break;
- case '{': // grace notes follow
- abcto = 0;
- h->tp = abc_check_track(h, h->tp);
- abc_track_clear_tiedvpos(h);
- abcgrace = 0;
- abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
- while( (ch=*p++) && (ch != '}') ) {
- p += abc_add_noteon(h, ch, p, h->tracktime+abcgrace, barsig, abcbeatvol, none, 0);
- p += abc_notelen(p, ¬elen, ¬ediv);
- if( *p=='-' ) {
- p++;
- if( h->tp->tail->flg != 1 )
- h->tp->tienote = h->tp->tail;
- }
- notediv *= 4; // grace notes factor 4 shorter (1/8 => 1/32)
- abcgrace += notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
- abc_add_noteoff(h, h->tp, h->tracktime + abcgrace);
- }
- h->tracktime += abcgrace;
- abc_add_sync(h, h->tp, h->tracktime);
- if( h->gchordon && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- break;
- case '|': // bar symbols
- abcto = 0;
- if( h->gchordon && h->tp && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- bartime = h->tracktime;
- if( h->tp && h->tp->vpos ) h->tp = abc_locate_track(h, h->tp->v, 0); // reset from voice overlay
- if( isdigit(*p) ) { // different endings in repeats |i,j,n-r,s,...
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += abc_getnumber(p, ¬elen);
- abc_add_variant_start(h, h->tp, h->tracktime, notelen);
- while( *p==',' || *p=='-' ) {
- if( *p==',' ) {
- p++;
- p += abc_getnumber(p, ¬elen);
- abc_add_variant_choise(h->tp, notelen);
- }
- else {
- p++;
- p += abc_getnumber(p, ¬ediv);
- while( notelen < notediv ) {
- notelen++;
- abc_add_variant_choise(h->tp, notelen);
- }
- }
- }
- break;
- }
- if( *p==':' ) { // repeat start
- p++;
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_setloop(h, h->tp, h->tracktime);
- }
- break;
- case '&': // voice overlay
- abcto = 0;
- h->tracktime = bartime;
- h->tp = abc_check_track(h, h->tp);
- t = h->tp->vpos;
- h->tp = abc_locate_track(h, h->tp->v, t? t+1: DRONEPOS2+1);
- break;
- case ']': // staff break, end of song
- abcto = 0;
- break;
- case ':': // repeat jump
- abcto = 0;
- h->tp = abc_check_track(h, h->tp);
- j = (h->tp->slidevol == -2)? jumpfade: jumpnormal;
- abc_add_setjumploop(h, h->tp, h->tracktime, j);
- abc_add_partbreak(h, h->tp, h->tracktime);
- if( *p==':' ) { // repeat start without intermediate bar symbol
- p++;
- abc_add_setloop(h, h->tp, h->tracktime);
- }
- break;
- case '"': // chord notation
- if( !strchr("_^<>@", *p) && !isdigit(*p) ) { // if it's not a annotation string
- h->tp = abc_check_track(h, h->tp);
- if( !h->tpc ) h->tpc = abc_locate_track(h, h->tp->v, 0);
- if( h->tp == h->tpc ) abc_add_chord(p, h, h->tpc, h->tracktime); // only do chords for one voice
- }
- abcto = 0;
- while( (ch=*p++) && (ch != '"') ) {
- if( !strnicmp(p,"fade",4) && h->track && h->track->slidevol > -2 )
- abc_globalslide(h, h->tracktime, -2); // set volumeslide to fade away...
- if( !strnicmp(p,"to coda",7) ) {
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_tocoda(h, h->tp, h->tracktime);
- p+=7;
- abcto = -1;
- }
- else
- if( !isspace(*p) ) abcto = 0;
- if( !strnicmp(p,"to",2) && (isspace(p[2]) || p[2] == '"') ) abcto = 1;
- }
- if( !ch ) abcstate = INSKIPFORQUOTE;
- break;
- case '\\': // skip the rest of this line, should be the end of the line anyway
- while( (ch=*p++) )
- ;
- ch = '\\'; // remember for invoice tempo changes....
- break;
- case '!': // line break, or deprecated old style decoration
- case '+': // decorations new style
- if( !strncmp(p,"coda",4) && p[4] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- if( abcto ) {
- if( abcto > 0 ) {
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_tocoda(h, h->tp, h->tracktime);
- }
- }
- else {
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_coda(h, h->tp, h->tracktime);
- }
- p += 5;
- abcto = 0;
- break;
- }
- abcto = 0;
- if( !strncmp(p,"arpeggio",8) && p[8] == ch ) {
- abcarpeggio = 1;
- p += 9;
- break;
- }
- if( !strncmp(p,"crescendo(",10) && p[10] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 1);
- p += 11;
- break;
- }
- if( !strncmp(p,"crescendo)",10) && p[10] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 0);
- p += 11;
- break;
- }
- if( !strncmp(p,"<(",2) && p[2] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 1);
- p += 3;
- break;
- }
- if( !strncmp(p,"<)",2) && p[2] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 0);
- p += 3;
- break;
- }
- if( !strncmp(p,"dimimuendo(",11) && p[11] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, -1);
- p += 12;
- break;
- }
- if( !strncmp(p,"diminuendo)",11) && p[11] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 0);
- p += 12;
- break;
- }
- if( !strncmp(p,">(",2) && p[2] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, -1);
- p += 3;
- break;
- }
- if( !strncmp(p,">)",2) && p[2] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_globalslide(h, h->tracktime, 0);
- p += 3;
- break;
- }
- if( !strncmp(p,"upbow",5) && p[5] == ch ) {
- abceffect = bow;
- abceffoper = 1;
- p += 6;
- break;
- }
- if( !strncmp(p,"downbow",7) && p[7] == ch ) {
- abceffect = bow;
- abceffoper = 0;
- p += 8;
- break;
- }
- if( !strncmp(p,"trill",5) && p[5] == ch ) {
- abceffect = trill;
- abceffoper = 0;
- p += 6;
- break;
- }
- if( !strncmp(p,"trill(",6) && p[6] == ch ) {
- abceffect = trill;
- abceffoper = 255;
- p += 7;
- break;
- }
- if( !strncmp(p,"trill)",6) && p[6] == ch ) {
- abceffect = none;
- abceffoper = 0;
- p += 7;
- break;
- }
- if( !strncmp(p,"accent",6) && p[6] == ch ) {
- abceffect = accent;
- abceffoper = 0;
- p += 7;
- break;
- }
- if( !strncmp(p,"emphasis",8) && p[8] == ch ) {
- abceffect = accent;
- abceffoper = 0;
- p += 9;
- break;
- }
- if( !strncmp(p,">",1) && p[1] == ch ) {
- abceffect = accent;
- abceffoper = 0;
- p += 2;
- break;
- }
- if( !strncmp(p,"fermata",7) && p[7] == ch ) {
- abcfermata = 1;
- p += 8;
- break;
- }
- if( !strncmp(p,"fine",4) && p[4] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_fine(h, h->tp, h->tracktime);
- p += 5;
- break;
- }
- if( !strncmp(p,"segno",5) && p[5] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_segno(h, h->tp, h->tracktime);
- p += 6;
- break;
- }
- if( !strncmp(p,"tocoda",6) && p[6] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- abc_add_partbreak(h, h->tp, h->tracktime);
- abc_add_tocoda(h, h->tp, h->tracktime);
- p += 7;
- break;
- }
- if( !strncmp(p,"D.C.",4) && p[4] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
- abc_add_setjumploop(h, h->tp, h->tracktime, j);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += 5;
- break;
- }
- if( !strncmp(p,"D.S.",4) && p[4] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- j = (h->tp->slidevol == -2)? jumpdsfade: jumpdasegno;
- abc_add_setjumploop(h, h->tp, h->tracktime, j);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += 5;
- break;
- }
- if( !strncmp(p,"dacapo",6) && p[6] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
- abc_add_setjumploop(h, h->tp, h->tracktime, j);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += 7;
- break;
- }
- if( !strncmp(p,"dacoda",6) && p[6] == ch ) {
- h->tp = abc_check_track(h, h->tp);
- j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
- abc_add_setjumploop(h, h->tp, h->tracktime, j);
- abc_add_partbreak(h, h->tp, h->tracktime);
- p += 7;
- break;
- }
- if( ch == '!' ) {
- for( t=0; p[t] && strchr("|[:]!",p[t])==0 && !isspace(p[t]); t++ ) ;
- if( p[t] == '!' ) { // volume and other decorations, deprecated
- h->tp = abc_check_track(h, h->tp);
- abcvol = abc_parse_decorations(h, h->tp, p);
- p = &p[t+1];
- }
- }
- else {
- h->tp = abc_check_track(h, h->tp);
- abcvol = abc_parse_decorations(h, h->tp, p);
- while( (ch=*p++) && (ch != '+') )
- ;
- }
- break;
- case '`': // back quotes are for readability
- break;
- case '.': // staccato marks
- break;
- default: // some kinda note must follow
- if( strchr("abcdefgABCDEFG^_=X",ch) ) {
- h->tp = abc_check_track(h, h->tp);
- abc_track_clear_tiedvpos(h);
- abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
- p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper);
- if( abceffoper != 255 ) abceffect = none;
- p += abc_notelen(p, ¬elen, ¬ediv);
- if( *p=='-' ) {
- p++;
- if( h->tp->tail->flg != 1 )
- h->tp->tienote = h->tp->tail;
- }
- tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr);
- while( isspace(*p) ) p++; // allow spacing in broken rithm notation
- p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe);
- thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
- if( abcfermata ) {
- thistime <<= 1;
- abcfermata = 0;
- }
- if( thistime > abcgrace ) {
- thistime -= abcgrace;
- abcgrace = 0;
- }
- else {
- abcgrace -= thistime;
- thistime = abcticks(h->speed);
- abcgrace += abcticks(h->speed);
- }
- h->tracktime += thistime;
- if( thistime > abcticks(h->speed) )
- abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs - (( ch0 == '.')? thistime / 2: 0));
- else
- abc_add_noteoff(h, h->tp, h->tracktime);
- abc_add_sync(h, h->tp, h->tracktime);
- if( h->gchordon && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- abcarpeggio = 0;
- break;
- }
- if( strchr("zx",ch) ) {
- h->tp = abc_check_track(h, h->tp);
- abc_track_clear_tiednote(h);
- p += abc_notelen(p, ¬elen, ¬ediv);
- tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr);
- while( isspace(*p) ) p++; // allow spacing in broken rithm notation
- p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe);
- thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
- if( abcfermata ) {
- thistime <<= 1;
- abcfermata = 0;
- }
- if( thistime > abcgrace ) {
- thistime -= abcgrace;
- abcgrace = 0;
- }
- else {
- abcgrace -= thistime;
- thistime = abcticks(h->speed);
- abcgrace += abcticks(h->speed);
- }
- h->tracktime += thistime;
- abc_add_sync(h, h->tp, h->tracktime);
- if( h->gchordon && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- abcarpeggio = 0;
- break;
- }
- if( strchr("Z",ch) ) {
- h->tp = abc_check_track(h, h->tp);
- abc_track_clear_tiednote(h);
- p += abc_notelen(p, ¬elen, ¬ediv);
- thistime = notelen_notediv_to_ticks(h->speed, notelen*mnotelen, notediv*mnotediv);
- if( abcfermata ) {
- thistime <<= 1;
- abcfermata = 0;
- }
- if( thistime > abcgrace ) {
- thistime -= abcgrace;
- abcgrace = 0;
- }
- else {
- abcgrace -= thistime;
- thistime = abcticks(h->speed);
- abcgrace += abcticks(h->speed);
- }
- h->tracktime += thistime;
- sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature
- abc_add_sync(h, h->tp, h->tracktime);
- if( h->gchordon && (h->tp == h->tpc) )
- abc_add_gchord(h, h->tracktime, bartime);
- if( h->drumon && (h->tp == h->tpr) )
- abc_add_drum(h, h->tracktime, bartime);
- abcarpeggio = 0;
- break;
- }
- if( isalpha(ch) && *p==':' ) {
- // some unprocessed field line?
- while( *p ) p++; // skip it
- break;
- }
- break;
- }
- ch0 = ch; // remember previous char, can be staccato dot...
- if( pp ) { // did we have a U: macro substitution?
- if( !*p ) {
- p = pp;
- pp = 0;
- }
- }
- }
- }
- }
- }
- if( mmsp ) mmfclose(mmstack[mmsp]);
- }
- ABC_CleanupMacros(h); // we dont need them anymore
- if( !h->track ) {
- char buf[10];
- sprintf(buf,"%d",abcxnumber);
- abc_message("abc X:%s has no body", buf);
- h->track = abc_check_track(h, h->track); // for sanity...
- }
- if( abcstate == INBODY ) {
- // last but not least shut off all pending events
- abc_recalculate_tracktime(h);
- for( ttp=h->track; ttp; ttp=ttp->next )
- abc_add_noteoff(h,ttp,h->tracktime);
- abc_add_partbreak(h, h->track, h->tracktime);
- t = abc_patno(h, h->tracktime);
- if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++;
- if( global_part == ' ' ) {
- partpat[26][1] = t;
- if( abcparts ) {
- for( t=0; t<26; t++ )
- if( partpat[t][0] < partpat[t][1] ) break;
- if( t == 26 ) {
- abc_message("parts (%s) set but not used", abcparts);
- abc_set_parts(&abcparts, 0); // forget the parts array
- }
- }
- }
- else
- partpat[global_part - 'A'][1] = t;
- if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat);
- orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen);
- }
- abc_synchronise_tracks(h); // distribute all control events
- abc_recalculate_tracktime(h);
-/*
-
- abctrack:
- tracktick long
- note byte
- octave byte
- instrument byte
- effects byte
-
- tick = tracktick modulo speed
- row = (tracktick div speed) modulo 64
- pat = (tracktick div speed) div 64
- ord = calculated
-
-*/
- if( (p=getenv(ABC_ENV_DUMPTRACKS)) ) {
- printf("P:%s\n",abcparts);
- for( t=0; t<26; t++ )
- if( partpat[t][1] >= partpat[t][0] )
- printf(" %c ",t+'A');
- if( partpat[26][1] >= partpat[26][0] )
- printf("All");
- printf("\n");
- for( t=0; t<27; t++ )
- if( partpat[t][1] >= partpat[t][0] )
- printf("%3d ",partpat[t][0]);
- printf("\n");
- for( t=0; t<27; t++ )
- if( partpat[t][1] >= partpat[t][0] )
- printf("%3d ",partpat[t][1]);
- printf("\n");
- for( t=0; (int)tmemsize = PTMEM_LAST; // Number of memory slots to reserve!
- of->modtype = _mm_strdup(of->allochandle, ABC_Version);
- of->numpat = 1+(modticks(h->tracktime) / h->speed / 64);
- of->numpos = orderlen;
- of->reppos = 0;
- of->initspeed = h->speed;
- of->numchn = abc_numtracks(h);
- of->numtrk = of->numpat * of->numchn;
- of->initvolume = 64;
- of->pansep = 128;
- // orderlist
- if(!AllocPositions(of, orderlen)) {
- avoid_reentry = 0;
- return FALSE;
- }
- for(t=0; tpositions[t] = orderlist[t];
- _mmalloc_close(h->ho); // get rid of orderlist memory
-#else
- m_nType = MOD_TYPE_ABC;
- numpat = 1+(modticks(h->tracktime) / h->speed / 64);
- m_nDefaultSpeed = h->speed;
- m_nChannels = abc_numtracks(h);
- m_dwSongFlags = SONG_LINEARSLIDES;
- m_nMinPeriod = 28 << 2;
- m_nMaxPeriod = 1712 << 3;
- // orderlist
- for(t=0; t < (uint)orderlen; t++)
- Order[t] = orderlist[t];
- free(orderlist); // get rid of orderlist memory
-#endif
-#ifdef NEWMIKMOD
- // ==============================
- // Load the pattern info now!
- if(!AllocTracks(of)) return 0;
- if(!AllocPatterns(of)) return 0;
- of->ut = utrk_init(of->numchn, h->allochandle);
- utrk_memory_reset(of->ut);
- utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE);
- ABC_ReadPatterns(of, h, of->numpat);
- // load instruments after building the patterns (chan == 10 track handling)
- if( !PAT_Load_Instruments(of) ) {
- avoid_reentry = 0;
- return FALSE;
- }
- // ============================================================
- // set panning positions
- for(t=0; tnumchn; t++) {
- of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val
- }
-#else
- // ==============================
- // Load the pattern info now!
- if( ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels) ) {
- // :^( need one more channel to handle the global events ;^b
- m_nChannels++;
- h->tp = abc_locate_track(h, "", 99);
- abc_add_sync(h, h->tp, h->tracktime);
- for( t=0; t
-*/
-
-///////////////////////////////////////////////////
-//
-// AMF module loader
-//
-// There is 2 types of AMF files:
-// - ASYLUM Music Format
-// - Advanced Music Format(DSM)
-//
-///////////////////////////////////////////////////
-#include "stdafx.h"
-#include "sndfile.h"
-
-//#define AMFLOG
-
-//#pragma warning(disable:4244)
-
-#pragma pack(1)
-
-typedef struct _AMFFILEHEADER
-{
- UCHAR szAMF[3];
- UCHAR version;
- CHAR title[32];
- UCHAR numsamples;
- UCHAR numorders;
- USHORT numtracks;
- UCHAR numchannels;
-} AMFFILEHEADER;
-
-typedef struct _AMFSAMPLE
-{
- UCHAR type;
- CHAR samplename[32];
- CHAR filename[13];
- ULONG offset;
- ULONG length;
- USHORT c2spd;
- UCHAR volume;
-} AMFSAMPLE;
-
-
-#pragma pack()
-
-
-#ifdef AMFLOG
-extern void Log(LPCSTR, ...);
-#endif
-
-VOID AMF_Unpack(MODCOMMAND *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels)
-//-------------------------------------------------------------------------------
-{
- UINT lastinstr = 0;
- UINT nTrkSize = bswapLE16(*(USHORT *)pTrack);
- nTrkSize += (UINT)pTrack[2] <<16;
- pTrack += 3;
- while (nTrkSize--)
- {
- UINT row = pTrack[0];
- UINT cmd = pTrack[1];
- UINT arg = pTrack[2];
- if (row >= nRows) break;
- MODCOMMAND *m = pPat + row * nChannels;
- if (cmd < 0x7F) // note+vol
- {
- m->note = cmd+1;
- if (!m->instr) m->instr = lastinstr;
- m->volcmd = VOLCMD_VOLUME;
- m->vol = arg;
- } else
- if (cmd == 0x7F) // duplicate row
- {
- signed char rdelta = (signed char)arg;
- int rowsrc = (int)row + (int)rdelta;
- if ((rowsrc >= 0) && (rowsrc < (int)nRows)) memcpy(m, &pPat[rowsrc*nChannels],sizeof(pPat[rowsrc*nChannels]));
- } else
- if (cmd == 0x80) // instrument
- {
- m->instr = arg+1;
- lastinstr = m->instr;
- } else
- if (cmd == 0x83) // volume
- {
- m->volcmd = VOLCMD_VOLUME;
- m->vol = arg;
- } else
- // effect
- {
- UINT command = cmd & 0x7F;
- UINT param = arg;
- switch(command)
- {
- // 0x01: Set Speed
- case 0x01: command = CMD_SPEED; break;
- // 0x02: Volume Slide
- // 0x0A: Tone Porta + Vol Slide
- // 0x0B: Vibrato + Vol Slide
- case 0x02: command = CMD_VOLUMESLIDE;
- case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL;
- case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL;
- if (param & 0x80) param = (-(signed char)param)&0x0F;
- else param = (param&0x0F)<<4;
- break;
- // 0x04: Porta Up/Down
- case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = -(signed char)param; }
- else { command = CMD_PORTAMENTODOWN; } break;
- // 0x06: Tone Portamento
- case 0x06: command = CMD_TONEPORTAMENTO; break;
- // 0x07: Tremor
- case 0x07: command = CMD_TREMOR; break;
- // 0x08: Arpeggio
- case 0x08: command = CMD_ARPEGGIO; break;
- // 0x09: Vibrato
- case 0x09: command = CMD_VIBRATO; break;
- // 0x0C: Pattern Break
- case 0x0C: command = CMD_PATTERNBREAK; break;
- // 0x0D: Position Jump
- case 0x0D: command = CMD_POSITIONJUMP; break;
- // 0x0F: Retrig
- case 0x0F: command = CMD_RETRIG; break;
- // 0x10: Offset
- case 0x10: command = CMD_OFFSET; break;
- // 0x11: Fine Volume Slide
- case 0x11: if (param) { command = CMD_VOLUMESLIDE;
- if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F);
- else param = 0x0F|((param&0x0F)<<4);
- } else command = 0; break;
- // 0x12: Fine Portamento
- // 0x16: Extra Fine Portamento
- case 0x12:
- case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0;
- command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN;
- if (param & 0x80) param = mask|((-(signed char)param)&0x0F);
- else param |= mask;
- } else command = 0; break;
- // 0x13: Note Delay
- case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break;
- // 0x14: Note Cut
- case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break;
- // 0x15: Set Tempo
- case 0x15: command = CMD_TEMPO; break;
- // 0x17: Panning
- case 0x17: param = (param+64)&0x7F;
- if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param/2; } command = 0; }
- else { command = CMD_PANNING8; }
- // Unknown effects
- default: command = param = 0;
- }
- if (command)
- {
- m->command = command;
- m->param = param;
- }
- }
- pTrack += 3;
- }
-}
-
-
-
-BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength)
-//-----------------------------------------------------------
-{
- AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream;
- DWORD dwMemPos;
-
- if ((!lpStream) || (dwMemLength < 2048)) return FALSE;
- if ((!strncmp((LPCTSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096))
- {
- UINT numorders, numpats, numsamples;
-
- dwMemPos = 32;
- numpats = lpStream[dwMemPos+3];
- numorders = lpStream[dwMemPos+4];
- numsamples = 64;
- dwMemPos += 6;
- if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders)
- || (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return FALSE;
- m_nType = MOD_TYPE_AMF0;
- m_nChannels = 8;
- m_nInstruments = 0;
- m_nSamples = 31;
- m_nDefaultTempo = 125;
- m_nDefaultSpeed = 6;
- for (UINT iOrd=0; iOrdnFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]);
- psmp->nVolume = lpStream[dwMemPos+23];
- psmp->nGlobalVol = 64;
- if (psmp->nVolume > 0x40) psmp->nVolume = 0x40;
- psmp->nVolume <<= 2;
- psmp->nLength = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+25)));
- psmp->nLoopStart = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+29)));
- psmp->nLoopEnd = psmp->nLoopStart + bswapLE32(*((LPDWORD)(lpStream+dwMemPos+33)));
- if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength))
- {
- psmp->uFlags = CHN_LOOP;
- } else
- {
- psmp->nLoopStart = psmp->nLoopEnd = 0;
- }
- if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1;
- dwMemPos += 37;
- }
- for (UINT iPat=0; iPatnote = 0;
-
- if (pin[0])
- {
- p->note = pin[0] + 13;
- }
- p->instr = pin[1];
- p->command = pin[2];
- p->param = pin[3];
- if (p->command > 0x0F)
- {
- #ifdef AMFLOG
- Log("0x%02X.0x%02X ?", p->command, p->param);
- #endif
- p->command = 0;
- }
- ConvertModCommand(p);
- pin += 4;
- p++;
- }
- dwMemPos += 64*32;
- }
- // Read samples
- for (UINT iData=0; iDatanLength)
- {
- dwMemPos += ReadSample(psmp, RS_PCM8S, (LPCSTR)(lpStream+dwMemPos), dwMemLength);
- }
- }
- return TRUE;
- }
- ////////////////////////////
- // DSM/AMF
- USHORT *ptracks[MAX_PATTERNS];
- DWORD sampleseekpos[MAX_SAMPLES];
-
- if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F')
- || (pfh->version < 10) || (pfh->version > 14) || (!bswapLE16(pfh->numtracks))
- || (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS)
- || (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES)
- || (pfh->numchannels < 4) || (pfh->numchannels > 32))
- return FALSE;
- memcpy(m_szNames[0], pfh->title, 32);
- dwMemPos = sizeof(AMFFILEHEADER);
- m_nType = MOD_TYPE_AMF;
- m_nChannels = pfh->numchannels;
- m_nSamples = pfh->numsamples;
- m_nInstruments = 0;
- // Setup Channel Pan Positions
- if (pfh->version >= 11)
- {
- signed char *panpos = (signed char *)(lpStream + dwMemPos);
- UINT nchannels = (pfh->version >= 13) ? 32 : 16;
- for (UINT i=0; i 256) { pan = 128; ChnSettings[i].dwFlags |= CHN_SURROUND; }
- ChnSettings[i].nPan = pan;
- }
- dwMemPos += nchannels;
- } else
- {
- for (UINT i=0; i<16; i++)
- {
- ChnSettings[i].nPan = (lpStream[dwMemPos+i] & 1) ? 0x30 : 0xD0;
- }
- dwMemPos += 16;
- }
- // Get Tempo/Speed
- m_nDefaultTempo = 125;
- m_nDefaultSpeed = 6;
- if (pfh->version >= 13)
- {
- if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos];
- if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1];
- dwMemPos += 2;
- }
- // Setup sequence list
- for (UINT iOrd=0; iOrdnumorders)
- {
- Order[iOrd] = iOrd;
- PatternSize[iOrd] = 64;
- if (pfh->version >= 14)
- {
- PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos));
- dwMemPos += 2;
- }
- ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos);
- dwMemPos += m_nChannels * sizeof(USHORT);
- }
- }
- if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return TRUE;
- // Read Samples
- UINT maxsampleseekpos = 0;
- for (UINT iIns=0; iInssamplename, 32);
- memcpy(pins->name, psh->filename, 13);
- pins->nLength = bswapLE32(psh->length);
- pins->nC4Speed = bswapLE16(psh->c2spd);
- pins->nGlobalVol = 64;
- pins->nVolume = psh->volume * 4;
- if (pfh->version >= 11)
- {
- pins->nLoopStart = bswapLE32(*(DWORD *)(lpStream+dwMemPos));
- pins->nLoopEnd = bswapLE32(*(DWORD *)(lpStream+dwMemPos+4));
- dwMemPos += 8;
- } else
- {
- pins->nLoopStart = bswapLE16(*(WORD *)(lpStream+dwMemPos));
- pins->nLoopEnd = pins->nLength;
- dwMemPos += 2;
- }
- sampleseekpos[iIns] = 0;
- if ((psh->type) && (bswapLE32(psh->offset) < dwMemLength-1))
- {
- sampleseekpos[iIns] = bswapLE32(psh->offset);
- if (bswapLE32(psh->offset) > maxsampleseekpos)
- maxsampleseekpos = bswapLE32(psh->offset);
- if ((pins->nLoopEnd > pins->nLoopStart + 2)
- && (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP;
- }
- }
- // Read Track Mapping Table
- USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos);
- UINT realtrackcnt = 0;
- dwMemPos += pfh->numtracks * sizeof(USHORT);
- for (UINT iTrkMap=0; iTrkMapnumtracks; iTrkMap++)
- {
- if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap];
- }
- // Store tracks positions
- BYTE **pTrackData = new BYTE *[realtrackcnt];
- memset(pTrackData, 0, sizeof(pTrackData));
- for (UINT iTrack=0; iTracknumorders; iPat++)
- {
- MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels);
- if (!p) break;
- Patterns[iPat] = p;
- for (UINT iChn=0; iChn