changeset 491:7c5e886205ef trunk

[svn] New code drop from blargg. Needs some work.
author chainsaw
date Tue, 24 Jan 2006 19:10:07 -0800
parents 025c253845a3
children ccb68bad47b2
files Plugins/Input/console/Audacious_Driver.cpp Plugins/Input/console/Audacious_Driver.h Plugins/Input/console/Blip_Buffer.cpp Plugins/Input/console/Blip_Buffer.h Plugins/Input/console/Blip_Synth.h Plugins/Input/console/Classic_Emu.cpp Plugins/Input/console/Classic_Emu.h Plugins/Input/console/Effects_Buffer.cpp Plugins/Input/console/Effects_Buffer.h Plugins/Input/console/Fir_Resampler.cpp Plugins/Input/console/Fir_Resampler.h Plugins/Input/console/Gb_Apu.cpp Plugins/Input/console/Gb_Apu.h Plugins/Input/console/Gb_Cpu.cpp Plugins/Input/console/Gb_Cpu.h Plugins/Input/console/Gb_Oscs.cpp Plugins/Input/console/Gb_Oscs.h Plugins/Input/console/Gbs_Emu.cpp Plugins/Input/console/Gbs_Emu.h Plugins/Input/console/Gym_Emu.cpp Plugins/Input/console/Gym_Emu.h Plugins/Input/console/Makefile.am Plugins/Input/console/Multi_Buffer.cpp Plugins/Input/console/Multi_Buffer.h Plugins/Input/console/Music_Emu.cpp Plugins/Input/console/Music_Emu.h Plugins/Input/console/Nes_Apu.cpp Plugins/Input/console/Nes_Apu.h Plugins/Input/console/Nes_Cpu.cpp Plugins/Input/console/Nes_Cpu.h Plugins/Input/console/Nes_Namco.cpp Plugins/Input/console/Nes_Namco.h Plugins/Input/console/Nes_Oscs.cpp Plugins/Input/console/Nes_Oscs.h Plugins/Input/console/Nes_Vrc6.cpp Plugins/Input/console/Nes_Vrc6.h Plugins/Input/console/Nsf_Emu.cpp Plugins/Input/console/Nsf_Emu.h Plugins/Input/console/Panning_Buffer.cpp Plugins/Input/console/Panning_Buffer.h Plugins/Input/console/Sms_Apu.cpp Plugins/Input/console/Sms_Apu.h Plugins/Input/console/Sms_Oscs.h Plugins/Input/console/Snes_Spc.cpp Plugins/Input/console/Snes_Spc.h Plugins/Input/console/Spc_Cpu.cpp Plugins/Input/console/Spc_Cpu.h Plugins/Input/console/Spc_Dsp.cpp Plugins/Input/console/Spc_Dsp.h Plugins/Input/console/Spc_Emu.cpp Plugins/Input/console/Spc_Emu.h Plugins/Input/console/Tagged_Data.h Plugins/Input/console/Vgm_Emu.cpp Plugins/Input/console/Vgm_Emu.h Plugins/Input/console/abstract_file.cpp Plugins/Input/console/abstract_file.h Plugins/Input/console/blargg_common.h Plugins/Input/console/blargg_endian.h Plugins/Input/console/blargg_source.h Plugins/Input/console/boost/config.hpp Plugins/Input/console/boost/cstdint.hpp Plugins/Input/console/boost/static_assert.hpp Plugins/Input/console/changes.txt Plugins/Input/console/design.txt Plugins/Input/console/gme_notes.txt Plugins/Input/console/gme_readme.txt Plugins/Input/console/notes.txt Plugins/Input/console/readme.txt Plugins/Input/console/todo.txt Plugins/Input/console/ym2612.cpp Plugins/Input/console/ym2612.h
diffstat 71 files changed, 5390 insertions(+), 7982 deletions(-) [+]
line wrap: on
line diff
--- a/Plugins/Input/console/Audacious_Driver.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Audacious_Driver.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -6,526 +6,669 @@
  * http://www.slack.net/~ant/libs/
  */
 
-#include "Audacious_Driver.h"
-
-extern "C" {
-
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include "libaudacious/configdb.h"
 #include "libaudacious/util.h"
 #include "libaudacious/titlestring.h"
-#include "libaudacious/vfs.h"
-#include "audacious/input.h"
+extern "C" {
 #include "audacious/output.h"
-#include "libaudcore/playback.h"
+}
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+
+// Game_Music_Emu
+#include "Nsf_Emu.h"
+#include "Nsfe_Emu.h"
+#include "Gbs_Emu.h"
+#include "Vgm_Emu.h"
+#include "Gym_Emu.h"
+#include "Spc_Emu.h"
+
+#include "Track_Emu.h"
+#include "Vfs_File.h"
+#include "Gzip_File.h"
+#include "blargg_endian.h"
+
+//typedef Vfs_File_Reader Audacious_Reader; // will use VFS once it handles gzip transparently
+typedef Gzip_File_Reader Audacious_Reader;
 
+struct AudaciousConsoleConfig {
+	gint loop_length;   // length of tracks that lack timing information
+	gboolean resample;  // whether or not to resample
+	gint resample_rate; // rate to resample at
+	gboolean nsfe_playlist; // if true, use optional NSFE playlist
+};
+static AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE };
+static GThread* decode_thread;
+static GStaticMutex playback_mutex = G_STATIC_MUTEX_INIT;
+static volatile gboolean console_ip_is_going;
+static volatile long pending_seek;
+extern InputPlugin console_ip;
+static Music_Emu* emu = 0;
+static Track_Emu track_emu;
+
+static void unload_file()
+{
+	delete emu;
+	emu = NULL;
+}
+
+// Information
+
+typedef unsigned char byte;
+
+#define DUPE_FIELD( field ) g_strndup( field, sizeof (field) );
+
+struct track_info_t
+{
+	int track;  // track to get info for
+	int length; // in msec, -1 = unknown
+	int loop;   // in msec, -1 = unknown, 0 = not looped
+	int intro;  // in msec, -1 = unknown
+	
+	TitleInput* ti;
 };
 
-struct AudaciousConsoleConfig audcfg = { 180, FALSE, 32000 };
-
-#include <cstring>
-#include <cstdlib>
-#include <cctype>
+// NSFE
 
-static Spc_Emu *spc = NULL;
-static Nsf_Emu *nsf = NULL;
-static Gbs_Emu *gbs = NULL;
-static Gym_Emu *gym = NULL;
-static Vgm_Emu *vgm = NULL;
-static GThread *decode_thread;
-GStaticMutex playback_mutex = G_STATIC_MUTEX_INIT;
+void get_nsfe_info( Nsfe_Info const& nsfe, track_info_t* out )
+{
+	Nsfe_Info::info_t const& h = nsfe.info();
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	out->ti->track_name = g_strdup( nsfe.track_name( out->track ) );
+	int time = nsfe.track_time( out->track );
+	if ( time > 0 )
+		out->length = time;
+	if ( nsfe.info().track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
 
-static void *play_loop_spc(gpointer arg);
-static void *play_loop_nsf(gpointer arg);
-static void *play_loop_gbs(gpointer arg);
-static void *play_loop_gym(gpointer arg);
-static void *play_loop_vgm(gpointer arg);
-static void console_init(void);
-extern "C" void console_aboutbox(void);
-static void console_stop(void);
-static void console_pause(gshort p);
-static int get_time(void);
-static gboolean console_ip_is_going;
-extern InputPlugin console_ip;
-static int playing_type;
+inline void get_info_emu( Nsfe_Emu& emu, track_info_t* out )
+{
+	emu.enable_playlist( audcfg.nsfe_playlist ); // to do: kind of hacky
+	get_nsfe_info( emu, out );
+}
+
+inline void get_file_info( Nsfe_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	Nsfe_Info nsfe;
+	if ( !nsfe.load( h, in ) )
+	{
+		nsfe.enable_playlist( audcfg.nsfe_playlist );
+		get_nsfe_info( nsfe, out );
+	}
+}
+
+// NSF
+
+static void get_nsf_info_( Nsf_Emu::header_t const& h, track_info_t* out )
+{
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	if ( h.track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
 
-static int is_our_file(gchar *filename)
+inline void get_info_emu( Nsf_Emu& emu, track_info_t* out )
+{
+	get_nsf_info_( emu.header(), out );
+}
+
+inline void get_file_info( Nsf_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_nsf_info_( h, out );
+}
+
+// GBS
+
+static void get_gbs_info_( Gbs_Emu::header_t const& h, track_info_t* out )
 {
-	VFSFile *file;
-	gchar magic[4];
-	if (file = vfs_fopen(filename, "rb")) {
-        	vfs_fread(magic, 1, 4, file);
-		if (!strncmp(magic, "SNES", 4)) {
-			vfs_fclose(file);
-			return PLAY_TYPE_SPC;
-		}
-		if (!strncmp(magic, "NESM", 4)) {
-			vfs_fclose(file);
-			return PLAY_TYPE_NSF;
-		}
-		if (!strncmp(magic, "GYMX", 4)) {
-			vfs_fclose(file);
-			return PLAY_TYPE_GYM;
-		}
-		if (!strncmp(magic, "GBS", 3)) {
-			vfs_fclose(file);
-			return PLAY_TYPE_GBS;
-		}
-		if (!strncmp(magic, "Vgm", 3)) {
-			vfs_fclose(file);
-			return PLAY_TYPE_VGM;
-		}
-		vfs_fclose(file);
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	if ( h.track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
+
+inline void get_info_emu( Gbs_Emu& emu, track_info_t* out )
+{
+	get_gbs_info_( emu.header(), out );
+}
+
+inline void get_file_info( Gbs_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_gbs_info_( h, out );
+}
+
+// GYM
+
+static void get_gym_info_( Gym_Emu::header_t const& h, track_info_t* out )
+{
+	if ( !memcmp( h.tag, "GYMX", 4 ) )
+	{
+		out->ti->performer  = DUPE_FIELD( h.copyright );
+		out->ti->album_name = DUPE_FIELD( h.game );
+		out->ti->track_name = DUPE_FIELD( h.song );
+		out->ti->comment    = DUPE_FIELD( h.comment );
 	}
-	return 0;
 }
 
-static gint strtoi(gchar *buf,size_t len) {
-	guint num = 0;
-	while (len && isdigit(*buf)) {
-		num *= 10;
-		num += *buf - '0';
-		buf++;
-		len--;
+static void get_gym_timing_( Gym_Emu const& emu, track_info_t* out )
+{
+	out->length = emu.track_length() * 50 / 3; // 1000 / 60
+	out->loop = 0;
+	
+	long loop = get_le32( emu.header().loop_start );
+	if ( loop )
+	{
+		out->intro = loop * 50 / 3;
+		out->loop = out->length - out->intro;
+		out->length = -1;
 	}
-	return num;
+}
+
+inline void get_info_emu( Gym_Emu& emu, track_info_t* out )
+{
+	get_gym_info_( emu.header(), out );
+	get_gym_timing_( emu, out );
+}
+
+inline void get_file_info( Gym_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_gym_info_( h, out );
+	
+	// have to load and parse entire GYM file to determine length
+	// to do: could make more efficient by manually parsing data (format is simple)
+	// rather than loading into emulator with its FM chips and resampler
+	Gym_Emu* emu = new Gym_Emu;
+	if ( emu && !emu->set_sample_rate( 44100 ) && !emu->load( h, in ) )
+		get_gym_timing_( *emu, out );
+	delete emu;
 }
 
-static gchar *get_title_spc(gchar *filename)
-{
-	gchar *title;
-	Spc_Reader reader;
-	Spc_Emu::header_t header;
+// SPC
 
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	if (header.version > 10)
+static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
+{
+	// header
+	byte const* end = begin + size;
+	if ( size < 8 || memcmp( begin, "xid6", 4 ) )
+		return;
+	long info_size = get_le32( begin + 4 );
+	byte const* in = begin + 8; 
+	if ( end - in > info_size )
+		end = in + info_size;
+	
+	while ( end - in >= 4 )
 	{
-		TitleInput *tinput;
-
-		tinput = bmp_title_input_new();
+		// header
+		int id   = in [0];
+		int data = in [3] * 0x100 + in [2];
+		int type = in [1];
+		int len  = type ? data : 0;
+		in += 4;
+		if ( len > end - in )
+			break; // block goes past end of data
+		
+		// handle specific block types
+		switch ( id )
+		{
+			case 0x01: out->ti->track_name = g_strndup( (char*) in, len ); break;
+			case 0x02: out->ti->album_name = g_strndup( (char*) in, len ); break;
+			case 0x03: out->ti->performer  = g_strndup( (char*) in, len ); break;
+			case 0x07: out->ti->comment    = g_strndup( (char*) in, len ); break;
+			//case 0x31: // loop length, but I haven't found any SPC files that use this
+		}
+		
+		// skip to next block
+		in += len;
 		
-		header.author[31] = '\0';
-		header.game[31] = '\0';
-		header.song[31] = '\0';
-
-		tinput->performer = g_strdup(header.author);
-		tinput->album_name = g_strdup(header.game);
-		tinput->track_name = g_strdup(header.song);		
+		// blocks are supposed to be 4-byte aligned with zero-padding...
+		byte const* unaligned = in;
+		while ( (in - begin) & 3 && in < end )
+		{
+			if ( *in++ != 0 )
+			{
+				// ...but some files have no padding
+				in = unaligned;
+				break;
+			}
+		}
+	}
+}
 
-		tinput->file_name = g_path_get_basename(filename);
-		tinput->file_path = g_path_get_dirname(filename);
-
-		title = xmms_get_titlestring(xmms_get_gentitle_format(),
-				tinput);
+static void get_spc_info_( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size,
+		track_info_t* out )
+{
+	// decode length (can be in text or binary format)
+	char s [4] = { h.len_secs [0], h.len_secs [1], h.len_secs [2], 0 };
+	int len_secs = (unsigned char) s [1] * 0x100 + s [0];
+	if ( s [1] >= ' ' || (!s [1] && isdigit( s [0] )) )
+		len_secs = atoi( s );
+	if ( len_secs )
+		out->length = len_secs * 1000;
+	
+	if ( xid6_size )
+		get_spc_xid6( xid6, xid6_size, out );
+	
+	// use header to fill any remaining fields
+	if ( !out->ti->performer  ) out->ti->performer  = DUPE_FIELD( h.author );
+	if ( !out->ti->album_name ) out->ti->album_name = DUPE_FIELD( h.game );
+	if ( !out->ti->track_name ) out->ti->track_name = DUPE_FIELD( h.song );
+}
 
-		g_free(tinput);
-	}
-	else
-		title = g_path_get_basename(filename);
-
-	return title;
+inline void get_info_emu( Spc_Emu& emu, track_info_t* out )
+{
+	get_spc_info_( emu.header(), emu.trailer(), emu.trailer_size(), out );
 }
 
-static gchar *get_title_nsf(gchar *filename)
+inline void get_file_info( Spc_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
 {
-	gchar *title;
-	Nsf_Reader reader;
-	Nsf_Emu::header_t header;
+	// handle xid6 data at end of file
+	long const xid6_skip = 0x10200 - sizeof (Spc_Emu::header_t);
+	long xid6_size = in.remain() - xid6_skip;
+	blargg_vector<byte> xid6;
+	if ( xid6_size <= 0 || xid6.resize( xid6_size ) || in.skip( xid6_skip ) ||
+			in.read( xid6.begin(), xid6.size() ) )
+		xid6_size = 0;
+	
+	get_spc_info_( h, xid6.begin(), xid6_size, out );
+}
+
+// VGM
+
+static void get_gd3_str( byte const* in, byte const* end, gchar** out )
+{
+	int len = (end - in) / 2 - 1;
+	if ( len > 0 )
+	{
+		*out = g_strndup( "", len );
+		if ( !*out )
+			return;
+		for ( int i = 0; i < len; i++ )
+			(*out) [i] = in [i * 2]; // to do: convert to utf-8
+	}
+}
 
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
+static byte const* skip_gd3_str( byte const* in, byte const* end )
+{
+	while ( end - in >= 2 )
+	{
+		in += 2;
+		if ( !(in [-2] | in [-1]) )
+			break;
+	}
+	return in;
+}
+
+static byte const* get_gd3_pair( byte const* in, byte const* end, gchar** out )
+{
+	byte const* mid = skip_gd3_str( in, end );
+	if ( out )
+		get_gd3_str( in, mid, out );
+	return skip_gd3_str( mid, end );
+}
 
-	if (header.game)
+static void get_vgm_gd3( byte const* in, long size, track_info_t* out )
+{
+	byte const* end = in + size;
+	in = get_gd3_pair( in, end, &out->ti->track_name );
+	in = get_gd3_pair( in, end, &out->ti->album_name );
+	in = get_gd3_pair( in, end, 0 ); // system
+	in = get_gd3_pair( in, end, &out->ti->performer );
+	in = get_gd3_pair( in, end, 0 ); // copyright
+	// ... other fields (release date, dumper, notes)
+}
+
+static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
+{
+	long length = get_le32( h.track_duration );
+	if ( length > 0 )
 	{
-		TitleInput *tinput;
-
-		tinput = bmp_title_input_new();
+		out->length = length * 10 / 441; // 1000 / 44100 (VGM files used 44100 as timebase)
+		out->loop = 0;
 		
-		header.author[31] = '\0';
-		header.copyright[31] = '\0';
-		header.game[31] = '\0';
+		long loop = get_le32( h.loop_duration );
+		if ( loop > 0 && get_le32( h.loop_offset ) )
+		{
+			out->loop = loop * 10 / 441;
+			out->intro = out->length - out->loop;
+			out->length = -1;
+		}
+	}
+}
 
-		tinput->performer = g_strdup(header.author);
-		tinput->album_name = g_strdup(header.copyright);
-		tinput->track_name = g_strdup(header.game);		
+inline void get_info_emu( Vgm_Emu& emu, track_info_t* out )
+{
+	get_vgm_length( emu.header(), out );
+	
+	int size;
+	byte const* data = emu.gd3_data( &size );
+	if ( data )
+		get_vgm_gd3( data + 12, size, out );
+}
 
-		tinput->file_name = g_path_get_basename(filename);
-		tinput->file_path = g_path_get_dirname(filename);
-
-		title = xmms_get_titlestring(xmms_get_gentitle_format(),
-				tinput);
-
-		g_free(tinput);
-	}
-	else
-		title = g_path_get_basename(filename);
-
-	return title;
+inline void get_file_info( Vgm_Emu::header_t const& vgm_h, Data_Reader& in, track_info_t* out )
+{
+	get_vgm_length( vgm_h, out );
+	
+	// find gd3 header
+	long gd3_offset = get_le32( vgm_h.gd3_offset ) + offsetof(Vgm_Emu::header_t,gd3_offset) -
+			sizeof vgm_h;
+	long gd3_max_size = in.remain() - gd3_offset;
+	byte gd3_h [12];
+	if ( gd3_offset <= 0 || gd3_max_size < (int) sizeof gd3_h )
+		return;
+	
+	// read gd3 header
+	if ( in.skip( gd3_offset ) || in.read( gd3_h, sizeof gd3_h ) )
+		return;
+	
+	// check header signature and version
+	if ( memcmp( gd3_h, "Gd3 ", 4 ) || get_le32( gd3_h + 4 ) >= 0x200 )
+		return;
+	
+	// get and check size
+	long gd3_size = get_le32( gd3_h + 8 );
+	if ( gd3_size > gd3_max_size - 12 )
+		return;
+	
+	// read and parse gd3 data
+	blargg_vector<byte> gd3;
+	if ( gd3.resize( gd3_size ) || in.read( gd3.begin(), gd3.size() ) )
+		return;
+	
+	get_vgm_gd3( gd3.begin(), gd3.size(), out );
 }
 
-static gchar *get_title_gbs(gchar *filename)
-{
-	gchar *title;
-	Gbs_Reader reader;
-	Gbs_Emu::header_t header;
+// File identification
+
+enum { type_none = 0, type_spc, type_nsf, type_nsfe, type_vgm, type_gbs, type_gym };
+
+int const tag_size = 4;
+typedef char tag_t [tag_size];
 
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
+static int identify_file( gchar* path, tag_t tag )
+{
+	// GYM file format doesn't require *any* header, just the ".gym" extension
+	if ( g_str_has_suffix( path, ".gym" ) ) // to do: is pathname in unicode?
+		return type_gym;
+	// to do: trust suffix for all file types, avoiding having to look inside files?
+	
+	int result = type_none;
+	if ( !memcmp( tag, "SNES", 4 ) ) result = type_spc;
+	if ( !memcmp( tag, "NESM", 4 ) ) result = type_nsf;
+	if ( !memcmp( tag, "NSFE", 4 ) ) result = type_nsfe;
+	if ( !memcmp( tag, "GYMX", 4 ) ) result = type_gym;
+	if ( !memcmp( tag, "GBS" , 3 ) ) result = type_gbs;
+	if ( !memcmp( tag, "Vgm ", 4 ) ) result = type_vgm;
+	return result;
+}
 
-	if (header.game)
-	{
-		TitleInput *tinput;
+static gint is_our_file( gchar* path )
+{
+	Audacious_Reader in;
+	tag_t tag;
+	return !in.open( path ) && !in.read( tag, sizeof tag ) && identify_file( path, tag );
+}
+
+// Get info
 
-		tinput = bmp_title_input_new();
-		
-		header.author[31] = '\0';
-		header.copyright[31] = '\0';
-		header.game[31] = '\0';
-
-		tinput->performer = g_strdup(header.author);
-		tinput->album_name = g_strdup(header.copyright);
-		tinput->track_name = g_strdup(header.game);		
+static int begin_get_info( const char* path, track_info_t* out )
+{
+	out->track  = 0;
+	out->length = -1;
+	out->loop   = -1;
+	out->intro  = -1;
+	TitleInput* fields = bmp_title_input_new();
+	out->ti = fields;
+	if ( !fields )
+		return true;
+	
+	fields->file_name = g_path_get_basename( path );
+	fields->file_path = g_path_get_dirname( path );
+	return false;
+}
 
-		tinput->file_name = g_path_get_basename(filename);
-		tinput->file_path = g_path_get_dirname(filename);
-
-		title = xmms_get_titlestring(xmms_get_gentitle_format(),
-				tinput);
-
-		g_free(tinput);
-	}
-	else
-		title = g_path_get_basename(filename);
-
-	return title;
+static char* end_get_info( track_info_t const& info, int* length, bool* has_length )
+{
+	*length = info.length;
+	if ( has_length )
+		*has_length = (*length > 0);
+	
+	if ( *length <= 0 )
+		*length = audcfg.loop_length * 1000;
+	
+	// use filename for formats that don't have field for name of game
+	// to do: strip off file extension
+	if ( !info.ti->track_name )
+		info.ti->track_name = g_strdup( info.ti->file_name );
+	
+	char* result = xmms_get_titlestring( xmms_get_gentitle_format(), info.ti );
+	g_free( info.ti );
+	return result;
 }
 
-static gchar *get_title_gym(gchar *filename)
+template<class Header>
+inline void get_info_t( tag_t tag, Data_Reader& in, track_info_t* out, Header* )
 {
-	gchar *title;
-	Gym_Reader reader;
-	Gym_Emu::header_t header;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	if (header.song)
-	{
-		TitleInput *tinput;
-
-		tinput = bmp_title_input_new();
-		
-		header.game[31] = '\0';
-		header.copyright[31] = '\0';
-		header.song[31] = '\0';
-
-		tinput->performer = g_strdup(header.game);
-		tinput->album_name = g_strdup(header.copyright);
-		tinput->track_name = g_strdup(header.song);		
-
-		tinput->file_name = g_path_get_basename(filename);
-		tinput->file_path = g_path_get_dirname(filename);
-
-		title = xmms_get_titlestring(xmms_get_gentitle_format(),
-				tinput);
-
-		g_free(tinput);
-	}
-	else
-		title = g_path_get_basename(filename);
-
-	return title;
+	Header h;
+	memcpy( &h, tag, tag_size );
+	if ( !in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
+		get_file_info( h, in, out );
 }
 
-static gchar *get_title_vgm(gchar *filename)
+static void get_song_info( char* path, char** title, int* length )
 {
-	gchar *title;
-	title = g_path_get_basename(filename);
-	return title;
+	int track = 0; // to do: way to select other tracks
+	
+	*length = -1;
+	*title = NULL;
+	Audacious_Reader in;
+	tag_t tag;
+	if ( in.open( path ) || in.read( tag, sizeof tag ) )
+		return;
+	
+	int type = identify_file( path, tag );
+	if ( !type )
+		return;
+	
+	track_info_t info;
+	if ( begin_get_info( path, &info ) )
+		return;
+	info.track = track;
+	
+	switch ( type )
+	{
+		case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break;
+		case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break;
+		case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break;
+		case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break;
+		case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break;
+		case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break;
+	}
+	*title = end_get_info( info, length, 0 );
 }
 
-static gchar *get_title(gchar *filename)
+// Playback
+
+static int silence_pending;
+
+static void* play_loop_track( gpointer )
 {
-	switch (is_our_file(filename))
+	g_static_mutex_lock( &playback_mutex );
+	
+	while ( console_ip_is_going )
 	{
-		case PLAY_TYPE_SPC:
-			return get_title_spc(filename);
-			break;
-		case PLAY_TYPE_NSF:
-			return get_title_nsf(filename);
-			break;
-		case PLAY_TYPE_GBS:
-			return get_title_gbs(filename);
-			break;
-		case PLAY_TYPE_GYM:
-			return get_title_gym(filename);
-			break;
-		case PLAY_TYPE_VGM:
-			return get_title_vgm(filename);
-			break;
+		int const buf_size = 1024;
+		Music_Emu::sample_t buf [buf_size];
+		
+		// wait for free space
+		while ( console_ip.output->buffer_free() < (int) sizeof buf )
+			xmms_usleep( 10000 );
+		
+		// handle pending seek
+		long s = pending_seek;
+		pending_seek = -1; // to do: use atomic swap
+		if ( s >= 0 )
+			track_emu.seek( s );
+		
+		// fill buffer
+		if ( track_emu.play( buf_size, buf ) )
+			console_ip_is_going = false;
+		produce_audio( console_ip.output->written_time(), FMT_S16_NE, 1, sizeof buf, buf, NULL );
 	}
-
+	
+	// stop playing
+	unload_file();
+	console_ip.output->close_audio();
+	console_ip_is_going = FALSE;
+	g_static_mutex_unlock( &playback_mutex );
+	// to do: should decode_thread be cleared here?
+	g_thread_exit( NULL );
 	return NULL;
 }
 
-static void get_song_info(char *filename, char **title, int *length)
-{
-	(*title) = get_title(filename);
-
-	if (audcfg.loop_length)
-		(*length) = audcfg.loop_length;
-	else
-		(*length) = -1;
-}
-
-static void play_file_spc(char *filename)
+template<class Emu>
+void load_file( tag_t tag, Data_Reader& in, long rate, track_info_t* out, Emu* dummy )
 {
-	gchar *name;
-	Spc_Reader reader;
-	Spc_Emu::header_t header;
-	gint samplerate;
-
-	if (audcfg.resample == TRUE)
-		samplerate = audcfg.resample_rate;
-	else
-		samplerate = Spc_Emu::native_sample_rate;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	spc = new Spc_Emu;
-	spc->init(samplerate);
-	spc->load(header, reader);
-	spc->start_track(0);
-
-	console_ip_is_going = TRUE;
-
-	name = get_title(filename);
-
-	spc->length = strtoi(header.len_secs,3);
-
-	if (spc->length > 0)
-		console_ip.set_info(name, spc->length * 1000,
-			spc->voice_count() * 1000, samplerate, 2);
-	else if (audcfg.loop_length)
-		console_ip.set_info(name, audcfg.loop_length * 1000, 
-			spc->voice_count() * 1000, samplerate, 2);
-	else
-		console_ip.set_info(name, -1, spc->voice_count() * 1000,
-			samplerate, 2);
-
-	g_free(name);
-
-        if (!console_ip.output->open_audio(FMT_S16_NE, samplerate, 2))
-                 return;
-
-	playing_type = PLAY_TYPE_SPC;
-
-	decode_thread = g_thread_create(play_loop_spc, spc, TRUE, NULL);
-}
-
-static void play_file_nsf(char *filename)
-{
-	gchar *name;
-	Nsf_Reader reader;
-	Nsf_Emu::header_t header;
-	gint samplerate;
-
-	if (audcfg.resample == TRUE)
-		samplerate = audcfg.resample_rate;
-	else
-		samplerate = 44100;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	nsf = new Nsf_Emu;
-	nsf->init(samplerate);
-	nsf->load(header, reader);
-	nsf->start_track(0);
-
-	console_ip_is_going = TRUE;
-
-	name = get_title(filename);
-
-	if (audcfg.loop_length)
-		console_ip.set_info(name, audcfg.loop_length * 1000, 
-			nsf->voice_count() * 1000, samplerate, 2);
-	else
-		console_ip.set_info(name, -1, nsf->voice_count() * 1000,
-			samplerate, 2);
-
-	g_free(name);
-
-        if (!console_ip.output->open_audio(FMT_S16_NE, samplerate, 2))
-                 return;
-
-	playing_type = PLAY_TYPE_NSF;
-
-	decode_thread = g_thread_create(play_loop_nsf, nsf, TRUE, NULL);
+	typename Emu::header_t h;
+	memcpy( &h, tag, tag_size );
+	if ( in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
+		return;
+	
+	Emu* local_emu = new Emu;
+	if ( !local_emu || local_emu->set_sample_rate( rate ) || local_emu->load( h, in ) )
+	{
+		delete local_emu; // delete NULL is safe
+		return;
+	}
+	
+	emu = local_emu;
+	get_info_emu( *local_emu, out );
 }
 
-static void play_file_gbs(char *filename)
+static void play_file( char* path )
 {
-	gchar *name;
-	Gbs_Reader reader;
-	Gbs_Emu::header_t header;
-	gint samplerate;
-
-	if (audcfg.resample == TRUE)
-		samplerate = audcfg.resample_rate;
-	else
-		samplerate = 44100;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	gbs = new Gbs_Emu;
-	gbs->init(samplerate);
-	gbs->load(header, reader);
-	gbs->start_track(0);
+	int track = 0; // to do: some way to select other tracks
 
+	// open and identify file
+	unload_file();
+	Audacious_Reader in;
+	tag_t tag;
+	if ( in.open( path ) || in.read( tag, sizeof tag ) )
+		return;
+	int type = identify_file( path, tag );
+	
+	// setup info
+	long sample_rate = 44100;
+	if ( type == type_spc )
+		sample_rate = Spc_Emu::native_sample_rate;
+	if ( audcfg.resample )
+		sample_rate = audcfg.resample_rate;
+	track_info_t info;
+	info.track = track;
+	if ( begin_get_info( path, &info ) )
+		return;
+	
+	// load in emulator and get info
+	switch ( type )
+	{
+		case type_nsf: load_file( tag, in, sample_rate, &info, (Nsf_Emu*) 0 ); break;
+		case type_nsfe:load_file( tag, in, sample_rate, &info, (Nsfe_Emu*)0 ); break;
+		case type_gbs: load_file( tag, in, sample_rate, &info, (Gbs_Emu*) 0 ); break;
+		case type_gym: load_file( tag, in, sample_rate, &info, (Gym_Emu*) 0 ); break;
+		case type_vgm: load_file( tag, in, sample_rate, &info, (Vgm_Emu*) 0 ); break;
+		case type_spc: load_file( tag, in, sample_rate, &info, (Spc_Emu*) 0 ); break;
+	}
+	in.close();
+	if ( !emu )
+		return;
+	
+	// set info
+	int length = -1;
+	bool has_length = false;
+	char* title = end_get_info( info, &length, &has_length );
+	if ( title )
+	{
+		console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 );
+		g_free( title );
+	}
+	
+	// start
+    if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) )
+		return;
+	pending_seek = -1;
+	track_emu.start_track( emu, track, length, !has_length );
 	console_ip_is_going = TRUE;
-
-	name = get_title(filename);
-
-	if (audcfg.loop_length)
-		console_ip.set_info(name, audcfg.loop_length * 1000, 
-			gbs->voice_count() * 1000, samplerate, 2);
-	else
-		console_ip.set_info(name, -1, gbs->voice_count() * 1000,
-			samplerate, 2);
-
-	g_free(name);
-
-        if (!console_ip.output->open_audio(FMT_S16_NE, samplerate, 2))
-                 return;
-
-	playing_type = PLAY_TYPE_GBS;
-
-	decode_thread = g_thread_create(play_loop_gbs, gbs, TRUE, NULL);
+	decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL );
 }
 
-static void play_file_gym(char *filename)
+static void seek( gint time )
 {
-	gchar *name;
-	Gym_Reader reader;
-	Gym_Emu::header_t header;
-	gint samplerate;
-
-	if (audcfg.resample == TRUE)
-		samplerate = audcfg.resample_rate;
-	else
-		samplerate = 44100;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	gym = new Gym_Emu;
-	gym->init(samplerate);
-	gym->load(header, reader);
-	gym->start_track(0);
+	// to do: be sure seek works at all
+	// to do: disallow seek on slow formats (SPC, GYM, VGM using FM)?
+	pending_seek = time;
+}
 
-	console_ip_is_going = TRUE;
-
-	name = get_title(filename);
+static void console_stop(void)
+{
+	console_ip_is_going = FALSE;
+	if ( decode_thread )
+	{
+		g_thread_join( decode_thread );
+		decode_thread = NULL;
+	}
+	console_ip.output->close_audio();
+	unload_file();
+}
 
-	if (gym->track_length() > 0)
-		console_ip.set_info(name, gym->track_length() * 1000, 
-			gym->voice_count() * 1000, samplerate, 2);
-	else if (audcfg.loop_length)
-		console_ip.set_info(name, audcfg.loop_length * 1000, 
-			gym->voice_count() * 1000, samplerate, 2);
-	else
-		console_ip.set_info(name, -1, gym->voice_count() * 1000,
-			samplerate, 2);
+static void console_pause(gshort p)
+{
+	console_ip.output->pause(p);
+}
 
-	g_free(name);
-
-        if (!console_ip.output->open_audio(FMT_S16_NE, samplerate, 2))
-                 return;
-
-	playing_type = PLAY_TYPE_GYM;
-
-	decode_thread = g_thread_create(play_loop_gym, gym, TRUE, NULL);
+static int get_time(void)
+{
+	return console_ip_is_going ? console_ip.output->output_time() : -1;
 }
 
-static void play_file_vgm(char *filename)
-{
-	gchar *name;
-	Vgm_Reader reader;
-	Vgm_Emu::header_t header;
-	gint samplerate;
+// Setup
 
-	if (audcfg.resample == TRUE)
-		samplerate = audcfg.resample_rate;
-	else
-		samplerate = 44100;
-
-	reader.open(filename);
-	reader.read(&header, sizeof(header));
-
-	vgm = new Vgm_Emu;
-	vgm->init(samplerate);
-	vgm->load(header, reader);
-	vgm->start_track(0);
+static void console_init(void)
+{
+	ConfigDb *db;
 
-	console_ip_is_going = TRUE;
-
-	name = get_title(filename);
+	db = bmp_cfg_db_open();
 
-	if (vgm->track_length() > 0)
-		console_ip.set_info(name, vgm->track_length() * 1000, 
-			vgm->voice_count() * 1000, samplerate, 2);
-	else if (audcfg.loop_length)
-		console_ip.set_info(name, audcfg.loop_length * 1000, 
-			vgm->voice_count() * 1000, samplerate, 2);
-	else
-		console_ip.set_info(name, -1, vgm->voice_count() * 1000,
-			samplerate, 2);
-
-	g_free(name);
-
-        if (!console_ip.output->open_audio(FMT_S16_NE, samplerate, 2))
-                 return;
-
-	playing_type = PLAY_TYPE_VGM;
-
-	decode_thread = g_thread_create(play_loop_vgm, vgm, TRUE, NULL);
+	bmp_cfg_db_get_int(db, "console", "loop_length", &audcfg.loop_length);
+	bmp_cfg_db_get_bool(db, "console", "resample", &audcfg.resample);
+	bmp_cfg_db_get_int(db, "console", "resample_rate", &audcfg.resample_rate);
+	bmp_cfg_db_get_bool(db, "console", "nsfe_playlist", &audcfg.nsfe_playlist);
+	
+	bmp_cfg_db_close(db);
 }
 
-static void play_file(char *filename)
+extern "C" void console_aboutbox(void)
 {
-	switch (is_our_file(filename))
-	{
-		case PLAY_TYPE_SPC:
-			play_file_spc(filename);
-			break;
-		case PLAY_TYPE_NSF:
-			play_file_nsf(filename);
-			break;
-		case PLAY_TYPE_GBS:
-			play_file_gbs(filename);
-			break;
-		case PLAY_TYPE_GYM:
-			play_file_gym(filename);
-			break;
-		case PLAY_TYPE_VGM:
-			play_file_vgm(filename);
-			break;
-	}
+	xmms_show_message(_("About the Console Music Decoder"),
+			_("Console music decoder engine based on Game_Music_Emu 0.3.0.\n"
+			  "Audacious implementation by: William Pitcock <nenolod@nenolod.net>, "
+			// Please do not put my hotpop.com address in the clear (I hate spam)
+			  "Shay Green <hotpop.com@blargg>"),
+			_("Ok"),
+			FALSE, NULL, NULL);
 }
 
-static void seek(gint time)
+InputPlugin console_ip =
 {
-	// XXX: Not yet implemented
-}
-
-InputPlugin console_ip = {
 	NULL,
 	NULL,
 	NULL,
@@ -542,7 +685,7 @@
 	get_time,
 	NULL,
 	NULL,
-	NULL,	
+	NULL,   
 	NULL,
 	NULL,
 	NULL,
@@ -552,219 +695,9 @@
 	NULL
 };
 
-static void console_stop(void)
-{
-	console_ip_is_going = FALSE;
-        g_thread_join(decode_thread);
-        console_ip.output->close_audio();
-}
-
 extern "C" InputPlugin *get_iplugin_info(void)
 {
-        console_ip.description = g_strdup_printf(_("SPC, GYM, NSF, VGM and GBS module decoder"));
-        return &console_ip;
-}
-
-static void console_pause(gshort p)
-{
-        console_ip.output->pause(p);
-}
-
-static void *play_loop_spc(gpointer arg)
-{
-	g_static_mutex_lock(&playback_mutex);
-	Spc_Emu *my_spc = (Spc_Emu *) arg;
-	Music_Emu::sample_t buf[1024];
-
-	for (;;)
-	{
-		if (!console_ip_is_going)
-			break;
-
-		my_spc->play(1024, buf);
-
-		if ((console_ip.output->output_time() / 1000) >
-			spc->length && spc->length != 0)
-			break;
-		if ((console_ip.output->output_time() / 1000) > 
-			audcfg.loop_length && audcfg.loop_length != 0)
-			break;
-		produce_audio(console_ip.output->written_time(),
-			FMT_S16_NE, 1, 2048, buf, NULL);
-	        while(console_ip.output->buffer_free() < 2048)
-			xmms_usleep(10000);
-	}
-
-        delete spc;
-        console_ip.output->close_audio();
-	console_ip_is_going = FALSE;
-	playing_type = PLAY_TYPE_NONE;
-	g_static_mutex_unlock(&playback_mutex);
-        g_thread_exit(NULL);
-
-        return NULL;
-}
-
-static void *play_loop_nsf(gpointer arg)
-{
-	g_static_mutex_lock(&playback_mutex);
-	Nsf_Emu *my_nsf = (Nsf_Emu *) arg;
-        Music_Emu::sample_t buf[1024];
-
-	for (;;)
-	{
-		if (!console_ip_is_going)
-			break;
-
-		my_nsf->play(1024, buf);
-
-		if ((console_ip.output->output_time() / 1000) > 
-			audcfg.loop_length && audcfg.loop_length != 0)
-			break;
-		produce_audio(console_ip.output->written_time(),
-			FMT_S16_NE, 1, 2048, buf, NULL);
-	        while(console_ip.output->buffer_free() < 2048)
-			xmms_usleep(10000);
-	}
-
-        delete nsf;
-        console_ip.output->close_audio();
-	console_ip_is_going = FALSE;
-	playing_type = PLAY_TYPE_NONE;
-	g_static_mutex_unlock(&playback_mutex);
-        g_thread_exit(NULL);
-
-        return NULL;
+	console_ip.description = g_strdup_printf(_("SPC, VGM, NSF/NSFE, GBS, and GYM module decoder"));
+	return &console_ip;
 }
 
-static void *play_loop_gbs(gpointer arg)
-{
-	g_static_mutex_lock(&playback_mutex);
-	Gbs_Emu *my_gbs = (Gbs_Emu *) arg;
-        Music_Emu::sample_t buf[1024];
-
-	for (;;)
-	{
-		if (!console_ip_is_going)
-			break;
-
-		my_gbs->play(1024, buf);
-
-		if ((console_ip.output->output_time() / 1000) > 
-			audcfg.loop_length && audcfg.loop_length != 0)
-			break;
-		produce_audio(console_ip.output->written_time(),
-			FMT_S16_NE, 1, 2048, buf, NULL);
-	        while(console_ip.output->buffer_free() < 2048)
-			xmms_usleep(10000);
-	}
-
-        delete gbs;
-        console_ip.output->close_audio();
-	console_ip_is_going = FALSE;
-	playing_type = PLAY_TYPE_NONE;
-	g_static_mutex_unlock(&playback_mutex);
-        g_thread_exit(NULL);
-
-        return NULL;
-}
-
-static void *play_loop_gym(gpointer arg)
-{
-	g_static_mutex_lock(&playback_mutex);
-	Gym_Emu *my_gym = (Gym_Emu *) arg;
-        Music_Emu::sample_t buf[1024];
-
-	for (;;)
-	{
-		if (!console_ip_is_going)
-			break;
-
-		my_gym->play(1024, buf);
-
-		if ((console_ip.output->output_time() / 1000) > 
-			gym->track_length() && gym->track_length() != 0)
-			break;
-		if ((console_ip.output->output_time() / 1000) > 
-			audcfg.loop_length && audcfg.loop_length != 0)
-			break;
-		produce_audio(console_ip.output->written_time(),
-			FMT_S16_NE, 1, 2048, buf, NULL);
-	        while(console_ip.output->buffer_free() < 2048)
-			xmms_usleep(10000);
-	}
-
-        delete gym;
-        console_ip.output->close_audio();
-	console_ip_is_going = FALSE;
-	playing_type = PLAY_TYPE_NONE;
-	g_static_mutex_unlock(&playback_mutex);
-        g_thread_exit(NULL);
-
-        return NULL;
-}
-
-static void *play_loop_vgm(gpointer arg)
-{
-	g_static_mutex_lock(&playback_mutex);
-	Vgm_Emu *my_vgm = (Vgm_Emu *) arg;
-        Music_Emu::sample_t buf[1024];
-
-	for (;;)
-	{
-		if (!console_ip_is_going)
-			break;
-
-		my_vgm->play(1024, buf);
-
-		if ((console_ip.output->output_time() / 1000) > 
-			vgm->track_length() && vgm->track_length() != 0)
-			break;
-		if ((console_ip.output->output_time() / 1000) > 
-			audcfg.loop_length && audcfg.loop_length != 0)
-			break;
-		produce_audio(console_ip.output->written_time(),
-			FMT_S16_NE, 1, 2048, buf, NULL);
-	        while(console_ip.output->buffer_free() < 2048)
-			xmms_usleep(10000);
-	}
-
-        delete vgm;
-        console_ip.output->close_audio();
-	console_ip_is_going = FALSE;
-	playing_type = PLAY_TYPE_NONE;
-	g_static_mutex_unlock(&playback_mutex);
-        g_thread_exit(NULL);
-
-        return NULL;
-}
-
-static int get_time(void)
-{
-	if (console_ip_is_going == TRUE)
-		return console_ip.output->output_time();
-	else
-		return -1;
-}
-
-static void console_init(void)
-{
-	ConfigDb *db;
-
-	db = bmp_cfg_db_open();
-
-	bmp_cfg_db_get_int(db, "console", "loop_length", &audcfg.loop_length);
-	bmp_cfg_db_get_bool(db, "console", "resample", &audcfg.resample);
-	bmp_cfg_db_get_int(db, "console", "resample_rate", &audcfg.resample_rate);
-
-	bmp_cfg_db_close(db);
-}
-
-extern "C" void console_aboutbox(void)
-{
-	xmms_show_message(_("About the Console Music Decoder"),
-			_("Console music decoder engine based on Game_Music_Emu 0.2.4.\n"
-			  "Audacious implementation by: William Pitcock <nenolod@nenolod.net>"),
-			_("Ok"),
-			FALSE, NULL, NULL);
-}
--- a/Plugins/Input/console/Audacious_Driver.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Audacious: Cross platform media player
- * Copyright (c) 2005  Audacious team
- *
- * Header for the Audacious plugin.
- */
-
-#ifndef AUDACIOUS_DRIVER_H
-#define AUDACIOUS_DRIVER_H
-
-#include <glib.h>
-
-#include "Blip_Buffer.h"
-#include "Blip_Synth.h"
-#include "Classic_Emu.h"
-#include "Effects_Buffer.h"
-#include "Fir_Resampler.h"
-#include "Gb_Apu.h"
-#include "Gb_Cpu.h"
-#include "Gb_Oscs.h"
-#include "Gbs_Emu.h"
-#include "Gym_Emu.h"
-#include "Multi_Buffer.h"
-#include "Music_Emu.h"
-#include "Nes_Apu.h"
-#include "Nes_Cpu.h"
-#include "Nes_Namco.h"
-#include "Nes_Oscs.h"
-#include "Nes_Vrc6.h"
-#include "Nsf_Emu.h"
-#include "Panning_Buffer.h"
-#include "Sms_Apu.h"
-#include "Sms_Oscs.h"
-#include "Snes_Spc.h"
-#include "Spc_Cpu.h"
-#include "Spc_Dsp.h"
-#include "Spc_Emu.h"
-#include "Tagged_Data.h"
-#include "Vgm_Emu.h"
-#include "abstract_file.h"
-#include "blargg_common.h"
-#include "blargg_endian.h"
-#include "blargg_source.h"
-#include "ym2612.h"
-
-struct AudaciousConsoleConfig {
-	gint loop_length;	// length to loop in seconds
-	gboolean resample;	// whether or not to resample
-	gint resample_rate;	// rate to resample at
-};
-
-#define PLAY_TYPE_NONE		0
-#define PLAY_TYPE_SPC		1
-#define PLAY_TYPE_NSF		2
-#define PLAY_TYPE_VGM		3
-#define PLAY_TYPE_GBS		4
-#define PLAY_TYPE_GYM		5
-
-#endif
--- a/Plugins/Input/console/Blip_Buffer.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Blip_Buffer.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,12 +1,15 @@
 
-// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
 
 #include "Blip_Buffer.h"
 
+#include <assert.h>
+#include <limits.h>
 #include <string.h>
+#include <stdlib.h>
 #include <math.h>
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -17,378 +20,387 @@
 Public License along with this module; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
 
-#include BLARGG_SOURCE_BEGIN
+int const buffer_extra = blip_widest_impulse_ + 2;
 
 Blip_Buffer::Blip_Buffer()
 {
-	samples_per_sec = 44100;
-	buffer_ = NULL;
-	
-	// try to cause assertion failure if buffer is used before these are set
-	clocks_per_sec = 0;
-	factor_ = ~0ul;
+	factor_ = LONG_MAX;
 	offset_ = 0;
+	buffer_ = 0;
 	buffer_size_ = 0;
+	sample_rate_ = 0;
+	reader_accum = 0;
+	bass_shift = 0;
+	clock_rate_ = 0;
+	bass_freq_ = 16;
 	length_ = 0;
 	
-	bass_freq_ = 16;
+	// assumptions code makes about implementation-defined features
+	#ifndef NDEBUG
+		// right shift of negative value preserves sign
+		buf_t_ i = -0x7FFFFFFE;
+		assert( (i >> 1) == -0x3FFFFFFF );
+		
+		// casting to short truncates to 16 bits and sign-extends
+		i = 0x18000;
+		assert( (short) i == -0x8000 );
+	#endif
 }
 
-void Blip_Buffer::clear( bool entire_buffer )
+Blip_Buffer::~Blip_Buffer()
 {
-	long count = (entire_buffer ? buffer_size_ : samples_avail());
+	free( buffer_ );
+}
+
+void Blip_Buffer::clear( int entire_buffer )
+{
 	offset_ = 0;
 	reader_accum = 0;
-	memset( buffer_, sample_offset & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) );
+	if ( buffer_ )
+	{
+		long count = (entire_buffer ? buffer_size_ : samples_avail());
+		memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) );
+	}
 }
 
-blargg_err_t Blip_Buffer::sample_rate( long new_rate, int msec )
+Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
 {
-	size_t new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64;
-	if ( msec != blip_default_length )
+	// start with maximum length that resampled time can represent
+	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64;
+	if ( msec != blip_max_length )
 	{
-		size_t s = (new_rate * (msec + 1) + 999) / 1000;
+		long s = (new_rate * (msec + 1) + 999) / 1000;
 		if ( s < new_size )
 			new_size = s;
 		else
-			require( false ); // requested buffer length exceeds limit
+			assert( 0 ); // fails if requested buffer length exceeds limit
 	}
 	
 	if ( buffer_size_ != new_size )
 	{
-		delete [] buffer_;
-		buffer_ = NULL; // allow for exception in allocation below
-		buffer_size_ = 0;
-		offset_ = 0;
-		
-		buffer_ = new buf_t_ [new_size + widest_impulse_];
-		if ( !buffer_ )
+		void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ );
+		if ( !p )
 			return "Out of memory";
+		buffer_ = (buf_t_*) p;
 	}
 	
 	buffer_size_ = new_size;
+	
+	// update things based on the sample rate
+	sample_rate_ = new_rate;
 	length_ = new_size * 1000 / new_rate - 1;
 	if ( msec )
 		assert( length_ == msec ); // ensure length is same as that passed in
-	
-	samples_per_sec = new_rate;
-	if ( clocks_per_sec )
-		clock_rate( clocks_per_sec ); // recalculate factor
-	
-	bass_freq( bass_freq_ ); // recalculate shift
+	if ( clock_rate_ )
+		clock_rate( clock_rate_ );
+	bass_freq( bass_freq_ );
 	
 	clear();
 	
-	return blargg_success;
+	return 0; // success
 }
 
-void Blip_Buffer::clock_rate( long cps )
+blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const
 {
-	clocks_per_sec = cps;
-	factor_ = (unsigned long) floor( (double) samples_per_sec / cps *
-			(1L << BLIP_BUFFER_ACCURACY) + 0.5 );
-	require( factor_ > 0 ); // clock_rate/sample_rate ratio is too large
-}
-
-Blip_Buffer::~Blip_Buffer()
-{
-	delete [] buffer_;
+	double ratio = (double) sample_rate_ / clock_rate;
+	long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+	assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large
+	return (blip_resampled_time_t) factor;
 }
 
 void Blip_Buffer::bass_freq( int freq )
 {
 	bass_freq_ = freq;
-	if ( freq == 0 ) {
-		bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere
-		return;
+	int shift = 31;
+	if ( freq > 0 )
+	{
+		shift = 13;
+		long f = (freq << 16) / sample_rate_;
+		while ( (f >>= 1) && --shift ) { }
 	}
-	bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) );
-	if ( bass_shift < 0 )
-		bass_shift = 0;
-	if ( bass_shift > 24 )
-		bass_shift = 24;
-}
-
-long Blip_Buffer::count_samples( blip_time_t t ) const {
-	return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY);
-}
-
-void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb )
-{
-	fine_bits = fb;
-	width = w;
-	impulses = (imp_t*) imps;
-	generate = true;
-	volume_unit_ = -1.0;
-	res = r;
-	buf = NULL;
-	
-	impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)];
-	offset = 0;
+	bass_shift = shift;
 }
 
-const int impulse_bits = 15;
-const long impulse_amp = 1L << impulse_bits;
-const long impulse_offset = impulse_amp / 2;
-
-void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const
+void Blip_Buffer::end_frame( blip_time_t t )
 {
-	long offset = ((long) unit << impulse_bits) - impulse_offset * unit +
-			(1 << (impulse_bits - 1));
-	imp_t* imp = imp_in;
-	imp_t* fimp = impulse;
-	for ( int n = res / 2 + 1; n--; )
-	{
-		int error = unit;
-		for ( int nn = width; nn--; )
-		{
-			long a = ((long) *fimp++ * unit + offset) >> impulse_bits;
-			error -= a - unit;
-			*imp++ = (imp_t) a;
-		}
-		
-		// add error to middle
-		imp [-width / 2 - 1] += (imp_t) error;
-	}
-	
-	if ( res > 2 ) {
-		// second half is mirror-image
-		const imp_t* rev = imp - width - 1;
-		for ( int nn = (res / 2 - 1) * width - 1; nn--; )
-			*imp++ = *--rev;
-		*imp++ = (imp_t) unit;
-	}
-	
-	// copy to odd offset
-	*imp++ = (imp_t) unit;
-	memcpy( imp, imp_in, (res * width - 1) * sizeof *imp );
-}
-
-const int max_res = 1 << blip_res_bits_;
-
-void Blip_Impulse_::fine_volume_unit()
-{
-	// to do: find way of merging in-place without temporary buffer
-	
-	imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_];
-	scale_impulse( (offset & 0xffff) << fine_bits, temp );
-	imp_t* imp2 = impulses + res * 2 * width;
-	scale_impulse( offset & 0xffff, imp2 );
-	
-	// merge impulses
-	imp_t* imp = impulses;
-	imp_t* src2 = temp;
-	for ( int n = res / 2 * 2 * width; n--; ) {
-		*imp++ = *imp2++;
-		*imp++ = *imp2++;
-		*imp++ = *src2++;
-		*imp++ = *src2++;
-	}
+	offset_ += t * factor_;
+	assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length
 }
 
-void Blip_Impulse_::volume_unit( double new_unit )
+void Blip_Buffer::remove_silence( long count )
 {
-	if ( new_unit == volume_unit_ )
-		return;
-	
-	if ( generate )
-		treble_eq( blip_eq_t( -8.87, 8800, 44100 ) );
-	
-	volume_unit_ = new_unit;
-	
-	offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 );
-	
-	if ( fine_bits )
-		fine_volume_unit();
-	else
-		scale_impulse( offset & 0xffff, impulses );
+	assert( count <= samples_avail() ); // tried to remove more samples than available
+	offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
 }
 
-static const double pi = 3.1415926535897932384626433832795029L;
-
-void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq )
+long Blip_Buffer::count_samples( blip_time_t t ) const
 {
-	if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff &&
-			new_eq.sample_rate == eq.sample_rate )
-		return; // already calculated with same parameters
-	
-	generate = false;
-	eq = new_eq;
-	
-	double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50)
-	if ( treble < 0.000005 )
-		treble = 0.000005;
-	
-	const double treble_freq = 22050.0; // treble level at 22 kHz harmonic
-	const double sample_rate = eq.sample_rate;
-	const double pt = treble_freq * 2 / sample_rate;
-	double cutoff = eq.cutoff * 2 / sample_rate;
-	if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) {
-		cutoff = 0.5;
-		treble = 1.0;
-	}
-	
-	// DSF Synthesis (See T. Stilson & J. Smith (1996),
-	// Alias-free digital synthesis of classic analog waveforms)
-	
-	// reduce adjacent impulse interference by using small part of wide impulse
-	const double n_harm = 4096;
-	const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) );
-	const double rescale = 1.0 / pow( rolloff, n_harm * cutoff );
-	
-	const double pow_a_n = rescale * pow( rolloff, n_harm );
-	const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff );
-	
-	double total = 0.0;
-	const double to_angle = pi / 2 / n_harm / max_res;
-	
-	float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2];
-	const int size = max_res * (width - 2) / 2;
-	for ( int i = size; i--; )
-	{
-		double angle = (i * 2 + 1) * to_angle;
-		
-		// equivalent
-		//double y =     dsf( angle, n_harm * cutoff, 1.0 );
-		//y -= rescale * dsf( angle, n_harm * cutoff, rolloff );
-		//y += rescale * dsf( angle, n_harm,          rolloff );
-		
-		const double cos_angle = cos( angle );
-		const double cos_nc_angle = cos( n_harm * cutoff * angle );
-		const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle );
-		
-		double b = 2.0 - 2.0 * cos_angle;
-		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
-		
-		double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle);
-		double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) -
-				pow_a_n * cos( n_harm * angle ) -
-				pow_a_nc * rolloff * cos_nc1_angle +
-				pow_a_nc * cos_nc_angle;
-		
-		// optimization of a / b + c / d
-		double y = (a * d + c * b) / (b * d);
-		
-		// fixed window which affects wider impulses more
-		if ( width > 12 ) {
-			double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle );
-			y *= window * window;
-		}
-		
-		total += (float) y;
-		buf [i] = (float) y;
-	}
-	
-	// integrate runs of length 'max_res'
-	double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half
-	imp_t* imp = impulse;
-	const int step = max_res / res;
-	int offset = res > 1 ? max_res : max_res / 2;
-	for ( int n = res / 2 + 1; n--; offset -= step )
-	{
-		for ( int w = -width / 2; w < width / 2; w++ )
-		{
-			double sum = 0;
-			for ( int i = max_res; i--; )
-			{
-				int index = w * max_res + offset + i;
-				if ( index < 0 )
-					index = -index - 1;
-				if ( index < size )
-					sum += buf [index];
-			}
-			*imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) );
-		}
-	}
-	
-	// rescale
-	double unit = volume_unit_;
-	if ( unit >= 0 ) {
-		volume_unit_ = -1;
-		volume_unit( unit );
-	}
+	unsigned long last_sample  = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
+	unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
+	return (long) (last_sample - first_sample);
+}
+
+blip_time_t Blip_Buffer::count_clocks( long count ) const
+{
+	if ( count > buffer_size_ )
+		count = buffer_size_;
+	blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+	return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_);
 }
 
 void Blip_Buffer::remove_samples( long count )
 {
-	require( buffer_ ); // sample rate must have been set
+	if ( count )
+	{
+		remove_silence( count );
+		
+		// copy remaining samples to beginning and clear old samples
+		long remain = samples_avail() + buffer_extra;
+		memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
+		memset( buffer_ + remain, 0, count * sizeof *buffer_ );
+	}
+}
+
+// Blip_Synth_
+
+Blip_Synth_::Blip_Synth_( short* p, int w ) :
+	impulses( p ),
+	width( w )
+{
+	volume_unit_ = 0.0;
+	kernel_unit = 0;
+	buf = 0;
+	last_amp = 0;
+	delta_factor = 0;
+}
+
+static double const pi = 3.1415926535897932384626433832795029;
+
+static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff )
+{
+	if ( cutoff >= 0.999 )
+		cutoff = 0.999;
 	
-	if ( !count ) // optimization
-		return;
-	
-	remove_silence( count );
+	if ( treble < -300.0 )
+		treble = -300.0;
+	if ( treble > 5.0 )
+		treble = 5.0;
 	
-	// copy remaining samples to beginning and clear old samples
-	long remain = samples_avail() + widest_impulse_;
-	if ( count >= remain )
-		memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) );
-	else
-		memcpy(  buffer_, buffer_ + count, remain * sizeof (buf_t_) );
-	memset( buffer_ + remain, sample_offset & 0xFF, count * sizeof (buf_t_) );
+	double const maxh = 4096.0;
+	double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) );
+	double const pow_a_n = pow( rolloff, maxh - maxh * cutoff );
+	double const to_angle = pi / 2 / maxh / oversample;
+	for ( int i = 0; i < count; i++ )
+	{
+		double angle = ((i - count) * 2 + 1) * to_angle;
+		double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle );
+		double cos_nc_angle = cos( maxh * cutoff * angle );
+		double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle );
+		double cos_angle = cos( angle );
+		
+		c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
+		double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
+		double b = 2.0 - cos_angle - cos_angle;
+		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
+		
+		out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
+	}
+}
+
+void blip_eq_t::generate( float* out, int count ) const
+{
+	// lower cutoff freq for narrow kernels with their wider transition band
+	// (8 points->1.49, 16 points->1.15)
+	double oversample = blip_res * 2.25 / count + 0.85;
+	double half_rate = sample_rate * 0.5;
+	if ( cutoff_freq )
+		oversample = half_rate / cutoff_freq;
+	double cutoff = rolloff_freq * oversample / half_rate;
+	
+	gen_sinc( out, count, blip_res * oversample, treble, cutoff );
+	
+	// apply (half of) hamming window
+	double to_fraction = pi / (count - 1);
+	for ( int i = count; i--; )
+		out [i] *= 0.54 - 0.46 * cos( i * to_fraction );
 }
 
-#include BLARGG_ENABLE_OPTIMIZER
+void Blip_Synth_::adjust_impulse()
+{
+	// sum pairs for each phase and add error correction to end of first half
+	int const size = impulses_size();
+	for ( int p = blip_res; p-- >= blip_res / 2; )
+	{
+		int p2 = blip_res - 2 - p;
+		long error = kernel_unit;
+		for ( int i = 1; i < size; i += blip_res )
+		{
+			error -= impulses [i + p ];
+			error -= impulses [i + p2];
+		}
+		if ( p == p2 )
+			error /= 2; // phase = 0.5 impulse uses same half for both sides
+		impulses [size - blip_res + p] += error;
+		//printf( "error: %ld\n", error );
+	}
+	
+	//for ( int i = blip_res; i--; printf( "\n" ) )
+	//  for ( int j = 0; j < width / 2; j++ )
+	//      printf( "%5ld,", impulses [j * blip_res + i + 1] );
+}
 
-long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo )
+void Blip_Synth_::treble_eq( blip_eq_t const& eq )
 {
-	require( buffer_ ); // sample rate must have been set
+	float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
+	
+	int const half_size = blip_res / 2 * (width - 1);
+	eq.generate( &fimpulse [blip_res], half_size );
+	
+	int i;
+	
+	// need mirror slightly past center for calculation
+	for ( i = blip_res; i--; )
+		fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i];
+	
+	// starts at 0
+	for ( i = 0; i < blip_res; i++ )
+		fimpulse [i] = 0.0f;
+	
+	// find rescale factor
+	double total = 0.0;
+	for ( i = 0; i < half_size; i++ )
+		total += fimpulse [blip_res + i];
+	
+	//double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
+	//double const base_unit = 37888.0; // allows treble to +5 dB
+	double const base_unit = 32768.0; // necessary for blip_unscaled to work
+	double rescale = base_unit / 2 / total;
+	kernel_unit = (long) base_unit;
+	
+	// integrate, first difference, rescale, convert to int
+	double sum = 0.0;
+	double next = 0.0;
+	int const impulses_size = this->impulses_size();
+	for ( i = 0; i < impulses_size; i++ )
+	{
+		impulses [i] = (short) floor( (next - sum) * rescale + 0.5 );
+		sum += fimpulse [i];
+		next += fimpulse [i + blip_res];
+	}
+	adjust_impulse();
 	
+	// volume might require rescaling
+	double vol = volume_unit_;
+	if ( vol )
+	{
+		volume_unit_ = 0.0;
+		volume_unit( vol );
+	}
+}
+
+void Blip_Synth_::volume_unit( double new_unit )
+{
+	if ( new_unit != volume_unit_ )
+	{
+		// use default eq if it hasn't been set yet
+		if ( !kernel_unit )
+			treble_eq( -8.0 );
+		
+		volume_unit_ = new_unit;
+		double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
+		
+		if ( factor > 0.0 )
+		{
+			int shift = 0;
+			
+			// if unit is really small, might need to attenuate kernel
+			while ( factor < 2.0 )
+			{
+				shift++;
+				factor *= 2.0;
+			}
+			
+			if ( shift )
+			{
+				kernel_unit >>= shift;
+				assert( kernel_unit > 0 ); // fails if volume unit is too low
+				
+				// keep values positive to avoid round-towards-zero of sign-preserving
+				// right shift for negative values
+				long offset = 0x8000 + (1 << (shift - 1));
+				long offset2 = 0x8000 >> shift;
+				for ( int i = impulses_size(); i--; )
+					impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2);
+				adjust_impulse();
+			}
+		}
+		delta_factor = (int) floor( factor + 0.5 );
+		//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
+	}
+}
+
+long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo )
+{
 	long count = samples_avail();
 	if ( count > max_samples )
 		count = max_samples;
 	
-	if ( !count )
-		return 0; // optimization
-	
-	int sample_offset = this->sample_offset;
-	int bass_shift = this->bass_shift;
-	buf_t_* buf = buffer_;
-	long accum = reader_accum;
-	
-	if ( !stereo ) {
-		for ( long n = count; n--; ) {
-			long s = accum >> accum_fract;
-			accum -= accum >> bass_shift;
-			accum += (long (*buf++) - sample_offset) << accum_fract;
-			*out++ = (blip_sample_t) s;
-			
-			// clamp sample
-			if ( (BOOST::int16_t) s != s )
-				out [-1] = blip_sample_t (0x7FFF - (s >> 24));
+	if ( count )
+	{
+		int const sample_shift = blip_sample_bits - 16;
+		int const bass_shift = this->bass_shift;
+		long accum = reader_accum;
+		buf_t_* in = buffer_;
+		
+		if ( !stereo )
+		{
+			for ( long n = count; n--; )
+			{
+				long s = accum >> sample_shift;
+				accum -= accum >> bass_shift;
+				accum += *in++;
+				*out++ = (blip_sample_t) s;
+				
+				// clamp sample
+				if ( (blip_sample_t) s != s )
+					out [-1] = (blip_sample_t) (0x7FFF - (s >> 24));
+			}
 		}
+		else
+		{
+			for ( long n = count; n--; )
+			{
+				long s = accum >> sample_shift;
+				accum -= accum >> bass_shift;
+				accum += *in++;
+				*out = (blip_sample_t) s;
+				out += 2;
+				
+				// clamp sample
+				if ( (blip_sample_t) s != s )
+					out [-2] = (blip_sample_t) (0x7FFF - (s >> 24));
+			}
+		}
+		
+		reader_accum = accum;
+		remove_samples( count );
 	}
-	else {
-		for ( long n = count; n--; ) {
-			long s = accum >> accum_fract;
-			accum -= accum >> bass_shift;
-			accum += (long (*buf++) - sample_offset) << accum_fract;
-			*out = (blip_sample_t) s;
-			out += 2;
-			
-			// clamp sample
-			if ( (BOOST::int16_t) s != s )
-				out [-2] = blip_sample_t (0x7FFF - (s >> 24));
-		}
-	}
-	
-	reader_accum = accum;
-	
-	remove_samples( count );
-	
 	return count;
 }
 
-void Blip_Buffer::mix_samples( const blip_sample_t* in, long count )
+void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
 {
-	buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)];
+	buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
 	
+	int const sample_shift = blip_sample_bits - 16;
 	int prev = 0;
-	while ( count-- ) {
-		int s = *in++;
-		*buf += s - prev;
+	while ( count-- )
+	{
+		long s = (long) *in++ << sample_shift;
+		*out += s - prev;
 		prev = s;
-		++buf;
+		++out;
 	}
-	*buf -= *--in;
+	*out -= prev;
 }
 
--- a/Plugins/Input/console/Blip_Buffer.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Blip_Buffer.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,248 +1,354 @@
 
-// Buffer of sound samples into which band-limited waveforms can be synthesized
-// using Blip_Wave or Blip_Synth.
+// Band-limited sound synthesis and buffering
 
-// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Blip_Buffer 0.4.0
 
 #ifndef BLIP_BUFFER_H
 #define BLIP_BUFFER_H
 
-#include "blargg_common.h"
-
-class Blip_Reader;
-
-// Source time unit.
+// Time unit at source clock rate
 typedef long blip_time_t;
 
-// Type of sample produced. Signed 16-bit format.
-typedef BOOST::int16_t blip_sample_t;
-
-// Make buffer as large as possible (currently about 65000 samples)
-const int blip_default_length = 0;
+// Output samples are 16-bit signed, with a range of -32768 to 32767
+typedef short blip_sample_t;
+enum { blip_sample_max = 32767 };
 
 class Blip_Buffer {
 public:
-	// Construct an empty buffer.
-	Blip_Buffer();
-	~Blip_Buffer();
+	typedef const char* blargg_err_t;
+	
+	// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
+	// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
+	// isn't enough memory, returns error without affecting current buffer setup.
+	blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
+	
+	// Set number of source time units per second
+	void clock_rate( long );
 	
-	// Set output sample rate and buffer length in milliseconds (1/1000 sec),
-	// then clear buffer. If length is not specified, make as large as possible.
-	// If there is insufficient memory for the buffer, sets the buffer length
-	// to 0 and returns error string (or propagates exception if compiler supports it).
-	blargg_err_t sample_rate( long samples_per_sec, int msec_length = blip_default_length );
+	// End current time frame of specified duration and make its samples available
+	// (along with any still-unread samples) for reading with read_samples(). Begins
+	// a new time frame at the end of the current frame.
+	void end_frame( blip_time_t time );
+	
+	// Read at most 'max_samples' out of buffer into 'dest', removing them from from
+	// the buffer. Returns number of samples actually read and removed. If stereo is
+	// true, increments 'dest' one extra time after writing each sample, to allow
+	// easy interleving of two channels into a stereo output buffer.
+	long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 );
+	
+// Additional optional features
+
+	// Current output sample rate
+	long sample_rate() const;
 	
 	// Length of buffer, in milliseconds
 	int length() const;
 	
-	// Current output sample rate
-	long sample_rate() const;
-	
 	// Number of source time units per second
-	void clock_rate( long );
 	long clock_rate() const;
 	
-	// Set frequency at which high-pass filter attenuation passes -3dB
+	// Set frequency high-pass filter frequency, where higher values reduce bass more
 	void bass_freq( int frequency );
 	
-	// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
-	// false, just clear out any samples waiting rather than the entire buffer.
-	void clear( bool entire_buffer = true );
+	// Number of samples delay from synthesis to samples read out
+	int output_latency() const;
 	
-	// End current time frame of specified duration and make its samples available
-	// (along with any still-unread samples) for reading with read_samples(). Begin
-	// a new time frame at the end of the current frame. All transitions must have
-	// been added before 'time'.
-	void end_frame( blip_time_t time );
+	// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
+	// false, just clears out any samples waiting rather than the entire buffer.
+	void clear( int entire_buffer = 1 );
 	
 	// Number of samples available for reading with read_samples()
 	long samples_avail() const;
 	
-	// Read at most 'max_samples' out of buffer into 'dest', removing them from from
-	// the buffer. Return number of samples actually read and removed. If stereo is
-	// true, increment 'dest' one extra time after writing each sample, to allow
-	// easy interleving of two channels into a stereo output buffer.
-	long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false );
-	
 	// Remove 'count' samples from those waiting to be read
 	void remove_samples( long count );
 	
-	// Number of samples delay from synthesis to samples read out
-	int output_latency() const;
-	
+// Experimental features
 	
-	// Experimental external buffer mixing support
-	
-	// Number of raw samples that can be mixed within frame of specified duration
+	// Number of raw samples that can be mixed within frame of specified duration.
 	long count_samples( blip_time_t duration ) const;
 	
 	// Mix 'count' samples from 'buf' into buffer.
-	void mix_samples( const blip_sample_t* buf, long count );
+	void mix_samples( blip_sample_t const* buf, long count );
 	
+	// Count number of clocks needed until 'count' samples will be available.
+	// If buffer can't even hold 'count' samples, returns number of clocks until
+	// buffer becomes full.
+	blip_time_t count_clocks( long count ) const;
 	
 	// not documented yet
-	
+	typedef unsigned long blip_resampled_time_t;
 	void remove_silence( long count );
-	
-	typedef unsigned long resampled_time_t;
+	blip_resampled_time_t resampled_duration( int t ) const     { return t * factor_; }
+	blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; }
+	blip_resampled_time_t clock_rate_factor( long clock_rate ) const;
+public:
+	Blip_Buffer();
+	~Blip_Buffer();
 	
-	resampled_time_t resampled_time( blip_time_t t ) const {
-		return t * resampled_time_t (factor_) + offset_;
-	}
-	
-	resampled_time_t resampled_duration( int t ) const {
-		return t * resampled_time_t (factor_);
-	}
-	
+	// Deprecated
+	typedef blip_resampled_time_t resampled_time_t;
+	blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }
+	blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); }
 private:
 	// noncopyable
 	Blip_Buffer( const Blip_Buffer& );
 	Blip_Buffer& operator = ( const Blip_Buffer& );
-
-	// Don't use the following members. They are public only for technical reasons.
-	public:
-		enum { widest_impulse_ = 24 };
-		typedef BOOST::uint16_t buf_t_;
-		
-		unsigned long factor_;
-		resampled_time_t offset_;
-		buf_t_* buffer_;
-		unsigned buffer_size_;
-	private:
-		long reader_accum;
-		int bass_shift;
-		long samples_per_sec;
-		long clocks_per_sec;
-		int bass_freq_;
-		int length_;
-		
-		enum { accum_fract = 15 }; // less than 16 to give extra sample range
-		enum { sample_offset = 0x7F7F }; // repeated byte allows memset to clear buffer
-		
-		friend class Blip_Reader;
+public:
+	typedef long buf_t_;
+	unsigned long factor_;
+	blip_resampled_time_t offset_;
+	buf_t_* buffer_;
+	long buffer_size_;
+private:
+	long reader_accum;
+	int bass_shift;
+	long sample_rate_;
+	long clock_rate_;
+	int bass_freq_;
+	int length_;
+	friend class Blip_Reader;
 };
 
-// Low-pass equalization parameters (see notes.txt)
-class blip_eq_t {
-public:
-	blip_eq_t( double treble = 0 );
-	blip_eq_t( double treble, long cutoff, long sample_rate );
-private:
-	double treble;
-	long cutoff;
-	long sample_rate;
-	friend class Blip_Impulse_;
-};
+#ifdef HAVE_CONFIG_H
+	#include "config.h"
+#endif
 
-// not documented yet (see Multi_Buffer.cpp for an example of use)
-class Blip_Reader {
-	const Blip_Buffer::buf_t_* buf;
-	long accum;
-	#ifdef __MWERKS__
-	void operator = ( struct foobar ); // helps optimizer
-	#endif
-public:
-	// avoid anything which might cause optimizer to put object in memory
-	
-	int begin( Blip_Buffer& blip_buf ) {
-		buf = blip_buf.buffer_;
-		accum = blip_buf.reader_accum;
-		return blip_buf.bass_shift;
-	}
-	
-	int read() const {
-		return accum >> Blip_Buffer::accum_fract;
-	}
-	
-	void next( int bass_shift = 9 ) {
-		accum -= accum >> bass_shift;
-		accum += ((long) *buf++ - Blip_Buffer::sample_offset) << Blip_Buffer::accum_fract;
-	}
-	
-	void end( Blip_Buffer& blip_buf ) {
-		blip_buf.reader_accum = accum;
-	}
-};
-
-
-
-// End of public interface
-	
+// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
+// but reduce maximum buffer size.
 #ifndef BLIP_BUFFER_ACCURACY
 	#define BLIP_BUFFER_ACCURACY 16
 #endif
 
-const int blip_res_bits_ = 5;
+// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
+// noticeable broadband noise when synthesizing high frequency square waves.
+// Affects size of Blip_Synth objects since they store the waveform directly.
+#ifndef BLIP_PHASE_BITS
+	#define BLIP_PHASE_BITS 6
+#endif
 
-typedef BOOST::uint32_t blip_pair_t_;
-
-class Blip_Impulse_ {
-	typedef BOOST::uint16_t imp_t;
+	// Internal
+	typedef unsigned long blip_resampled_time_t;
+	int const blip_widest_impulse_ = 16;
+	int const blip_res = 1 << BLIP_PHASE_BITS;
+	class blip_eq_t;
 	
-	blip_eq_t eq;
-	double  volume_unit_;
-	imp_t*  impulses;
-	imp_t*  impulse;
-	int     width;
-	int     fine_bits;
-	int     res;
-	bool    generate;
+	class Blip_Synth_ {
+		double volume_unit_;
+		short* const impulses;
+		int const width;
+		long kernel_unit;
+		int impulses_size() const { return blip_res / 2 * width + 1; }
+		void adjust_impulse();
+	public:
+		Blip_Buffer* buf;
+		int last_amp;
+		int delta_factor;
+		
+		Blip_Synth_( short* impulses, int width );
+		void treble_eq( blip_eq_t const& );
+		void volume_unit( double );
+	};
+
+// Quality level. Start with blip_good_quality.
+const int blip_med_quality  = 8;
+const int blip_good_quality = 12;
+const int blip_high_quality = 16;
+
+// Range specifies the greatest expected change in amplitude. Calculate it
+// by finding the difference between the maximum and minimum expected
+// amplitudes (max - min).
+template<int quality,int range>
+class Blip_Synth {
+public:
+	// Set overall volume of waveform
+	void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
+	
+	// Configure low-pass filter (see notes.txt)
+	void treble_eq( blip_eq_t const& eq )       { impl.treble_eq( eq ); }
 	
-	void fine_volume_unit();
-	void scale_impulse( int unit, imp_t* ) const;
+	// Get/set Blip_Buffer used for output
+	Blip_Buffer* output() const                 { return impl.buf; }
+	void output( Blip_Buffer* b )               { impl.buf = b; impl.last_amp = 0; }
+	
+	// Update amplitude of waveform at given time. Using this requires a separate
+	// Blip_Synth for each waveform.
+	void update( blip_time_t time, int amplitude );
+
+// Low-level interface
+
+	// Add an amplitude transition of specified delta, optionally into specified buffer
+	// rather than the one set with output(). Delta can be positive or negative.
+	// The actual change in amplitude is delta * (volume / range)
+	void offset( blip_time_t, int delta, Blip_Buffer* ) const;
+	void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); }
+	
+	// Works directly in terms of fractional output samples. Contact author for more.
+	void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
+	
+	// Same as offset(), except code is inlined for higher performance
+	void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const {
+		offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
+	}
+	void offset_inline( blip_time_t t, int delta ) const {
+		offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
+	}
+	
 public:
-	Blip_Buffer*    buf;
-	BOOST::uint32_t offset;
-	
-	void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 );
-	void volume_unit( double );
-	void treble_eq( const blip_eq_t& );
+	Blip_Synth() : impl( impulses, quality ) { }
+private:
+	typedef short imp_t;
+	imp_t impulses [blip_res * (quality / 2) + 1];
+	Blip_Synth_ impl;
 };
 
-inline blip_eq_t::blip_eq_t( double t ) :
-		treble( t ), cutoff( 0 ), sample_rate( 44100 ) {
-}
+// Low-pass equalization parameters
+class blip_eq_t {
+public:
+	// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce
+	// treble, small positive values (0 to 5.0) increase treble.
+	blip_eq_t( double treble_db = 0 );
+	
+	// See notes.txt
+	blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
+	
+private:
+	double treble;
+	long rolloff_freq;
+	long sample_rate;
+	long cutoff_freq;
+	void generate( float* out, int count ) const;
+	friend class Blip_Synth_;
+};
+
+int const blip_sample_bits = 30;
+
+// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples
+class Blip_Reader {
+public:
+	// Begin reading samples from buffer. Returns value to pass to next() (can
+	// be ignored if default bass_freq is acceptable).
+	int begin( Blip_Buffer& );
+	
+	// Current sample
+	long read() const               { return accum >> (blip_sample_bits - 16); }
+	
+	// Current raw sample in full internal resolution
+	long read_raw() const           { return accum; }
+	
+	// Advance to next sample
+	void next( int bass_shift = 9 )         { accum += *buf++ - (accum >> bass_shift); }
+	
+	// End reading samples from buffer. The number of samples read must now be removed
+	// using Blip_Buffer::remove_samples().
+	void end( Blip_Buffer& b )              { b.reader_accum = accum; }
+	
+private:
+	const Blip_Buffer::buf_t_* buf;
+	long accum;
+};
+
+
+// End of public interface
+
+
+#include <assert.h>
 
-inline blip_eq_t::blip_eq_t( double t, long c, long sr ) :
-		treble( t ), cutoff( c ), sample_rate( sr ) {
+// Compatibility with older version
+const long blip_unscaled = 65535;
+const int blip_low_quality  = blip_med_quality;
+const int blip_best_quality = blip_high_quality;
+
+#define BLIP_FWD( i ) {                     \
+	long t0 = i0 * delta + buf [fwd + i];   \
+	long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i]; \
+	i0 = imp [blip_res * (i + 2)];          \
+	buf [fwd + i] = t0;                     \
+	buf [fwd + 1 + i] = t1; }
+
+#define BLIP_REV( r ) {                     \
+	long t0 = i0 * delta + buf [rev - r];   \
+	long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];   \
+	i0 = imp [blip_res * (r - 1)];          \
+	buf [rev - r] = t0;                     \
+	buf [rev + 1 - r] = t1; }
+
+template<int quality,int range>
+inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
+		int delta, Blip_Buffer* blip_buf ) const
+{
+	// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
+	// need for a longer buffer as set by set_sample_rate().
+	assert( (long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
+	delta *= impl.delta_factor;
+	int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
+	imp_t const* imp = impulses + blip_res - phase;
+	long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
+	long i0 = *imp;
+	
+	int const fwd = (blip_widest_impulse_ - quality) / 2;
+	int const rev = fwd + quality - 2;
+	
+	BLIP_FWD( 0 )
+	if ( quality > 8  ) BLIP_FWD( 2 )
+	if ( quality > 12 ) BLIP_FWD( 4 )
+	{
+		int const mid = quality / 2 - 1;
+		long t0 = i0 * delta + buf [fwd + mid - 1];
+		long t1 = imp [blip_res * mid] * delta + buf [fwd + mid];
+		imp = impulses + phase;
+		i0 = imp [blip_res * mid];
+		buf [fwd + mid - 1] = t0;
+		buf [fwd + mid] = t1;
+	}
+	if ( quality > 12 ) BLIP_REV( 6 )
+	if ( quality > 8  ) BLIP_REV( 4 )
+	BLIP_REV( 2 )
+	
+	long t0 = i0 * delta + buf [rev];
+	long t1 = *imp * delta + buf [rev + 1];
+	buf [rev] = t0;
+	buf [rev + 1] = t1;
 }
 
-inline int Blip_Buffer::length() const {
-	return length_;
-}
+#undef BLIP_FWD
+#undef BLIP_REV
 
-inline long Blip_Buffer::samples_avail() const {
-	return long (offset_ >> BLIP_BUFFER_ACCURACY);
+template<int quality,int range>
+void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
+{
+	offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
 }
 
-inline long Blip_Buffer::sample_rate() const {
-	return samples_per_sec;
-}
-
-inline void Blip_Buffer::end_frame( blip_time_t t ) {
-	offset_ += t * factor_;
-	assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer",
-			samples_avail() <= buffer_size_ ));
+template<int quality,int range>
+void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
+{
+	int delta = amp - impl.last_amp;
+	impl.last_amp = amp;
+	offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
 }
 
-inline void Blip_Buffer::remove_silence( long count ) {
-	assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available",
-			count <= samples_avail() ));
-	offset_ -= resampled_time_t (count) << BLIP_BUFFER_ACCURACY;
+inline blip_eq_t::blip_eq_t( double t ) :
+		treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
+inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) :
+		treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
+
+inline int  Blip_Buffer::length() const         { return length_; }
+inline long Blip_Buffer::samples_avail() const  { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); }
+inline long Blip_Buffer::sample_rate() const    { return sample_rate_; }
+inline int  Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; }
+inline long Blip_Buffer::clock_rate() const     { return clock_rate_; }
+inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); }
+
+inline int Blip_Reader::begin( Blip_Buffer& blip_buf )
+{
+	buf = blip_buf.buffer_;
+	accum = blip_buf.reader_accum;
+	return blip_buf.bass_shift;
 }
 
-inline int Blip_Buffer::output_latency() const {
-	return widest_impulse_ / 2;
-}
-
-inline long Blip_Buffer::clock_rate() const {
-	return clocks_per_sec;
-}
-
-// MSVC6 fix
-typedef Blip_Buffer::resampled_time_t blip_resampled_time_t;
-
-#include "Blip_Synth.h"
+int const blip_max_length = 0;
+int const blip_default_length = 250;
 
 #endif
 
--- a/Plugins/Input/console/Blip_Synth.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-
-// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding
-// waveforms to a Blip_Buffer.
-
-// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
-
-#ifndef BLIP_SYNTH_H
-#define BLIP_SYNTH_H
-
-#ifndef BLIP_BUFFER_H
-	#include "Blip_Buffer.h"
-#endif
-
-// Quality level. Higher levels are slower, and worse in a few cases.
-// Use blip_good_quality as a starting point.
-const int blip_low_quality = 1;
-const int blip_med_quality = 2;
-const int blip_good_quality = 3;
-const int blip_high_quality = 4;
-
-// Blip_Synth is a transition waveform synthesizer which adds band-limited
-// offsets (transitions) into a Blip_Buffer. For a simpler interface, use
-// Blip_Wave (below).
-//
-// Range specifies the greatest expected offset that will occur. For a
-// waveform that goes between +amp and -amp, range should be amp * 2 (half
-// that if it only goes between +amp and 0). When range is large, a higher
-// accuracy scheme is used; to force this even when range is small, pass
-// the negative of range (i.e. -range).
-template<int quality,int range>
-class Blip_Synth {
-	BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 );
-	BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 );
-	enum {
-		abs_range = (range < 0) ? -range : range,
-		fine_mode = (range > 512 || range < 0),
-		width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_),
-		res = 1 << blip_res_bits_,
-		impulse_size = width / 2 * (fine_mode + 1),
-		base_impulses_size = width / 2 * (res / 2 + 1),
-		fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 :
-			abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 :
-			abs_range <= 2048 ? 7 : 8) : 0)
-	};
-	blip_pair_t_  impulses [impulse_size * res * 2 + base_impulses_size];
-	Blip_Impulse_ impulse;
-public:
-	Blip_Synth()                            { impulse.init( impulses, width, res, fine_bits ); }
-	
-	// Configure low-pass filter (see notes.txt). Not optimized for real-time control
-	void treble_eq( const blip_eq_t& eq )   { impulse.treble_eq( eq ); }
-	
-	// Set volume of a transition at amplitude 'range' by setting volume_unit
-	// to v / range
-	void volume( double v )                 { impulse.volume_unit( v * (1.0 / abs_range) ); }
-	
-	// Set base volume unit of transitions, where 1.0 is a full swing between the
-	// positive and negative extremes. Not optimized for real-time control.
-	void volume_unit( double unit )         { impulse.volume_unit( unit ); }
-	
-	// Default Blip_Buffer used for output when none is specified for a given call
-	Blip_Buffer* output() const             { return impulse.buf; }
-	void output( Blip_Buffer* b )           { impulse.buf = b; }
-	
-	// Add an amplitude offset (transition) with an amplitude of delta * volume_unit
-	// into the specified buffer (default buffer if none specified) at the
-	// specified source time. Amplitude can be positive or negative. To increase
-	// performance by inlining code at the call site, use offset_inline().
-	void offset( blip_time_t, int delta, Blip_Buffer* ) const;
-	
-	void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
-	void offset_resampled( blip_resampled_time_t t, int o ) const {
-		offset_resampled( t, o, impulse.buf );
-	}
-	void offset( blip_time_t t, int delta ) const {
-		offset( t, delta, impulse.buf );
-	}
-	void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const {
-		offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
-	}
-	void offset_inline( blip_time_t time, int delta ) const {
-		offset_inline( time, delta, impulse.buf );
-	}
-};
-
-// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer.
-// A wave is built from a series of delays and new amplitudes. This provides a
-// simpler interface than Blip_Synth.
-template<int quality,int range>
-class Blip_Wave {
-	Blip_Synth<quality,range> synth;
-	blip_time_t time_;
-	int last_amp;
-public:
-	// Start wave at time 0 and amplitude 0
-	Blip_Wave()                         : time_( 0 ), last_amp( 0 ) { }
-	
-	// See Blip_Synth for description
-	void volume( double v )             { synth.volume( v ); }
-	void volume_unit( double v )        { synth.volume_unit( v ); }
-	void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); }
-	Blip_Buffer* output() const         { return synth.output(); }
-	void output( Blip_Buffer* b )       { synth.output( b ); if ( !b ) time_ = last_amp = 0; }
-	
-	// Current time in frame
-	blip_time_t time() const            { return time_; }
-	void time( blip_time_t t )          { time_ = t; }
-	
-	// Current amplitude of wave
-	int amplitude() const               { return last_amp; }
-	void amplitude( int );
-	
-	// Move forward by 't' time units
-	void delay( blip_time_t t )         { time_ += t; }
-	
-	// End time frame of specified duration. Localize time to new frame.
-	void end_frame( blip_time_t duration ) {
-		assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame",
-				duration <= time_ ));
-		time_ -= duration;
-	}
-};
-
-
-
-// End of public interface
-	
-	template<int quality,int range>
-	void Blip_Wave<quality,range>::amplitude( int amp ) {
-		int delta = amp - last_amp;
-		last_amp = amp;
-		synth.offset_inline( time_, delta );
-	}
-	
-	template<int quality,int range>
-	inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
-			int delta, Blip_Buffer* blip_buf ) const
-	{
-		typedef blip_pair_t_ pair_t;
-		
-		unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1;
-		assert(( "Blip_Synth/Blip_wave: Went past end of buffer",
-				sample_index < blip_buf->buffer_size_ ));
-		enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 };
-		pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index];
-		
-		enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ };
-		enum { mask = res * 2 - 1 };
-		const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size];
-		
-		pair_t offset = impulse.offset * delta;
-		
-		if ( !fine_bits )
-		{
-			// normal mode
-			for ( int n = width / 4; n; --n )
-			{
-				pair_t t0 = buf [0] - offset;
-				pair_t t1 = buf [1] - offset;
-				
-				t0 += imp [0] * delta;
-				t1 += imp [1] * delta;
-				imp += 2;
-				
-				buf [0] = t0;
-				buf [1] = t1;
-				buf += 2;
-			}
-		}
-		else
-		{
-			// fine mode
-			enum { sub_range = 1 << fine_bits };
-			delta += sub_range / 2;
-			int delta2 = (delta & (sub_range - 1)) - sub_range / 2;
-			delta >>= fine_bits;
-			
-			for ( int n = width / 4; n; --n )
-			{
-				pair_t t0 = buf [0] - offset;
-				pair_t t1 = buf [1] - offset;
-				
-				t0 += imp [0] * delta2;
-				t0 += imp [1] * delta;
-				
-				t1 += imp [2] * delta2;
-				t1 += imp [3] * delta;
-				
-				imp += 4;
-				
-				buf [0] = t0;
-				buf [1] = t1;
-				buf += 2;
-			}
-		}
-	}
-	
-	template<int quality,int range>
-	void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
-		offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
-	}
-
-#endif
-
--- a/Plugins/Input/console/Classic_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Classic_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,11 +1,11 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Classic_Emu.h"
 
 #include "Multi_Buffer.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -21,82 +21,86 @@
 Classic_Emu::Classic_Emu()
 {
 	buf = NULL;
-	std_buf = NULL;
-	set_equalizer( equalizer_t( -8.87, 8800 ) );
+	stereo_buffer = NULL;
 }
 
 Classic_Emu::~Classic_Emu()
 {
-	delete std_buf;
+	delete stereo_buffer;
 }
 
-void Classic_Emu::update_eq_()
+void Classic_Emu::set_equalizer( equalizer_t const& eq )
 {
-	update_eq( blip_eq_t( equalizer_.treble, equalizer_.cutoff, buf->sample_rate() ) );
-	buf->bass_freq( equalizer_.bass );
-}
-
-void Classic_Emu::set_equalizer( const equalizer_t& eq )
-{
-	equalizer_ = eq;
+	Music_Emu::set_equalizer( eq );
+	update_eq( eq.treble );
 	if ( buf )
-		update_eq_();
+		buf->bass_freq( equalizer().bass );
 }
 	
-blargg_err_t Classic_Emu::init( long sample_rate )
+blargg_err_t Classic_Emu::set_sample_rate( long sample_rate )
 {
-	buf = NULL;
-	delete std_buf;
-	std_buf = NULL;
+	if ( !buf )
+	{
+		if ( !stereo_buffer )
+			BLARGG_CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer );
+		buf = stereo_buffer;
+	}
 	
-	Stereo_Buffer* sb = new Stereo_Buffer;
-	if ( !sb )
-		return "Out of memory";
-	std_buf = sb;
-	
-	BLARGG_RETURN_ERR( sb->sample_rate( sample_rate, 1000 / 20 ) );
-
-	buf = std_buf;
-	return blargg_success;
+	BLARGG_RETURN_ERR( buf->set_sample_rate( sample_rate, 1000 / 20 ) );
+	return Music_Emu::set_sample_rate( sample_rate );
 }
 
 void Classic_Emu::mute_voices( int mask )
 {
-	require( buf ); // init() must have been called
+	require( buf ); // set_sample_rate() must have been called
 	
-	mute_mask_ = mask;
+	Music_Emu::mute_voices( mask );
 	for ( int i = voice_count(); i--; )
 	{
-		if ( mask & (1 << i) ) {
-			set_voice( i, NULL );
+		if ( mask & (1 << i) )
+		{
+			set_voice( i, NULL, NULL, NULL );
 		}
-		else {
+		else
+		{
 			Multi_Buffer::channel_t ch = buf->channel( i );
 			set_voice( i, ch.center, ch.left, ch.right );
 		}
 	}
 }
 
-blargg_err_t Classic_Emu::setup_buffer( long clock_rate )
+blargg_err_t Classic_Emu::setup_buffer( long new_clock_rate )
 {
-	require( buf ); // init() must have been called
+	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
 	
+	clock_rate = new_clock_rate;
 	buf->clock_rate( clock_rate );
-	update_eq_();
-	return buf->set_channel_count( voice_count() );
+	BLARGG_RETURN_ERR( buf->set_channel_count( voice_count() ) );
+	set_equalizer( equalizer() );
+	remute_voices();
+	return blargg_success;
 }
 
-void Classic_Emu::starting_track()
+void Classic_Emu::start_track( int track )
 {
-	require( buf ); // init() must have been called
-	
-	mute_voices( 0 );
+	Music_Emu::start_track( track );
 	buf->clear();
 }
 
-blargg_err_t Classic_Emu::play( long count, sample_t* out )
+blip_time_t Classic_Emu::run_clocks( blip_time_t t, bool* )
 {
-	require( buf ); // init() must have been called
+	assert( false );
+	return t;
+}
+
+blip_time_t Classic_Emu::run( int msec, bool* added_stereo )
+{
+	return run_clocks( (long) msec * clock_rate / 1000, added_stereo );
+}
+
+void Classic_Emu::play( long count, sample_t* out )
+{
+	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
 	
 	long remain = count;
 	while ( remain )
@@ -105,12 +109,9 @@
 		if ( remain )
 		{
 			bool added_stereo = false;
-			blip_time_t cyc = run( buf->length(), &added_stereo );
-			if ( !cyc )
-				return "Emulation error";
-			buf->end_frame( cyc, added_stereo );
+			blip_time_t clocks_emulated = run( buf->length(), &added_stereo );
+			buf->end_frame( clocks_emulated, added_stereo );
 		}
 	}
-	return blargg_success;
 }
 
--- a/Plugins/Input/console/Classic_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Classic_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -2,7 +2,7 @@
 // Classic game music emulator interface base class for emulators which use Blip_Buffer
 // for sound output.
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef CLASSIC_EMU_H
 #define CLASSIC_EMU_H
@@ -10,60 +10,39 @@
 #include "Music_Emu.h"
 class Blip_Buffer;
 class blip_eq_t;
-class Multi_Buffer;
+typedef long blip_time_t;
 
 class Classic_Emu : public Music_Emu {
 public:
 	Classic_Emu();
 	~Classic_Emu();
-	
-	// Initialize emulator with specified sample rate. Sample output is in stereo.
-	virtual blargg_err_t init( long sample_rate );
-	
-	// Initialize emulator using custom output buffer
-	blargg_err_t init( Multi_Buffer* buf );
-	
-	// Frequency equalizer parameters (see notes.txt)
-	struct equalizer_t {
-		double treble; // treble level at 22kHz, in dB (-3.0dB = 0.50)
-		long cutoff;   // beginning of low-pass rolloff, in Hz
-		long bass;     // high-pass breakpoint, in Hz
-		equalizer_t( double treble_ = 0, long cutoff_ = 0, int bass_ = 33 ) :
-				treble( treble_ ), cutoff( cutoff_ ), bass( bass_ ) { }
-	};
-	
-	// Current frequency equalizater parameters
-	const equalizer_t& equalizer() const;
-	
-	// Set frequency equalizer parameters
-	void set_equalizer( const equalizer_t& );
-	
-	// See Music_Emu.h
+	blargg_err_t set_sample_rate( long sample_rate );
+	void set_buffer( Multi_Buffer* );
 	void mute_voices( int );
-	blargg_err_t play( long, sample_t* );
-	
-	
-// End of public interface
+	void play( long, sample_t* );
+	void start_track( int track );  
+	void set_equalizer( equalizer_t const& );
+public:
+	// deprecated
+	blargg_err_t init( long rate ) { return set_sample_rate( rate ); }
 protected:
-	virtual void starting_track();
 	virtual blargg_err_t setup_buffer( long clock_rate );
 	virtual void set_voice( int index, Blip_Buffer* center,
-			Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ) = 0;
-	virtual long run( int msec, bool* added_stereo = NULL ) = 0;
+			Blip_Buffer* left, Blip_Buffer* right ) = 0;
+	virtual blip_time_t run( int msec, bool* added_stereo );
+	virtual blip_time_t run_clocks( blip_time_t, bool* added_stereo );
 	virtual void update_eq( blip_eq_t const& ) = 0;
 private:
 	Multi_Buffer* buf;
-	Multi_Buffer* std_buf; // owned
-	equalizer_t equalizer_;
-	void update_eq_();
+	Multi_Buffer* stereo_buffer;
+	long clock_rate;
 };
 
-inline blargg_err_t Classic_Emu::init( Multi_Buffer* buf_ ) {
-	buf = buf_;
-	return blargg_success;
+inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf )
+{
+	assert( !buf && new_buf );
+	buf = new_buf;
 }
-inline const Classic_Emu::equalizer_t& Classic_Emu::equalizer() const {
-	return equalizer_;
-}
+
 #endif
 
--- a/Plugins/Input/console/Effects_Buffer.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,466 +0,0 @@
-
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
-
-#include "Effects_Buffer.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-typedef long fixed_t;
-
-#define TO_FIXED( f )   fixed_t ((f) * (1L << 15) + 0.5)
-#define FMUL( x, y )    (((x) * (y)) >> 15)
-
-const unsigned echo_size = 4096;
-const unsigned echo_mask = echo_size - 1;
-BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2
-
-const unsigned reverb_size = 8192 * 2;
-const unsigned reverb_mask = reverb_size - 1;
-BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2
-
-Effects_Buffer::config_t::config_t()
-{
-	pan_1 = 0.0;
-	pan_2 = 0.0;
-	reverb_delay = 88;
-	reverb_level = 0.10;
-	echo_delay = 61;
-	echo_level = 0.12;
-	delay_variance = 18;
-	effects_enabled = false;
-}
-
-Effects_Buffer::Effects_Buffer()
-{
-	echo_buf = NULL;
-	echo_pos = 0;
-	
-	reverb_buf = NULL;
-	reverb_pos = 0;
-	
-	stereo_remain = 0;
-	effect_remain = 0;
-	effects_enabled = false;
-	config( config_t() );
-}
-
-Effects_Buffer::~Effects_Buffer()
-{
-	delete [] echo_buf;
-	delete [] reverb_buf;
-}
-
-blargg_err_t Effects_Buffer::sample_rate( long rate, int msec )
-{
-	if ( !echo_buf )
-	{
-		echo_buf = new blip_sample_t [echo_size];
-		if ( !echo_buf )
-			return "Out of memory";
-	}
-	
-	if ( !reverb_buf )
-	{
-		reverb_buf = new blip_sample_t [reverb_size];
-		if ( !reverb_buf )
-			return "Out of memory";
-	}
-	
-	for ( int i = 0; i < buf_count; i++ )
-		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
-	
-	length_ = msec;
-	sample_rate_ = rate;
-	
-	config( config_ );
-	
-	return blargg_success;
-}
-
-void Effects_Buffer::clock_rate( long rate )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].clock_rate( rate );
-}
-
-void Effects_Buffer::bass_freq( int freq )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].bass_freq( freq );
-}
-
-void Effects_Buffer::clear()
-{
-	stereo_remain = 0;
-	effect_remain = 0;
-	memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) );
-	memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) );
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].clear();
-}
-
-inline int pin_range( int n, int max, int min = 0 )
-{
-	if ( n < min )
-		return min;
-	if ( n > max )
-		return max;
-	return n;
-}
-
-void Effects_Buffer::config( const config_t& cfg )
-{
-	// clear echo and reverb buffers
-	if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf ) {
-		memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) );
-		memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) );
-	}
-	
-	config_ = cfg;
-	
-	if ( config_.effects_enabled )
-	{
-		// convert to internal format
-		
-		chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 );
-		chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0];
-		
-		chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 );
-		chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0];
-		
-		chans.reverb_level = TO_FIXED( config_.reverb_level );
-		chans.echo_level = TO_FIXED( config_.echo_level );
-		
-		const int delay_offset = (int) config_.delay_variance * sample_rate_ / (1000 * 2);
-		
-		const int reverb_sample_delay = (int) config_.reverb_delay * sample_rate_ / 1000;
-		chans.reverb_delay_l = pin_range( reverb_size -
-				(reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 );
-		chans.reverb_delay_r = pin_range( reverb_size + 1 -
-				(reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 );
-		
-		const int echo_sample_delay = (int) config_.echo_delay * sample_rate_ / 1000;
-		chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset),
-				echo_size - 1 );
-		chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
-				echo_size - 1 );
-		
-		// set up outputs
-		for ( unsigned i = 0; i < chan_count; i++ ) {
-			channel_t& o = channels [i];
-			if ( i < 2 ) {
-				o.center = &bufs [i];
-				o.left   = &bufs [3];
-				o.right  = &bufs [4];
-			}
-			else {
-				o.center = &bufs [2];
-				o.left   = &bufs [5];
-				o.right  = &bufs [6];
-			}
-		}
-		
-	}
-	else {
-		// set up outputs
-		for ( unsigned i = 0; i < chan_count; i++ ) {
-			channel_t& o = channels [i];
-			o.center = &bufs [0];
-			o.left   = &bufs [1];
-			o.right  = &bufs [2];
-		}
-	}
-}
-
-void Effects_Buffer::end_frame( blip_time_t clock_count, bool stereo )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].end_frame( clock_count );
-	
-	if ( stereo )
-		stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency();
-	
-	if ( effects_enabled || config_.effects_enabled )
-		effect_remain = bufs [0].samples_avail() + bufs [0].output_latency();
-	
-	effects_enabled = config_.effects_enabled;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
-{
-	require( total_samples % 2 == 0 ); // count must be even
-	
-	long remain = bufs [0].samples_avail();
-	if ( remain > (unsigned) total_samples / 2 )
-		remain = (unsigned) total_samples / 2;
-	total_samples = remain;
-	while ( remain )
-	{
-		int active_bufs = buf_count;
-		long count = remain;
-		
-		if ( effect_remain ) {
-			if ( count > effect_remain )
-				count = effect_remain;
-			
-			if ( stereo_remain ) {
-				mix_enhanced( out, count );
-			}
-			else {
-				mix_mono_enhanced( out, count );
-				active_bufs = 3;
-			}
-		}
-		else if ( stereo_remain ) {
-			mix_stereo( out, count );
-			active_bufs = 3; 
-		}
-		else {
-			mix_mono( out, count );
-			active_bufs = 1; 
-		}
-		
-		out += count * 2;
-		remain -= count;
-		
-		stereo_remain -= count;
-		if ( stereo_remain < 0 )
-			stereo_remain = 0;
-		
-		effect_remain -= count;
-		if ( effect_remain < 0 )
-			effect_remain = 0;
-		
-		for ( int i = 0; i < buf_count; i++ ) {
-			if ( i < active_bufs )
-				bufs [i].remove_samples( count );
-			else
-				bufs [i].remove_silence( count ); // keep time synchronized
-		}
-	}
-	
-	return total_samples * 2;
-}
-
-void Effects_Buffer::mix_mono( blip_sample_t* out, long count )
-{
-	Blip_Reader c;
-	int shift = c.begin( bufs [0] );
-	
-	// unrolled loop
-	for ( long n = count >> 1; n--; )
-	{
-		long cs0 = c.read();
-		c.next( shift );
-		
-		long cs1 = c.read();
-		c.next( shift );
-		
-		if ( (BOOST::int16_t) cs0 != cs0 )
-			cs0 = 0x7FFF - (cs0 >> 24);
-		((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16);
-		
-		if ( (BOOST::int16_t) cs1 != cs1 )
-			cs1 = 0x7FFF - (cs1 >> 24);
-		((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16);
-		out += 4;
-	}
-	
-	if ( count & 1 ) {
-		int s = c.read();
-		c.next( shift );
-		out [0] = s;
-		out [1] = s;
-		if ( (BOOST::int16_t) s != s ) {
-			s = 0x7FFF - (s >> 24);
-			out [0] = s;
-			out [1] = s;
-		}
-	}
-	
-	c.end( bufs [0] );
-}
-
-void Effects_Buffer::mix_stereo( blip_sample_t* out, long count )
-{
-	Blip_Reader l; l.begin( bufs [1] );
-	Blip_Reader r; r.begin( bufs [2] );
-	Blip_Reader c;
-	int shift = c.begin( bufs [0] );
-	
-	while ( count-- ) {
-		int cs = c.read();
-		c.next( shift );
-		int left = cs + l.read();
-		int right = cs + r.read();
-		l.next( shift );
-		r.next( shift );
-		
-		if ( (BOOST::int16_t) left != left )
-			left = 0x7FFF - (left >> 24);
-		
-		out [0] = left;
-		out [1] = right;
-		
-		out += 2;
-		
-		if ( (BOOST::int16_t) right != right )
-			out [-1] = 0x7FFF - (right >> 24);
-	}
-	
-	c.end( bufs [0] );
-	r.end( bufs [2] );
-	l.end( bufs [1] );
-}
-
-void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out, long count )
-{
-	Blip_Reader sq1; sq1.begin( bufs [0] );
-	Blip_Reader sq2; sq2.begin( bufs [1] );
-	Blip_Reader center;
-	int shift = center.begin( bufs [2] );
-	
-	int echo_pos = this->echo_pos;
-	int reverb_pos = this->reverb_pos;
-	
-	while ( count-- )
-	{
-		int sum1_s = sq1.read();
-		int sum2_s = sq2.read();
-		
-		sq1.next( shift );
-		sq2.next( shift );
-		
-		int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
-				FMUL( sum2_s, chans.pan_2_levels [0] ) +
-				reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
-		
-		int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
-				FMUL( sum2_s, chans.pan_2_levels [1] ) +
-				reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
-		
-		fixed_t reverb_level = chans.reverb_level;
-		reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
-		reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
-		reverb_pos = (reverb_pos + 2) & reverb_mask;
-		
-		int sum3_s = center.read();
-		center.next( shift );
-		
-		int left = new_reverb_l + sum3_s + FMUL( chans.echo_level,
-				echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
-		int right = new_reverb_r + sum3_s + FMUL( chans.echo_level,
-				echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
-		
-		echo_buf [echo_pos] = sum3_s;
-		echo_pos = (echo_pos + 1) & echo_mask;
-		
-		if ( (BOOST::int16_t) left != left )
-			left = 0x7FFF - (left >> 24);
-		
-		out [0] = left;
-		out [1] = right;
-		
-		out += 2;
-		
-		if ( (BOOST::int16_t) right != right )
-			out [-1] = 0x7FFF - (right >> 24);
-	}
-	this->reverb_pos = reverb_pos;
-	this->echo_pos = echo_pos;
-	
-	sq1.end( bufs [0] );
-	sq2.end( bufs [1] );
-	center.end( bufs [2] );
-}
-
-void Effects_Buffer::mix_enhanced( blip_sample_t* out, long count )
-{
-	Blip_Reader l1; l1.begin( bufs [3] );
-	Blip_Reader r1; r1.begin( bufs [4] );
-	Blip_Reader l2; l2.begin( bufs [5] );
-	Blip_Reader r2; r2.begin( bufs [6] );
-	Blip_Reader sq1; sq1.begin( bufs [0] );
-	Blip_Reader sq2; sq2.begin( bufs [1] );
-	Blip_Reader center;
-	int shift = center.begin( bufs [2] );
-	
-	int echo_pos = this->echo_pos;
-	int reverb_pos = this->reverb_pos;
-	
-	while ( count-- )
-	{
-		int sum1_s = sq1.read();
-		int sum2_s = sq2.read();
-		
-		sq1.next( shift );
-		sq2.next( shift );
-		
-		int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
-				FMUL( sum2_s, chans.pan_2_levels [0] ) + l1.read() +
-				reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
-		
-		int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
-				FMUL( sum2_s, chans.pan_2_levels [1] ) + r1.read() +
-				reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
-		
-		l1.next( shift );
-		r1.next( shift );
-		
-		fixed_t reverb_level = chans.reverb_level;
-		reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
-		reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
-		reverb_pos = (reverb_pos + 2) & reverb_mask;
-		
-		int sum3_s = center.read();
-		center.next( shift );
-		
-		int left = new_reverb_l + sum3_s + l2.read() + FMUL( chans.echo_level,
-				echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
-		int right = new_reverb_r + sum3_s + r2.read() + FMUL( chans.echo_level,
-				echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
-		
-		l2.next( shift );
-		r2.next( shift );
-		
-		echo_buf [echo_pos] = sum3_s;
-		echo_pos = (echo_pos + 1) & echo_mask;
-		
-		if ( (BOOST::int16_t) left != left )
-			left = 0x7FFF - (left >> 24);
-		
-		out [0] = left;
-		out [1] = right;
-		
-		out += 2;
-		
-		if ( (BOOST::int16_t) right != right )
-			out [-1] = 0x7FFF - (right >> 24);
-	}
-	this->reverb_pos = reverb_pos;
-	this->echo_pos = echo_pos;
-	
-	sq1.end( bufs [0] );
-	sq2.end( bufs [1] );
-	center.end( bufs [2] );
-	l1.end( bufs [3] );
-	r1.end( bufs [4] );
-	l2.end( bufs [5] );
-	r2.end( bufs [6] );
-}
-
--- a/Plugins/Input/console/Effects_Buffer.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-
-// Multi-channel effects buffer with panning, echo and reverb effects
-
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
-
-#ifndef EFFECTS_BUFFER_H
-#define EFFECTS_BUFFER_H
-
-#include "Multi_Buffer.h"
-
-// Effects_Buffer uses several buffers and outputs stereo sample pairs.
-class Effects_Buffer : public Multi_Buffer {
-public:
-	Effects_Buffer();
-	~Effects_Buffer();
-	
-	// Channel  Effect    Center Pan
-	// ---------------------------------
-	//    0     reverb       pan_1
-	//    1     reverb       pan_2
-	//    2      echo         -
-	//    3      echo         -
-	//    4      echo         -
-	
-	// Channel configuration
-	struct config_t {
-		double pan_1;           // -1.0 = left, 0.0 = center, 1.0 = right
-		double pan_2;
-		double echo_delay;      // msec
-		double echo_level;      // 0.0 to 1.0
-		double reverb_delay;    // msec
-		double delay_variance;  // difference between left/right delays (msec)
-		double reverb_level;    // 0.0 to 1.0
-		bool effects_enabled;   // if false, use optimized simple mixer
-		config_t();
-	};
-	
-	// Set configuration of buffer
-	void config( const config_t& );
-	
-	// See Multi_Buffer.h
-	blargg_err_t sample_rate( long samples_per_sec, int msec );
-	void clock_rate( long );
-	void bass_freq( int );
-	void clear();
-	channel_t channel( int );
-	void end_frame( blip_time_t, bool was_stereo = true );
-	long read_samples( blip_sample_t*, long );
-	
-
-// End of public interface
-private:
-	typedef long fixed_t;
-	
-	enum { buf_count = 7 };
-	Blip_Buffer bufs [buf_count];
-	enum { chan_count = 5 };
-	channel_t channels [chan_count];
-	config_t config_;
-	long stereo_remain;
-	long effect_remain;
-	bool effects_enabled;
-	
-	blip_sample_t* reverb_buf;
-	blip_sample_t* echo_buf;
-	int reverb_pos;
-	int echo_pos;
-	
-	struct {
-		fixed_t pan_1_levels [2];
-		fixed_t pan_2_levels [2];
-		int echo_delay_l;
-		int echo_delay_r;
-		fixed_t echo_level;
-		int reverb_delay_l;
-		int reverb_delay_r;
-		fixed_t reverb_level;
-	} chans;
-	
-	void mix_mono( blip_sample_t*, long );
-	void mix_stereo( blip_sample_t*, long );
-	void mix_enhanced( blip_sample_t*, long );
-	void mix_mono_enhanced( blip_sample_t*, long );
-};
-
-	inline Effects_Buffer::channel_t Effects_Buffer::channel( int i ) {
-		return channels [i % chan_count];
-	}
-	
-#endif
-
--- a/Plugins/Input/console/Fir_Resampler.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Fir_Resampler.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,14 +1,14 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Fir_Resampler.h"
 
-#include "libaudacious/vfs.h"
 #include <string.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <math.h>
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2004-2006 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
@@ -21,15 +21,20 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-static const double pi = 3.1415926535897932384626433832795029L;
+// to do: fix problems with rolloff < 0.99 or so, and rolloff == 1.0, and related problems
+
+// Sinc impulse genertor
 
 const bool show_impulse = 0;
 
+static const double pi = 3.1415926535897932384626433832795029L;
+
 class Dsf {
 	double rolloff;
 	double factor;
 public:
-	Dsf( double r ) : rolloff( r ) {
+	Dsf( double r ) : rolloff( r )
+	{
 		factor = 1.0;
 		//if ( rolloff < 1.0 )
 		//  factor = 1.0 / (*this)( 0 );
@@ -40,7 +45,7 @@
 		double const n_harm = 256;
 		angle /= n_harm;
 		double pow_a_n = pow( rolloff, n_harm );
-		double rescale = 1.0 / n_harm;
+		//double rescale = 1.0 / n_harm;
 		
 		double num = 1.0 - rolloff * cos( angle ) -
 				pow_a_n * cos( n_harm * angle ) +
@@ -51,8 +56,8 @@
 	}
 };
 
-template<class T,class Sinc>
-void gen_sinc( int width, double offset, double spacing, int count, double scale, T* p,
+template<class Sinc>
+void gen_sinc( int width, double offset, double spacing, int count, double scale, short* p,
 		const Sinc& sinc )
 {
 	double range = pi * (width / 2);
@@ -66,68 +71,72 @@
 		double y = 0.0;
 		if ( fabs( w ) < 1.0 )
 		{
-			y = cos( pi * w ) * 0.5 + 0.5;
-			y *= sinc( a );
+			double window = cos( pi * w ) * 0.5 + 0.5;
+			y = sinc( a ) * window;
 		}
 		
-		*p++ = (T) (y * scale);
+		*p++ = (short) (y * scale);
 		a += step;
 	}
 }
 
-static double plain_sinc( double a ) {
+static double plain_sinc( double a )
+{
 	return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
 }
 
-Fir_Resampler::Fir_Resampler()
+// Fir_Resampler
+
+Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) :
+	width_( width ),
+	write_offset( width * stereo - stereo ),
+	impulses( impulses_ )
 {
+	write_pos = NULL;
 	res = 1;
+	imp = 0;
 	skip_bits = 0;
-	step = 2;
-	buf = NULL;
-	write_pos = NULL;
-	buf_size = 0;
+	step = stereo;
+	ratio_ = 1.0;
 }
 
-Fir_Resampler::~Fir_Resampler() {
-	free( buf );
+Fir_Resampler_::~Fir_Resampler_()
+{
 }
 
-void Fir_Resampler::clear()
+void Fir_Resampler_::clear()
 {
 	imp = 0;
-	if ( buf ) {
-		write_pos = buf + latency;
-		memset( buf, 0, (write_pos - buf) * sizeof *buf );
+	if ( buf.size() )
+	{
+		write_pos = &buf [write_offset];
+		memset( buf.begin(), 0, write_offset * sizeof buf [0] );
 	}
 }
 
-blargg_err_t Fir_Resampler::buffer_size( int new_size )
+blargg_err_t Fir_Resampler_::buffer_size( int new_size )
 {
-	new_size += latency;
-	void* new_buf = realloc( buf, new_size * sizeof *buf );
-	if ( !new_buf )
-		return "Out of memory";
-	buf = (sample_t*) new_buf;
-	buf_size = new_size;
+	BLARGG_RETURN_ERR( buf.resize( new_size + write_offset ) );
 	clear();
 	return blargg_success;
 }
 	
-double Fir_Resampler::time_ratio( double ratio, double rolloff, double volume )
+double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
 {
-	this->ratio = ratio;
+	ratio_ = new_factor;
 	
 	double fstep = 0.0;
 	{
 		double least_error = 2;
 		double pos = 0;
 		res = -1;
-		for ( int r = 1; r <= max_res; r++ ) {
-			pos += ratio;
+		for ( int r = 1; r <= max_res; r++ )
+		{
+			pos += ratio_;
 			double nearest = floor( pos + 0.5 );
 			double error = fabs( pos - nearest );
-			if ( error < least_error ) {
+			if ( error < least_error )
+			{
 				res = r;
 				fstep = nearest / res;
 				least_error = error;
@@ -137,137 +146,108 @@
 	
 	skip_bits = 0;
 	
-	step = 2 * (int) floor( fstep );
+	step = stereo * (int) floor( fstep );
 	
-	ratio = fstep;
+	ratio_ = fstep;
 	fstep = fmod( fstep, 1.0 );
 	
-	double filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio;
+	double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
 	double pos = 0.0;
+	input_per_cycle = 0;
 	Dsf dsf( rolloff );
 	for ( int i = 0; i < res; i++ )
 	{
 		if ( show_impulse )
 			printf( "pos = %f\n", pos );
 		
-		gen_sinc( int (width * filter + 1) & ~1, pos, filter, (int) width,
-				double (0x7fff * volume * filter), impulses [i], dsf );
+		gen_sinc( int (width_ * filter + 1) & ~1, pos, filter, (int) width_,
+				double (0x7fff * gain * filter), impulses + i * width_, dsf );
 		
-		if ( show_impulse ) {
-			for ( int j = 0; j < width; j++ )
-				printf( "%d ", (int) impulses [i] [j] );
+		if ( show_impulse )
+		{
+			for ( int j = 0; j < width_; j++ )
+				printf( "%d ", (int) impulses [i * width_ + j] );
 			printf( "\n" );
 		}
 		
 		pos += fstep;
-		if ( pos >= 0.9999999 ) {
+		input_per_cycle += step;
+		if ( pos >= 0.9999999 )
+		{
 			pos -= 1.0;
 			skip_bits |= 1 << i;
+			input_per_cycle++;
 		}
 	}
 	
-	if ( show_impulse ) {
-		printf( "skip = %X\n", skip_bits );
+	if ( show_impulse )
+	{
+		printf( "skip = %8lX\n", (long) skip_bits );
 		printf( "step = %d\n", step );
 	}
 	
 	clear();
 	
-	return ratio;
+	return ratio_;
+}
+
+int Fir_Resampler_::input_needed( long output_count ) const
+{
+	long input_count = 0;
+	
+	unsigned long skip = skip_bits >> imp;
+	int remain = res - imp;
+	while ( (output_count -= 2) > 0 )
+	{
+		input_count += step + (skip & 1) * stereo;
+		skip >>= 1;
+		if ( !--remain )
+		{
+			skip = skip_bits;
+			remain = res;
+		}
+		output_count -= 2;
+	}
+	
+	long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
+	if ( input_extra < 0 )
+		input_extra = 0;
+	return input_extra;
 }
 
-#include BLARGG_ENABLE_OPTIMIZER
-
-int Fir_Resampler::read( sample_t* out_begin, int count )
+int Fir_Resampler_::avail_( long input_count ) const
 {
-	sample_t* out = out_begin;
-	const sample_t* in = buf;
-	sample_t* end_pos = write_pos;
+	int cycle_count = input_count / input_per_cycle;
+	int output_count = cycle_count * res * stereo;
+	input_count -= cycle_count * input_per_cycle;
+	
 	unsigned long skip = skip_bits >> imp;
-	sample_t const* imp = impulses [this->imp];
-	int remain = res - this->imp;
-	int const step = this->step;
-	
-	count = (count >> 1) + 1;
-	
-	// to do: optimize loop to use a single counter rather than 'in' and 'count'
-	
-	if ( end_pos - in >= width * 2 )
+	int remain = res - imp;
+	while ( input_count >= 0 )
 	{
-		end_pos -= width * 2;
-		do
+		input_count -= step + (skip & 1) * stereo;
+		skip >>= 1;
+		if ( !--remain )
 		{
-			count--;
-			
-			// accumulate in extended precision
-			long l = 0;
-			long r = 0;
-			
-			const sample_t* i = in;
-			if ( !count )
-				break;
-			
-			for ( int n = width / 2; n--; )
-			{
-				int pt0 = imp [0];
-				int pt1 = imp [1];
-				imp += 2;
-				l += (pt0 * i [0]) + (pt1 * i [2]);
-				r += (pt0 * i [1]) + (pt1 * i [3]);
-				i += 4;
-			}
-			
-			remain--;
-			
-			l >>= 15;
-			r >>= 15;
-			
-			in += step + ((skip * 2) & 2);
-			skip >>= 1;
-			
-			if ( !remain ) {
-				imp = impulses [0];
-				skip = skip_bits;
-				remain = res;
-			}
-			
-			out [0] = l;
-			out [1] = r;
-			out += 2;
+			skip = skip_bits;
+			remain = res;
 		}
-		while ( in <= end_pos );
+		output_count += 2;
 	}
-	
-	this->imp = res - remain;
-	
-	int left = write_pos - in;
-	write_pos = buf + left;
-	assert( unsigned (write_pos - buf) <= buf_size );
-	memmove( buf, in, left * sizeof *in );
-	
-	return out - out_begin;
+	return output_count;
 }
 
-int Fir_Resampler::skip_input( int count )
+int Fir_Resampler_::skip_input( long count )
 {
-	int remain = write_pos - buf;
-	int avail = remain - width * 2;
+	int remain = write_pos - buf.begin();
+	int avail = remain - width_ * stereo;
 	if ( count > avail )
 		count = avail;
 	
 	remain -= count;
-	write_pos = buf + remain;
-	assert( unsigned (write_pos - buf) <= buf_size );
-	memmove( buf, buf + count, remain * sizeof *buf );
+	write_pos = &buf [remain];
+	memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
 	
 	return count;
 }
 
-/*
-int Fir_Resampler::skip( int count )
-{
-	count = int (count * ratio) & ~1;
-	count = skip_input( count );
-	return int (count / ratio) & ~1;
-}
-*/
--- a/Plugins/Input/console/Fir_Resampler.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Fir_Resampler.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,76 +1,174 @@
 
-// Finite Impulse Response (FIR) Resampler
+// Finite impulse response (FIR) resampler with adjustable FIR size
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef FIR_RESAMPLER_H
 #define FIR_RESAMPLER_H
 
 #include "blargg_common.h"
+#include <string.h>
 
-class Fir_Resampler {
-	enum { width = 24 };
-	enum { latency = (width - 1) * 2 };
+class Fir_Resampler_ {
 public:
+	
+	// Use Fir_Resampler<width> (below)
+	
+	// Set input/output resampling ratio and optionally low-pass rolloff and gain.
+	// Returns actual ratio used (rounded to internal precision).
+	double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 );
+	
+	// Current input/output ratio
+	double ratio() const { return ratio_; }
+	
+// Input
+	
 	typedef short sample_t;
 	
-	Fir_Resampler();
-	~Fir_Resampler();
-	
-	// interface hasn't been stabilized yet
-	
-	// Set size of buffer. Return true if out of memory.
+	// Resize and clear input buffer
 	blargg_err_t buffer_size( int );
 	
-	// Set input/output resampling ratio and frequency rolloff. Return
-	// actual (rounded) ratio used.
-	double time_ratio( double ratio, double rolloff = 0.999, double volume = 1.0 );
-	
-	// Remove any buffered samples and clear buffer
+	// Clear input buffer. At least two output samples will be available after
+	// two input samples are written.
 	void clear();
 	
-	// Pointer to buffer to write input samples to
+	// Number of input samples that can be written
+	int max_write() const { return buf.end() - write_pos; }
+	
+	// Pointer to place to write input samples
 	sample_t* buffer() { return write_pos; }
 	
-	// Maximum number of samples that can be written to buffer at current position
-	int max_write() const { return buf_size - (write_pos - buf); }
-	
-	// Number of unread input samples
-	int written() const { return write_pos - buf - latency; }
+	// Notify resampler that 'count' input samples have been written
+	void write( long count );
 	
-	// Advance buffer position by 'count' samples. Call after writing 'count' samples
-	// to buffer().
-	void write( int count );
+	// Number of input samples in buffer
+	int written() const { return write_pos - &buf [write_offset]; }
 	
-	// True if there are there aren't enough input samples to read at least one
-	// output sample.
-	bool empty() const { return (write_pos - buf) <= latency; }
+	// Skip 'count' input samples. Returns number of samples actually skipped.
+	int skip_input( long count );
+	
+// Output
 	
-	// Resample and output at most 'count' into 'buf', then remove input samples from
-	// buffer. Return number of samples written into 'buf'.
-	int read( sample_t* buf, int count );
+	// Number of extra input samples needed until 'count' output samples are available
+	int input_needed( long count ) const;
 	
-	// Skip at most 'count' *input* samples. Return number of samples actually skipped.
-	int skip_input( int count );
+	// Number of output samples available
+	int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); }
 	
-private:
+public:
+	~Fir_Resampler_();
+protected:
+	enum { stereo = 2 };
 	enum { max_res = 32 };
-	
-	sample_t* buf;
+	blargg_vector<sample_t> buf;
 	sample_t* write_pos;
-	double ratio;
-	int buf_size;
 	int res;
 	int imp;
+	int const width_;
+	int const write_offset;
 	unsigned long skip_bits;
 	int step;
-	sample_t impulses [max_res] [width];
+	int input_per_cycle;
+	double ratio_;
+	sample_t* impulses;
+	
+	Fir_Resampler_( int width, sample_t* );
+	int avail_( long input_count ) const;
+};
+
+// Width is number of points in FIR. Must be even and 4 or more. More points give
+// better quality and rolloff effectiveness, and take longer to calculate.
+template<int width>
+class Fir_Resampler : public Fir_Resampler_ {
+	BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 );
+	short impulses [max_res] [width];
+public:
+	Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { }
+	
+	// Read at most 'count' samples. Returns number of samples actually read.
+	typedef short sample_t;
+	int read( sample_t* out, long count );
 };
 
-	inline void Fir_Resampler::write( int count ) {
-		write_pos += count;
-		assert( unsigned (write_pos - buf) <= buf_size );
+// End of public interface
+
+inline void Fir_Resampler_::write( long count )
+{
+	write_pos += count;
+	assert( write_pos <= buf.end() );
+}
+
+template<int width>
+int Fir_Resampler<width>::read( sample_t* out_begin, long count )
+{
+	sample_t* out = out_begin;
+	const sample_t* in = buf.begin();
+	sample_t* end_pos = write_pos;
+	unsigned long skip = skip_bits >> this->imp;
+	sample_t const* imp = impulses [this->imp];
+	int remain = res - this->imp;
+	int const step = this->step;
+	
+	count >>= 1;
+	
+	if ( end_pos - in >= width * stereo )
+	{
+		end_pos -= width * stereo;
+		do
+		{
+			count--;
+			
+			// accumulate in extended precision
+			long l = 0;
+			long r = 0;
+			
+			const sample_t* i = in;
+			if ( count < 0 )
+				break;
+			
+			for ( int n = width / 2; n; --n )
+			{
+				int pt0 = imp [0];
+				l += pt0 * i [0];
+				r += pt0 * i [1];
+				int pt1 = imp [1];
+				imp += 2;
+				l += pt1 * i [2];
+				r += pt1 * i [3];
+				i += 4;
+			}
+			
+			remain--;
+			
+			l >>= 15;
+			r >>= 15;
+			
+			in += (skip * stereo) & stereo;
+			skip >>= 1;
+			in += step;
+			
+			if ( !remain )
+			{
+				imp = impulses [0];
+				skip = skip_bits;
+				remain = res;
+			}
+			
+			out [0] = l;
+			out [1] = r;
+			out += 2;
+		}
+		while ( in <= end_pos );
 	}
+	
+	this->imp = res - remain;
+	
+	int left = write_pos - in;
+	write_pos = &buf [left];
+	memmove( buf.begin(), in, left * sizeof *in );
+	
+	return out - out_begin;
+}
 
 #endif
 
--- a/Plugins/Input/console/Gb_Apu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Apu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,12 +1,11 @@
 
-// Gb_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
 
 #include "Gb_Apu.h"
 
-#include "libaudacious/vfs.h"
 #include <string.h>
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -19,17 +18,32 @@
 
 #include BLARGG_SOURCE_BEGIN
 
+int const vol_reg    = 0xFF24;
+int const status_reg = 0xFF26;
+
 Gb_Apu::Gb_Apu()
 {
 	square1.synth = &square_synth;
 	square2.synth = &square_synth;
-	square1.has_sweep = true;
+	wave.synth  = &other_synth;
+	noise.synth = &other_synth;
 	
 	oscs [0] = &square1;
 	oscs [1] = &square2;
 	oscs [2] = &wave;
 	oscs [3] = &noise;
 	
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Gb_Osc& osc = *oscs [i];
+		osc.regs = &regs [i * 5];
+		osc.output = NULL;
+		osc.outputs [0] = NULL;
+		osc.outputs [1] = NULL;
+		osc.outputs [2] = NULL;
+		osc.outputs [3] = NULL;
+	}
+	
 	volume( 1.0 );
 	reset();
 }
@@ -41,16 +55,18 @@
 void Gb_Apu::treble_eq( const blip_eq_t& eq )
 {
 	square_synth.treble_eq( eq );
-	wave.synth.treble_eq( eq );
-	noise.synth.treble_eq( eq );
+	other_synth.treble_eq( eq );
 }
 
-void Gb_Apu::volume( double vol )
+void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
 {
-	vol *= 0.60 / osc_count;
-	square_synth.volume( vol );
-	wave.synth.volume( vol );
-	noise.synth.volume( vol );
+	require( (unsigned) index < osc_count );
+	require( (center && left && right) || (!center && !left && !right) );
+	Gb_Osc& osc = *oscs [index];
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
 }
 
 void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
@@ -59,6 +75,28 @@
 		osc_output( i, center, left, right );
 }
 
+void Gb_Apu::update_volume()
+{
+	// to do: doesn't handle differing left/right global volume
+	int data = regs [vol_reg - start_addr];
+	double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit;
+	square_synth.volume( vol );
+	other_synth.volume( vol );
+}
+
+static unsigned char const powerup_regs [0x30] = {
+	0x80,0x3F,0x00,0xFF,0xBF, // square 1
+	0xFF,0x3F,0x00,0xFF,0xBF, // square 2
+	0x7F,0xFF,0x9F,0xFF,0xBF, // wave
+	0xFF,0xFF,0x00,0x00,0xBF, // noise
+	0x00, // left/right enables
+	0x77, // master volume
+	0x80, // power
+	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+	0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
+	0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
+};
+
 void Gb_Apu::reset()
 {
 	next_frame_time = 0;
@@ -70,31 +108,19 @@
 	square2.reset();
 	wave.reset();
 	noise.reset();
+	noise.bits = 1;
+	wave.wave_pos = 0;
 	
-	memset( regs, 0, sizeof regs );
+	// avoid click at beginning
+	regs [vol_reg - start_addr] = 0x77;
+	update_volume();
+	
+	regs [status_reg - start_addr] = 0x01; // force power
+	write_register( 0, status_reg, 0x00 );
 }
 
-void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
-{
-	require( (unsigned) index < osc_count );
-	
-	Gb_Osc& osc = *oscs [index];
-	if ( center && !left && !right )
-	{
-		// mono
-		left = center;
-		right = center;
-	}
-	else
-	{
-		// must be silenced or stereo
-		require( (!left && !right) || (left && right) );
-	}
-	osc.outputs [1] = right;
-	osc.outputs [2] = left;
-	osc.outputs [3] = center;
-	osc.output = osc.outputs [osc.output_select];
-}
+// to do: remove
+static unsigned long abs_time;
 
 void Gb_Apu::run_until( gb_time_t end_time )
 {
@@ -109,12 +135,24 @@
 			time = end_time;
 		
 		// run oscillators
-		for ( int i = 0; i < osc_count; ++i ) {
+		for ( int i = 0; i < osc_count; ++i )
+		{
 			Gb_Osc& osc = *oscs [i];
-			if ( osc.output ) {
+			if ( osc.output )
+			{
+				int playing = false;
+				if ( osc.enabled && osc.volume &&
+						(!(osc.regs [4] & osc.len_enabled_mask) || osc.length) )
+					playing = -1;
 				if ( osc.output != osc.outputs [3] )
 					stereo_found = true;
-				osc.run( last_time, time );
+				switch ( i )
+				{
+				case 0: square1.run( last_time, time, playing ); break;
+				case 1: square2.run( last_time, time, playing ); break;
+				case 2: wave   .run( last_time, time, playing ); break;
+				case 3: noise  .run( last_time, time, playing ); break;
+				}
 			}
 		}
 		last_time = time;
@@ -131,7 +169,8 @@
 		noise.clock_length();
 		
 		frame_count = (frame_count + 1) & 3;
-		if ( frame_count == 0 ) {
+		if ( frame_count == 0 )
+		{
 			// 64 Hz actions
 			square1.clock_envelope();
 			square2.clock_envelope();
@@ -145,11 +184,16 @@
 
 bool Gb_Apu::end_frame( gb_time_t end_time )
 {
-	run_until( end_time );
+	if ( end_time > last_time )
+		run_until( end_time );
 	
+	abs_time += end_time;
+	
+	assert( next_frame_time >= end_time );
 	next_frame_time -= end_time;
-	assert( next_frame_time >= 0 );
-	last_time = 0;
+	
+	assert( last_time >= end_time );
+	last_time -= end_time;
 	
 	bool result = stereo_found;
 	stereo_found = false;
@@ -158,9 +202,7 @@
 
 void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data )
 {
-	// function now takes actual address, i.e. 0xFFXX
-	require( start_addr <= addr && addr <= end_addr );
-	require( (unsigned) data <= 0xff );
+	require( (unsigned) data < 0x100 );
 	
 	int reg = addr - start_addr;
 	if ( (unsigned) reg >= register_count )
@@ -168,56 +210,98 @@
 	
 	run_until( time );
 	
+	int old_reg = regs [reg];
 	regs [reg] = data;
 	
-	if ( addr < 0xff24 )
+	if ( addr < vol_reg )
+	{
+		write_osc( reg / 5, reg, data );
+	}
+	else if ( addr == vol_reg && data != old_reg ) // global volume
 	{
-		// oscillator
-		int index = reg / 5;
-		oscs [index]->write_register( reg - index * 5, data ); 
+		// return all oscs to 0
+		for ( int i = 0; i < osc_count; i++ )
+		{
+			Gb_Osc& osc = *oscs [i];
+			int amp = osc.last_amp;
+			osc.last_amp = 0;
+			if ( amp && osc.enabled && osc.output )
+				other_synth.offset( time, -amp, osc.output );
+		}
+		
+		if ( wave.outputs [3] )
+			other_synth.offset( time, 30, wave.outputs [3] );
+		
+		update_volume();
+		
+		if ( wave.outputs [3] )
+			other_synth.offset( time, -30, wave.outputs [3] );
+		
+		// oscs will update with new amplitude when next run
 	}
-	else if ( addr == 0xff25 || addr == 0xff26 )
+	else if ( addr == 0xFF25 || addr == status_reg )
 	{
-		int flags = (regs [0xff26 - start_addr] & 0x80) ? regs [0xff25 - start_addr] : 0;
+		int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
+		int flags = regs [0xFF25 - start_addr] & mask;
 		
 		// left/right assignments
 		for ( int i = 0; i < osc_count; i++ )
 		{
 			Gb_Osc& osc = *oscs [i];
+			osc.enabled &= mask;
 			int bits = flags >> i;
 			Blip_Buffer* old_output = osc.output;
-			osc.output_select = ((bits >> 3) & 2) | (bits & 1);
+			osc.output_select = (bits >> 3 & 2) | (bits & 1);
 			osc.output = osc.outputs [osc.output_select];
-			if ( osc.output != old_output && osc.last_amp ) {
-				if ( old_output )
-					square_synth.offset( time, -osc.last_amp, old_output );
+			if ( osc.output != old_output )
+			{
+				int amp = osc.last_amp;
 				osc.last_amp = 0;
+				if ( amp && old_output )
+					other_synth.offset( time, -amp, old_output );
+			}
+		}
+		
+		if ( addr == status_reg && data != old_reg )
+		{
+			if ( !(data & 0x80) )
+			{
+				for ( int i = 0; i < (int) sizeof powerup_regs; i++ )
+				{
+					if ( i != status_reg - start_addr )
+						write_register( time, i + start_addr, powerup_regs [i] );
+				}
+			}
+			else
+			{
+				//dprintf( "APU powered on\n" );
 			}
 		}
 	}
-	else if ( addr >= 0xff30 )
+	else if ( addr >= 0xFF30 )
 	{
-		// separate samples now (simplifies oscillator)
-		int index = (addr & 0x0f) * 2;
+		
+		int index = (addr & 0x0F) * 2;
 		wave.wave [index] = data >> 4;
-		wave.wave [index + 1] = data & 0x0f;
+		wave.wave [index + 1] = data & 0x0F;
 	}
 }
 
 int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr )
 {
-	// function now takes actual address, i.e. 0xFFXX
-	require( start_addr <= addr && addr <= end_addr );
-	
 	run_until( time );
 	
-	int data = regs [addr - start_addr];
+	int index = addr - start_addr;
+	require( (unsigned) index < register_count );
+	int data = regs [index];
 	
-	if ( addr == 0xff26 ) { // status
-		data &= 0xf0;
-		for ( int i = 0; i < osc_count; i++ ) {
+	if ( addr == status_reg )
+	{
+		data = (data & 0x80) | 0x70;
+		for ( int i = 0; i < osc_count; i++ )
+		{
 			const Gb_Osc& osc = *oscs [i];
-			if ( osc.enabled && (osc.length || !osc.length_enabled) )
+			if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) )
 				data |= 1 << i;
 		}
 	}
--- a/Plugins/Input/console/Gb_Apu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Apu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,62 +1,63 @@
 
 // Nintendo Game Boy PAPU sound chip emulator
 
-// Gb_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Gb_Snd_Emu 0.1.4
 
 #ifndef GB_APU_H
 #define GB_APU_H
 
-typedef long gb_time_t;     // clock cycle count
+typedef long     gb_time_t; // clock cycle count
 typedef unsigned gb_addr_t; // 16-bit address
 
 #include "Gb_Oscs.h"
 
 class Gb_Apu {
 public:
-	Gb_Apu();
-	~Gb_Apu();
 	
-	// Overall volume of all oscillators, where 1.0 is full volume.
+	// Set overall volume of all oscillators, where 1.0 is full volume
 	void volume( double );
 	
-	// Treble equalization (see notes.txt).
+	// Set treble equalization
 	void treble_eq( const blip_eq_t& );
 	
-	// Reset oscillators and internal state.
-	void reset();
+	// Outputs can be assigned to a single buffer for mono output, or to three
+	// buffers for stereo output (using Stereo_Buffer to do the mixing).
 	
 	// Assign all oscillator outputs to specified buffer(s). If buffer
-	// is NULL, silence all oscillators.
+	// is NULL, silences all oscillators.
 	void output( Blip_Buffer* mono );
 	void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
 	
-	// Assign oscillator output to buffer(s). Valid indicies are 0 to
-	// osc_count - 1, which refer to Square 1, Square 2, Wave, and
-	// Noise, respectively. If buffer is NULL, silence oscillator.
+	// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3,
+	// which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL,
+	// silences oscillator.
 	enum { osc_count = 4 };
 	void osc_output( int index, Blip_Buffer* mono );
 	void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
 	
+	// Reset oscillators and internal state
+	void reset();
+	
 	// Reads and writes at addr must satisfy start_addr <= addr <= end_addr
-	enum { start_addr = 0xff10 };
-	enum { end_addr   = 0xff3f };
+	enum { start_addr = 0xFF10 };
+	enum { end_addr   = 0xFF3f };
 	enum { register_count = end_addr - start_addr + 1 };
 	
-	// Write 'data' to address at specified time. Previous writes and reads
-	// within the current frame must not have specified a later time.
+	// Write 'data' to address at specified time
 	void write_register( gb_time_t, gb_addr_t, int data );
 	
-	// Read from address at specified time. Previous writes and reads within
-	// the current frame must not have specified a later time.
+	// Read from address at specified time
 	int read_register( gb_time_t, gb_addr_t );
 	
 	// Run all oscillators up to specified time, end current time frame, then
-	// start a new frame at time 0. Return true if any oscillators added
+	// start a new frame at time 0. Returns true if any oscillators added
 	// sound to one of the left/right buffers, false if they only added
 	// to the center buffer.
 	bool end_frame( gb_time_t );
 	
-	static void begin_debug_log();
+public:
+	Gb_Apu();
+	~Gb_Apu();
 private:
 	// noncopyable
 	Gb_Apu( const Gb_Apu& );
@@ -65,6 +66,7 @@
 	Gb_Osc*     oscs [osc_count];
 	gb_time_t   next_frame_time;
 	gb_time_t   last_time;
+	double      volume_unit;
 	int         frame_count;
 	bool        stereo_found;
 	
@@ -72,20 +74,24 @@
 	Gb_Square   square2;
 	Gb_Wave     wave;
 	Gb_Noise    noise;
-	Gb_Square::Synth square_synth; // shared between squares
 	BOOST::uint8_t regs [register_count];
+	Gb_Square::Synth square_synth; // used by squares
+	Gb_Wave::Synth   other_synth;  // used by wave and noise
 	
+	void update_volume();
 	void run_until( gb_time_t );
-	friend class Gb_Apu_Reflector;
+	void write_osc( int index, int reg, int data );
 };
 
-	inline void Gb_Apu::output( Blip_Buffer* mono ) {
-		output( mono, NULL, NULL );
-	}
+inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); }
 	
-	inline void Gb_Apu::osc_output( int index, Blip_Buffer* mono ) {
-		osc_output( index, mono, NULL, NULL );
-	}
+inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); }
+
+inline void Gb_Apu::volume( double vol )
+{
+	volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol;
+	update_volume();
+}
 
 #endif
 
--- a/Plugins/Input/console/Gb_Cpu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Cpu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,5 +1,5 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Gb_Cpu.h"
 
@@ -8,7 +8,7 @@
 
 #include "blargg_endian.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -41,27 +41,25 @@
 //  10898   CB 37   SWAP A
 //  10208   CB 66   BIT 4,(HL)
 
-static void write_unmapped( Gbs_Emu*, gb_addr_t, int )
-{
-	check( false );
-}
+#if BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
 
-static int read_unmapped( Gbs_Emu*, gb_addr_t )
+Gb_Cpu::Gb_Cpu( Gbs_Emu* gbs_emu )
 {
-	check( false );
-	return 0;
-}
-
-Gb_Cpu::Gb_Cpu()
-{
+	callback_data = gbs_emu;
 	rst_base = 0;
-	callback_data = NULL;
-	data_writer [page_count] = write_unmapped;
-	data_reader [page_count] = read_unmapped;
 	reset();
 }
 
-void Gb_Cpu::reset( const void* unmapped_code_page )
+inline void Gb_Cpu::set_code_page( int i, uint8_t const* p )
+{
+	code_map [i] = p - PAGE_OFFSET( i * page_size );
+}
+
+void Gb_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
 {
 	interrupts_enabled = false;
 	remain_ = 0;
@@ -78,32 +76,33 @@
 	r.l = 0;
 	
 	for ( int i = 0; i < page_count + 1; i++ )
-		code_map [i] = (const uint8_t*) unmapped_code_page;
-	
-	map_memory( 0, 0x10000, read_unmapped, write_unmapped );
+	{
+		set_code_page( i, (uint8_t*) unmapped_code_page );
+		data_reader [i] = read;
+		data_writer [i] = write;
+	}
 }
 
 void Gb_Cpu::map_code( gb_addr_t start, unsigned long size, const void* data )
 {
-	// start end end must fall on page bounadries
-	require( start % page_size == 0 && size % page_size == 0 );
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
 	
 	unsigned first_page = start / page_size;
 	for ( unsigned i = size / page_size; i--; )
-		code_map [first_page + i] = (uint8_t*) data + i * page_size;
+		set_code_page( first_page + i, (uint8_t*) data + i * page_size );
 }
 
 void Gb_Cpu::map_memory( gb_addr_t start, unsigned long size, reader_t read, writer_t write )
 {
-	// start end end must fall on page bounadries
-	require( start % page_size == 0 && size % page_size == 0 );
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
 	
-	if ( !read )
-		read = read_unmapped;
-	if ( !write )
-		write = write_unmapped;
 	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; ) {
+	for ( unsigned i = size / page_size; i--; )
+	{
 		data_reader [first_page + i] = read;
 		data_writer [first_page + i] = write;
 	}
@@ -115,19 +114,22 @@
 #define READ( addr )        (data_reader [(addr) >> page_bits]( callback_data, addr ))
 #define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data ))
 
-#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [(addr) & (page_size - 1)])
+#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
 #define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
 
-int Gb_Cpu::read( gb_addr_t addr ) {
+int Gb_Cpu::read( gb_addr_t addr )
+{
 	return READ( addr );
 }
 
-BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) {
-	return (uint8_t*) &READ_PROG( addr );
+void Gb_Cpu::write( gb_addr_t addr, int data )
+{
+	WRITE( addr, data );
 }
 
-void Gb_Cpu::write( gb_addr_t addr, int data ) {
-	WRITE( addr, data );
+BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr )
+{
+	return (uint8_t*) &READ_PROG( addr );
 }
 
 #ifndef GB_CPU_GLUE_ONLY
@@ -143,6 +145,7 @@
 {
 	const int cycles_per_instruction = 4;
 	
+	// to do: use cycle table
 	remain_ = cycle_count + cycles_per_instruction;
 	
 	Gb_Cpu::result_t result = result_cycles;
@@ -161,12 +164,14 @@
 			uint8_t c, b, e, d, l, h, a, unused;
 #   define R8( n ) (r8_ [(n) ^ 1]) 
 #else
-#   error "Byte order of CPU must be known."
+#   error "Byte order of CPU must be known"
 #endif
 		} rg; // registers
+		
 		struct {
 			BOOST::uint16_t bc, de, hl, unused; // pairs
 		} rp;
+		
 		uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
 		BOOST::uint16_t r16 [4]; // indexed pairs
 	};
@@ -183,53 +188,41 @@
 	unsigned sp = r.sp;
 	unsigned flags = r.flags;
 	
-	goto loop;
-	
-	unsigned data;
+loop:
 	
-jr_taken:
-	pc += (BOOST::int8_t) data;
-inc_pc_loop:
-	pc++;
-loop:
+	int new_remain = remain_ - cycles_per_instruction;
 	
 	check( (unsigned) pc < 0x10000 );
 	check( (unsigned) sp < 0x10000 );
 	check( (flags & ~0xf0) == 0 );
 	
-	// Read opcode and first operand. Optimize if processor's byte order is known
-	// and non-portable constructs are allowed.
-#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
-	pc++;
-	unsigned op = data >> 8;
-	data = (uint8_t) data;
-
-#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	uint8_t const* page = code_map [pc >> page_bits];
+	unsigned op = page [PAGE_OFFSET( pc )];
+	unsigned data = page [PAGE_OFFSET( pc ) + 1];
+	check( op == 0xC9 || &READ_PROG( pc + 1 ) == &page [PAGE_OFFSET( pc ) + 1] );
+	
 	pc++;
-	unsigned op = (uint8_t) data;
-	data >>= 8;
-
-#else
-	unsigned op = READ_PROG( pc );
-	pc++;
-	data = READ_PROG( pc );
 	
-#endif
-	
-	if ( (remain_ -= cycles_per_instruction) <= 0 )
+	remain_ = new_remain;
+	if ( new_remain <= 0 )
 		goto stop;
 	
 	switch ( op )
 	{
 
+#define BRANCH( cond )          \
+{                               \
+	pc++;                       \
+	int offset = (BOOST::int8_t) data;  \
+	if ( !(cond) ) goto loop;   \
+	pc += offset;               \
+	goto loop;                  \
+}
+
 // Most Common
 
 	case 0x20: // JR NZ
-		if ( !(flags & z_flag) )
-			goto jr_taken;
-		goto inc_pc_loop;
+		BRANCH( !(flags & z_flag) )
 	
 	case 0x21: // LD HL,IMM (common)
 		rp.hl = READ_PROG16( pc );
@@ -237,9 +230,7 @@
 		goto loop;
 	
 	case 0x28: // JR Z
-		if ( flags & z_flag )
-			goto jr_taken;
-		goto inc_pc_loop;
+		BRANCH( flags & z_flag )
 	
 	{
 		unsigned temp;
@@ -326,9 +317,9 @@
 		data = pc + 2;
 		pc = READ_PROG16( pc );
 	push:
-		sp--;
+		sp = (sp - 1) & 0xFFFF;
 		WRITE( sp, data >> 8 );
-		sp--;
+		sp = (sp - 1) & 0xFFFF;
 		WRITE( sp, data & 0xff );
 		goto loop;
 	
@@ -338,8 +329,8 @@
 	case 0xC9: // RET (most common)
 	ret:
 		pc = READ( sp );
-		pc += 0x100 * READ( sp + 1 );
-		sp += 2;
+		pc += 0x100 * READ( (sp + 1) & 0xFFFF );
+		sp = (sp + 2) & 0xFFFF;
 		goto loop;
 	
 	case 0x00: // NOP
@@ -628,35 +619,43 @@
 	
 	case 0x06: // LD B,IMM
 		rg.b = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x0E: // LD C,IMM
 		rg.c = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x16: // LD D,IMM
 		rg.d = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x1E: // LD E,IMM
 		rg.e = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x26: // LD H,IMM
 		rg.h = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x2E: // LD L,IMM
 		rg.l = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x36: // LD (HL),IMM
 		WRITE( rp.hl, data );
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	case 0x3E: // LD A,IMM
 		rg.a = data;
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 
 // Increment/Decrement
 
@@ -667,7 +666,7 @@
 		goto loop;
 	
 	case 0x33: // INC SP
-		sp++;
+		sp = (sp + 1) & 0xFFFF;
 		goto loop;
 
 	case 0x0B: // DEC BC
@@ -677,7 +676,7 @@
 		goto loop;
 	
 	case 0x3B: // DEC SP
-		sp--;
+		sp = (sp - 1) & 0xFFFF;
 		goto loop;
 	
 	case 0x34: // INC (HL)
@@ -731,7 +730,7 @@
 		unsigned prev;
 		
 	case 0xF8: // LD HL,SP+imm
-		temp = BOOST::int8_t (data) & 0xffff; // sign-extend to 16 bits
+		temp = BOOST::int8_t (data); // sign-extend to 16 bits
 		pc++;
 		flags = 0;
 		temp += sp;
@@ -739,12 +738,12 @@
 		goto add_16_hl;
 	
 	case 0xE8: // ADD SP,IMM
-		temp = BOOST::int8_t (data) & 0xffff; // sign-extend to 16 bits
+		temp = BOOST::int8_t (data); // sign-extend to 16 bits
 		pc++;
 		flags = 0;
 		temp += sp;
 		prev = sp;
-		sp = temp;
+		sp = temp & 0xffff;
 		goto add_16_comm;
 
 	case 0x39: // ADD HL,SP
@@ -934,15 +933,15 @@
 	case 0xD1: // POP DE
 	case 0xE1:{// POP HL (common)
 		int temp = READ( sp );
-		r16 [(op >> 4) & 3] = temp + 0x100 * READ( sp + 1 );
-		sp += 2;
+		r16 [(op >> 4) & 3] = temp + 0x100 * READ( (sp + 1) & 0xFFFF );
+		sp = (sp + 2) & 0xFFFF;
 		goto loop;
 	}
 	
 	case 0xF1: // POP FA
 		rg.a = READ( sp );
-		flags = READ( sp + 1 ) & 0xf0;
-		sp += 2;
+		flags = READ( (sp + 1) & 0xFFFF ) & 0xf0;
+		sp = (sp + 2) & 0xFFFF;
 		goto loop;
 
 	case 0xC5: // PUSH BC
@@ -1007,17 +1006,13 @@
 		goto loop;
 
 	case 0x18: // JR
-		goto jr_taken;
+		BRANCH( true )
 	
 	case 0x30: // JR NC
-		if ( !(flags & c_flag) )
-			goto jr_taken;
-		goto inc_pc_loop;
+		BRANCH( !(flags & c_flag) )
 	
 	case 0x38: // JR C
-		if ( flags & c_flag )
-			goto jr_taken;
-		goto inc_pc_loop;
+		BRANCH( flags & c_flag )
 	
 	case 0xE9: // JP_HL
 		pc = rp.hl;
@@ -1093,7 +1088,8 @@
 		goto stop;
 	}
 	
-	assert( false ); // all opcodes should end with a goto
+	// If this fails then the case above is missing an opcode
+	assert( false );
 	
 stop:
 	pc--;
--- a/Plugins/Input/console/Gb_Cpu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Cpu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,14 +1,14 @@
 
 // Nintendo Game Boy CPU emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef GB_CPU_H
 #define GB_CPU_H
 
 #include "blargg_common.h"
 
-typedef unsigned gb_addr_t; // 16-bit address
+typedef unsigned gb_addr_t; // 16-bit CPU address
 
 class Gbs_Emu;
 
@@ -17,19 +17,19 @@
 	typedef BOOST::uint8_t uint8_t;
 	enum { page_bits = 8 };
 	enum { page_count = 0x10000 >> page_bits };
-	const uint8_t* code_map [page_count + 1];
+	uint8_t const* code_map [page_count + 1];
 	long remain_;
+	Gbs_Emu* callback_data;
 public:
-	Gb_Cpu();
+	
+	Gb_Cpu( Gbs_Emu* );
 	
-	// Set all registers to 0, unmap all memory, and map all code pages
-	// to unmapped_page.
-	void reset( const void* unmapped_page = NULL );
+	// Memory read/write function types. Reader must return value from 0 to 255.
+	typedef int (*reader_t)( Gbs_Emu*, gb_addr_t );
+	typedef void (*writer_t)( Gbs_Emu*, gb_addr_t, int data );
 	
-	// Memory read/write function types. Memory reader return value must be 0 to 255.
-	Gbs_Emu* callback_data; // passed to memory read/write functions
-	typedef int (*reader_t)( Gbs_Emu* callback_data, gb_addr_t );
-	typedef void (*writer_t)( Gbs_Emu* callback_data, gb_addr_t, int );
+	// Clear registers, unmap memory, and map code pages to unmapped_page.
+	void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL );
 	
 	// Memory mapping functions take a block of memory of specified 'start' address
 	// and 'size' in bytes. Both start address and size must be a multiple of page_size.
@@ -44,11 +44,14 @@
 	// Access memory as the emulated CPU does.
 	int  read( gb_addr_t );
 	void write( gb_addr_t, int data );
-	uint8_t* get_code( gb_addr_t ); // for use in a debugger
+	uint8_t* get_code( gb_addr_t ); // non-const to allow debugger to modify code
+	
+	// Push a byte on the stack
+	void push_byte( int );
 	
 	// Game Boy Z80 registers. *Not* kept updated during a call to run().
 	struct registers_t {
-		BOOST::uint16_t pc;
+		long pc; // more than 16 bits to allow overflow detection
 		BOOST::uint16_t sp;
 		uint8_t flags;
 		uint8_t a;
@@ -58,12 +61,13 @@
 		uint8_t e;
 		uint8_t h;
 		uint8_t l;
-	} r;
+	};
+	registers_t r;
 	
-	// Interrupt enable flag set by EI and cleared by DI.
+	// Interrupt enable flag set by EI and cleared by DI
 	bool interrupts_enabled;
 	
-	// Base address for RST vectors (normally 0).
+	// Base address for RST vectors (normally 0)
 	gb_addr_t rst_base;
 	
 	// Reasons that run() returns
@@ -74,10 +78,10 @@
 	};
 	
 	// Run CPU for at least 'count' cycles, or until one of the above conditions
-	// arises. Return reason for stopping.
+	// arises. Returns reason for stopping.
 	result_t run( long count );
 	
-	// Number of clock cycles remaining for current run() call.
+	// Number of clock cycles remaining for most recent run() call
 	long remain() const;
 	
 private:
@@ -85,13 +89,15 @@
 	Gb_Cpu( const Gb_Cpu& );
 	Gb_Cpu& operator = ( const Gb_Cpu& );
 	
-	reader_t data_reader [page_count + 1]; // extra entry to catch overflow addresses
+	reader_t data_reader [page_count + 1]; // extra entry catches address overflow
 	writer_t data_writer [page_count + 1];
+	void set_code_page( int, uint8_t const* );
 };
 
-	inline long Gb_Cpu::remain() const {
-		return remain_;
-	}
+inline long Gb_Cpu::remain() const
+{
+	return remain_;
+}
 
 #endif
 
--- a/Plugins/Input/console/Gb_Oscs.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Oscs.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,11 +1,11 @@
 
-// Gb_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
 
 #include "Gb_Apu.h"
 
 #include <string.h>
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -18,387 +18,316 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-const int trigger = 0x80;
-
 // Gb_Osc
 
-Gb_Osc::Gb_Osc()
-{
-	output = NULL;
-	outputs [0] = NULL;
-	outputs [1] = NULL;
-	outputs [2] = NULL;
-	outputs [3] = NULL;
-}
-
 void Gb_Osc::reset()
 {
 	delay = 0;
 	last_amp = 0;
-	period = 2048;
-	volume = 0;
-	frequency = 0;
 	length = 0;
-	enabled = false;
-	length_enabled = false;
 	output_select = 3;
 	output = outputs [output_select];
 }
 
 void Gb_Osc::clock_length()
 {
-	if ( length_enabled && length )
-		--length;
-}
-
-void Gb_Osc::write_register( int reg, int value )
-{
-	if ( reg == 4 )
-		length_enabled = value & 0x40;
+	if ( (regs [4] & len_enabled_mask) && length )
+		length--;
 }
 
 // Gb_Env
 
-void Gb_Env::reset()
-{
-	env_period = 0;
-	env_dir = 0;
-	env_delay = 0;
-	new_env_period = 0;
-	new_env_dir = 0;
-	new_volume = 0;
-	Gb_Osc::reset();
-}
-
-Gb_Env::Gb_Env() {
-}
-
 void Gb_Env::clock_envelope()
 {
-	if ( env_delay && !--env_delay ) {
-		env_delay = env_period;
-		if ( env_dir ) {
-			if ( volume < 15 )
-				++volume;
-		}
-		else if ( volume > 0 ) {
-			--volume;
-		}
+	if ( env_delay && !--env_delay )
+	{
+		env_delay = regs [2] & 7;
+		int v = volume - 1 + (regs [2] >> 2 & 2);
+		if ( (unsigned) v < 15 )
+			volume = v;
 	}
 }
 
-void Gb_Env::write_register( int reg, int value )
+bool Gb_Env::write_register( int reg, int data )
 {
-	if ( reg == 2 ) {
-		new_env_period = value & 7;
-		new_env_dir = value & 8;
-		new_volume = value >> 4;
-		if ( value == 0 && volume )
-			// to do: find correct behavior
-			volume = 0;
-		//enabled = new_volume != 0;
-		enabled = true;
+	switch ( reg )
+	{
+	case 1:
+		length = 64 - (regs [1] & 0x3f);
+		break;
+	
+	case 2:
+		if ( !(data >> 4) )
+			enabled = false;
+		break;
+	
+	case 4:
+		if ( data & trigger )
+		{
+			env_delay = regs [2] & 7;
+			volume = regs [2] >> 4;
+			enabled = true;
+			if ( length == 0 )
+				length = 64;
+			return true;
+		}
 	}
-	else if ( reg == 4 && (value & trigger) ) {
-		env_period = new_env_period;
-		env_delay = new_env_period;
-		env_dir = new_env_dir;
-		volume = new_volume;
-	}
-	Gb_Osc::write_register( reg, value );
+	return false;
 }
 
 // Gb_Square
 
 void Gb_Square::reset()
 {
-	phase = 1;
-	duty = 1;
-	
-	sweep_period = 0;
-	sweep_delay = 0;
-	sweep_shift = 0;
-	sweep_dir = 0;
+	phase = 0;
 	sweep_freq = 0;
-	
+	sweep_delay = 0;
 	Gb_Env::reset();
 }
 
-Gb_Square::Gb_Square()
-{
-	has_sweep = false;
-}
-
 void Gb_Square::clock_sweep()
 {
-	if ( sweep_period && sweep_delay && !--sweep_delay ) {
+	int sweep_period = (regs [0] & period_mask) >> 4;
+	if ( sweep_period && sweep_delay && !--sweep_delay )
+	{
 		sweep_delay = sweep_period;
-		frequency = sweep_freq;
-		period = (2048 - frequency) * 4;
+		regs [3] = sweep_freq & 0xFF;
+		regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
 		
-		int offset = sweep_freq >> sweep_shift;
-		if ( sweep_dir )
+		int offset = sweep_freq >> (regs [0] & shift_mask);
+		if ( regs [0] & 0x08 )
 			offset = -offset;
 		sweep_freq += offset;
-		if ( sweep_freq < 0 || sweep_freq >= 2048 ) {
-			sweep_delay = 0;
-			sweep_freq = 2048; // stop sound output
+		
+		if ( sweep_freq < 0 )
+		{
+			sweep_freq = 0;
+		}
+		else if ( sweep_freq >= 2048 )
+		{
+			sweep_delay = 0; // don't modify channel frequency any further
+			sweep_freq = 2048; // silence sound immediately
 		}
 	}
 }
 
-void Gb_Square::write_register( int reg, int value )
+void Gb_Square::run( gb_time_t time, gb_time_t end_time, int playing )
 {
-	switch ( reg ) {
-		case 0:
-			sweep_period = (value >> 4) & 3;
-			sweep_shift = value & 7;
-			sweep_dir = value & 0x08;
-			break;
-		
-		case 1:
-			length = 64 - (value & 0x3f);
-			duty = (value >> 5) & 6; // duty = { 1, 2, 4, 6 }
-			if ( !duty )
-				duty = 1;
-			break;
-		
-		case 3:
-			frequency = (frequency & ~0xFF) + value;
-			break;
-		
-		case 4:
-			frequency = (value & 7) * 0x100 + (frequency & 0xFF);
-			if ( value & trigger ) {
-				sweep_freq = frequency;
-				if ( has_sweep && sweep_period && sweep_shift ) {
-					sweep_delay = 1;
-					clock_sweep();
-					sweep_delay = sweep_period;
-				}
-				enabled = true;
-			}
-			break;
+	if ( sweep_freq == 2048 )
+		playing = false;
+	
+	static unsigned char const table [4] = { 1, 2, 4, 6 };
+	int const duty = table [regs [1] >> 6];
+	int amp = volume & playing;
+	if ( phase >= duty )
+		amp = -amp;
+	
+	int frequency = this->frequency();
+	if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
+	{
+		// really high frequency results in DC at half volume
+		amp = volume >> 1;
+		playing = false;
+	}
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
 	}
 	
-	period = (2048 - frequency) * 4;
+	time += delay;
+	if ( !playing )
+		time = end_time;
 	
-	Gb_Env::write_register( reg, value );
+	if ( time < end_time )
+	{
+		int const period = (2048 - frequency) * 4;
+		Blip_Buffer* const output = this->output;
+		int phase = this->phase;
+		int delta = amp * 2;
+		do
+		{
+			phase = (phase + 1) & 7;
+			if ( phase == 0 || phase == duty )
+			{
+				delta = -delta;
+				synth->offset_inline( time, delta, output );
+			}
+			time += period;
+		}
+		while ( time < end_time );
+		
+		this->phase = phase;
+		last_amp = delta >> 1;
+	}
+	delay = time - end_time;
 }
 
-void Gb_Square::run( gb_time_t time, gb_time_t end_time )
+// Gb_Noise
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Gb_Noise::run( gb_time_t time, gb_time_t end_time, int playing )
 {
-	if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 ) {
-		if ( last_amp ) {
-			synth->offset( time, -last_amp, output );
-			last_amp = 0;
-		}
-		delay = 0;
+	int amp = volume & playing;
+	int tap = 13 - (regs [3] & 8);
+	if ( bits >> tap & 2 )
+		amp = -amp;
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
 	}
-	else
+		
+	time += delay;
+	if ( !playing )
+		time = end_time;
+	
+	if ( time < end_time )
 	{
-		int amp = (phase < duty) ? volume : -volume;
-		if ( amp != last_amp ) {
-			synth->offset( time, amp - last_amp, output );
-			last_amp = amp;
-		}
+		static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
+		int period = table [regs [3] & 7] << (regs [3] >> 4);
 		
-		time += delay;
-		if ( time < end_time )
+		// keep parallel resampled time to eliminate time conversion in the loop
+		Blip_Buffer* const output = this->output;
+		const blip_resampled_time_t resampled_period =
+				output->resampled_duration( period );
+		blip_resampled_time_t resampled_time = output->resampled_time( time );
+		unsigned bits = this->bits;
+		int delta = amp * 2;
+		
+		do
 		{
-			Blip_Buffer* const output = this->output;
-			const int duty = this->duty;
-			int phase = this->phase;
-			amp *= 2;
-			do {
-				phase = (phase + 1) & 7;
-				if ( phase == 0 || phase == duty ) {
-					amp = -amp;
-					synth->offset_inline( time, amp, output );
-				}
-				time += period;
+			unsigned changed = (bits >> tap) + 1;
+			time += period;
+			bits <<= 1;
+			if ( changed & 2 )
+			{
+				delta = -delta;
+				bits |= 1;
+				synth->offset_resampled( resampled_time, delta, output );
 			}
-			while ( time < end_time );
-			
-			this->phase = phase;
-			last_amp = amp >> 1;
+			resampled_time += resampled_period;
 		}
-		delay = time - end_time;
+		while ( time < end_time );
+		
+		this->bits = bits;
+		last_amp = delta >> 1;
 	}
+	delay = time - end_time;
 }
 
-
 // Gb_Wave
 
-void Gb_Wave::reset()
-{
-	volume_shift = 0;
-	wave_pos = 0;
-	memset( wave, 0, sizeof wave );
-	Gb_Osc::reset();
-}
-
-Gb_Wave::Gb_Wave() {
-}
-
-void Gb_Wave::write_register( int reg, int value )
+inline void Gb_Wave::write_register( int reg, int data )
 {
-	switch ( reg ) {
-		case 0:
-			enabled = value & 0x80;
-			break;
-		
-		case 1:
-			length = 256 - value;
-			break;
-		
-		case 2:
-			volume = ((value >> 5) & 3);
-			volume_shift = (volume - 1) & 7; // silence = 7
-			break;
-		
-		case 3:
-			frequency = (frequency & ~0xFF) + value;
-			break;
-		
-		case 4:
-			frequency = (value & 7) * 0x100 + (frequency & 0xFF);
-			//if ( value & trigger )
-			//  wave_pos = 0;
-			break;
-		
-	}
+	switch ( reg )
+	{
+	case 0:
+		if ( !(data & 0x80) )
+			enabled = false;
+		break;
 	
-	period = (2048 - frequency) * 2;
+	case 1:
+		length = 256 - regs [1];
+		break;
 	
-	Gb_Osc::write_register( reg, value );
-}
-
-void Gb_Wave::run( gb_time_t time, gb_time_t end_time )
-{
-	if ( !enabled || (!length && length_enabled) || !volume ) {
-		if ( last_amp ) {
-			synth.offset( time, -last_amp, output );
-			last_amp = 0;
-		}
-		delay = 0;
-	}
-	else
-	{
-		// wave data or shift may have changed
-		int diff = (wave [wave_pos] >> volume_shift) * 2 - last_amp;
-		if ( diff ) {
-			last_amp += diff;
-			synth.offset( time, diff, output );
+	case 2:
+		volume = data >> 5 & 3;
+		break;
+	
+	case 4:
+		if ( data & trigger & regs [0] )
+		{
+			wave_pos = 0;
+			enabled = true;
+			if ( length == 0 )
+				length = 256;
 		}
-		
-		time += delay;
-		if ( time < end_time )
-		{
-		 	unsigned wave_pos = this->wave_pos;
-		 	
-			do {
-				wave_pos = (wave_pos + 1) % wave_size;
-				int amp = (wave [wave_pos] >> volume_shift) * 2;
-				int diff = amp - last_amp;
-				if ( diff ) {
-					last_amp = amp;
-					synth.offset_inline( time, diff, output );
-				}
-				time += period;
-			}
-			while ( time < end_time );
-			
-			this->wave_pos = wave_pos;
-		}
-		delay = time - end_time;
 	}
 }
 
-
-// Gb_Noise
-
-void Gb_Noise::reset()
+void Gb_Wave::run( gb_time_t time, gb_time_t end_time, int playing )
 {
-	bits = 1;
-	tap = 14;
-	Gb_Env::reset();
-}
-
-Gb_Noise::Gb_Noise() {
-}
-
-void Gb_Noise::write_register( int reg, int value )
-{
-	if ( reg == 1 ) {
-		length = 64 - (value & 0x3f);
+	int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7
+	int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
+	
+	int frequency = this->frequency();
+	if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
+	{
+		amp = 30 >> volume_shift & playing;
+		playing = false;
 	}
-	else if ( reg == 3 ) {
-		tap = 14 - (value & 8);
-		// noise formula and frequency tested against Metroid 2 and Zelda LA
-		int divisor = (value & 7) * 16;
-		if ( !divisor )
-			divisor = 8;
-		period = divisor << (value >> 4);
-	}
-	else if ( reg == 4 && value & trigger ) {
-		bits = ~0u;
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
 	}
 	
-	Gb_Env::write_register( reg, value );
+	time += delay;
+	if ( !playing )
+		time = end_time;
+	
+	if ( time < end_time )
+	{
+		Blip_Buffer* const output = this->output;
+		int const period = (2048 - frequency) * 2;
+	 	int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
+	 	
+		do
+		{
+			int amp = (wave [wave_pos] >> volume_shift) * 2;
+			wave_pos = (wave_pos + 1) & (wave_size - 1);
+			int delta = amp - last_amp;
+			if ( delta )
+			{
+				last_amp = amp;
+				synth->offset_inline( time, delta, output );
+			}
+			time += period;
+		}
+		while ( time < end_time );
+		
+		this->wave_pos = (wave_pos - 1) & (wave_size - 1);
+	}
+	delay = time - end_time;
 }
 
-#include BLARGG_ENABLE_OPTIMIZER
+// Gb_Apu::write_osc
 
-void Gb_Noise::run( gb_time_t time, gb_time_t end_time )
+void Gb_Apu::write_osc( int index, int reg, int data )
 {
-	if ( !enabled || (!length && length_enabled) || !volume ) {
-		if ( last_amp ) {
-			synth.offset( time, -last_amp, output );
-			last_amp = 0;
-		}
-		delay = 0;
-	}
-	else
+	reg -= index * 5;
+	Gb_Square* sq = &square2;
+	switch ( index )
 	{
-		int amp = bits & 1 ? -volume : volume;
-		if ( amp != last_amp ) {
-			synth.offset( time, amp - last_amp, output );
-			last_amp = amp;
-		}
-		
-		time += delay;
-		if ( time < end_time )
+	case 0:
+		sq = &square1;
+	case 1:
+		if ( sq->write_register( reg, data ) && index == 0 )
 		{
-			Blip_Buffer* const output = this->output;
-			// keep parallel resampled time to eliminate multiplication in the loop
-			const Blip_Buffer::resampled_time_t resampled_period =
-					output->resampled_duration( period );
-			Blip_Buffer::resampled_time_t resampled_time = output->resampled_time( time );
-			const unsigned mask = ~(1u << tap);
-			unsigned bits = this->bits;
-			amp *= 2;
-			
-			do {
-				unsigned feedback = bits;
-				bits >>= 1;
-				feedback = 1 & (feedback ^ bits);
-				time += period;
-				bits = (feedback << tap) | (bits & mask);
-				// feedback just happens to be true only when the level needs to change
-				// (the previous and current bits are different)
-				if ( feedback ) {
-					amp = -amp;
-					synth.offset_resampled( resampled_time, amp, output );
-				}
-				resampled_time += resampled_period;
+			square1.sweep_freq = square1.frequency();
+			if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) )
+			{
+				square1.sweep_delay = 1; // cause sweep to recalculate now
+				square1.clock_sweep();
 			}
-			while ( time < end_time );
-			
-			this->bits = bits;
-			last_amp = amp >> 1;
 		}
-		delay = time - end_time;
+		break;
+	
+	case 2:
+		wave.write_register( reg, data );
+		break;
+	
+	case 3:
+		if ( noise.write_register( reg, data ) )
+			noise.bits = 0x7FFF;
 	}
 }
 
--- a/Plugins/Input/console/Gb_Oscs.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gb_Oscs.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,97 +1,86 @@
 
 // Private oscillators used by Gb_Apu
 
-// Gb_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Gb_Snd_Emu 0.1.4
 
 #ifndef GB_OSCS_H
 #define GB_OSCS_H
 
+#include "blargg_common.h"
 #include "Blip_Buffer.h"
 
-struct Gb_Osc {
+struct Gb_Osc
+{
+	enum { trigger = 0x80 };
+	enum { len_enabled_mask = 0x40 };
+	
 	Blip_Buffer* outputs [4]; // NULL, right, left, center
 	Blip_Buffer* output;
 	int output_select;
+	BOOST::uint8_t* regs; // osc's 5 registers
 	
 	int delay;
 	int last_amp;
-	int period;
 	int volume;
-	int frequency;
 	int length;
 	bool enabled;
-	bool length_enabled;
 	
-	Gb_Osc();
-	
+	void reset();
 	void clock_length();
-	void reset();
-	virtual void run( gb_time_t begin, gb_time_t end ) = 0;
-	virtual void write_register( int reg, int value );
+	int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
 };
 
-struct Gb_Env : Gb_Osc {
-	int env_period;
-	int env_dir;
+struct Gb_Env : Gb_Osc
+{
 	int env_delay;
-	int new_env_period;
-	int new_env_dir;
-	int new_volume;
 	
-	Gb_Env();
 	void reset();
 	void clock_envelope();
-	void write_register( int, int );
+	bool write_register( int, int );
 };
 
-struct Gb_Square : Gb_Env {
-	int phase;
-	int duty;
+struct Gb_Square : Gb_Env
+{
+	enum { period_mask = 0x70 };
+	enum { shift_mask  = 0x07 };
 	
-	int sweep_period;
+	typedef Blip_Synth<blip_good_quality,1> Synth;
+	Synth const* synth;
 	int sweep_delay;
-	int sweep_shift;
-	int sweep_dir;
 	int sweep_freq;
-	bool has_sweep;
-	
-	typedef Blip_Synth<blip_good_quality,15 * 2> Synth;
-	const Synth* synth;
+	int phase;
 	
-	Gb_Square();
 	void reset();
-	void run( gb_time_t, gb_time_t );
-	void write_register( int, int );
 	void clock_sweep();
+	void run( gb_time_t, gb_time_t, int playing );
 };
 
-struct Gb_Wave : Gb_Osc {
-	int volume_shift;
-	unsigned wave_pos;
+struct Gb_Noise : Gb_Env
+{
+	typedef Blip_Synth<blip_med_quality,1> Synth;
+	Synth const* synth;
+	unsigned bits;
+	
+	void run( gb_time_t, gb_time_t, int playing );
+};
+
+struct Gb_Wave : Gb_Osc
+{
+	typedef Blip_Synth<blip_med_quality,1> Synth;
+	Synth const* synth;
+	int wave_pos;
 	enum { wave_size = 32 };
 	BOOST::uint8_t wave [wave_size];
 	
-	typedef Blip_Synth<blip_med_quality,15 * 2> Synth;
-	Synth synth;
-	
-	Gb_Wave();
-	void reset();
-	void run( gb_time_t, gb_time_t );
 	void write_register( int, int );
+	void run( gb_time_t, gb_time_t, int playing );
 };
 
-struct Gb_Noise : Gb_Env {
-	unsigned bits;
-	int tap;
-	
-	typedef Blip_Synth<blip_med_quality,15 * 2> Synth;
-	Synth synth;
-	
-	Gb_Noise();
-	void reset();
-	void run( gb_time_t, gb_time_t );
-	void write_register( int, int );
-};
+inline void Gb_Env::reset()
+{
+	env_delay = 0;
+	Gb_Osc::reset();
+}
 
 #endif
 
--- a/Plugins/Input/console/Gbs_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gbs_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,5 +1,5 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Gbs_Emu.h"
 
@@ -7,7 +7,7 @@
 
 #include "blargg_endian.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -20,12 +20,18 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-const long clock_rate = 4194304;
+#ifndef RUN_GB_CPU
+	#define RUN_GB_CPU( cpu, n ) cpu.run( n )
+#endif
+
 const long bank_size = 0x4000;
 const gb_addr_t ram_addr = 0xa000;
-const gb_addr_t halt_addr = 0x9eff;
+const gb_addr_t halt_addr = 0x9EFE;
 static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size];
 
+Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq   = { -47.0, 2000 };
+Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = {   0.0, 300 };
+
 // RAM
 
 int Gbs_Emu::read_ram( Gbs_Emu* emu, gb_addr_t addr )
@@ -43,7 +49,7 @@
 int Gbs_Emu::read_unmapped( Gbs_Emu*, gb_addr_t addr )
 {
 	dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr );
-	return 0xff; // open bus value (probably due to pull-up resistors)
+	return 0xFF; // open bus value
 }
 
 void Gbs_Emu::write_unmapped( Gbs_Emu*, gb_addr_t addr, int )
@@ -65,12 +71,18 @@
 
 void Gbs_Emu::set_bank( int n )
 {
-	if ( n >= bank_count ) {
+	if ( n >= bank_count )
+	{
 		n = 0;
 		dprintf( "Set to non-existent bank %d\n", (int) n );
 	}
 	if ( n == 0 && bank_count > 1 )
-		dprintf( "Selected ROM bank 0\n" );
+	{
+		// to do: what is the correct behavior? Current Wario Land 3 and
+		// Tetris DX GBS rips require that this have no effect or set to bank 1.
+		//return;
+		//dprintf( "Selected ROM bank 0\n" );
+	}
 	rom_bank = &rom [n * bank_size];
 	cpu.map_code( bank_size, bank_size, rom_bank );
 }
@@ -78,7 +90,7 @@
 void Gbs_Emu::write_rom( Gbs_Emu* emu, gb_addr_t addr, int data )
 {
 	if ( unsigned (addr - 0x2000) < 0x2000 )
-		emu->set_bank( data & 0x1f );
+		emu->set_bank( data & 0x1F );
 }
 
 // I/O: Timer, APU
@@ -86,7 +98,10 @@
 void Gbs_Emu::set_timer( int modulo, int rate )
 {
 	if ( timer_mode )
-		play_period = gb_time_t (256 - modulo) << (((rate - 1) & 3) * 2 + 4 - double_speed);
+	{
+		static byte const rates [4] = { 10, 4, 6, 8 };
+		play_period = (gb_time_t) (256 - modulo) << (rates [rate & 3] - double_speed);
+	}
 }
 
 inline gb_time_t Gbs_Emu::clock() const
@@ -97,71 +112,67 @@
 int Gbs_Emu::read_io( Gbs_Emu* emu, gb_addr_t addr )
 {
 	// hi_page is accessed most
-	if ( addr >= 0xff80 )
-		return emu->hi_page [addr & 0xff];
+	if ( addr >= 0xFF80 )
+		return emu->hi_page [addr & 0xFF];
 	
 	if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count )
 		return emu->apu.read_register( emu->clock(), addr );
 	
-	if ( addr == 0xff00 )
+	if ( addr == 0xFF00 )
 		return 0; // joypad
 	
-	dprintf( "Unhandled I/O read 0x%4x\n", (unsigned) addr );
+	dprintf( "Unhandled I/O read 0x%4X\n", (unsigned) addr );
 	
-	return 0xff;
+	return 0xFF;
 }
 
 void Gbs_Emu::write_io( Gbs_Emu* emu, gb_addr_t addr, int data )
 {
 	// apu is accessed most
-	if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) {
+	if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count )
+	{
 		emu->apu.write_register( emu->clock(), addr, data );
 	}
-	else {
-		emu->hi_page [addr & 0xff] = data;
+	else
+	{
+		emu->hi_page [addr & 0xFF] = data;
 		
-		if ( addr == 0xff06 || addr == 0xff07 )
+		if ( addr == 0xFF06 || addr == 0xFF07 )
 			emu->set_timer( emu->hi_page [6], emu->hi_page [7] );
 		
-		if ( addr == 0xffff )
-			dprintf( "Wrote interrupt mask\n" );
+		//if ( addr == 0xFFFF )
+		//  dprintf( "Wrote interrupt mask\n" );
 	}
 }
 
-Gbs_Emu::Gbs_Emu( double gain )
+Gbs_Emu::Gbs_Emu( double gain ) : cpu( this )
 {
-	rom = NULL;
-	
 	apu.volume( gain );
 	
-	// to do: decide on equalization parameters
-	set_equalizer( equalizer_t( -32, 8000, 90 ) );
+	static equalizer_t const eq = { -1.0, 120 };
+	set_equalizer( eq );
 	
  	// unmapped code is all HALT instructions
 	memset( unmapped_code, 0x76, sizeof unmapped_code );
 	
 	// cpu
-	cpu.callback_data = this;
-	cpu.reset( unmapped_code );
+	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
 	cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom );
 	cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom );
-	cpu.map_memory( 0x8000, 0x8000, read_unmapped, write_unmapped );
 	cpu.map_memory( ram_addr, 0x4000, read_ram, write_ram );
 	cpu.map_code(   ram_addr, 0x4000, ram );
-	cpu.map_code(   0xff00, 0x0100, hi_page );
-	cpu.map_memory( 0xff00, 0x0100, read_io, write_io );
+	cpu.map_code(   0xFF00, 0x0100, hi_page );
+	cpu.map_memory( 0xFF00, 0x0100, read_io, write_io );
 }
 
 Gbs_Emu::~Gbs_Emu()
 {
-	unload();
 }
 
 void Gbs_Emu::unload()
 {
-	delete [] rom;
-	rom = NULL;
 	cpu.r.pc = halt_addr;
+	rom.clear();
 }
 
 void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
@@ -174,31 +185,39 @@
 	apu.treble_eq( eq );
 }
 
-blargg_err_t Gbs_Emu::load( const header_t& h, Emu_Reader& in )
+blargg_err_t Gbs_Emu::load( Data_Reader& in )
 {
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Gbs_Emu::load( const header_t& h, Data_Reader& in )
+{
+	header_ = h;
 	unload();
 	
 	// check compatibility
-	if ( 0 != memcmp( h.tag, "GBS", 3 ) )
+	if ( 0 != memcmp( header_.tag, "GBS", 3 ) )
 		return "Not a GBS file";
-	if ( h.vers != 1 )
+	if ( header_.vers != 1 )
 		return "Unsupported GBS format";
 	
 	// gather relevant fields
-	load_addr = get_le16( h.load_addr );
-	init_addr = get_le16( h.init_addr );
-	play_addr = get_le16( h.play_addr );
-	stack_ptr = get_le16( h.stack_ptr );
-	double_speed = (h.timer_mode & 0x80) != 0;
-	timer_modulo_init = h.timer_modulo;
-	timer_mode = h.timer_mode;
+	load_addr = get_le16( header_.load_addr );
+	init_addr = get_le16( header_.init_addr );
+	play_addr = get_le16( header_.play_addr );
+	stack_ptr = get_le16( header_.stack_ptr );
+	double_speed = (header_.timer_mode & 0x80) != 0;
+	timer_modulo_init = header_.timer_modulo;
+	timer_mode = header_.timer_mode;
 	if ( !(timer_mode & 0x04) )
 		timer_mode = 0; // using vbl
 	
 	#ifndef NDEBUG
 	{
-		if ( h.timer_mode & 0x78 )
-			dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) h.timer_mode );
+		if ( header_.timer_mode & 0x78 )
+			dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) header_.timer_mode );
 		
 		if ( load_addr < 0x400 || load_addr >= 0x8000 ||
 				init_addr < 0x400 || init_addr >= 0x8000 ||
@@ -209,25 +228,23 @@
 	
 	// rom
 	bank_count = (load_addr + in.remain() + bank_size - 1) / bank_size;
-	long rom_size = bank_count * bank_size;
-	rom = new BOOST::uint8_t [rom_size];
-	if ( !rom )
-		return "Out of memory";
-	memset( rom, 0, rom_size );
+	BLARGG_RETURN_ERR( rom.resize( bank_count * bank_size ) );
+	memset( rom.begin(), 0, rom.size() );
 	blargg_err_t err = in.read( &rom [load_addr], in.remain() );
-	if ( err ) {
+	if ( err )
+	{
 		unload();
 		return err;
 	}
 	
 	// cpu
 	cpu.rst_base = load_addr;
-	cpu.map_code( 0x0000, 0x4000, rom );
+	cpu.map_code( 0x0000, 0x4000, rom.begin() );
 	
-	voice_count_ = Gb_Apu::osc_count;
-	track_count_ = h.track_count;
+	set_voice_count( Gb_Apu::osc_count );
+	set_track_count( header_.track_count );
 	
-	return setup_buffer( clock_rate );
+	return setup_buffer( 4194304 );
 }
 
 const char** Gbs_Emu::voice_names() const
@@ -239,32 +256,28 @@
 // Emulation
 
 static const BOOST::uint8_t sound_data [Gb_Apu::register_count] = {
-	0x80, 0xbf, 0x00, 0x00, 0xbf, // square 1
-	0x00, 0x3f, 0x00, 0x00, 0xbf, // square 2
-	0x7f, 0xff, 0x9f, 0x00, 0xbf, // wave
-	0x00, 0xff, 0x00, 0x00, 0xbf, // noise
-	
-	0x77, 0xf3, 0xf1, // vin/volume, status, power mode
-	
+	0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1
+	0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2
+	0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave
+	0x00, 0xFF, 0x00, 0x00, 0xBF, // noise
+	0x77, 0xF3, 0xF1, // vin/volume, status, power mode
 	0, 0, 0, 0, 0, 0, 0, 0, 0, // unused
-	
-	0xac, 0xdd, 0xda, 0x48, 0x36, 0x02, 0xcf, 0x16, // waveform data
-	0x2c, 0x04, 0xe5, 0x2c, 0xac, 0xdd, 0xda, 0x48
+	0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data
+	0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48
 };
 
 void Gbs_Emu::cpu_jsr( gb_addr_t addr )
 {
 	cpu.write( --cpu.r.sp, cpu.r.pc >> 8 );
-	cpu.write( --cpu.r.sp, cpu.r.pc&0xff );
+	cpu.write( --cpu.r.sp, cpu.r.pc&0xFF );
 	cpu.r.pc = addr;
 }
 
-blargg_err_t Gbs_Emu::start_track( int track_index )
+void Gbs_Emu::start_track( int track_index )
 {
-	require( rom ); // file must be loaded
-	require( (unsigned) track_index < track_count() );
+	require( rom.size() ); // file must be loaded
 	
-	starting_track();
+	Classic_Emu::start_track( track_index );
 	
 	apu.reset();
 	
@@ -273,7 +286,7 @@
 	
 	// configure hardware
 	set_bank( bank_count > 1 );
-	for ( int i = 0; i < sizeof sound_data; i++ )
+	for ( int i = 0; i < (int) sizeof sound_data; i++ )
 		apu.write_register( 0, i + apu.start_addr, sound_data [i] );
 	play_period = 70224; // 59.73 Hz
 	set_timer( timer_modulo_init, timer_mode ); // ignored if using vbl
@@ -291,22 +304,20 @@
 	cpu.r.pc = halt_addr;
 	cpu.r.sp = stack_ptr;
 	cpu_jsr( init_addr );
-	
-	return blargg_success;
 }
 
-blip_time_t Gbs_Emu::run( int msec, bool* added_stereo )
+blip_time_t Gbs_Emu::run_clocks( blip_time_t duration, bool* added_stereo )
 {
-	require( rom ); // file must be loaded
+	require( rom.size() ); // file must be loaded
 	
-	gb_time_t duration = (gb_time_t) (clock_rate * (1.0 / 1000.0) * msec);
 	cpu_time = 0;
 	while ( cpu_time < duration )
 	{
 		// check for idle cpu
 		if ( cpu.r.pc == halt_addr )
 		{
-			if ( next_play > duration ) {
+			if ( next_play > duration )
+			{
 				cpu_time = duration;
 				break;
 			}
@@ -319,21 +330,24 @@
 		
 		long count = duration - cpu_time;
 		cpu_time = duration;
-		Gb_Cpu::result_t result = cpu.run( count );
+		Gb_Cpu::result_t result = RUN_GB_CPU( cpu, count );
 		cpu_time -= cpu.remain();
 		
 		if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) ||
 				result == Gb_Cpu::result_badop )
 		{
-			if ( result == Gb_Cpu::result_halt && cpu.r.pc < cpu.page_size )
+			if ( cpu.r.pc > 0xFFFF )
 			{
 				dprintf( "PC wrapped around\n" );
+				cpu.r.pc &= 0xFFFF;
 			}
 			else
 			{
+				log_error();
 				dprintf( "Bad opcode $%.2x at $%.4x\n",
 						(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
-				return 0; // error
+				cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
+				cpu_time += 6;
 			}
 		}
 	}
@@ -350,25 +364,3 @@
 	return cpu_time;
 }
 
-Gbs_Reader::Gbs_Reader() : file( NULL ) {
-}
-
-Gbs_Reader::~Gbs_Reader() {
-	close();
-}
-
-blargg_err_t Gbs_Reader::read_head(Gbs_Emu::header_t *header) {
-	vfs_fread(&header->tag,        1, 3,file);
-	vfs_fread(&header->vers,       1, 1,file);
-	vfs_fread(&header->track_count,1, 1,file);
-	vfs_fread(&header->first_track,1, 1,file);
-	vfs_fread(&header->load_addr,  1, 2,file);
-	vfs_fread(&header->init_addr,  1, 2,file);
-	vfs_fread(&header->play_addr,  1, 2,file);
-	vfs_fread(&header->stack_ptr,  1, 2,file);
-	vfs_fread(&header->timer_modulo,1, 1,file);
-	vfs_fread(&header->timer_mode, 1, 2,file);
-	vfs_fread(&header->game,       1,32,file);
-	vfs_fread(&header->author,     1,32,file);
-	vfs_fread(&header->copyright,  1,32,file);
-}
--- a/Plugins/Input/console/Gbs_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gbs_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
-// Game Boy GBS-format game music file emulator
+// Nintendo Game Boy GBS music file emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef GBS_EMU_H
 #define GBS_EMU_H
@@ -12,12 +12,14 @@
 
 class Gbs_Emu : public Classic_Emu {
 public:
+	
 	// Sets internal gain, where 1.0 results in almost no clamping. Default gain
 	// roughly matches volume of other emulators.
-	Gbs_Emu( double gain = 1.3 );
-	~Gbs_Emu();
+	Gbs_Emu( double gain = 1.2 );
 	
-	struct header_t {
+	// GBS file header
+	struct header_t
+	{
 		char tag [3];
 		byte vers;
 		byte track_count;
@@ -36,22 +38,31 @@
 	};
 	BOOST_STATIC_ASSERT( sizeof (header_t) == 112 );
 	
-	// Load GBS, given its header and reader for remaining data
-	blargg_err_t load( const header_t&, Emu_Reader& );
+	// Load GBS data
+	blargg_err_t load( Data_Reader& );
+	
+	// Load GBS using already-loaded header and remaining data
+	blargg_err_t load( header_t const&, Data_Reader& );
 	
+	// Header for currently loaded GBS
+	header_t const& header() const { return header_; }
+	
+	// Equalizer profiles for Game Boy Color speaker and headphones
+	static equalizer_t const handheld_eq;
+	static equalizer_t const headphones_eq;
+	
+public:
+	~Gbs_Emu();
 	const char** voice_names() const;
-	blargg_err_t start_track( int );
-	
-
-// End of public interface
+	void start_track( int );
 protected:
 	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
 	void update_eq( blip_eq_t const& );
-	blip_time_t run( int, bool* );
+	blip_time_t run_clocks( blip_time_t, bool* );
 private:
 	// rom
 	const byte* rom_bank;
-	byte* rom;
+	blargg_vector<byte> rom;
 	void unload();
 	int bank_count;
 	void set_bank( int );
@@ -75,14 +86,16 @@
 	
 	// hardware
 	Gb_Apu apu;
-	byte hi_page [0x100];
 	void set_timer( int tma, int tmc );
 	static int read_io( Gbs_Emu*, gb_addr_t );
 	static void write_io( Gbs_Emu*, gb_addr_t, int );
 	static int read_unmapped( Gbs_Emu*, gb_addr_t );
 	static void write_unmapped( Gbs_Emu*, gb_addr_t, int );
 	
-	// cpu and ram
+	// large objects
+	
+	header_t header_;
+	byte hi_page [0x100];
 	Gb_Cpu cpu;
 	void cpu_jsr( gb_addr_t );
 	gb_time_t clock() const;
@@ -91,15 +104,5 @@
 	static void write_ram( Gbs_Emu*, gb_addr_t, int );
 };
 
-class Gbs_Reader : public Std_File_Reader {
-	VFSFile* file;
-public:
-	Gbs_Reader();
-	~Gbs_Reader();
-
-	// Custom reader for Gbs headers [tempfix]
-	blargg_err_t read_head( Gbs_Emu::header_t* );
-};
-
 #endif
 
--- a/Plugins/Input/console/Gym_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gym_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,13 +1,12 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Gym_Emu.h"
 
-#include "ym2612.h"
+#include <string.h>
+#include "blargg_endian.h"
 
-#include <string.h>
-
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -20,6 +19,9 @@
 
 #include BLARGG_SOURCE_BEGIN
 
+double const gain = 3.0;
+double const oversample_factor = 5 / 3.0;
+
 const long base_clock = 53700300;
 const long clock_rate = base_clock / 15;
 
@@ -27,8 +29,6 @@
 {
 	data = NULL;
 	pos = NULL;
-	mem = NULL;
-	pairs_per_frame = 0;
 }
 
 Gym_Emu::~Gym_Emu()
@@ -38,50 +38,43 @@
 
 void Gym_Emu::unload()
 {
-	delete [] mem;
-	mem = NULL;
 	data = NULL;
 	pos = NULL;
-	track_ended_ = false;
+	set_track_ended( false );
+	mem.clear();
 }
 
-blargg_err_t Gym_Emu::init( long sample_rate, double gain, double oversample_ )
+blargg_err_t Gym_Emu::set_sample_rate( long sample_rate )
 {
-	require( oversample_ <= 4.0 ); 
-	
 	blip_eq_t eq( -32, 8000, sample_rate );
 	apu.treble_eq( eq );
-	apu.volume( 0.27 * gain );
+	apu.volume( 0.135 * gain );
 	dac_synth.treble_eq( eq );
-	dac_synth.volume( 0.25 * gain );
-	oversample = resampler.time_ratio( oversample_, 0.990, gain );
-
-	pairs_per_frame = sample_rate / 60;
-	oversamples_per_frame = int (pairs_per_frame * oversample) * 2 + 2;
-	clocks_per_sample = (double) clock_rate / sample_rate;
+	dac_synth.volume( 0.125 / 256 * gain );
 	
-	BLARGG_RETURN_ERR( resampler.buffer_size( oversamples_per_frame + 256 ) );
-	
-	BLARGG_RETURN_ERR( blip_buf.sample_rate( sample_rate, 1000 / 30 ) );
-	
-	BLARGG_RETURN_ERR( fm.set_rate( (long) (sample_rate * oversample), base_clock / 7 ) );
-	
+	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 60 ) );
 	blip_buf.clock_rate( clock_rate );
 	
-	return blargg_success;
+	double factor = Dual_Resampler::setup( oversample_factor, 0.990, gain );
+	double fm_sample_rate = sample_rate * factor;
+	BLARGG_RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
+	BLARGG_RETURN_ERR( Dual_Resampler::resize( sample_rate / 60 ) );
+	
+	return Music_Emu::set_sample_rate( sample_rate );
 }
 
 void Gym_Emu::mute_voices( int mask )
 {
+	Music_Emu::mute_voices( mask );
 	fm.mute_voices( mask );
-	dac_disabled = mask & 0x40;
+	dac_muted = mask & 0x40;
 	apu.output( (mask & 0x80) ? NULL : &blip_buf );
 }
 
 const char** Gym_Emu::voice_names() const
 {
 	static const char* names [] = {
-		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "SN76489"
+		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
 	};
 	return names;
 }
@@ -96,7 +89,8 @@
 		if ( data_offset )
 			*data_offset = sizeof h;
 	}
-	else if ( h.tag [0] != 0 && h.tag [0] != 1 ) {
+	else if ( h.tag [0] != 0 && h.tag [0] != 1 )
+	{
 		// not a headerless GYM
 		// to do: more thorough check, or just require a damn header
 		return "Not a GYM file";
@@ -107,26 +101,20 @@
 
 blargg_err_t Gym_Emu::load_( const void* file, long data_offset, long file_size )
 {
-	require( pairs_per_frame );
+	require( blip_buf.length() );
 	
 	data = (const byte*) file + data_offset;
 	data_end = (const byte*) file + file_size;
 	
 	loop_begin = NULL;
-	loop_offset = 0;
 	if ( data_offset )
-	{
-		const header_t& h = *(header_t*) file;
-		loop_offset =
-				h.loop [3] * 0x1000000L +
-				h.loop [2] * 0x10000L +
-				h.loop [1] * 0x100L +
-				h.loop [0];
-	}
+		header_ = *(header_t*) file;
+	else
+		memset( &header_, 0, sizeof header_ );
 	
-	track_count_ = 1;
-	voice_count_ = 8;
-	mute_voices( 0 );
+	set_voice_count( 8 );
+	set_track_count( 1 );
+	remute_voices();
 	
 	return blargg_success;
 }
@@ -135,7 +123,7 @@
 {
 	unload();
 	
-	if ( file_size < sizeof (header_t) )
+	if ( file_size < (int) sizeof (header_t) )
 		return "Not a GYM file";
 	
 	int data_offset = 0;
@@ -144,29 +132,30 @@
 	return load_( file, data_offset, file_size );
 }
 
-blargg_err_t Gym_Emu::load( const header_t& h, Emu_Reader& in )
+blargg_err_t Gym_Emu::load( Data_Reader& in )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Gym_Emu::load( const header_t& h, Data_Reader& in )
 {
 	unload();
 	
 	int data_offset = 0;
 	BLARGG_RETURN_ERR( check_header( h, &data_offset ) );
 	
-	long file_size = sizeof h + in.remain();
-	mem = new byte [file_size];
-	if ( !mem )
-		return "Out of memory";
-	memcpy( mem, &h, sizeof h );
-	BLARGG_RETURN_ERR( in.read( mem + sizeof h, file_size - sizeof h ) );
+	BLARGG_RETURN_ERR( mem.resize( sizeof h + in.remain() ) );
+	memcpy( mem.begin(), &h, sizeof h );
+	BLARGG_RETURN_ERR( in.read( &mem [sizeof h], mem.size() - sizeof h ) );
 	
-	return load_( mem, data_offset, file_size );
+	return load_( mem.begin(), data_offset, mem.size() );
 }
 
-int Gym_Emu::track_length() const
+long Gym_Emu::track_length() const
 {
-	if ( loop_offset || loop_begin )
-		return 0;
-	
-	long time = 0; // 1/60 sec frames
+	long time = 0;
 	const byte* p = data;
 	while ( p < data_end )
 	{
@@ -178,104 +167,172 @@
 			
 			case 1:
 			case 2:
-				++p;
-			case 3:
-				++p;
+				p += 2;
 				break;
 			
-			default:
-				dprintf( "Bad command: %02X\n", (int) p [-1] );
+			case 3:
+				p += 1;
 				break;
 		}
 	}
-	
-	return (time + 30 + 59) / 60;
+	return time;
 }
 
-blargg_err_t Gym_Emu::start_track( int )
+void Gym_Emu::start_track( int track )
 {
 	require( data );
 	
+	Music_Emu::start_track( track );
+	
 	pos = &data [0];
-	extra_pos = 0;
-	loop_remain = loop_offset;
+	loop_remain = get_le32( header_.loop_start );
 	
 	prev_dac_count = 0;
 	dac_enabled = false;
-	last_dac = -1;
+	dac_amp = -1;
 	
 	fm.reset();
 	apu.reset();
-	blip_buf.clear( false );
-	resampler.clear();
+	blip_buf.clear();
+	Dual_Resampler::clear();
+}
+
+void Gym_Emu::run_dac( int dac_count )
+{
+	// Guess beginning and end of sample and adjust rate and buffer position accordingly.
+	
+	// count dac samples in next frame
+	int next_dac_count = 0;
+	const byte* p = this->pos;
+	int cmd;
+	while ( (cmd = *p++) != 0 )
+	{
+		int data = *p++;
+		if ( cmd <= 2 )
+			++p;
+		if ( cmd == 1 && data == 0x2A )
+			next_dac_count++;
+	}
 	
-	track_ended_ = false;
+	// detect beginning and end of sample
+	int rate_count = dac_count;
+	int start = 0;
+	if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count )
+	{
+		rate_count = next_dac_count;
+		start = next_dac_count - dac_count;
+	}
+	else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count )
+	{
+		rate_count = prev_dac_count;
+	}
 	
-	return blargg_success;
+	// Evenly space samples within buffer section being used
+	blip_resampled_time_t period =
+			blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
+	
+	blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
+			period * start + (period >> 1);
+	
+	int dac_amp = this->dac_amp;
+	if ( dac_amp < 0 )
+		dac_amp = dac_buf [0];
+	
+	for ( int i = 0; i < dac_count; i++ )
+	{
+		int delta = dac_buf [i] - dac_amp;
+		dac_amp += delta;
+		dac_synth.offset_resampled( time, delta, &blip_buf );
+		time += period;
+	}
+	this->dac_amp = dac_amp;
 }
 
-void Gym_Emu::play_frame( sample_t* out )
+void Gym_Emu::parse_frame()
 {
-	parse_frame();
+	int dac_count = 0;
+	const byte* pos = this->pos;
 	
-	// run SMS APU and buffer
-	blip_time_t clock_count = (blip_time_t) ((pairs_per_frame + 1 - blip_buf.samples_avail()) *
-			clocks_per_sample);
-	apu.end_frame( clock_count );
-	blip_buf.end_frame( clock_count );
-	assert( unsigned (blip_buf.samples_avail() - pairs_per_frame) <= 4 );
+	if ( loop_remain && !--loop_remain )
+		loop_begin = pos; // find loop on first time through sequence
 	
-	// run fm
-	const int sample_count = oversamples_per_frame - resampler.written();
-	sample_t* buf = resampler.buffer();
-	memset( buf, 0, sample_count * sizeof *buf );
-	fm.run( buf, sample_count );
-	resampler.write( sample_count );
-	int count = resampler.read( sample_buf, pairs_per_frame * 2 );
-	assert( count <= sample_buf_size );
-	assert( unsigned (count - pairs_per_frame * 2) < 32 );
+	int cmd;
+	while ( (cmd = *pos++) != 0 )
+	{
+		int data = *pos++;
+		if ( cmd == 1 )
+		{
+			int data2 = *pos++;
+			if ( data != 0x2A )
+			{
+				if ( data == 0x2B )
+					dac_enabled = (data2 & 0x80) != 0;
+				
+				fm.write0( data, data2 );
+			}
+			else if ( dac_count < (int) sizeof dac_buf )
+			{
+				dac_buf [dac_count] = data2;
+				dac_count += dac_enabled;
+			}
+		}
+		else if ( cmd == 2 )
+		{
+			fm.write1( data, *pos++ );
+		}
+		else if ( cmd == 3 )
+		{
+			apu.write_data( 0, data );
+		}
+		else
+		{
+			// to do: many GYM streams are full of errors, and error count should
+			// reflect cases where music is really having problems
+			//log_error(); 
+			--pos; // put data back
+		}
+	}
 	
-	// mix outputs
-	mix_samples( out );
-	blip_buf.remove_samples( pairs_per_frame );
+	// loop
+	if ( pos >= data_end )
+	{
+		if ( pos > data_end )
+			log_error();
+		
+		if ( loop_begin )
+			pos = loop_begin;
+		else
+			set_track_ended();
+	}
+	this->pos = pos;
+	
+	// dac
+	if ( dac_count && !dac_muted )
+		run_dac( dac_count );
+	prev_dac_count = dac_count;
 }
 
-blargg_err_t Gym_Emu::play( long count, sample_t* out )
+int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
+{
+	if ( !track_ended() )
+		parse_frame();
+	
+	apu.end_frame( blip_time );
+	
+	memset( buf, 0, sample_count * sizeof *buf );
+	fm.run( sample_count >> 1, buf );
+	
+	return sample_count;
+}
+
+void Gym_Emu::play( long count, sample_t* out )
 {
 	require( pos );
 	
-	const int samples_per_frame = pairs_per_frame * 2;
-	
-	// empty extra buffer
-	if ( extra_pos ) {
-		int n = samples_per_frame - extra_pos;
-		if ( n > count )
-			n = count;
-		memcpy( out, sample_buf + extra_pos, n * sizeof *out );
-		out += n;
-		count -= n;
-		extra_pos = (extra_pos + n) % samples_per_frame;
-	}
-	
-	// entire frames
-	while ( count >= samples_per_frame ) {
-		play_frame( out );
-		out += samples_per_frame;
-		count -= samples_per_frame;
-	}
-	
-	// extra
-	if ( count ) {
-		play_frame( sample_buf );
-		extra_pos = count;
-		memcpy( out, sample_buf, count * sizeof *out );
-		out += count;
-	}
-	
-	return blargg_success;
+	Dual_Resampler::play( count, out, blip_buf );
 }
 
-blargg_err_t Gym_Emu::skip( long count )
+void Gym_Emu::skip( long count )
 {
 	// to do: figure out why total muting generated access violation on MorphOS
 	const int buf_size = 1024;
@@ -287,173 +344,7 @@
 		if ( n > count )
 			n = count;
 		count -= n;
-		BLARGG_RETURN_ERR( play( n, buf ) );
-	}
-	
-	return blargg_success;
-}
-
-void Gym_Emu::run_dac( int dac_count )
-{
-	if ( !dac_disabled )
-	{
-		// Guess beginning and end of sample and adjust rate and buffer position accordingly.
-		
-		// count dac samples in next frame
-		int next_dac_count = 0;
-		const byte* p = this->pos;
-		int cmd;
-		while ( (cmd = *p++) != 0 ) {
-			int data = *p++;
-			if ( cmd <= 2 )
-				++p;
-			if ( cmd == 1 && data == 0x2A )
-				next_dac_count++;
-		}
-		
-		// adjust
-		int rate_count = dac_count;
-		int start = 0;
-		if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) {
-			rate_count = next_dac_count;
-			start = next_dac_count - dac_count;
-		}
-		else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) {
-			rate_count = prev_dac_count;
-		}
-		
-		// Evenly space samples within buffer section being used
-		Blip_Buffer::resampled_time_t period =
-				blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
-		
-		Blip_Buffer::resampled_time_t time = blip_buf.resampled_time( 0 ) +
-				period * start + (period >> 1);
-		
-		int last_dac = this->last_dac;
-		if ( last_dac < 0 )
-			last_dac = dac_buf [0];
-		
-		for ( int i = 0; i < dac_count; i++ )
-		{
-			int diff = dac_buf [i] - last_dac;
-			last_dac += diff;
-			dac_synth.offset_resampled( time, diff, &blip_buf );
-			time += period;
-		}
-		this->last_dac = last_dac;
-	}
-	
-	int const step = (int) (6 * oversample);
-	int remain = (int) (pairs_per_frame * oversample);
-	while ( remain ) {
-		int n = step;
-		if ( n > remain )
-			n = remain;
-		remain -= n;
-		fm.run_timer( n );
+		play( n, buf );
 	}
 }
 
-void Gym_Emu::parse_frame()
-{
-	if ( track_ended_ )
-		return;
-	
-	int dac_count = 0;
-	
-	const byte* pos = this->pos;
-	if ( loop_remain && !--loop_remain )
-		loop_begin = pos; // find loop on first time through sequence
-	int cmd;
-	while ( (cmd = *pos++) != 0 )
-	{
-		int data = *pos++;
-		if ( cmd == 1 ) {
-			int data2 = *pos++;
-			if ( data == 0x2A ) {
-				if ( dac_count < sizeof dac_buf ) {
-					dac_buf [dac_count] = data2;
-					dac_count += dac_enabled;
-				}
-			}
-			else {
-				if ( data == 0x2B )
-					dac_enabled = (data2 & 0x80) != 0;
-				
-				fm.write( 0, data );
-				fm.write( 1, data2 );
-			}
-		}
-		else if ( cmd == 2 ) {
-			fm.write( 2, data );
-			fm.write( 3, *pos++ );
-		}
-		else if ( cmd == 3 ) {
-			apu.write_data( 0, data );
-		}
-		else {
-			dprintf( "Bad command: %02X\n", (int) cmd );
-			--pos; // put data back
-		}
-	}
-	// loop
-	if ( pos >= data_end ) {
-		if ( loop_begin )
-			pos = loop_begin;
-		else
-			track_ended_ = true;
-	}
-	this->pos = pos;
-	
-	// dac
-	if ( dac_count )
-		run_dac( dac_count );
-	prev_dac_count = dac_count;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Gym_Emu::mix_samples( sample_t* out )
-{
-	// Mix one frame of Blip_Buffer (SMS APU and PCM) and resampled YM audio
-	Blip_Reader sn;
-	int bass = sn.begin( blip_buf );
-	const sample_t* ym = sample_buf;
-	
-	for ( int n = pairs_per_frame; n--; )
-	{
-		int s = sn.read();
-		long l = ym [0] * 2 + s;
-		sn.next( bass );
-		if ( (BOOST::int16_t) l != l )
-			l = 0x7FFF - (l >> 24);
-		long r = ym [1] * 2 + s;
-		ym += 2;
-		out [0] = l;
-		out [1] = r;
-		out += 2;
-		if ( (BOOST::int16_t) r != r )
-			out [-1] = 0x7FFF - (r >> 24);
-	}
-	
-	sn.end( blip_buf );
-}
-
-
-Gym_Reader::Gym_Reader() : file( NULL ) {
-}
-
-Gym_Reader::~Gym_Reader() {
-	close();
-}
-
-blargg_err_t Gym_Reader::read_head(Gym_Emu::header_t *header) {
-	vfs_fread(&header->tag,      1,  4,file);
-	vfs_fread(&header->song,     1, 32,file);
-	vfs_fread(&header->game,     1, 32,file);
-	vfs_fread(&header->emulator, 1, 32,file);
-	vfs_fread(&header->dumper,   1, 32,file);
-	vfs_fread(&header->comment,  1,256,file);
-	vfs_fread(&header->loop,     1,  4,file);
-	vfs_fread(&header->packed,   1,  4,file);
-}
--- a/Plugins/Input/console/Gym_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Gym_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,29 +1,22 @@
 
 // Sega Genesis GYM music file emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef GYM_EMU_H
 #define GYM_EMU_H
 
-#include "Fir_Resampler.h"
-#include "Blip_Buffer.h"
+#include "Dual_Resampler.h"
+#include "Ym2612_Emu.h"
 #include "Music_Emu.h"
 #include "Sms_Apu.h"
-#include "ym2612.h"
 
-class Gym_Emu : public Music_Emu {
+class Gym_Emu : public Music_Emu, private Dual_Resampler {
 public:
-	Gym_Emu();
-	~Gym_Emu();
 	
-	// Initialize emulator with given sample rate, gain, and oversample. A gain of 1.0
-	// results in almost no clamping. Default gain roughly matches volume of other emulators.
-	// The FM chip is synthesized at an increased rate governed by the oversample factor,
-	// where 1.0 results in no oversampling and > 1.0 results in oversampling.
-	blargg_err_t init( long sample_rate, double gain = 1.5, double oversample = 5 / 3.0 );
-	
-	struct header_t {
+	// GYM file header
+	struct header_t
+	{
 	    char tag [4];
 	    char song [32];
 	    char game [32];
@@ -31,7 +24,7 @@
 	    char emulator [32];
 	    char dumper [32];
 	    char comment [256];
-	    byte loop [4];
+	    byte loop_start [4]; // in 1/60 seconds, 0 if not looped
 	    byte packed [4];
 	    
 	    enum { track_count = 1 }; // one track per file
@@ -39,72 +32,64 @@
 	};
 	BOOST_STATIC_ASSERT( sizeof (header_t) == 428 );
 	
-	// Load GYM, given its header and reader for remaining data
-	blargg_err_t load( const header_t&, Emu_Reader& );
+	// Load GYM data
+	blargg_err_t load( Data_Reader& );
 	
-	// Load GYM, given pointer to complete file data. Keeps reference
-	// to data, but doesn't free it.
-	blargg_err_t load( const void*, long size );
+	// Load GYM file using already-loaded header and remaining data
+	blargg_err_t load( header_t const&, Data_Reader& );
+	blargg_err_t load( void const* data, long size ); // keeps pointer to data
+	
+	// Header for currently loaded GYM (cleared to zero if GYM lacks header)
+	header_t const& header() const { return header_; }
 	
-	// Length of track, in seconds (0 if looped)
-	int track_length() const;
-	
+	// Length of track in 1/60 seconds
+	enum { gym_rate = 60 }; // GYM time units (frames) per second
+	long track_length() const;
+
+public:
+	typedef Music_Emu::sample_t sample_t;
+	Gym_Emu();
+	~Gym_Emu();
+	blargg_err_t set_sample_rate( long sample_rate );
 	void mute_voices( int );
-	blargg_err_t start_track( int );
-	blargg_err_t play( long count, sample_t* );
+	void start_track( int );
+	void play( long count, sample_t* );
 	const char** voice_names() const;
-	blargg_err_t skip( long count );
-	
-// End of public interface
+	void skip( long count );
+public:
+	// deprecated
+	blargg_err_t init( long r, double gain = 1.5, double oversample = 5 / 3.0 )
+	{
+		return set_sample_rate( r );
+	}
+protected:
+	int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf );
 private:
 	// sequence data begin, loop begin, current position, end
 	const byte* data;
 	const byte* loop_begin;
 	const byte* pos;
 	const byte* data_end;
-	long loop_offset;
 	long loop_remain; // frames remaining until loop beginning has been located
-	byte* mem;
+	blargg_vector<byte> mem;
+	header_t header_;
 	blargg_err_t load_( const void* file, long data_offset, long file_size );
-	
-	// frames
-	double oversample;
-	double clocks_per_sample;
-	int pairs_per_frame;
-	int oversamples_per_frame;
+	void unload();
 	void parse_frame();
-	void play_frame( sample_t* );
-	void mix_samples( sample_t* );
 	
 	// dac (pcm)
-	int last_dac;
+	int dac_amp;
 	int prev_dac_count;
 	bool dac_enabled;
-	bool dac_disabled;
+	bool dac_muted;
 	void run_dac( int );
 	
 	// sound
-	int extra_pos; // extra samples remaining from last read
 	Blip_Buffer blip_buf;
-	YM2612_Emu fm;
-	Blip_Synth<blip_med_quality,256> dac_synth;
+	Ym2612_Emu fm;
+	Blip_Synth<blip_med_quality,1> dac_synth;
 	Sms_Apu apu;
-	Fir_Resampler resampler;
 	byte dac_buf [1024];
-	enum { sample_buf_size = 4096 };
-	sample_t sample_buf [sample_buf_size];
-	
-	void unload();
-};
-
-class Gym_Reader : public Std_File_Reader {
-	VFSFile* file;
-public:
-	Gym_Reader();
-	~Gym_Reader();
-	
-	// Custom reader for SPC headers [tempfix]
-	blargg_err_t read_head( Gym_Emu::header_t* );
 };
 
 #endif
--- a/Plugins/Input/console/Makefile.am	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Makefile.am	Tue Jan 24 19:10:07 2006 -0800
@@ -3,35 +3,41 @@
 libdir = $(plugindir)/$(INPUT_PLUGIN_DIR)
 
 libconsole_la_SOURCES = \
-	Blip_Buffer.cpp		\
-	Classic_Emu.cpp		\
-	Effects_Buffer.cpp	\
-	Fir_Resampler.cpp	\
-	Gb_Apu.cpp		\
-	Gb_Cpu.cpp		\
-	Gb_Oscs.cpp		\
-	Gbs_Emu.cpp		\
-	Gym_Emu.cpp		\
-	Multi_Buffer.cpp	\
-	Music_Emu.cpp		\
-	Nes_Apu.cpp		\
-	Nes_Cpu.cpp		\
-	Nes_Namco.cpp		\
-	Nes_Oscs.cpp		\
-	Nes_Vrc6.cpp		\
-	Nsf_Emu.cpp		\
-	Panning_Buffer.cpp	\
-	Sms_Apu.cpp		\
-	Snes_Spc.cpp		\
-	Spc_Cpu.cpp		\
-	Spc_Dsp.cpp		\
-	Spc_Emu.cpp		\
-	Vgm_Emu.cpp		\
-	abstract_file.cpp	\
-	ym2612.cpp		\
+	Blip_Buffer.cpp     \
+	Classic_Emu.cpp     \
+	Dual_Resampler.cpp  \
+	Fir_Resampler.cpp   \
+	Gb_Apu.cpp      \
+	Gb_Cpu.cpp      \
+	Gb_Oscs.cpp     \
+	Gbs_Emu.cpp     \
+	Gym_Emu.cpp     \
+	Multi_Buffer.cpp    \
+	Music_Emu.cpp       \
+	Nes_Apu.cpp     \
+	Nes_Cpu.cpp     \
+	Nes_Fme7_Apu.cpp    \
+	Nes_Namco_Apu.cpp   \
+	Nes_Oscs.cpp        \
+	Nes_Vrc6_Apu.cpp    \
+	Nsfe_Emu.cpp        \
+	Nsf_Emu.cpp     \
+	Sms_Apu.cpp     \
+	Snes_Spc.cpp        \
+	Spc_Cpu.cpp     \
+	Spc_Dsp.cpp     \
+	Spc_Emu.cpp     \
+	Vgm_Emu.cpp     \
+	abstract_file.cpp   \
+	Vfs_File.cpp        \
+	Gzip_File.cpp       \
+	Vgm_Emu_Impl.cpp    \
+	Ym2413_Emu.cpp      \
+	Ym2612_Emu.cpp      \
+	Track_Emu.cpp       \
 	Audacious_Driver.cpp
 
 libconsole_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-libconsole_la_LIBADD = $(GTK_LIBS) $(top_builddir)/libaudacious/libaudacious.la
-INCLUDES = $(GTK_CFLAGS) $(ARCH_DEFINES) -I$(top_srcdir)/intl -I$(top_srcdir) -Iboost/
+libconsole_la_LIBADD = -lz $(GTK_LIBS) $(top_builddir)/libaudacious/libaudacious.la
+INCLUDES = $(GTK_CFLAGS) $(ARCH_DEFINES) -I$(top_srcdir)/intl -I$(top_srcdir)
 
--- a/Plugins/Input/console/Multi_Buffer.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Multi_Buffer.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,9 +1,9 @@
 
-// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
 
 #include "Multi_Buffer.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -16,10 +16,11 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-Multi_Buffer::Multi_Buffer()
+Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
 {
 	length_ = 0;
 	sample_rate_ = 0;
+	channels_changed_count_ = 1;
 }
 
 blargg_err_t Multi_Buffer::set_channel_count( int )
@@ -27,7 +28,7 @@
 	return blargg_success;
 }
 
-Mono_Buffer::Mono_Buffer()
+Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
 {
 }
 
@@ -35,15 +36,24 @@
 {
 }
 
-blargg_err_t Mono_Buffer::sample_rate( long rate, int msec )
+blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
 {
-	BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) );
-	length_ = buf.length();
-	sample_rate_ = buf.sample_rate();
-	return blargg_success;
+	BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) );
+	return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
 }
 
-Mono_Buffer::channel_t Mono_Buffer::channel( int index )
+// Silent_Buffer
+
+Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
+{
+	chan.left   = NULL;
+	chan.center = NULL;
+	chan.right  = NULL;
+}
+
+// Mono_Buffer
+
+Mono_Buffer::channel_t Mono_Buffer::channel( int )
 {
 	channel_t ch;
 	ch.center = &buf;
@@ -57,7 +67,9 @@
 	buf.end_frame( t );
 }
 
-Stereo_Buffer::Stereo_Buffer()
+// Stereo_Buffer
+
+Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
 {
 	chan.center = &bufs [0];
 	chan.left = &bufs [1];
@@ -68,13 +80,11 @@
 {
 }
 
-blargg_err_t Stereo_Buffer::sample_rate( long rate, int msec )
+blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
 {
 	for ( int i = 0; i < buf_count; i++ )
-		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
-	length_ = msec;
-	sample_rate_ = rate;
-	return blargg_success;
+		BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
+	return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
 }
 
 void Stereo_Buffer::clock_rate( long rate )
--- a/Plugins/Input/console/Multi_Buffer.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Multi_Buffer.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,18 +1,19 @@
 
 // Multi-channel sound buffer interface, and basic mono and stereo buffers
 
-// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Blip_Buffer 0.4.0
 
 #ifndef MULTI_BUFFER_H
 #define MULTI_BUFFER_H
 
+#include "blargg_common.h"
 #include "Blip_Buffer.h"
 
-// Multi_Buffer is an interface to one or more Blip_Buffers mapped to one or
-// more channels consisting of left, center, and right buffers.
+// Interface to one or more Blip_Buffers mapped to one or more channels
+// consisting of left, center, and right buffers.
 class Multi_Buffer {
 public:
-	Multi_Buffer();
+	Multi_Buffer( int samples_per_frame );
 	virtual ~Multi_Buffer() { }
 	
 	// Set the number of channels available
@@ -27,7 +28,7 @@
 	virtual channel_t channel( int index ) = 0;
 	
 	// See Blip_Buffer.h
-	//virtual blargg_err_t sample_rate( long rate, int msec = 0 );
+	virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0;
 	virtual void clock_rate( long ) = 0;
 	virtual void bass_freq( int ) = 0;
 	virtual void clear() = 0;
@@ -41,21 +42,31 @@
 	// this time frame.
 	virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0;
 	
+	// Number of samples per output frame (1 = mono, 2 = stereo)
+	int samples_per_frame() const;
+	
+	// Count of changes to channel configuration. Incremented whenever
+	// a change is made to any of the Blip_Buffers for any channel.
+	unsigned channels_changed_count() { return channels_changed_count_; }
+	
 	// See Blip_Buffer.h
 	virtual long read_samples( blip_sample_t*, long ) = 0;
+	virtual long samples_avail() const = 0;
 	
 protected:
-	// Derived classes must set these to appropriate values, which are returned
-	// by the corresponding public function.
-	long sample_rate_;
-	int length_;
+	void channels_changed() { channels_changed_count_++; }
 private:
 	// noncopyable
 	Multi_Buffer( const Multi_Buffer& );
 	Multi_Buffer& operator = ( const Multi_Buffer& );
+	
+	unsigned channels_changed_count_;
+	long sample_rate_;
+	int length_;
+	int const samples_per_frame_;
 };
 
-// Mono_Buffer uses a single buffer and outputs mono samples.
+// Uses a single buffer and outputs mono samples.
 class Mono_Buffer : public Multi_Buffer {
 	Blip_Buffer buf;
 public:
@@ -63,36 +74,39 @@
 	~Mono_Buffer();
 	
 	// Buffer used for all channels
-	Blip_Buffer* center();
+	Blip_Buffer* center() { return &buf; }
 	
 	// See Multi_Buffer
-	blargg_err_t sample_rate( long rate, int msec = blip_default_length );
+	blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
 	void clock_rate( long );
 	void bass_freq( int );
 	void clear();
 	channel_t channel( int );
 	void end_frame( blip_time_t, bool unused = true );
+	long samples_avail() const;
 	long read_samples( blip_sample_t*, long );
 };
 
-// Stereo_Buffer uses three buffers (one for center) and outputs stereo sample pairs.
+// Uses three buffers (one for center) and outputs stereo sample pairs.
 class Stereo_Buffer : public Multi_Buffer {
 public:
 	Stereo_Buffer();
 	~Stereo_Buffer();
 	
 	// Buffers used for all channels
-	Blip_Buffer* center();
-	Blip_Buffer* left();
-	Blip_Buffer* right();
+	Blip_Buffer* center()       { return &bufs [0]; }
+	Blip_Buffer* left()         { return &bufs [1]; }
+	Blip_Buffer* right()        { return &bufs [2]; }
 	
 	// See Multi_Buffer
-	blargg_err_t sample_rate( long, int msec = blip_default_length );
+	blargg_err_t set_sample_rate( long, int msec = blip_default_length );
 	void clock_rate( long );
 	void bass_freq( int );
 	void clear();
 	channel_t channel( int index );
 	void end_frame( blip_time_t, bool added_stereo = true );
+	
+	long samples_avail() const;
 	long read_samples( blip_sample_t*, long );
 	
 private:
@@ -106,52 +120,56 @@
 	void mix_mono( blip_sample_t*, long );
 };
 
+// Silent_Buffer generates no samples, useful where no sound is wanted
+class Silent_Buffer : public Multi_Buffer {
+	channel_t chan;
+public:
+	Silent_Buffer();
+	
+	blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
+	void clock_rate( long ) { }
+	void bass_freq( int ) { }
+	void clear() { }
+	channel_t channel( int ) { return chan; }
+	void end_frame( blip_time_t, bool unused = true ) { }
+	long samples_avail() const { return 0; }
+	long read_samples( blip_sample_t*, long ) { return 0; }
+};
+
 
 // End of public interface
 
-inline Blip_Buffer* Stereo_Buffer::left() {
-	return &bufs [1];
-}
-
-inline Blip_Buffer* Stereo_Buffer::center() {
-	return &bufs [0];
+inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec )
+{
+	sample_rate_ = rate;
+	length_ = msec;
+	return blargg_success;
 }
 
-inline Blip_Buffer* Stereo_Buffer::right() {
-	return &bufs [2];
-}
-
-inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) {
-	return chan;
-}
-
-inline long Multi_Buffer::sample_rate() const {
-	return sample_rate_;
+inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec )
+{
+	return Multi_Buffer::set_sample_rate( rate, msec );
 }
 
-inline int Multi_Buffer::length() const {
-	return length_;
-}
+inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
+
+inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; }
 
-inline Blip_Buffer* Mono_Buffer::center() {
-	return &buf;
-}
+inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int ) { return chan; }
 
-inline void Mono_Buffer::clock_rate( long rate ) {
-	buf.clock_rate( rate );
-}
+inline long Multi_Buffer::sample_rate() const { return sample_rate_; }
+
+inline int Multi_Buffer::length() const { return length_; }
 
-inline void Mono_Buffer::clear() {
-	buf.clear();
-}
+inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); }
+
+inline void Mono_Buffer::clear() { buf.clear(); }
 
-inline void Mono_Buffer::bass_freq( int freq ) {
-	buf.bass_freq( freq );
-}
+inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); }
 
-inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) {
-	return buf.read_samples( p, s );
-}
+inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); }
+
+inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); }
 
 #endif
 
--- a/Plugins/Input/console/Music_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Music_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,11 +1,11 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Music_Emu.h"
 
 #include <string.h>
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -18,11 +18,17 @@
 
 #include BLARGG_SOURCE_BEGIN
 
+Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 };
+
 Music_Emu::Music_Emu()
 {
+	equalizer_.treble = -1.0;
+	equalizer_.bass = 60;
+	sample_rate_ = 0;
+	voice_count_ = 0;
 	mute_mask_ = 0;
 	track_count_ = 0;
-	voice_count_ = 0;
+	error_count_ = 0;
 	track_ended_ = false;
 }
 
@@ -30,12 +36,14 @@
 {
 }
 
-void Music_Emu::mute_voices( int )
+blargg_err_t Music_Emu::load_file( const char* path )
 {
-	// empty
+	Std_File_Reader in;
+	BLARGG_RETURN_ERR( in.open( path ) );
+	return load( in );
 }
 
-blargg_err_t Music_Emu::skip( long count )
+void Music_Emu::skip( long count )
 {
 	const int buf_size = 1024;
 	sample_t buf [buf_size];
@@ -46,8 +54,9 @@
 		int saved_mute = mute_mask_;
 		mute_voices( ~0 );
 		
-		while ( count > threshold / 2 ) {
-			BLARGG_RETURN_ERR( play( buf_size, buf ) );
+		while ( count > threshold / 2 )
+		{
+			play( buf_size, buf );
 			count -= buf_size;
 		}
 		
@@ -60,10 +69,8 @@
 		if ( n > count )
 			n = count;
 		count -= n;
-		BLARGG_RETURN_ERR( play( n, buf ) );
+		play( n, buf );
 	}
-	
-	return blargg_success;
 }
 
 const char** Music_Emu::voice_names() const
--- a/Plugins/Input/console/Music_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Music_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,22 +1,44 @@
 
 // Game music emulator interface base class
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef MUSIC_EMU_H
 #define MUSIC_EMU_H
 
 #include "blargg_common.h"
-
 #include "abstract_file.h"
-typedef Data_Reader Emu_Reader; // File reader base class
-typedef Std_File_Reader Emu_Std_Reader; // Read from standard file
-typedef Mem_File_Reader Emu_Mem_Reader; // Read from block of memory
+class Multi_Buffer;
 
 class Music_Emu {
 public:
-	Music_Emu();
-	virtual ~Music_Emu();
+	
+	// Initialize emulator with specified sample rate. Currently should only be
+	// called once.
+	virtual blargg_err_t set_sample_rate( long sample_rate ) = 0;
+	
+	// Load music file
+	blargg_err_t load_file( const char* path );
+	
+	// Start a track, where 0 is the first track. Might un-mute any muted voices.
+	virtual void start_track( int ) = 0;
+	
+	// Generate 'count' samples info 'buf'. Output is in stereo unless using custom
+	// buffer that generates mono output.
+	typedef short sample_t;
+	virtual void play( long count, sample_t* buf ) = 0;
+	
+// Additional optional features
+	
+	// Request use of custom multichannel buffer. Only supported by "classic" emulators;
+	// on others this has no effect. Should be called only once *before* set_sample_rate().
+	virtual void set_buffer( Multi_Buffer* ) { }
+	
+	// Load music file data from custom source
+	virtual blargg_err_t load( Data_Reader& ) = 0;
+	
+	// Sample rate sound is generated at
+	long sample_rate() const;
 	
 	// Number of voices used by currently loaded file
 	int voice_count() const;
@@ -24,43 +46,92 @@
 	// Names of voices
 	virtual const char** voice_names() const;
 	
-	// Number of tracks. Zero if file hasn't been loaded yet.
-	int track_count() const;
-	
-	// Start a track, where 0 is the first track. Might un-mute any muted voices.
-	virtual blargg_err_t start_track( int ) = 0;
-	
 	// Mute voice n if bit n (1 << n) of mask is set
 	virtual void mute_voices( int mask );
 	
-	// Generate 'count' samples info 'buf'
-	typedef short sample_t;
-	virtual blargg_err_t play( long count, sample_t* buf ) = 0;
+	// Frequency equalizer parameters (see notes.txt)
+	struct equalizer_t {
+		double treble; // -50.0 = muffled, 0 = flat, +5.0 = extra-crisp
+		long   bass;   // 1 = full bass, 90 = average, 16000 = almost no bass
+	};
+	
+	// Current frequency equalizater parameters
+	const equalizer_t& equalizer() const;
+	
+	// Set frequency equalizer parameters
+	virtual void set_equalizer( equalizer_t const& );
+	
+	// Equalizer settings for TV speaker
+	static equalizer_t const tv_eq;
+	
+	// Number of tracks. Zero if file hasn't been loaded yet.
+	int track_count() const;
 	
 	// Skip 'count' samples
-	virtual blargg_err_t skip( long count );
+	virtual void skip( long count );
 	
-	// True if a track was started and has since ended. Currently only dumped
+	// True if a track was started and has since ended. Currently only logged
 	// format tracks (VGM, GYM) without loop points have an ending.
 	bool track_ended() const;
 	
+	// Number of errors encountered while playing track due to undefined CPU
+	// instructions in emulated formats and undefined stream events in
+	// logged formats.
+	int error_count() const;
 	
-// End of public interface
+	Music_Emu();
+	virtual ~Music_Emu();
+	
 protected:
-	typedef BOOST::uint8_t byte; // used often
-	int track_count_;
-	int voice_count_;
-	int mute_mask_;
-	bool track_ended_;
+	typedef BOOST::uint8_t byte;
+	void set_voice_count( int n ) { voice_count_ = n; }
+	void set_track_count( int n ) { track_count_ = n; }
+	void set_track_ended( bool b = true ) { track_ended_ = b; }
+	void log_error() { error_count_++; }
+	void remute_voices();
 private:
 	// noncopyable
 	Music_Emu( const Music_Emu& );
 	Music_Emu& operator = ( const Music_Emu& );
+	
+	equalizer_t equalizer_;
+	long sample_rate_;
+	int voice_count_;
+	int mute_mask_;
+	int track_count_;
+	int error_count_;
+	bool track_ended_;
 };
 
+// Deprecated
+typedef Data_Reader Emu_Reader;
+typedef Std_File_Reader Emu_Std_Reader;
+typedef Mem_File_Reader Emu_Mem_Reader;
+
+inline int Music_Emu::error_count() const   { return error_count_; }
 inline int Music_Emu::voice_count() const   { return voice_count_; }
 inline int Music_Emu::track_count() const   { return track_count_; }
 inline bool Music_Emu::track_ended() const  { return track_ended_; }
+inline void Music_Emu::mute_voices( int mask ) { mute_mask_ = mask; }
+inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); }
+inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; }
+inline void Music_Emu::set_equalizer( const equalizer_t& eq ) { equalizer_ = eq; }
+inline long Music_Emu::sample_rate() const { return sample_rate_; }
+
+inline blargg_err_t Music_Emu::set_sample_rate( long r )
+{
+	assert( !sample_rate_ ); // sample rate can't be changed once set
+	sample_rate_ = r;
+	return blargg_success;
+}
+
+inline void Music_Emu::start_track( int track )
+{
+	assert( (unsigned) track <= (unsigned) track_count() );
+	assert( sample_rate_ ); // set_sample_rate() must have been called first
+	track_ended_ = false;
+	error_count_ = 0;
+}
 
 #endif
 
--- a/Plugins/Input/console/Nes_Apu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Apu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,9 +1,9 @@
 
-// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
 
 #include "Nes_Apu.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -16,12 +16,14 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-Nes_Apu::Nes_Apu()
+int const amp_range = 15;
+
+Nes_Apu::Nes_Apu() :
+	square1( &square_synth ),
+	square2( &square_synth )
 {
 	dmc.apu = this;
 	dmc.rom_reader = NULL;
-	square1.synth = &square_synth;
-	square2.synth = &square_synth;
 	irq_notifier_ = NULL;
 	
 	oscs [0] = &square1;
@@ -47,25 +49,30 @@
 	dmc.synth.treble_eq( eq );
 }
 
-void Nes_Apu::volume( double v, bool nonlinear )
+void Nes_Apu::enable_nonlinear( double v )
 {
-	dmc.nonlinear = nonlinear;
-	if ( nonlinear )
-	{
-		square_synth.volume( 0.25751258 * 0.25 * v );
-		
-		const double tnd = 1.0 / 202 * 0.48;
-		triangle.synth.volume_unit( 3 * tnd );
-		noise.synth.volume_unit( 2 * tnd );
-		dmc.synth.volume_unit( tnd );
-	}
-	else
-	{
-		square_synth.volume( 0.1128 * v );
-		triangle.synth.volume( 0.12765 * v );
-		noise.synth.volume( 0.0741 * v );
-		dmc.synth.volume( 0.42545 * v );
-	}
+	dmc.nonlinear = true;
+	square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
+	
+	const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
+	triangle.synth.volume( 3.0 * tnd );
+	noise.synth.volume( 2.0 * tnd );
+	dmc.synth.volume( tnd );
+	
+	square1 .last_amp = 0;
+	square2 .last_amp = 0;
+	triangle.last_amp = 0;
+	noise   .last_amp = 0;
+	dmc     .last_amp = 0;
+}
+
+void Nes_Apu::volume( double v )
+{
+	dmc.nonlinear = false;
+	square_synth.volume(   0.1128  / amp_range * v );
+	triangle.synth.volume( 0.12765 / amp_range * v );
+	noise.synth.volume(    0.0741  / amp_range * v );
+	dmc.synth.volume(      0.42545 / 127 * v );
 }
 
 void Nes_Apu::output( Blip_Buffer* buffer )
@@ -87,6 +94,7 @@
 	dmc.reset();
 	
 	last_time = 0;
+	last_dmc_time = 0;
 	osc_enables = 0;
 	irq_flag = false;
 	earliest_irq_ = no_irq;
@@ -98,8 +106,8 @@
 		write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
 	
 	dmc.dac = initial_dmc_dac;
-	if ( !dmc.nonlinear )
-		dmc.last_amp = initial_dmc_dac; // prevent output transition
+	//if ( !dmc.nonlinear ) // to do: remove?
+	//  dmc.last_amp = initial_dmc_dac; // prevent output transition
 }
 
 void Nes_Apu::irq_changed()
@@ -123,11 +131,29 @@
 
 void Nes_Apu::run_until( nes_time_t end_time )
 {
+	require( end_time >= last_dmc_time );
+	if ( end_time > next_dmc_read_time() )
+	{
+		nes_time_t start = last_dmc_time;
+		last_dmc_time = end_time;
+		dmc.run( start, end_time );
+	}
+}
+
+void Nes_Apu::run_until_( nes_time_t end_time )
+{
 	require( end_time >= last_time );
 	
 	if ( end_time == last_time )
 		return;
 	
+	if ( last_dmc_time < end_time )
+	{
+		nes_time_t start = last_dmc_time;
+		last_dmc_time = end_time;
+		dmc.run( start, end_time );
+	}
+	
 	while ( true )
 	{
 		// earlier of next frame time or end time
@@ -141,7 +167,6 @@
 		square2.run( last_time, time );
 		triangle.run( last_time, time );
 		noise.run( last_time, time );
-		dmc.run( last_time, time );
 		last_time = time;
 		
 		if ( time == end_time )
@@ -152,10 +177,9 @@
 		switch ( frame++ )
 		{
 			case 0:
-				// set interrupt in mode 0
 				if ( !(frame_mode & 0xc0) ) {
+		 			next_irq = time + frame_period * 4 + 1;
 		 			irq_flag = true;
-		 			next_irq = time + frame_period * 4 + 1;
 		 		}
 		 		// fall through
 		 	case 2:
@@ -191,12 +215,42 @@
 	}
 }
 
+// to do: remove
+static long abs_time;
+
+template<class T>
+inline void zero_apu_osc( T* osc, nes_time_t time )
+{
+	Blip_Buffer* output = osc->output;
+	int last_amp = osc->last_amp;
+	osc->last_amp = 0;
+	if ( output && last_amp )
+		osc->synth.offset( time, -last_amp, output );
+}
+
 void Nes_Apu::end_frame( nes_time_t end_time )
 {
-	run_until( end_time );
+	if ( end_time > last_time )
+		run_until_( end_time );
+	
+	abs_time += end_time;
+	
+	if ( dmc.nonlinear )
+	{
+		zero_apu_osc( &square1,  last_time );
+		zero_apu_osc( &square2,  last_time );
+		zero_apu_osc( &triangle, last_time );
+		zero_apu_osc( &noise,    last_time );
+		zero_apu_osc( &dmc,      last_time );
+	}
 	
 	// make times relative to new frame
-	last_time = 0;
+	last_time -= end_time;
+	require( last_time >= 0 );
+	
+	last_dmc_time -= end_time;
+	require( last_dmc_time >= 0 );
+	
 	if ( next_irq != no_irq ) {
 		next_irq -= end_time;
 		assert( next_irq >= 0 );
@@ -230,7 +284,7 @@
 	if ( addr < start_addr || end_addr < addr )
 		return;
 	
-	run_until( time );
+	run_until_( time );
 	
 	if ( addr < 0x4014 )
 	{
@@ -309,7 +363,7 @@
 
 int Nes_Apu::read_status( nes_time_t time )
 {
-	run_until( time - 1 );
+	run_until_( time - 1 );
 	
 	int result = (dmc.irq_flag << 7) | (irq_flag << 6);
 	
@@ -317,7 +371,7 @@
 		if ( oscs [i]->length_counter )
 			result |= 1 << i;
 	
-	run_until( time );
+	run_until_( time );
 	
 	if ( irq_flag ) {
 		irq_flag = false;
--- a/Plugins/Input/console/Nes_Apu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Apu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,24 +1,51 @@
 
 // NES 2A03 APU sound chip emulator
 
-// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Nes_Snd_Emu 0.1.7
 
 #ifndef NES_APU_H
 #define NES_APU_H
 
-typedef long nes_time_t;     // CPU clock cycle count
+typedef long     nes_time_t; // CPU clock cycle count
 typedef unsigned nes_addr_t; // 16-bit memory address
 
 #include "Nes_Oscs.h"
 
-class Tagged_Data;
+struct apu_snapshot_t;
+class Nonlinear_Buffer;
 
 class Nes_Apu {
 public:
 	Nes_Apu();
 	~Nes_Apu();
 	
-// Initialization
+	// Set buffer to generate all sound into, or disable sound if NULL
+	void output( Blip_Buffer* );
+	
+	// Set memory reader callback used by DMC oscillator to fetch samples.
+	// When callback is invoked, 'user_data' is passed unchanged as the
+	// first parameter.
+	void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL );
+	
+	// All time values are the number of CPU clock cycles relative to the
+	// beginning of the current time frame. Before resetting the CPU clock
+	// count, call end_frame( last_cpu_time ).
+	
+	// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
+	enum { start_addr = 0x4000 };
+	enum { end_addr   = 0x4017 };
+	void write_register( nes_time_t, nes_addr_t, int data );
+	
+	// Read from status register at 0x4015
+	enum { status_addr = 0x4015 };
+	int read_status( nes_time_t );
+	
+	// Run all oscillators up to specified time, end current time frame, then
+	// start a new time frame at time 0. Time frames have no effect on emulation
+	// and each can be whatever length is convenient.
+	void end_frame( nes_time_t );
+	
+// Additional optional features (can be ignored without any problem)
 
 	// Reset internal frame counter, registers, and all oscillators.
 	// Use PAL timing if pal_timing is true, otherwise use NTSC timing.
@@ -26,32 +53,16 @@
 	// any audible click.
 	void reset( bool pal_timing = false, int initial_dmc_dac = 0 );
 	
-	// Set memory reader callback used by DMC oscillator to fetch samples.
-	//  When callback is invoked,  'user_data' is passed unchanged as the
-	// first parameter.
-	void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL );
-	
-	// Set IRQ time callback that is invoked when the time of earliest IRQ
-	// may have changed, or NULL to disable. When callback is invoked,
-	// 'user_data' is passed unchanged as the first parameter.
-	void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
-	
-	// Reflect complete state between tagged data container.
-	void reflect_state( Tagged_Data& );
+	// Save/load snapshot of exact emulation state
+	void save_snapshot( apu_snapshot_t* out ) const;
+	void load_snapshot( apu_snapshot_t const& );
 	
-// Sound output
-	
-	// Set overall volume (default is 1.0). Set nonlinear to true only when
-	// using special Nes_Nonlinearizer on output.
-	void volume( double, bool nonlinear = false );
+	// Set overall volume (default is 1.0)
+	void volume( double );
 	
-	// Set treble equalization (see notes.txt).
+	// Set treble equalization (see notes.txt)
 	void treble_eq( const blip_eq_t& );
 	
-	// Set sound output of all oscillators to buffer. If buffer is NULL, no
-	// sound is generated and emulation accuracy is reduced.
-	void output( Blip_Buffer* );
-	
 	// Set sound output of specific oscillator to buffer. If buffer is NULL,
 	// the specified oscillator is muted and emulation accuracy is reduced.
 	// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
@@ -59,45 +70,38 @@
 	enum { osc_count = 5 };
 	void osc_output( int index, Blip_Buffer* buffer );
 	
-// Emulation
-
-	// All time values are the number of CPU clock cycles relative to the
-	// beginning of the current time frame.
-
-	// Emulate memory write at specified time. Address must be in the range
-	// start_addr to end_addr.
-	enum { start_addr = 0x4000 };
-	enum { end_addr   = 0x4017 };
-	void write_register( nes_time_t, nes_addr_t, int data );
-	
-	// Emulate memory read of the status register at specified time.
-	enum { status_addr = 0x4015 };
-	int read_status( nes_time_t );
+	// Set IRQ time callback that is invoked when the time of earliest IRQ
+	// may have changed, or NULL to disable. When callback is invoked,
+	// 'user_data' is passed unchanged as the first parameter.
+	void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
 	
 	// Get time that APU-generated IRQ will occur if no further register reads
 	// or writes occur. If IRQ is already pending, returns irq_waiting. If no
 	// IRQ will occur, returns no_irq.
 	enum { no_irq = LONG_MAX / 2 + 1 };
 	enum { irq_waiting = 0 };
-	nes_time_t earliest_irq() const;
-	
-	// Run APU until specified time, so that any DMC memory reads can be
-	// accounted for (i.e. inserting CPU wait states).
-	void run_until( nes_time_t );
+	nes_time_t earliest_irq( nes_time_t ) const;
 	
 	// Count number of DMC reads that would occur if 'run_until( t )' were executed.
 	// If last_read is not NULL, set *last_read to the earliest time that
 	// 'count_dmc_reads( time )' would result in the same result.
 	int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;
 	
-	// Run all oscillators up to specified time, end current time frame, then
-	// start a new time frame at time 0. Time frames have no effect on emulation
-	// and each can be whatever length is convenient.
-	void end_frame( nes_time_t );
+	// Time when next DMC memory read will occur
+	nes_time_t next_dmc_read_time() const;
+	
+	// Run DMC until specified time, so that any DMC memory reads can be
+	// accounted for (i.e. inserting CPU wait states).
+	void run_until( nes_time_t );
 	
 // End of public interface.
-
 private:
+	friend class Nes_Nonlinearizer;
+	void enable_nonlinear( double volume );
+	static double nonlinear_tnd_gain() { return 0.75; }
+private:
+	friend struct Nes_Dmc;
+	
 	// noncopyable
 	Nes_Apu( const Nes_Apu& );
 	Nes_Apu& operator = ( const Nes_Apu& );
@@ -110,6 +114,7 @@
 	Nes_Dmc             dmc;
 	
 	nes_time_t last_time; // has been run until this time in current frame
+	nes_time_t last_dmc_time;
 	nes_time_t earliest_irq_;
 	nes_time_t next_irq;
 	int frame_period;
@@ -124,32 +129,46 @@
 	
 	void irq_changed();
 	void state_restored();
-	
-	friend struct Nes_Dmc;
+	void run_until_( nes_time_t );
 };
 
-	inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) {
-		assert(( "Nes_Apu::osc_output(): Index out of range", 0 <= osc && osc < osc_count ));
-		oscs [osc]->output = buf;
-	}
+inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf )
+{
+	assert( (unsigned) osc < osc_count );
+	oscs [osc]->output = buf;
+}
 
-	inline nes_time_t Nes_Apu::earliest_irq() const {
-		return earliest_irq_;
-	}
+inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const
+{
+	return earliest_irq_;
+}
+
+inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data )
+{
+	dmc.rom_reader_data = user_data;
+	dmc.rom_reader = func;
+}
 
-	inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) {
-		dmc.rom_reader_data = user_data;
-		dmc.rom_reader = func;
-	}
+inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data )
+{
+	irq_notifier_ = func;
+	irq_data = user_data;
+}
 
-	inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) {
-		irq_notifier_ = func;
-		irq_data = user_data;
-	}
+inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const
+{
+	return dmc.count_reads( time, last_read );
+}
 	
-	inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const {
-		return dmc.count_reads( time, last_read );
-	}
+inline nes_time_t Nes_Dmc::next_read_time() const
+{
+	if ( length_counter == 0 )
+		return Nes_Apu::no_irq; // not reading
 	
+	return apu->last_dmc_time + delay + long (bits_remain - 1) * period;
+}
+
+inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); }
+
 #endif
 
--- a/Plugins/Input/console/Nes_Cpu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Cpu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,13 +1,14 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/nes-emu/
 
 #include "Nes_Cpu.h"
 
+#include <string.h>
 #include <limits.h>
 
 #include "blargg_endian.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -20,42 +21,24 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-// Common instructions:
-// 7.40%  D0    BNE
-// 6.05%  F0    BEQ
-// 5.46%  BD    LDA abs,X
-// 4.44%  C8    INY
-// 4.32%  85    STA zp
-// 4.29%  C9    CMP imm
-// 3.74%  20    JSR
-// 3.66%  60    RTS
-// 3.23%  8D    STA abs
-// 3.02%  AD    LDA abs
-
-#ifndef NES_CPU_FIXED_CYCLES
-	#define NES_CPU_FIXED_CYCLES 0
+#if BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
 #endif
 
-static void write_unmapped( Nsf_Emu*, nes_addr_t, int )
-{
-	check( false );
-}
-
-static int read_unmapped( Nsf_Emu*, nes_addr_t )
-{
-	check( false );
-	return 0;
-}
-
 Nes_Cpu::Nes_Cpu()
 {
 	callback_data = NULL;
-	data_writer [page_count] = write_unmapped;
-	data_reader [page_count] = read_unmapped;
 	reset();
 }
 
-void Nes_Cpu::reset( const void* unmapped_code_page )
+inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
+{
+	code_map [i] = p - PAGE_OFFSET( i * page_size );
+}
+
+void Nes_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
 {
 	r.status = 0;
 	r.sp = 0;
@@ -64,131 +47,131 @@
 	r.x = 0;
 	r.y = 0;
 	
-	cycle_count = 0;
+	clock_count = 0;
 	base_time = 0;
-	#if NES_CPU_IRQ_SUPPORT
-		cycle_limit = 0;
-	#endif
+	clock_limit = 0;
+	irq_time_ = LONG_MAX / 2 + 1;
+	end_time_ = LONG_MAX / 2 + 1;
 	
 	for ( int i = 0; i < page_count + 1; i++ )
-		code_map [i] = (uint8_t*) unmapped_code_page;
-	
-	map_memory( 0, 0x10000, read_unmapped, write_unmapped );
+	{
+		set_code_page( i, (uint8_t*) unmapped_code_page );
+		data_reader [i] = read;
+		data_writer [i] = write;
+	}
 }
 
 void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data )
 {
-	// start end end must fall on page bounadries
-	require( start % page_size == 0 && size % page_size == 0 );
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 );
 	
 	unsigned first_page = start / page_size;
 	for ( unsigned i = size / page_size; i--; )
-		code_map [first_page + i] = (uint8_t*) data + i * page_size;
+		set_code_page( first_page + i, (uint8_t*) data + i * page_size );
+}
+
+void Nes_Cpu::set_reader( nes_addr_t start, unsigned long size, reader_t func )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 + page_size );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		data_reader [first_page + i] = func;
 }
 
-void Nes_Cpu::map_memory( nes_addr_t start, unsigned long size, reader_t read, writer_t write )
+void Nes_Cpu::set_writer( nes_addr_t start, unsigned long size, writer_t func )
 {
-	// start end end must fall on page bounadries
-	require( start % page_size == 0 && size % page_size == 0 );
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 + page_size );
 	
-	if ( !read )
-		read = read_unmapped;
-	if ( !write )
-		write = write_unmapped;
 	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; ) {
-		data_reader [first_page + i] = read;
-		data_writer [first_page + i] = write;
-	}
+	for ( unsigned i = size / page_size; i--; )
+		data_writer [first_page + i] = func;
 }
 
-// Note: 'addr' is evaulated more than once in some of the macros, so it
+// Note: 'addr' is evaulated more than once in the following macros, so it
 // must not contain side-effects.
 
-#define LOW_MEM( a )        (low_mem [int (a)])
-
 #define READ( addr )        (data_reader [(addr) >> page_bits]( callback_data, addr ))
 #define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data ))
 
-#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [(addr) & (page_size - 1)])
+#define READ_LOW( addr )        (low_mem [int (addr)])
+#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
+
+#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
 #define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
 
-#define PUSH( v )   (*--sp = (v))
-#define POP()       (*sp++)
+#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
+#define GET_SP()        ((sp - 1) & 0xff)
 
-int Nes_Cpu::read( nes_addr_t addr ) {
+#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
+
+int Nes_Cpu::read( nes_addr_t addr )
+{
 	return READ( addr );
 }
-	
-void Nes_Cpu::write( nes_addr_t addr, int value ) {
+
+void Nes_Cpu::write( nes_addr_t addr, int value )
+{
 	WRITE( addr, value );
 }
 
 #ifndef NES_CPU_GLUE_ONLY
 
-static const unsigned char cycle_table [256] = {                             
-	7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
-	6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
-	6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
-	6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
-	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
-	2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
-	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
-	2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
-	2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
-	2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
-	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7
+static const unsigned char clock_table [256] = {
+//  0 1 2 3 4 5 6 7 8 9 A B C D E F
+	7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
+	6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
+	6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
+	6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
+	3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
+	3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
+	2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
+	2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
 };
 
 #include BLARGG_ENABLE_OPTIMIZER
 
-// on machines with relatively few registers:
-// use local temporaries (if no function calls) over all other variables
-// these are most likely to be in registers, so use them over others if possible:
-// pc
-// nz (so calculate with nz first, then assign to other variable if necessary)
-// data
-// this
 Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
 {
-	#if NES_CPU_IRQ_SUPPORT
-		end_time( end );
-	#else
-		cycle_count -= (end - base_time);
-		base_time = end;
-	#endif
+	set_end_time( end );
 	
-	result_t result = result_cycles;
+	volatile result_t result = result_cycles;
 	
 #if BLARGG_CPU_POWERPC
 	// cache commonly-used values in registers
-	long cycle_count = this->cycle_count;
+	long clock_count = this->clock_count;
 	writer_t* const data_writer = this->data_writer;
 	reader_t* const data_reader = this->data_reader;
 	uint8_t* const low_mem = this->low_mem;
 #endif
 	
-	// stack pointer is kept one greater than usual 6502 stack pointer
-	#define SET_SP( v )     (sp = low_mem + 0x101 + (v))
-	#define GET_SP()        (sp - 0x101 - low_mem)
-	uint8_t* sp;
-	SET_SP( r.sp );
-	
 	// registers
 	unsigned pc = r.pc;
+	int sp;
+	SET_SP( r.sp );
 	int a = r.a;
 	int x = r.x;
 	int y = r.y;
 	
 	// status flags
 	
-	#define IS_NEG (int ((nz + 0x800) | (nz << (CHAR_BIT * sizeof (int) - 8))) < 0)
-	
 	const int st_n = 0x80;
 	const int st_v = 0x40;
 	const int st_r = 0x20;
@@ -198,103 +181,78 @@
 	const int st_z = 0x02;
 	const int st_c = 0x01;
 	
-	#define CALC_STATUS( out ) do {                 \
-		out = status & ~(st_n | st_z | st_c);       \
-		out |= (c >> 8) & st_c;                     \
-		if ( IS_NEG ) out |= st_n;                  \
-		if ( (nz & 0xFF) == 0 ) out |= st_z;        \
-	} while ( false )       
+	#define IS_NEG (nz & 0x880)
+	
+	#define CALC_STATUS( out ) do {             \
+		out = status & (st_v | st_d | st_i);    \
+		out |= (c >> 8) & st_c;                 \
+		if ( IS_NEG ) out |= st_n;              \
+		if ( !(nz & 0xFF) ) out |= st_z;        \
+	} while ( false )
 
-	#define SET_STATUS( in ) do {                           \
-		status = in & ~(st_n | st_z | st_c | st_b | st_r);  \
-		c = in << 8;                                        \
-		nz = in << 4;                                       \
-		nz &= 0x820;                                        \
-		nz ^= ~0xDF;                                        \
+	#define SET_STATUS( in ) do {               \
+		status = in & (st_v | st_d | st_i);     \
+		c = in << 8;                            \
+		nz = (in << 4) & 0x800;                 \
+		nz |= ~in & st_z;                       \
 	} while ( false )
 	
-	uint8_t status;
-	int c;  // store C as 'c' & 0x100.
-	int nz; // store Z as 'nz' & 0xFF == 0. see above for encoding of N.
+	int status;
+	int c;  // carry set if (c & 0x100) != 0
+	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
 	{
 		int temp = r.status;
 		SET_STATUS( temp );
 	}
-
+	
 	goto loop;
-	
-	unsigned data;
-	
-branch_taken: {
-	unsigned old_pc = pc;
-	pc += (BOOST::int8_t) data;
-	if ( !NES_CPU_FIXED_CYCLES )
-		cycle_count += 1 + (((old_pc ^ (pc + 1)) >> 8) & 1);
-}
-inc_pc_loop:            
-	pc++;
+dec_clock_loop:
+	clock_count--;
 loop:
-
-	check( (unsigned) pc < 0x10000 );
-	check( (unsigned) GET_SP() < 0x100 );
-	check( (unsigned) a < 0x100 );
-	check( (unsigned) x < 0x100 );
-	check( (unsigned) y < 0x100 );
 	
-	// Read opcode and first operand. Optimize if processor's byte order is known
-	// and non-portable constructs are allowed.
-#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
-	pc++;
-	unsigned opcode = data >> 8;
-	data = (uint8_t) data;
-
-#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	assert( unsigned (GET_SP()) < 0x100 );
+	assert( unsigned (a) < 0x100 );
+	assert( unsigned (x) < 0x100 );
+	assert( unsigned (y) < 0x100 );
+	
+	uint8_t const* page = code_map [pc >> page_bits];
+	unsigned opcode = page [PAGE_OFFSET( pc )];
+	unsigned data = page [PAGE_OFFSET( pc ) + 1];
 	pc++;
-	unsigned opcode = (uint8_t) data;
-	data >>= 8;
-
-#else
-	unsigned opcode = READ_PROG( pc );
-	pc++;
-	data = READ_PROG( pc );
 	
-#endif
-
-	if ( cycle_count >= cycle_limit )
+	// page crossing
+	//check( opcode == 0x60 || &READ_PROG( pc ) == &page [PAGE_OFFSET( pc )] );
+	
+	if ( clock_count >= clock_limit )
 		goto stop;
 	
-	cycle_count += NES_CPU_FIXED_CYCLES ? NES_CPU_FIXED_CYCLES : cycle_table [opcode];
+	clock_count += clock_table [opcode];
 	
 	#if BLARGG_CPU_POWERPC
-		this->cycle_count = cycle_count;
+		this->clock_count = clock_count;
 	#endif
 	
 	switch ( opcode )
 	{
+
+// Macros
+
 #define ADD_PAGE        (pc++, data += 0x100 * READ_PROG( pc ));
 #define GET_ADDR()      READ_PROG16( pc )
 
-#if NES_CPU_FIXED_CYCLES
-	#define HANDLE_PAGE_CROSSING( lsb )
-#else
-	#define HANDLE_PAGE_CROSSING( lsb ) cycle_count += (lsb) >> 8;
-#endif
+#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8;
 
-#define INC_DEC_XY( reg, n )        \
-	reg = uint8_t (nz = reg + n);   \
-	goto loop;
+#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
 
 #define IND_Y {                                                 \
-		int temp = LOW_MEM( data ) + y;                         \
-		data = temp + 0x100 * LOW_MEM( uint8_t (data + 1) );    \
+		int temp = READ_LOW( data ) + y;                        \
+		data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );   \
 		HANDLE_PAGE_CROSSING( temp );                           \
 	}
 	
 #define IND_X {                                                 \
 		int temp = data + x;                                    \
-		data = LOW_MEM( uint8_t (temp) ) + 0x100 * LOW_MEM( uint8_t (temp + 1) ); \
+		data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \
 	}
 	
 #define ARITH_ADDR_MODES( op )          \
@@ -307,7 +265,7 @@
 case op + 0x10: /* zp,X */              \
 	data = uint8_t (data + x);          \
 case op + 0x00: /* zp */                \
-	data = LOW_MEM( data );             \
+	data = READ_LOW( data );            \
 	goto imm##op;                       \
 case op + 0x14: /* abs,Y */             \
 	data += y;                          \
@@ -323,17 +281,78 @@
 case op + 0x04: /* imm */               \
 imm##op:                                \
 
+#define BRANCH( cond )      \
+{                           \
+	pc++;                   \
+	int offset = (BOOST::int8_t) data;  \
+	int extra_clock = (pc & 0xff) + offset; \
+	if ( !(cond) ) goto dec_clock_loop; \
+	pc += offset;       \
+	clock_count += (extra_clock >> 8) & 1;  \
+	goto loop;          \
+}
+
 // Often-Used
 
+	case 0xB5: // LDA zp,x
+		data = uint8_t (data + x);
+	case 0xA5: // LDA zp
+		a = nz = READ_LOW( data );
+		pc++;
+		goto loop;
+	
 	case 0xD0: // BNE
-		if ( (uint8_t) nz )
-			goto branch_taken;
-		goto inc_pc_loop;
+		BRANCH( (uint8_t) nz );
+	
+	case 0x20: { // JSR
+		int temp = pc + 1;
+		pc = READ_PROG16( pc );
+		WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, temp );
+		goto loop;
+	}
+	
+	case 0x4C: // JMP abs
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0xE8: INC_DEC_XY( x, 1 )  // INX
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG )
+	
+	ARITH_ADDR_MODES( 0xC5 ) // CMP
+		nz = a - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
 	
 	case 0xF0: // BEQ
-		if ( !(uint8_t) nz )
-			goto branch_taken;
-		goto inc_pc_loop;
+		BRANCH( !(uint8_t) nz );
+	
+	case 0x95: // STA zp,x
+		data = uint8_t (data + x);
+	case 0x85: // STA zp
+		pc++;
+		WRITE_LOW( data, a );
+		goto loop;
+	
+	case 0xC8: INC_DEC_XY( y, 1 )  // INY
+
+	case 0xA8: // TAY
+		y = a;
+	case 0x98: // TYA
+		a = nz = y;
+		goto loop;
+	
+	case 0xB9: // LDA abs,Y
+		data += y;
+		goto lda_ind_common;
 	
 	case 0xBD: // LDA abs,X
 		data += x;
@@ -342,41 +361,20 @@
 	case 0xAD: // LDA abs
 		ADD_PAGE
 	lda_ptr:
-		nz = a = READ( data );
-		goto inc_pc_loop;
-
-	case 0xC8: INC_DEC_XY( y, 1 )  // INY
-
-	case 0x95: // STA zp,x
-		data = uint8_t (data + x);
-	case 0x85: // STA zp
-		LOW_MEM( data ) = a;
-		goto inc_pc_loop;
-	
-	ARITH_ADDR_MODES( 0xC5 ) // CMP
-		nz = a - data;
-	compare_common:
-		c = ~nz;
-		goto inc_pc_loop;
-	
-	case 0x4C: // JMP abs
+		a = nz = READ( data );
 		pc++;
-		goto jmp_common;
-	
-	case 0x20: // JSR
-		pc++;
-		PUSH( pc >> 8 );
-		PUSH( pc );
-	jmp_common:
-		pc = data + 0x100 * READ_PROG( pc );
 		goto loop;
 	
 	case 0x60: // RTS
-		// often-used instruction
-		pc = POP();
-		pc += 0x100 * POP();
-		goto inc_pc_loop;
+		pc = 1 + READ_LOW( sp );
+		pc += READ_LOW( 0x100 | (sp - 0xff) ) * 0x100;
+		sp = (sp - 0xfe) | 0x100;
+		goto loop;
 
+	case 0x99: // STA abs,Y
+		data += y;
+		goto sta_ind_common;
+	
 	case 0x9D: // STA abs,X
 		data += x;
 	sta_ind_common:
@@ -384,29 +382,17 @@
 	case 0x8D: // STA abs
 		ADD_PAGE
 	sta_ptr:
+		pc++;
 		WRITE( data, a );
-		goto inc_pc_loop;
-	
-	case 0xB5: // LDA zp,x
-		data = uint8_t (data + x);
-	case 0xA5: // LDA zp
-		data = LOW_MEM( data );
-	case 0xA9: // LDA #imm
-		a = nz = data;
-		goto inc_pc_loop;
-
-	case 0xA8: // TAY
-		y = a;
-	case 0x98: // TYA
-		a = nz = y;
 		goto loop;
 	
-// Branch
+	case 0xA9: // LDA #imm
+		pc++;
+		a = data;
+		nz = data;
+		goto loop;
 
-#define BRANCH( cond )      \
-	if ( cond )             \
-		goto branch_taken;  \
-	goto inc_pc_loop;
+// Branch
 
 	case 0x50: // BVC
 		BRANCH( !(status & st_v) )
@@ -420,43 +406,41 @@
 	case 0x90: // BCC
 		BRANCH( !(c & 0x100) )
 	
-	case 0x10: // BPL
-		BRANCH( !IS_NEG )
-	
-	case 0x30: // BMI
-		BRANCH( IS_NEG )
-	
 // Load/store
 	
 	case 0x94: // STY zp,x
 		data = uint8_t (data + x);
 	case 0x84: // STY zp
-		LOW_MEM( data ) = y;
-		goto inc_pc_loop;
+		pc++;
+		WRITE_LOW( data, y );
+		goto loop;
 	
 	case 0x96: // STX zp,y
 		data = uint8_t (data + y);
 	case 0x86: // STX zp
-		LOW_MEM( data ) = x;
-		goto inc_pc_loop;
+		pc++;
+		WRITE_LOW( data, x );
+		goto loop;
 	
 	case 0xB6: // LDX zp,y
 		data = uint8_t (data + y);
 	case 0xA6: // LDX zp
-		data = LOW_MEM( data );
-	case 0xA2: // LDX imm
+		data = READ_LOW( data );
+	case 0xA2: // LDX #imm
+		pc++;
 		x = data;
 		nz = data;
-		goto inc_pc_loop;
+		goto loop;
 	
 	case 0xB4: // LDY zp,x
 		data = uint8_t (data + x);
 	case 0xA4: // LDY zp
-		data = LOW_MEM( data );
-	case 0xA0: // LDY imm
+		data = READ_LOW( data );
+	case 0xA0: // LDY #imm
+		pc++;
 		y = data;
 		nz = data;
-		goto inc_pc_loop;
+		goto loop;
 	
 	case 0xB1: // LDA (ind),Y
 		IND_Y
@@ -466,10 +450,6 @@
 		IND_X
 		goto lda_ptr;
 	
-	case 0xB9: // LDA abs,Y
-		data += y;
-		goto lda_ind_common;
-	
 	case 0x91: // STA (ind),Y
 		IND_Y
 		goto sta_ptr;
@@ -478,18 +458,15 @@
 		IND_X
 		goto sta_ptr;
 	
-	case 0x99: // STA abs,Y
-		data += y;
-		goto sta_ind_common;
-	
 	case 0xBC: // LDY abs,X
 		data += x;
 		HANDLE_PAGE_CROSSING( data );
 	case 0xAC:{// LDY abs
 		pc++;
 		unsigned addr = data + 0x100 * READ_PROG( pc );
+		pc++;
 		y = nz = READ( addr );
-		goto inc_pc_loop;
+		goto loop;
 	}
 	
 	case 0xBE: // LDX abs,y
@@ -498,8 +475,9 @@
 	case 0xAE:{// LDX abs
 		pc++;
 		unsigned addr = data + 0x100 * READ_PROG( pc );
+		pc++;
 		x = nz = READ( addr );
-		goto inc_pc_loop;
+		goto loop;
 	}
 	
 	{
@@ -511,7 +489,7 @@
 	case 0x8E: // STX abs
 		temp = x;
 	store_abs:
-		int addr = GET_ADDR();
+		unsigned addr = GET_ADDR();
 		WRITE( addr, temp );
 		pc += 2;
 		goto loop;
@@ -527,11 +505,14 @@
 	}
 	
 	case 0xE4: // CPX zp
-		data = LOW_MEM( data );
-	case 0xE0: // CPX imm
+		data = READ_LOW( data );
+	case 0xE0: // CPX #imm
 	cpx_data:
 		nz = x - data;
-		goto compare_common;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
 	
 	case 0xCC:{// CPY abs
 		unsigned addr = GET_ADDR();
@@ -541,42 +522,31 @@
 	}
 	
 	case 0xC4: // CPY zp
-		data = LOW_MEM( data );
-	case 0xC0: // CPY imm
+		data = READ_LOW( data );
+	case 0xC0: // CPY #imm
 	cpy_data:
 		nz = y - data;
-		goto compare_common;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
 	
 // Logical
 
 	ARITH_ADDR_MODES( 0x25 ) // AND
 		nz = (a &= data);
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	ARITH_ADDR_MODES( 0x45 ) // EOR
 		nz = (a ^= data);
-		goto inc_pc_loop;
+		pc++;
+		goto loop;
 	
 	ARITH_ADDR_MODES( 0x05 ) // ORA
 		nz = (a |= data);
-		goto inc_pc_loop;
-	
-// Add/Subtract
-
-	ARITH_ADDR_MODES( 0x65 ) // ADC
-	adc_imm: {
-		int carry = (c >> 8) & 1;
-		int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
-		c = nz = a + data + carry;
-		a = (uint8_t) nz;
-		status = (status & ~st_v) | ((ov >> 2) & 0x40);
-		goto inc_pc_loop;
-	}
-	
-	ARITH_ADDR_MODES( 0xE5 ) // SBC
-	case 0xEB: // unofficial equivalent
-		data ^= 0xFF;
-		goto adc_imm;
+		pc++;
+		goto loop;
 	
 	case 0x2C:{// BIT abs
 		unsigned addr = GET_ADDR();
@@ -586,14 +556,36 @@
 	}
 	
 	case 0x24: // BIT zp
-		nz = LOW_MEM( data );
+		nz = READ_LOW( data );
 	bit_common:
-		status = (status & ~st_v) | (nz & st_v);
-		if ( !(a & nz) ) // use special encoding since N and Z might both be set
-			nz = ((nz & 0x80) << 4) ^ ~0xFF;
-		goto inc_pc_loop;
+		pc++;
+		status &= ~st_v;
+		status |= nz & st_v;
+		// if result is zero, might also be negative, so use secondary N bit
+		if ( !(a & nz) )
+			nz = (nz << 4) & 0x800;
+		goto loop;
 		
-// Shift/Rotate
+// Add/subtract
+
+	ARITH_ADDR_MODES( 0xE5 ) // SBC
+	case 0xEB: // unofficial equivalent
+		data ^= 0xFF;
+		goto adc_imm;
+	
+	ARITH_ADDR_MODES( 0x65 ) // ADC
+	adc_imm: {
+		int carry = (c >> 8) & 1;
+		int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
+		status &= ~st_v;
+		status |= (ov >> 2) & 0x40;
+		c = nz = a + data + carry;
+		pc++;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+// Shift/rotate
 
 	case 0x4A: // LSR A
 		c = 0;
@@ -634,8 +626,9 @@
 		nz = (c >> 8) & 1;
 		nz |= (c = READ( data ) << 1);
 	rotate_common:
+		pc++;
 		WRITE( data, (uint8_t) nz );
-		goto inc_pc_loop;
+		goto loop;
 	
 	case 0x7E: // ROR abs,X
 		data += x;
@@ -665,7 +658,7 @@
 		c = 0;
 	case 0x66: // ROR zp
 	ror_zp: {
-		int temp = LOW_MEM( data );
+		int temp = READ_LOW( data );
 		nz = ((c >> 1) & 0x80) | (temp >> 1);
 		c = temp << 8;
 		goto write_nz_zp;
@@ -682,13 +675,13 @@
 	case 0x26: // ROL zp
 	rol_zp:
 		nz = (c >> 8) & 1;
-		nz |= (c = LOW_MEM( data ) << 1);
+		nz |= (c = READ_LOW( data ) << 1);
 		goto write_nz_zp;
 	
-// Increment/Decrement
+// Increment/decrement
 
-	case 0xE8: INC_DEC_XY( x, 1 )  // INX
 	case 0xCA: INC_DEC_XY( x, -1 ) // DEX
+	
 	case 0x88: INC_DEC_XY( y, -1 ) // DEY
 	
 	case 0xF6: // INC zp,x
@@ -702,10 +695,11 @@
 	case 0xC6: // DEC zp
 		nz = -1;
 	add_nz_zp:
-		nz += LOW_MEM( data );
+		nz += READ_LOW( data );
 	write_nz_zp:
-		LOW_MEM( data ) = nz;
-		goto inc_pc_loop;
+		pc++;
+		WRITE_LOW( data, nz );
+		goto loop;
 	
 	case 0xFE: // INC abs,x
 		HANDLE_PAGE_CROSSING( data + x );
@@ -756,56 +750,69 @@
 		goto loop;
 		
 	case 0x68: // PLA
-		a = nz = POP();
+		a = nz = READ_LOW( sp );
+		sp = (sp - 0xff) | 0x100;
 		goto loop;
 		
-	{
-		int temp;
 	case 0x40: // RTI
-		temp = POP();
-		pc = POP();
-		pc |= POP() << 8;
-		goto set_status;
-	case 0x28: // PLP
-		temp = POP();
-	set_status:
-		#if !NES_CPU_IRQ_SUPPORT
+		{
+			int temp = READ_LOW( sp );
+			pc   = READ_LOW( 0x100 | (sp - 0xff) );
+			pc  |= READ_LOW( 0x100 | (sp - 0xfe) ) * 0x100;
+			sp = (sp - 0xfd) | 0x100;
+			data = status;
 			SET_STATUS( temp );
+		}
+		if ( !((data ^ status) & st_i) )
+			goto loop; // I flag didn't change
+	i_flag_changed:
+		//dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") );
+		this->r.status = status; // update externally-visible I flag
+		// update clock_limit based on modified I flag
+		clock_limit = end_time_;
+		if ( end_time_ <= irq_time_ )
 			goto loop;
-		#endif
-		data = status & st_i;
+		if ( status & st_i )
+			goto loop;
+		clock_limit = irq_time_;
+		goto loop;
+	
+	case 0x28:{// PLP
+		int temp = READ_LOW( sp );
+		sp = (sp - 0xff) | 0x100;
+		data = status;
 		SET_STATUS( temp );
-		if ( !(data & ~status) )
-			goto loop;
-		result = result_cli; // I flag was just cleared
-		goto end;
+		if ( !((data ^ status) & st_i) )
+			goto loop; // I flag didn't change
+		if ( !(status & st_i) )
+			goto handle_cli;
+		goto handle_sei;
 	}
 	
 	case 0x08: { // PHP
 		int temp;
 		CALC_STATUS( temp );
-		temp |= st_b | st_r;
-		PUSH( temp );
+		PUSH( temp | st_b | st_r );
 		goto loop;
 	}
 	
 	case 0x6C: // JMP (ind)
 		data = GET_ADDR();
 		pc = READ( data );
-		data++;
-		pc |= READ( data ) << 8;
+		pc |= READ( (data & 0xff00) | ((data + 1) & 0xff) ) << 8;
 		goto loop;
 	
 	case 0x00: { // BRK
-		pc += 2; // verified
-		PUSH( pc >> 8 );
-		PUSH( pc );
-		status |= st_i;
+		pc++;
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		WRITE_LOW( 0x100 | (sp - 2), pc );
 		int temp;
 		CALC_STATUS( temp );
-		PUSH( temp | st_b | st_r );
+		sp = (sp - 3) | 0x100;
+		WRITE_LOW( sp, temp | st_b | st_r );
 		pc = READ_PROG16( 0xFFFE );
-		goto loop;
+		status |= st_i;
+		goto i_flag_changed;
 	}
 	
 // Flags
@@ -831,33 +838,63 @@
 		goto loop;
 	
 	case 0x58: // CLI
-		#if !NES_CPU_IRQ_SUPPORT
-			status &= ~st_i;
-			goto loop;
-		#endif
 		if ( !(status & st_i) )
 			goto loop;
 		status &= ~st_i;
+	handle_cli:
+		//dprintf( "%6d CLI\n", time() );
+		this->r.status = status; // update externally-visible I flag
+		if ( clock_count < end_time_ )
+		{
+			assert( clock_limit == end_time_ );
+			if ( end_time_ <= irq_time_ )
+				goto loop; // irq is later
+			if ( clock_count >= irq_time_ )
+				irq_time_ = clock_count + 1; // delay IRQ until after next instruction
+			clock_limit = irq_time_;
+			goto loop;
+		}
+		// execution is stopping now, so delayed CLI must be handled by caller
 		result = result_cli;
 		goto end;
 		
 	case 0x78: // SEI
+		if ( status & st_i )
+			goto loop;
 		status |= st_i;
-		goto loop;
+	handle_sei:
+		//dprintf( "%6d SEI\n", time() );
+		this->r.status = status; // update externally-visible I flag
+		clock_limit = end_time_;
+		if ( clock_count < irq_time_ )
+			goto loop;
+		result = result_sei; // IRQ will occur now, even though I flag is set
+		goto end;
 
-// undocumented
+// Undocumented
 
 	case 0x0C: case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: // SKW
 		pc++;
 	case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB
 	case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
-		goto inc_pc_loop;
-
+		pc++;
 	case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
 		goto loop;
 
-// unimplemented
+// Unimplemented
 	
+	case page_wrap_opcode: // HLT
+		if ( pc > 0x10000 )
+		{
+			// handle wrap-around (assumes caller has put page of HLT at 0x10000)
+			pc = (pc - 1) & 0xffff;
+			clock_count -= 2;
+			goto loop;
+		}
+		// fall through
+	case 0x02: case 0x12: case 0x22: case 0x32: // HLT
+	case 0x42: case 0x52: case 0x62: case 0x72:
+	case 0x92: case 0xB2: case 0xD2:
 	case 0x9B: // TAS
 	case 0x9C: // SAY
 	case 0x9E: // XAS
@@ -871,9 +908,6 @@
 	case 0x8B: // XAA
 	case 0xAB: // OAL
 	case 0xCB: // SAX
-	case 0x02: case 0x12: case 0x22: case 0x32: // ?
-	case 0x42: case 0x52: case 0x62: case 0x72:
-	case 0x92: case 0xB2: case 0xD2: case 0xF2:
 	case 0x83: case 0x87: case 0x8F: case 0x97: // AXS
 	case 0xA3: case 0xA7: case 0xAF: case 0xB3: case 0xB7: case 0xBF: // LAX
 	case 0xE3: case 0xE7: case 0xEF: case 0xF3: case 0xF7: case 0xFB: case 0xFF: // INS
@@ -885,6 +919,8 @@
 		result = result_badop;
 		goto stop;
 	}
+	
+	// If this fails then the case above is missing an opcode
 	assert( false );
 	
 stop:
@@ -897,16 +933,15 @@
 		r.status = temp;
 	}
 	
-	base_time += cycle_count;
-	#if NES_CPU_IRQ_SUPPORT
-		this->cycle_limit -= cycle_count;
-	#endif
-	this->cycle_count = 0;
+	base_time += clock_count;
+	clock_limit -= clock_count;
+	this->clock_count = 0;
 	r.pc = pc;
 	r.sp = GET_SP();
 	r.a = a;
 	r.x = x;
 	r.y = y;
+	irq_time_ = LONG_MAX / 2 + 1;
 	
 	return result;
 }
--- a/Plugins/Input/console/Nes_Cpu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Cpu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,123 +1,174 @@
 
 // Nintendo Entertainment System (NES) 6502 CPU emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef NES_CPU_H
 #define NES_CPU_H
 
 #include "blargg_common.h"
 
-typedef long nes_time_t;     // clock cycle count
+typedef long     nes_time_t; // clock cycle count
 typedef unsigned nes_addr_t; // 16-bit address
 
-class Nsf_Emu;
-
-#ifndef NES_CPU_IRQ_SUPPORT
-	#define NES_CPU_IRQ_SUPPORT 0
-#endif
+class Nes_Emu;
 
 class Nes_Cpu {
 	typedef BOOST::uint8_t uint8_t;
 	enum { page_bits = 11 };
 	enum { page_count = 0x10000 >> page_bits };
-	const uint8_t* code_map [page_count + 1];
+	uint8_t const* code_map [page_count + 1];
 public:
 	Nes_Cpu();
 	
-	// Clear registers, unmap memory, and map code pages to unmapped_page.
-	void reset( const void* unmapped_page = NULL );
+	// Memory read/write function types. Reader must return value from 0 to 255.
+	typedef int (*reader_t)( Nes_Emu*, nes_addr_t );
+	typedef void (*writer_t)( Nes_Emu*, nes_addr_t, int data );
+	void set_emu( Nes_Emu* emu ) { callback_data = emu; }
 	
-	// Memory read/write function types. Reader must return value from 0 to 255.
-	Nsf_Emu* callback_data;
-	typedef int  (*reader_t)( Nsf_Emu* callback_data, nes_addr_t );
-	typedef void (*writer_t)( Nsf_Emu* callback_data, nes_addr_t, int value );
+	// Clear registers, unmap memory, and map code pages to unmapped_page.
+	void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL );
 	
 	// Memory mapping functions take a block of memory of specified 'start' address
 	// and 'size' in bytes. Both start address and size must be a multiple of page_size.
 	enum { page_size = 1L << page_bits };
 	
-	// Map code memory to 'code' (memory accessed via the program counter)
+	// Map code memory (memory accessed via the program counter)
 	void map_code( nes_addr_t start, unsigned long size, const void* code );
 	
-	// Map data memory to read and write functions
+	// Set read function for address range
+	void set_reader( nes_addr_t start, unsigned long size, reader_t );
+	
+	// Set write function for address range
+	void set_writer( nes_addr_t start, unsigned long size, writer_t );
+	
+	// Set read and write functions for address range
 	void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t );
 	
 	// Access memory as the emulated CPU does.
 	int  read( nes_addr_t );
-	void write( nes_addr_t, int value );
-	uint8_t* get_code( nes_addr_t );
+	void write( nes_addr_t, int data );
+	uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code
+	
+	// Push a byte on the stack
+	void push_byte( int );
 	
 	// NES 6502 registers. *Not* kept updated during a call to run().
 	struct registers_t {
-		BOOST::uint16_t pc;
-		uint8_t a;
-		uint8_t x;
-		uint8_t y;
-		uint8_t status;
-		uint8_t sp;
-	} r;
+		long pc; // more than 16 bits to allow overflow detection
+		BOOST::uint8_t a;
+		BOOST::uint8_t x;
+		BOOST::uint8_t y;
+		BOOST::uint8_t status;
+		BOOST::uint8_t sp;
+	};
+	registers_t r;
 	
 	// Reasons that run() returns
 	enum result_t {
 		result_cycles,  // Requested number of cycles (or more) were executed
-		result_cli,     // I flag cleared
+		result_sei,     // I flag just set and IRQ time would generate IRQ now
+		result_cli,     // I flag just cleared but IRQ should occur *after* next instr
 		result_badop    // unimplemented/illegal instruction
 	};
 	
-	// Run CPU to or after end_time, or until a stop reason from above
-	// is encountered. Return the reason for stopping.
-	result_t run( nes_time_t end_time );
+	result_t run( nes_time_t end_time_ );
 	
-	nes_time_t time() const             { return base_time + cycle_count; }
-	void time( nes_time_t t );
-	nes_time_t end_time() const         { return base_time + cycle_limit; }
+	nes_time_t time() const             { return base_time + clock_count; }
+	void set_time( nes_time_t t );
 	void end_frame( nes_time_t );
-#if NES_CPU_IRQ_SUPPORT
-	void end_time( nes_time_t t )       { cycle_limit = t - base_time; }
-#endif
+	nes_time_t end_time() const         { return base_time + end_time_; }
+	nes_time_t irq_time() const         { return base_time + irq_time_; }
+	void set_end_time( nes_time_t t );
+	void set_irq_time( nes_time_t t );
 	
+	// If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped.
+	enum { page_wrap_opcode = 0xF2 };
+	
+	// One of the many opcodes that are undefined and stop CPU emulation.
+	enum { bad_opcode = 0xD2 };
+	
+	// End of public interface
 private:
 	// noncopyable
 	Nes_Cpu( const Nes_Cpu& );
 	Nes_Cpu& operator = ( const Nes_Cpu& );
 	
-	nes_time_t cycle_count;
+	nes_time_t clock_limit;
 	nes_time_t base_time;
-#if NES_CPU_IRQ_SUPPORT
-	nes_time_t cycle_limit;
-#else
-	enum { cycle_limit = 0 };
-#endif
+	nes_time_t clock_count;
+	nes_time_t irq_time_;
+	nes_time_t end_time_;
+	
+	Nes_Emu* callback_data;
 	
-	reader_t data_reader [page_count + 1]; // extra entry to catch address overflow
+	enum { irq_inhibit = 0x04 };
+	reader_t data_reader [page_count + 1]; // extra entry catches address overflow
 	writer_t data_writer [page_count + 1];
-
+	void set_code_page( int, uint8_t const* );
+	void update_clock_limit();
+	
 public:
 	// low_mem is a full page size so it can be mapped with code_map
 	uint8_t low_mem [page_size > 0x800 ? page_size : 0x800];
 };
 
-	inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) {
-		return (uint8_t*) &code_map [(addr) >> page_bits] [(addr) & (page_size - 1)];
-	}
+inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr )
+{
+	#if BLARGG_NONPORTABLE
+		return (uint8_t*) code_map [addr >> page_bits] + addr;
+	#else
+		return (uint8_t*) code_map [addr >> page_bits] + (addr & (page_size - 1));
+	#endif
+}
 	
-#if NES_CPU_IRQ_SUPPORT
-	inline void Nes_Cpu::time( nes_time_t t ) {
-		t -= time();
-		cycle_limit -= t;
-		base_time += t;
-	}
-#else
-	inline void Nes_Cpu::time( nes_time_t t ) {
-		cycle_count = t - base_time;
-	}
-#endif
-	
-	inline void Nes_Cpu::end_frame( nes_time_t end_time ) {
-		base_time -= end_time;
-		assert( time() >= 0 );
-	}
+inline void Nes_Cpu::update_clock_limit()
+{
+	nes_time_t t = end_time_;
+	if ( t > irq_time_ && !(r.status & irq_inhibit) )
+		t = irq_time_;
+	clock_limit = t;
+}
+
+inline void Nes_Cpu::set_end_time( nes_time_t t )
+{
+	end_time_ = t - base_time;
+	update_clock_limit();
+}
+
+inline void Nes_Cpu::set_irq_time( nes_time_t t )
+{
+	irq_time_ = t - base_time;
+	update_clock_limit();
+}
+
+inline void Nes_Cpu::end_frame( nes_time_t end_time_ )
+{
+	base_time -= end_time_;
+	assert( time() >= 0 );
+}
+
+inline void Nes_Cpu::set_time( nes_time_t t )
+{
+	t -= time();
+	clock_limit -= t;
+	end_time_   -= t;
+	irq_time_   -= t;
+	base_time   += t;
+}
+
+inline void Nes_Cpu::push_byte( int data )
+{
+	int sp = r.sp;
+	r.sp = (sp - 1) & 0xff;
+	low_mem [0x100 + sp] = data;
+}
+
+inline void Nes_Cpu::map_memory( nes_addr_t addr, unsigned long s, reader_t r, writer_t w )
+{
+	set_reader( addr, s, r );
+	set_writer( addr, s, w );
+}
 
 #endif
 
--- a/Plugins/Input/console/Nes_Namco.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-
-// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
-
-#include "Nes_Namco.h"
-
-#include "Tagged_Data.h"
-
-/* Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Nes_Namco::Nes_Namco()
-{
-	output( NULL );
-	volume( 1.0 );
-	reset();
-}
-
-Nes_Namco::~Nes_Namco() {
-}
-
-void Nes_Namco::reset()
-{
-	addr_reg = 0;
-	
-	int i;
-	for ( i = 0; i < reg_count; i++ )
-		reg [i] = 0;
-	
-	for ( i = 0; i < osc_count; i++ ) {
-		Namco_Osc& osc = oscs [i];
-		osc.delay = 0;
-		osc.last_amp = 0;
-		osc.wave_pos = 0;
-	}
-}
-
-void Nes_Namco::output( Blip_Buffer* buf )
-{
-	for ( int i = 0; i < osc_count; i++ )
-		osc_output( i, buf );
-}
-
-BOOST::uint8_t& Nes_Namco::access()
-{
-	int addr = addr_reg & 0x7f;
-	if ( addr_reg & 0x80 )
-		addr_reg = (addr + 1) | 0x80;
-	return reg [addr];
-}
-
-#define chars_to_long(s) ( (unsigned long)(s[0] << 24) | (unsigned long)(s[1] << 16) | \
-				(unsigned long)(s[2] << 8)  | (unsigned long)(s[3]) )
-
-void Nes_Namco::reflect_state( Tagged_Data& data )
-{
-	reflect_int16( data, chars_to_long("ADDR"), &addr_reg );
-	
-	static const char hex [17] = "0123456789ABCDEF";
-	int i;
-	for ( i = 0; i < reg_count; i++ )
-		reflect_int16( data, chars_to_long("RG\0\0") + hex [i >> 4] * 0x100 + hex [i & 15], &reg [i] );
-	
-	for ( i = 0; i < osc_count; i++ ) {
-		reflect_int32( data, chars_to_long("DLY0") + i, &oscs [i].delay );
-		reflect_int16( data, chars_to_long("POS0") + i, &oscs [i].wave_pos );
-	}
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Nes_Namco::run_until( nes_time_t nes_end_time )
-{
-	int active_oscs = ((reg [0x7f] >> 4) & 7) + 1;
-	for ( int i = osc_count - active_oscs; i < osc_count; i++ )
-	{
-		Namco_Osc& osc = oscs [i];
-		Blip_Buffer* output = osc.output;
-		if ( !output )
-			continue;
-		
-		Blip_Buffer::resampled_time_t time =
-				output->resampled_time( last_time ) + osc.delay;
-		Blip_Buffer::resampled_time_t end_time = output->resampled_time( nes_end_time );
-		osc.delay = 0;
-		if ( time < end_time )
-		{
-			const BOOST::uint8_t* osc_reg = &reg [i * 8 + 0x40];
-			if ( !(osc_reg [4] & 0xe0) )
-				continue;
-			
-			int volume = osc_reg [7] & 15;
-			if ( !volume )
-				continue;
-			
-			long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
-			if ( !freq )
-				continue;
-			Blip_Buffer::resampled_time_t period =
-					output->resampled_duration( 983040 ) / freq * active_oscs;
-			
-			int wave_size = (8 - ((osc_reg [4] >> 2) & 7)) * 4;
-			if ( !wave_size )
-				continue;
-			
-			int last_amp = osc.last_amp;
-			int wave_pos = osc.wave_pos;
-			
-			do {
-				// read wave sample
-				int addr = wave_pos + osc_reg [6];
-				int sample = reg [addr >> 1];
-				wave_pos++;
-				if ( addr & 1 )
-					sample >>= 4;
-				sample = (sample & 15) * volume;
-				
-				// output impulse if amplitude changed
-				int delta = sample - last_amp;
-				if ( delta ) {
-					last_amp = sample;
-					synth.offset_resampled( time, delta, output );
-				}
-				
-				// next sample
-				time += period;
-				if ( wave_pos >= wave_size )
-					wave_pos = 0;
-			}
-			while ( time < end_time );
-			
-			osc.wave_pos = wave_pos;
-			osc.last_amp = last_amp;
-		}
-		osc.delay = time - end_time;
-	}
-	
-	last_time = nes_end_time;
-}
-
--- a/Plugins/Input/console/Nes_Namco.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-
-// Namco 106 sound chip emulator
-
-// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
-
-#ifndef NES_NAMCO_H
-#define NES_NAMCO_H
-
-#include "Nes_Apu.h"
-
-class Tagged_Data;
-
-class Nes_Namco {
-public:
-	Nes_Namco();
-	~Nes_Namco();
-	
-	// See Nes_Apu.h for reference.
-	
-	void volume( double );
-	void treble_eq( const blip_eq_t& );
-	void output( Blip_Buffer* );
-	enum { osc_count = 8 };
-	void osc_output( int index, Blip_Buffer* );
-	void reset();
-	
-	// Read/write data register is at $4800
-	enum { data_reg_addr = 0x4800 };
-	void write_data( nes_time_t, int );
-	int read_data();
-	
-	// Write-only address register is at $F800
-	enum { addr_reg_addr = 0xF800 };
-	void write_addr( int );
-	
-	void end_frame( nes_time_t );
-	void reflect_state( Tagged_Data& );
-	
-// End of public interface
-
-private:
-	// noncopyable
-	Nes_Namco( const Nes_Namco& );
-	Nes_Namco& operator = ( const Nes_Namco& );
-	
-	struct Namco_Osc {
-		long delay;
-		Blip_Buffer* output;
-		short last_amp;
-		short wave_pos;
-	};
-	
-	Namco_Osc oscs [osc_count];
-	
-	nes_time_t last_time;
-	int addr_reg;
-	
-	enum { reg_count = 0x80 };
-	BOOST::uint8_t reg [reg_count];
-	Blip_Synth<blip_good_quality,15> synth;
-	
-	BOOST::uint8_t& access();
-	void run_until( nes_time_t );
-};
-
-	inline void Nes_Namco::volume( double v ) {
-		synth.volume( 0.10 / osc_count * v );
-	}
-
-	inline void Nes_Namco::treble_eq( const blip_eq_t& eq ) {
-		synth.treble_eq( eq );
-	}
-
-	inline void Nes_Namco::osc_output( int i, Blip_Buffer* buf ) {
-		assert( (unsigned) i < osc_count );
-		oscs [i].output = buf;
-	}
-
-	inline void Nes_Namco::write_addr( int v ) {
-		addr_reg = v;
-	}
-
-	inline int Nes_Namco::read_data() {
-		return access();
-	}
-
-	inline void Nes_Namco::write_data( nes_time_t time, int data ) {
-		run_until( time );
-		access() = data;
-	}
-
-	inline void Nes_Namco::end_frame( nes_time_t time ) {
-		run_until( time );
-		last_time -= time;
-		assert( last_time >= 0 );
-	}
-
-#endif
-
--- a/Plugins/Input/console/Nes_Oscs.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Oscs.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,9 +1,9 @@
 
-// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
 
 #include "Nes_Apu.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -94,7 +94,7 @@
 	if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
 	{
 		if ( last_amp ) {
-			synth->offset( time, -last_amp, output );
+			synth.offset( time, -last_amp, output );
 			last_amp = 0;
 		}
 		
@@ -122,13 +122,13 @@
 		
 		int delta = update_amp( amp );
 		if ( delta )
-			synth->offset( time, delta, output );
+			synth.offset( time, delta, output );
 		
 		time += delay;
 		if ( time < end_time )
 		{
 			Blip_Buffer* const output = this->output;
-			const Synth* synth = this->synth;
+			const Synth& synth = this->synth;
 			int delta = amp * 2 - volume;
 			int phase = this->phase;
 			
@@ -136,7 +136,7 @@
 				phase = (phase + 1) & (phase_range - 1);
 				if ( phase == 0 || phase == duty ) {
 					delta = -delta;
-					synth->offset_inline( time, delta, output );
+					synth.offset_inline( time, delta, output );
 				}
 				time += timer_period;
 			}
@@ -163,6 +163,14 @@
 		reg_written [3] = false;
 }
 
+inline int Nes_Triangle::calc_amp() const
+{
+	int amp = phase_range - phase;
+	if ( amp < 0 )
+		amp = phase - (phase_range + 1);
+	return amp;
+}
+
 void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
 {
 	if ( !output )
@@ -171,6 +179,10 @@
 	// to do: track phase when period < 3
 	// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
 	
+	int delta = update_amp( calc_amp() );
+	if ( delta )
+		synth.offset( time, delta, output );
+	
 	time += delay;
 	const int timer_period = period() + 1;
 	if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 )
@@ -204,6 +216,7 @@
 		if ( volume < 0 )
 			phase += phase_range;
 		this->phase = phase;
+		last_amp = calc_amp();
  	}
 	delay = time - end_time;
 }
@@ -217,21 +230,21 @@
 	buf = 0;
 	bits_remain = 1;
 	bits = 0;
-	buf_empty = true;
+	buf_full = false;
 	silence = true;
 	next_irq = Nes_Apu::no_irq;
 	irq_flag = false;
 	irq_enabled = false;
 	
 	Nes_Osc::reset();
-	period = 0x036;
+	period = 0x1ac;
 }
 
 void Nes_Dmc::recalc_irq()
 {
 	nes_time_t irq = Nes_Apu::no_irq;
 	if ( irq_enabled && length_counter )
-		irq = apu->last_time + delay +
+		irq = apu->last_dmc_time + delay +
 				((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1;
 	if ( irq != next_irq ) {
 		next_irq = irq;
@@ -247,7 +260,7 @@
 	if ( length_counter == 0 )
 		return 0; // not reading
 	
-	long first_read = apu->last_time + delay + long (bits_remain - 1) * period;
+	long first_read = next_read_time();
 	long avail = time - first_read;
 	if ( avail <= 0 )
 		return 0;
@@ -267,11 +280,11 @@
 }
 
 static const short dmc_period_table [2] [16] = {
-	0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC
-	0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036,
+	{0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC
+	 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036},
 	
-	0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested)
-	0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032  // to do: verify PAL periods
+	{0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested)
+	 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032} // to do: verify PAL periods
 };
 
 inline void Nes_Dmc::reload_sample()
@@ -280,20 +293,22 @@
 	length_counter = regs [3] * 0x10 + 1;
 }
 
-static const unsigned char dac_table [128] = {
-	 0,  0,  1,  2,  2,  3,  3,  4,  5,  5,  6,  7,  7,  8,  8,  9,
-	10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17, 18, 18,
-	19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26,
-	27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 32, 33, 33, 34,
-	34, 35, 35, 35, 36, 36, 37, 37, 38, 38, 38, 39, 39, 40, 40, 40,
-	41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 47,
-	47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 52, 52,
-	52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57
+static const unsigned char dac_table [128] =
+{
+	 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
+	15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
+	27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
+	39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
+	50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
+	59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
+	68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
+	76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
 };
 
 void Nes_Dmc::write_register( int addr, int data )
 {
-	if ( addr == 0 ) {
+	if ( addr == 0 )
+	{
 		period = dmc_period_table [pal_mode] [data & 15];
 		irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled
 		irq_flag &= irq_enabled;
@@ -301,17 +316,14 @@
 	}
 	else if ( addr == 1 )
 	{
+		int old_dac = dac;
+		dac = data & 0x7F;
+		
+		// adjust last_amp so that "pop" amplitude will be properly non-linear
+		// with respect to change in dac
+		int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]);
 		if ( !nonlinear )
-		{
-			// adjust last_amp so that "pop" amplitude will be properly non-linear
-			// with respect to change in dac
-			int old_amp = dac_table [dac];
-			dac = data & 0x7F;
-			int diff = dac_table [dac] - old_amp;
-			last_amp = dac - diff;
-		}
-		
-		dac = data & 0x7F;
+			last_amp = faked_nonlinear;
 	}
 }
 
@@ -324,12 +336,12 @@
 
 void Nes_Dmc::fill_buffer()
 {
-	if ( buf_empty && length_counter )
+	if ( !buf_full && length_counter )
 	{
-		require( rom_reader ); // dmc_reader must be set
+		require( rom_reader ); // rom_reader must be set
 		buf = rom_reader( rom_reader_data, 0x8000u + address );
 		address = (address + 1) & 0x7FFF;
-		buf_empty = false;
+		buf_full = true;
 		if ( --length_counter == 0 )
 		{
 			if ( regs [0] & loop_flag ) {
@@ -347,18 +359,17 @@
 
 void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
 {
+	int delta = update_amp( dac );
 	if ( !output )
-		return;
-	
-	int delta = update_amp( dac );
-	if ( delta )
+		silence = true;
+	else if ( delta )
 		synth.offset( time, delta, output );
 	
 	time += delay;
 	if ( time < end_time )
 	{
 		int bits_remain = this->bits_remain;
-		if ( silence && buf_empty )
+		if ( silence && !buf_full )
 		{
 			int count = (end_time - time + period - 1) / period;
 			bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1;
@@ -375,7 +386,7 @@
 			{
 				if ( !silence )
 				{
-					const int step = (bits & 1) * 4 - 2;
+					int step = (bits & 1) * 4 - 2;
 					bits >>= 1;
 					if ( unsigned (dac + step) <= 0x7F ) {
 						dac += step;
@@ -388,13 +399,15 @@
 				if ( --bits_remain == 0 )
 				{
 					bits_remain = 8;
-					if ( buf_empty ) {
+					if ( !buf_full ) {
 						silence = true;
 					}
 					else {
 						silence = false;
 						bits = buf;
-						buf_empty = true;
+						buf_full = false;
+						if ( !output )
+							silence = true;
 						fill_buffer();
 					}
 				}
@@ -441,7 +454,7 @@
 			// round to next multiple of period
 			time += (end_time - time + period - 1) / period * period;
 			
-			// approximate noise cycling while muted, by shuffling up noise register a bit
+			// approximate noise cycling while muted, by shuffling up noise register
 			// to do: precise muted noise cycling?
 			if ( !(regs [2] & mode_flag) ) {
 				int feedback = (noise << 13) ^ (noise << 14);
@@ -453,8 +466,8 @@
 			Blip_Buffer* const output = this->output;
 			
 			// using resampled time avoids conversion in synth.offset()
-			Blip_Buffer::resampled_time_t rperiod = output->resampled_duration( period );
-			Blip_Buffer::resampled_time_t rtime = output->resampled_time( time );
+			blip_resampled_time_t rperiod = output->resampled_duration( period );
+			blip_resampled_time_t rtime = output->resampled_time( time );
 			
 			int noise = this->noise;
 			int delta = amp * 2 - volume;
--- a/Plugins/Input/console/Nes_Oscs.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nes_Oscs.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,11 +1,12 @@
 
 // Private oscillators used by Nes_Apu
 
-// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Nes_Snd_Emu 0.1.7
 
 #ifndef NES_OSCS_H
 #define NES_OSCS_H
 
+#include "blargg_common.h"
 #include "Blip_Buffer.h"
 
 class Nes_Apu;
@@ -57,8 +58,10 @@
 	int phase;
 	int sweep_delay;
 	
-	typedef Blip_Synth<blip_good_quality,15> Synth;
-	const Synth* synth; // shared between squares
+	typedef Blip_Synth<blip_good_quality,1> Synth;
+	Synth const& synth; // shared between squares
+	
+	Nes_Square( Synth const* s ) : synth( *s ) { }
 	
 	void clock_sweep( int adjust );
 	void run( nes_time_t, nes_time_t );
@@ -74,8 +77,9 @@
 	enum { phase_range = 16 };
 	int phase;
 	int linear_counter;
-	Blip_Synth<blip_good_quality,15> synth;
+	Blip_Synth<blip_med_quality,1> synth;
 	
+	int calc_amp() const;
 	void run( nes_time_t, nes_time_t );
 	void clock_linear_counter();
 	void reset() {
@@ -89,7 +93,7 @@
 struct Nes_Noise : Nes_Envelope
 {
 	int noise;
-	Blip_Synth<blip_med_quality,15> synth;
+	Blip_Synth<blip_med_quality,1> synth;
 	
 	void run( nes_time_t, nes_time_t );
 	void reset() {
@@ -107,7 +111,7 @@
 	int buf;
 	int bits_remain;
 	int bits;
-	bool buf_empty;
+	bool buf_full;
 	bool silence;
 	
 	enum { loop_flag = 0x40 };
@@ -125,7 +129,7 @@
 	
 	Nes_Apu* apu;
 	
-	Blip_Synth<blip_med_quality,127> synth;
+	Blip_Synth<blip_med_quality,1> synth;
 	
 	void start();
 	void write_register( int, int );
@@ -135,6 +139,7 @@
 	void reload_sample();
 	void reset();
 	int count_reads( nes_time_t, nes_time_t* ) const;
+	nes_time_t next_read_time() const;
 };
 
 #endif
--- a/Plugins/Input/console/Nes_Vrc6.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-
-// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
-
-#include "Nes_Vrc6.h"
-
-#include "Tagged_Data.h"
-
-/* Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Nes_Vrc6::Nes_Vrc6()
-{
-	output( NULL );
-	volume( 1.0 );
-	reset();
-}
-
-void Nes_Vrc6::reset()
-{
-	last_time = 0;
-	for ( int i = 0; i < osc_count; i++ ) {
-		Vrc6_Osc& osc = oscs [i];
-		for ( int j = 0; j < reg_count; j++ )
-			osc.regs [j] = 0;
-		osc.delay = 0;
-		osc.last_amp = 0;
-		osc.phase = 1;
-		osc.amp = 0;
-	}
-}
-
-Nes_Vrc6::~Nes_Vrc6() {
-}
-
-void Nes_Vrc6::volume( double v )
-{
-	v *= 0.0967 * 2;
-	saw_synth.volume( v );
-	square_synth.volume( v * 0.5 );
-}
-
-void Nes_Vrc6::treble_eq( const blip_eq_t& eq )
-{
-	saw_synth.treble_eq( eq );
-	square_synth.treble_eq( eq );
-}
-
-void Nes_Vrc6::output( Blip_Buffer* buf )
-{
-	for ( int i = 0; i < osc_count; i++ )
-		osc_output( i, buf );
-}
-
-void Nes_Vrc6::run_until( nes_time_t time )
-{
-	run_square( oscs [0], time );
-	run_square( oscs [1], time );
-	run_saw( time );
-	last_time = time;
-}
-
-void Nes_Vrc6::write_osc( nes_time_t time, int osc_index, int reg, int data )
-{
-	require( (unsigned) osc_index < osc_count );
-	require( (unsigned) reg < reg_count );
-	
-	run_until( time );
-	oscs [osc_index].regs [reg] = data;
-	
-	// to do: remove? this messed up volume envelope in Akumajou Densetsu track 22
-	//if ( osc_index == 2 && reg == 2 )
-	//  oscs [2].amp = 0;
-}
-
-void Nes_Vrc6::end_frame( nes_time_t time )
-{
-	run_until( time );
-	last_time -= time;
-	assert( last_time >= 0 );
-}
-
-#define chars_to_long(s) ( (long)(s[0] << 24) | (long)(s[1] << 16) | \
-				(long)(s[2] << 8)  | (long)(s[3]) )
-
-void Nes_Vrc6::reflect_state( Tagged_Data& data )
-{
-	for ( int i = 0; i < osc_count; i++ )
-	{
-		Tagged_Data odata( data, chars_to_long("cCH0") + i );
-		Vrc6_Osc& osc = oscs [i];
-		for ( int r = 0; r < reg_count; r++ )
-			reflect_int16( odata, chars_to_long("REG0") + r, &osc.regs [r] );
-		reflect_int16( odata, chars_to_long("DELY"), &osc.delay );
-		reflect_int16( odata, chars_to_long("PHAS"), &osc.phase );
-		if ( i == 2 )
-			reflect_int16( odata, chars_to_long("AMPL"), &osc.amp );
-	}
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Nes_Vrc6::run_square( Vrc6_Osc& osc, nes_time_t end_time )
-{
-	Blip_Buffer* output = osc.output;
-	if ( !output )
-		return;
-	
-	int volume = osc.regs [0] & 15;
-	if ( !(osc.regs [2] & 0x80) )
-		volume = 0;
-	
-	int gate = osc.regs [0] & 0x80;
-	int duty = ((osc.regs [0] >> 4) & 7) + 1;
-	int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
-	nes_time_t time = last_time;
-	if ( delta ) {
-		osc.last_amp += delta;
-		square_synth.offset( time, delta, output );
-	}
-	
-	time += osc.delay;
-	osc.delay = 0;
-	int period = osc.period();
-	if ( volume && !gate && period > 4 )
-	{
-		if ( time < end_time )
-		{
-			int phase = osc.phase;
-			
-			do {
-				phase++;
-				if ( phase == 16 ) {
-					phase = 0;
-					osc.last_amp = volume;
-					square_synth.offset( time, volume, output );
-				}
-				if ( phase == duty ) {
-					osc.last_amp = 0;
-					square_synth.offset( time, -volume, output );
-				}
-				time += period;
-			}
-			while ( time < end_time );
-			
-			osc.phase = phase;
-		}
-		osc.delay = time - end_time;
-	}
-}
-
-void Nes_Vrc6::run_saw( nes_time_t end_time )
-{
-	Vrc6_Osc& osc = oscs [2];
-	Blip_Buffer* output = osc.output;
-	if ( !output )
-		return;
-	
-	int amp = osc.amp;
-	int amp_step = osc.regs [0] & 0x3F;
-	nes_time_t time = last_time;
-	int last_amp = osc.last_amp;
-	if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) )
-	{
-		osc.delay = 0;
-		int delta = (amp >> 3) - last_amp;
-		last_amp = amp >> 3;
-		saw_synth.offset( time, delta, output );
-	}
-	else
-	{
-		time += osc.delay;
-		if ( time < end_time )
-		{
-			int period = osc.period() * 2;
-			int phase = osc.phase;
-			
-			do {
-				if ( --phase == 0 ) {
-					phase = 7;
-					amp = 0;
-				}
-				
-				int delta = (amp >> 3) - last_amp;
-				if ( delta ) {
-					last_amp = amp >> 3;
-					saw_synth.offset( time, delta, output );
-				}
-				
-				time += period;
-				amp = (amp + amp_step) & 0xFF;
-			}
-			while ( time < end_time );
-			
-			osc.phase = phase;
-			osc.amp = amp;
-		}
-		
-		osc.delay = time - end_time;
-	}
-	
-	osc.last_amp = last_amp;
-}
-
--- a/Plugins/Input/console/Nes_Vrc6.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-
-// Konami VRC6 sound chip emulator
-
-// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
-
-#ifndef NES_VRC6_H
-#define NES_VRC6_H
-
-#include "Nes_Apu.h"
-
-class Tagged_Data;
-
-class Nes_Vrc6 {
-public:
-	Nes_Vrc6();
-	~Nes_Vrc6();
-	
-	// See Nes_Apu.h for reference.
-	
-	void volume( double );
-	void treble_eq( const blip_eq_t& );
-	void output( Blip_Buffer* );
-	enum { osc_count = 3 };
-	void osc_output( int index, Blip_Buffer* );
-	void reset();
-	
-	// Oscillator 0 write-only registers are at $9000-$9002
-	// Oscillator 1 write-only registers are at $A000-$A002
-	// Oscillator 2 write-only registers are at $B000-$B002
-	enum { reg_count = 3 };
-	enum { base_addr = 0x9000 };
-	enum { addr_step = 0x1000 };
-	void write_osc( nes_time_t, int osc, int reg, int data );
-	
-	void end_frame( nes_time_t );
-	void reflect_state( Tagged_Data& );
-	
-// End of public interface
-
-private:
-	// noncopyable
-	Nes_Vrc6( const Nes_Vrc6& );
-	Nes_Vrc6& operator = ( const Nes_Vrc6& );
-	
-	struct Vrc6_Osc {
-		BOOST::uint8_t regs [3];
-		Blip_Buffer* output;
-		int delay;
-		int last_amp;
-		int phase;
-		int amp; // only used by saw
-		
-		int period() const {
-			return (regs [2] & 0x0f) * 0x100L + regs [1] + 1;
-		}
-	};
-	
-	Vrc6_Osc oscs [osc_count];
-	nes_time_t last_time;
-	
-	Blip_Synth<blip_med_quality,31> saw_synth;
-	Blip_Synth<blip_good_quality,15> square_synth;
-	
-	void run_until( nes_time_t );
-	void run_square( Vrc6_Osc& osc, nes_time_t );
-	void run_saw( nes_time_t );
-};
-
-	inline void Nes_Vrc6::osc_output( int i, Blip_Buffer* buf ) {
-		assert( (unsigned) i < osc_count );
-		oscs [i].output = buf;
-	}
-
-#endif
-
--- a/Plugins/Input/console/Nsf_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nsf_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,14 +1,20 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Nsf_Emu.h"
 
 #include <string.h>
-#include "libaudacious/vfs.h"
+#include <stdio.h>
+
+#if !NSF_EMU_APU_ONLY
+	#include "Nes_Vrc6_Apu.h"
+	#include "Nes_Namco_Apu.h"
+	#include "Nes_Fme7_Apu.h"
+#endif
 
 #include "blargg_endian.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -21,6 +27,14 @@
 
 #include BLARGG_SOURCE_BEGIN
 
+#ifndef RUN_NES_CPU
+	#define RUN_NES_CPU( cpu, count ) cpu.run( count )
+#endif
+
+#ifndef NSF_BEGIN_FRAME
+	#define NSF_BEGIN_FRAME()
+#endif
+
 const unsigned low_mem_size = 0x800;
 const unsigned page_size = 0x1000;
 const long ram_size = 0x10000;
@@ -31,6 +45,12 @@
 
 const int vrc6_flag = 0x01;
 const int namco_flag = 0x10;
+const int fme7_flag = 0x20;
+
+static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size];
+
+Nes_Emu::equalizer_t const Nes_Emu::nes_eq     = {  -1.0, 80 };
+Nes_Emu::equalizer_t const Nes_Emu::famicom_eq = { -15.0, 80 };
 
 // ROM
 
@@ -44,11 +64,13 @@
 	unsigned bank = addr - bank_select_addr;
 	if ( bank < bank_count )
 	{
-		if ( data < emu->total_banks ) {
+		if ( data < emu->total_banks )
+		{
 			emu->cpu.map_code( (bank + 8) * page_size, page_size,
 					&emu->rom [data * page_size] );
 		}
-		else {
+		else
+		{
 			dprintf( "Bank %d out of range (%d banks total)\n",
 					data, (int) emu->total_banks );
 		}
@@ -79,12 +101,12 @@
 
 int Nsf_Emu::read_low_mem( Nsf_Emu* emu, nes_addr_t addr )
 {
-	return emu->cpu.low_mem [addr & (low_mem_size - 1)];
+	return emu->cpu.low_mem [addr];
 }
 
 void Nsf_Emu::write_low_mem( Nsf_Emu* emu, nes_addr_t addr, int data )
 {
-	emu->cpu.low_mem [addr & (low_mem_size - 1)] = data;
+	emu->cpu.low_mem [addr] = data;
 }
 
 // SRAM
@@ -104,30 +126,45 @@
 // Namco
 int Nsf_Emu::read_namco( Nsf_Emu* emu, nes_addr_t addr )
 {
-	if ( addr == Nes_Namco::data_reg_addr )
-		return emu->namco.read_data();
+	if ( addr == Nes_Namco_Apu::data_reg_addr )
+		return emu->namco->read_data();
 	return addr >> 8;
 }
 
 void Nsf_Emu::write_namco( Nsf_Emu* emu, nes_addr_t addr, int data )
 {
-	if ( addr == Nes_Namco::data_reg_addr )
-		emu->namco.write_data( emu->cpu.time(), data );
+	if ( addr == Nes_Namco_Apu::data_reg_addr )
+		emu->namco->write_data( emu->cpu.time(), data );
 }
 
 void Nsf_Emu::write_namco_addr( Nsf_Emu* emu, nes_addr_t addr, int data )
 {
-	if ( addr == Nes_Namco::addr_reg_addr )
-		emu->namco.write_addr( data );
+	if ( addr == Nes_Namco_Apu::addr_reg_addr )
+		emu->namco->write_addr( data );
 }
 
 // VRC6
 void Nsf_Emu::write_vrc6( Nsf_Emu* emu, nes_addr_t addr, int data )
 {
-	unsigned reg = addr & (Nes_Vrc6::addr_step - 1);
-	unsigned osc = unsigned (addr - Nes_Vrc6::base_addr) / Nes_Vrc6::addr_step;
-	if ( osc < Nes_Vrc6::osc_count && reg < Nes_Vrc6::reg_count )
-		emu->vrc6.write_osc( emu->cpu.time(), osc, reg, data );
+	unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1);
+	unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step;
+	if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count )
+		emu->vrc6->write_osc( emu->cpu.time(), osc, reg, data );
+}
+
+// FME-7
+void Nsf_Emu::write_fme7( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	switch ( addr & Nes_Fme7_Apu::addr_mask )
+	{
+		case Nes_Fme7_Apu::latch_addr:
+			emu->fme7->write_latch( data );
+			break;
+		
+		case Nes_Fme7_Apu::data_addr:
+			emu->fme7->write_data( emu->cpu.time(), data );
+			break;
+	}
 }
 
 #endif
@@ -136,48 +173,65 @@
 int Nsf_Emu::read_unmapped( Nsf_Emu*, nes_addr_t addr )
 {
 	dprintf( "Read unmapped $%.4X\n", (unsigned) addr );
-	return addr >> 8; // high byte of address stays on bus
+	return (addr >> 8) & 0xff; // high byte of address stays on bus
 }
 
-void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int )
+void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int data )
 {
-	if (// some games write to $8000 and $8001 repeatedly
-		addr != 0x8000 && addr != 0x8001 &&
-		
-		// probably namco sound mistakenly turned on in mck
-		addr != 0x4800 && addr != 0xF800 &&
-		
-		// memory mapper?
-		addr != 0xFFF8 )
-	{
-		dprintf( "Write unmapped $%.4X\n", (unsigned) addr );
-	}
+	#ifdef NDEBUG
+		return;
+	#endif
+	
+	// some games write to $8000 and $8001 repeatedly
+	if ( addr == 0x8000 || addr == 0x8001 )
+		return;
+	
+	// probably namco sound mistakenly turned on in mck
+	if ( addr == 0x4800 || addr == 0xF800 )
+		return;
+	
+	// memory mapper?
+	if ( addr == 0xFFF8 )
+		return;
+	
+	dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data );
 }
 
-static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size];
-
-Nsf_Emu::Nsf_Emu( double gain_ )
+Nes_Emu::Nes_Emu( double gain_ )
 {
-	rom = NULL;
+	cpu.set_emu( this );
 	play_addr = 0;
-	clocks_per_msec = 0;
 	gain = gain_;
-	cpu.callback_data = this;
-	set_equalizer( equalizer_t( -8.87, 8800, 110 ) );
 	apu.dmc_reader( pcm_read, this );
+	vrc6 = NULL;
+	namco = NULL;
+	fme7 = NULL;
+	Music_Emu::set_equalizer( nes_eq );
+	
 	// set unmapped code to illegal instruction
 	memset( unmapped_code, 0x32, sizeof unmapped_code );
 }
 
-Nsf_Emu::~Nsf_Emu()
+Nes_Emu::~Nes_Emu()
 {
 	unload();
 }
 
 void Nsf_Emu::unload()
 {
-	delete [] rom;
-	rom = NULL;
+	#if !NSF_EMU_APU_ONLY
+		delete vrc6;
+		vrc6 = NULL;
+		
+		delete namco;
+		namco = NULL;
+		
+		delete fme7;
+		fme7 = NULL;
+		
+	#endif
+	
+	rom.clear();
 }
 
 const char** Nsf_Emu::voice_names() const
@@ -193,58 +247,102 @@
 		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
 		"VRC6 Square 1", "VRC6 Square 2", "VRC6 Saw"
 	};
-	if ( exp_flags & namco_flag )
-		return namco_names;
-	if ( exp_flags & vrc6_flag )
+	static const char* dual_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"VRC6.1,N106.5&7", "VRC6.2,N106.4&6", "VRC6.3,N106.1-3"
+	};
+	
+	static const char* fme7_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"Square 3", "Square 4", "Square 5"
+	};
+	
+	if ( namco )
+		return vrc6 ? dual_names : namco_names;
+	
+	if ( vrc6 )
 		return vrc6_names;
+	
+	if ( fme7 )
+		return fme7_names;
+	
 	return base_names;
 }
 
 blargg_err_t Nsf_Emu::init_sound()
 {
-	if ( exp_flags & ~(namco_flag | vrc6_flag) )
+	if ( exp_flags & ~(namco_flag | vrc6_flag | fme7_flag) )
 		return "NSF requires unsupported expansion audio hardware";
 	
 	// map memory
-	cpu.reset( unmapped_code );
-	cpu.map_memory( 0, ram_size, read_unmapped, write_unmapped ); // unmapped
-	cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem ); // low mem
+	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
+	cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem );
 	cpu.map_code( 0, low_mem_size, cpu.low_mem );
-	cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd ); // apu
-	cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram ); // exram
-	cpu.map_memory( 0x6000, sram_size, read_sram, write_sram ); // sram
+	cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd );
+	cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram );
+	cpu.map_memory( 0x6000, sram_size, read_sram, write_sram );
 	cpu.map_code  ( 0x6000, sram_size, sram );
-	cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped ); // rom
+	cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped );
 	
-	voice_count_ = Nes_Apu::osc_count;
+	set_voice_count( Nes_Apu::osc_count );
 	
 	double adjusted_gain = gain;
 	
-#if NSF_EMU_APU_ONLY
+	#if NSF_EMU_APU_ONLY
+		if ( exp_flags )
+			return "NSF requires expansion audio hardware";
+	#else
+	
 	if ( exp_flags )
-		return "NSF requires expansion audio hardware";
-#else
+		set_voice_count( Nes_Apu::osc_count + 3 );
+	
 	// namco
-	if ( exp_flags & namco_flag ) {
+	if ( exp_flags & namco_flag )
+	{
+		namco = BLARGG_NEW Nes_Namco_Apu;
+		BLARGG_CHECK_ALLOC( namco );
+		
 		adjusted_gain *= 0.75;
-		voice_count_ += 3;
-		cpu.map_memory( Nes_Namco::data_reg_addr, Nes_Cpu::page_size,
+		cpu.map_memory( Nes_Namco_Apu::data_reg_addr, Nes_Cpu::page_size,
 				read_namco, write_namco );
-		cpu.map_memory( Nes_Namco::addr_reg_addr, Nes_Cpu::page_size,
+		cpu.map_memory( Nes_Namco_Apu::addr_reg_addr, Nes_Cpu::page_size,
 				 read_code, write_namco_addr );
 	}
 	
 	// vrc6
-	if ( exp_flags & vrc6_flag ) {
+	if ( exp_flags & vrc6_flag )
+	{
+		vrc6 = BLARGG_NEW Nes_Vrc6_Apu;
+		BLARGG_CHECK_ALLOC( vrc6 );
+		
 		adjusted_gain *= 0.75;
-		voice_count_ += 3;
-		for ( int i = 0; i < Nes_Vrc6::osc_count; i++ )
-			cpu.map_memory( Nes_Vrc6::base_addr + i * Nes_Vrc6::addr_step,
+		for ( int i = 0; i < Nes_Vrc6_Apu::osc_count; i++ )
+			cpu.map_memory( Nes_Vrc6_Apu::base_addr + i * Nes_Vrc6_Apu::addr_step,
 					Nes_Cpu::page_size, read_code, write_vrc6 );
 	}
 	
-	namco.volume( adjusted_gain );
-	vrc6.volume( adjusted_gain );
+	// fme7
+	if ( exp_flags & fme7_flag )
+	{
+		fme7 = BLARGG_NEW Nes_Fme7_Apu;
+		BLARGG_CHECK_ALLOC( fme7 );
+		
+		adjusted_gain *= 0.75;
+		cpu.map_memory( fme7->latch_addr, ram_size - fme7->latch_addr,
+				read_code, write_fme7 );
+	}
+	// to do: is gain adjustment even needed? other sound chip volumes should work
+	// naturally with the apu without change.
+	
+	if ( namco )
+		namco->volume( adjusted_gain );
+	
+	if ( vrc6 )
+		vrc6->volume( adjusted_gain );
+	
+	if ( fme7 )
+		fme7->volume( adjusted_gain );
+	
 #endif
 	
 	apu.volume( adjusted_gain );
@@ -254,33 +352,48 @@
 
 void Nsf_Emu::update_eq( blip_eq_t const& eq )
 {
-#if !NSF_EMU_APU_ONLY
-	vrc6.treble_eq( eq );
-	namco.treble_eq( eq );
-#endif
 	apu.treble_eq( eq );
+	
+	#if !NSF_EMU_APU_ONLY
+		if ( vrc6 )
+			vrc6->treble_eq( eq );
+		
+		if ( namco )
+			namco->treble_eq( eq );
+		
+		if ( fme7 )
+			fme7->treble_eq( eq );
+	#endif
 }
 
-blargg_err_t Nsf_Emu::load( const header_t& h, Emu_Reader& in )
+blargg_err_t Nsf_Emu::load( Data_Reader& in )
 {
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Nsf_Emu::load( const header_t& h, Data_Reader& in )
+{
+	header_ = h;
 	unload();
 	
 	// check compatibility
-	if ( 0 != memcmp( h.tag, "NESM\x1A", 5 ) )
+	if ( 0 != memcmp( header_.tag, "NESM\x1A", 5 ) )
 		return "Not an NSF file";
-	if ( h.vers != 1 )
+	if ( header_.vers != 1 )
 		return "Unsupported NSF format";
 	
 	// sound and memory
-	exp_flags = h.chip_flags;
+	exp_flags = header_.chip_flags;
 	blargg_err_t err = init_sound();
 	if ( err )
 		return err;
 	
 	// set up data
-	nes_addr_t load_addr = get_le16( h.load_addr );
-	init_addr = get_le16( h.init_addr );
-	play_addr = get_le16( h.play_addr );
+	nes_addr_t load_addr = get_le16( header_.load_addr );
+	init_addr = get_le16( header_.init_addr );
+	play_addr = get_le16( header_.play_addr );
 	if ( !load_addr ) load_addr = rom_begin;
 	if ( !init_addr ) init_addr = rom_begin;
 	if ( !play_addr ) play_addr = rom_begin;
@@ -289,13 +402,11 @@
 	
 	// set up rom
 	total_banks = (in.remain() + load_addr % page_size + page_size - 1) / page_size;
-	long rom_size = total_banks * page_size;
-	rom = new byte [rom_size];
-	if ( !rom )
-		return "Out of memory";
-	memset( rom, 0, rom_size );
+	BLARGG_RETURN_ERR( rom.resize( total_banks * page_size ) );
+	memset( rom.begin(), 0, rom.size() );
 	err = in.read( &rom [load_addr % page_size], in.remain() );
-	if ( err ) {
+	if ( err )
+	{
 		unload();
 		return err;
 	}
@@ -305,76 +416,88 @@
 	for ( int i = 0; i < bank_count; i++ )
 	{
 		unsigned bank = i - first_bank;
-		initial_banks [i] = (bank < total_banks) ? bank : 0;
+		initial_banks [i] = (bank < (unsigned) total_banks) ? bank : 0;
 		
-		if ( h.banks [i] ) {
+		if ( header_.banks [i] )
+		{
 			// bank-switched
-			memcpy( initial_banks, h.banks, sizeof initial_banks );
+			memcpy( initial_banks, header_.banks, sizeof initial_banks );
 			break;
 		}
 	}
 	
 	// playback rate
-	unsigned playback_rate = get_le16( h.ntsc_speed );
+	unsigned playback_rate = get_le16( header_.ntsc_speed );
 	unsigned standard_rate = 0x411A;
 	double clock_rate = 1789772.72727;
 	play_period = 262 * 341L * 4 + 2;
 	pal_only = false;
 	
 	// use pal speed if there is no ntsc speed
-	if ( (h.speed_flags & 3) == 1 ) {
+	if ( (header_.speed_flags & 3) == 1 )
+	{
 		pal_only = true;
 		play_period = 33247 * master_clock_divisor;
 		clock_rate = 1662607.125;
 		standard_rate = 0x4E20;
-		playback_rate = get_le16( h.pal_speed );
+		playback_rate = get_le16( header_.pal_speed );
 	}
 	
-	clocks_per_msec = clock_rate * (1.0 / 1000.0);
-	
 	// use custom playback rate if not the standard rate
 	if ( playback_rate && playback_rate != standard_rate )
 		play_period = long (clock_rate * playback_rate * master_clock_divisor /
 				1000000.0);
 	
 	// extra flags
-	int extra_flags = h.speed_flags;
+	int extra_flags = header_.speed_flags;
 	#if !NSF_EMU_EXTRA_FLAGS
 		extra_flags = 0;
 	#endif
 	needs_long_frames = (extra_flags & 0x10) != 0;
 	initial_pcm_dac = (extra_flags & 0x20) ? 0x3F : 0;
 
-	track_count_ = h.track_count;
+	set_track_count( header_.track_count );
 	
-	return setup_buffer( ( int) (clock_rate + 0.5) );
+	return setup_buffer( (long) (clock_rate + 0.5) );
 }
 
 void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
 {
-#if !NSF_EMU_APU_ONLY
-	if ( i >= Nes_Apu::osc_count ) {
-		vrc6.osc_output( i - Nes_Apu::osc_count, buf );
-		if ( i < 7 ) {
-			i &= 1;
-			namco.osc_output( i + 4, buf );
-			namco.osc_output( i + 6, buf );
-		}
-		else {
-			for ( int n = 0; n < namco.osc_count / 2; n++ )
-				namco.osc_output( n, buf );
-		}
+	if ( i < Nes_Apu::osc_count )
+	{
+		apu.osc_output( i, buf );
 		return;
 	}
-#endif
-	apu.osc_output( i, buf );
+	
+	#if !NSF_EMU_APU_ONLY
+		if ( vrc6 )
+			vrc6->osc_output( i - Nes_Apu::osc_count, buf );
+		
+		if ( fme7 )
+			fme7->osc_output( i - Nes_Apu::osc_count, buf );
+		
+		if ( namco )
+		{
+			if ( i < 7 )
+			{
+				i &= 1;
+				namco->osc_output( i + 4, buf );
+				namco->osc_output( i + 6, buf );
+			}
+			else
+			{
+				for ( int n = 0; n < namco->osc_count / 2; n++ )
+					namco->osc_output( n, buf );
+			}
+		}
+	#endif
 }
 
-blargg_err_t Nsf_Emu::start_track( int track )
+void Nsf_Emu::start_track( int track )
 {
-	require( rom ); // file must be loaded
+	require( rom.size() ); // file must be loaded
 	
-	starting_track();
+	Classic_Emu::start_track( track );
 	
 	// clear memory
 	memset( cpu.low_mem, 0, sizeof cpu.low_mem );
@@ -389,12 +512,16 @@
 	apu.write_register( 0, 0x4015, 0x0F );
 	apu.write_register( 0, 0x4017, needs_long_frames ? 0x80 : 0 );
 	
-#if !NSF_EMU_APU_ONLY
-	if ( exp_flags ) {
-		namco.reset();
-		vrc6.reset();
-	}
-#endif
+	#if !NSF_EMU_APU_ONLY
+		if ( namco )
+			namco->reset();
+		
+		if ( vrc6 )
+			vrc6->reset();
+		
+		if ( fme7 )
+			fme7->reset();
+	#endif
 	
 	// reset cpu
 	cpu.r.pc = exram_addr;
@@ -408,49 +535,66 @@
 	cpu_jsr( init_addr, -1 );
 	next_play = 0;
 	play_extra = 0;
-	
-	return blargg_success;
 }
 
 void Nsf_Emu::cpu_jsr( nes_addr_t pc, int adj )
 {
-	unsigned ret_addr = cpu.r.pc + adj;
+	unsigned addr = cpu.r.pc + adj;
 	cpu.r.pc = pc;
-	cpu.low_mem [cpu.r.sp-- + 0x100] = ret_addr >> 8;
-	cpu.low_mem [cpu.r.sp-- + 0x100] = ret_addr;
+	cpu.push_byte( addr >> 8 );
+	cpu.push_byte( addr );
 }
 
-blip_time_t Nsf_Emu::run( int msec, bool* )
+void Nsf_Emu::call_play()
+{
+	cpu_jsr( play_addr, -1 );
+}
+
+blip_time_t Nsf_Emu::run_clocks( blip_time_t duration, bool* )
 {
 	// run cpu
-	blip_time_t duration = (blip_time_t) (clocks_per_msec * msec);
-	cpu.time( 0 );
+	cpu.set_time( 0 );
+	bool first_illegal = true; // avoid swamping output with illegal instruction errors
 	while ( cpu.time() < duration )
 	{
 		// check for idle cpu
 		if ( cpu.r.pc == exram_addr )
 		{
-			if ( next_play > duration ) {
-				cpu.time( duration );
+			if ( next_play > duration )
+			{
+				cpu.set_time( duration );
 				break;
 			}
 			
 			if ( next_play > cpu.time() )
-				cpu.time( next_play );
+				cpu.set_time( next_play );
 			
 			nes_time_t period = (play_period + play_extra) / master_clock_divisor;
 			play_extra = play_period - period * master_clock_divisor;
 			next_play += period;
-			cpu_jsr( play_addr, -1 );
+			call_play();
 		}
 		
-		Nes_Cpu::result_t result = cpu.run( duration );
+		Nes_Cpu::result_t result = RUN_NES_CPU( cpu, duration );
 		if ( result == Nes_Cpu::result_badop && cpu.r.pc != exram_addr )
 		{
-			dprintf( "Bad opcode $%.2x at $%.4x\n",
-					(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
-			
-			return 0; // error
+			if ( cpu.r.pc > 0xffff )
+			{
+				cpu.r.pc &= 0xffff;
+				dprintf( "PC wrapped around\n" );
+			}
+			else
+			{
+				cpu.r.pc = (cpu.r.pc + 1) & 0xffff;
+				cpu.set_time( cpu.time() + 4 );
+				log_error();
+				if ( first_illegal )
+				{
+					first_illegal = false;
+					dprintf( "Bad opcode $%.2x at $%.4x\n",
+							(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
+				}
+			}
 		}
 	}
 	
@@ -461,38 +605,18 @@
 		next_play = 0;
 	apu.end_frame( duration );
 	
-#if !NSF_EMU_APU_ONLY
-	if ( exp_flags & namco_flag )
-		namco.end_frame( duration );
-	if ( exp_flags & vrc6_flag )
-		vrc6.end_frame( duration );
-#endif
+	#if !NSF_EMU_APU_ONLY
+		if ( namco )
+			namco->end_frame( duration );
+		
+		if ( vrc6 )
+			vrc6->end_frame( duration );
+		
+		if ( fme7 )
+			fme7->end_frame( duration );
+		
+	#endif
 	
 	return duration;
 }
 
-Nsf_Reader::Nsf_Reader() : file( NULL ) {
-}
-
-Nsf_Reader::~Nsf_Reader() {
-	close();
-}
-
-blargg_err_t Nsf_Reader::read_head(Nsf_Emu::header_t *header) {
-	vfs_fread(&header->tag,         1, 5,file);
-	vfs_fread(&header->vers,        1, 1,file);
-	vfs_fread(&header->track_count, 1, 1,file);
-	vfs_fread(&header->first_track, 1, 1,file);
-	vfs_fread(&header->load_addr,   1, 2,file);
-	vfs_fread(&header->init_addr,   1, 2,file);
-	vfs_fread(&header->play_addr,   1, 2,file);
-	vfs_fread(&header->game,        1,32,file);
-	vfs_fread(&header->author,      1,32,file);
-	vfs_fread(&header->copyright,   1,32,file);
-	vfs_fread(&header->ntsc_speed,  1, 2,file);
-	vfs_fread(&header->banks,       1, 3,file);
-	vfs_fread(&header->pal_speed,   1, 2,file);
-	vfs_fread(&header->speed_flags, 1, 1,file);
-	vfs_fread(&header->chip_flags,  1, 1,file);
-	vfs_fread(&header->unused,      1, 2,file);
-}
--- a/Plugins/Input/console/Nsf_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Nsf_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
-// Nintendo Entertainment System (NES) NSF-format game music file emulator
+// Nintendo Entertainment System (NES) NSF music file emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef NSF_EMU_H
 #define NSF_EMU_H
@@ -10,20 +10,18 @@
 #include "Nes_Apu.h"
 #include "Nes_Cpu.h"
 
-// If NSF_EMU_APU_ONLY is non-zero, external sound chip support is disabled
-#if !NSF_EMU_APU_ONLY
-	#include "Nes_Vrc6.h"
-	#include "Nes_Namco.h"
-#endif
+typedef Nes_Emu Nsf_Emu;
 
-class Nsf_Emu : public Classic_Emu {
+class Nes_Emu : public Classic_Emu {
 public:
+
 	// Set internal gain, where 1.0 results in almost no clamping. Default gain
 	// roughly matches volume of other emulators.
-	Nsf_Emu( double gain = 1.4 );
-	~Nsf_Emu();
+	Nes_Emu( double gain = 1.4 );
 	
-	struct header_t {
+	// NSF file header
+	struct header_t
+	{
 		char tag [5];
 		byte vers;
 		byte track_count;
@@ -45,20 +43,30 @@
 	};
 	BOOST_STATIC_ASSERT( sizeof (header_t) == 0x80 );
 	
-	// Load NSF, given its header and reader for remaining data
-	blargg_err_t load( const header_t&, Emu_Reader& );
+	// Load NSF data
+	blargg_err_t load( Data_Reader& );
+	
+	// Load NSF using already-loaded header and remaining data
+	blargg_err_t load( header_t const&, Data_Reader& );
 	
-	blargg_err_t start_track( int );
+	// Header for currently loaded NSF
+	header_t const& header() const { return header_; }
+	
+	// Equalizer profiles for US NES and Japanese Famicom
+	static equalizer_t const nes_eq;
+	static equalizer_t const famicom_eq;
+	
+public:
+	~Nes_Emu();
+	void start_track( int );
 	Nes_Apu* apu_() { return &apu; }
 	const char** voice_names() const;
-	
-
-// End of public interface
 protected:
 	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
 	void update_eq( blip_eq_t const& );
-	blip_time_t run( int, bool* );
-private:
+	blip_time_t run_clocks( blip_time_t, bool* );
+	virtual void call_play();
+protected:
 	// initial state
 	enum { bank_count = 8 };
 	byte initial_banks [bank_count];
@@ -71,7 +79,6 @@
 	int exp_flags;
 	
 	// timing
-	double clocks_per_msec;
 	nes_time_t next_play;
 	long play_period;
 	int play_extra;
@@ -81,10 +88,29 @@
 	
 	// rom
 	int total_banks;
-	byte* rom;
+	blargg_vector<byte> rom;
 	static int read_code( Nsf_Emu*, nes_addr_t );
 	void unload();
 	
+	blargg_err_t init_sound();
+	
+	// expansion sound
+	
+	class Nes_Namco_Apu* namco;
+	static int read_namco( Nsf_Emu*, nes_addr_t );
+	static void write_namco( Nsf_Emu*, nes_addr_t, int );
+	static void write_namco_addr( Nsf_Emu*, nes_addr_t, int );
+	
+	class Nes_Vrc6_Apu* vrc6;
+	static void write_vrc6( Nsf_Emu*, nes_addr_t, int );
+	
+	class Nes_Fme7_Apu* fme7;
+	static void write_fme7( Nsf_Emu*, nes_addr_t, int );
+	
+	// large objects
+	
+	header_t header_;
+	
 	// cpu
 	Nes_Cpu cpu;
 	void cpu_jsr( unsigned pc, int adj );
@@ -94,43 +120,18 @@
 	static void write_unmapped( Nsf_Emu*, nes_addr_t, int );
 	static void write_exram( Nsf_Emu*, nes_addr_t, int );
 	
-	blargg_err_t init_sound();
-	
 	// apu
 	Nes_Apu apu;
 	static int read_snd( Nsf_Emu*, nes_addr_t );
 	static void write_snd( Nsf_Emu*, nes_addr_t, int );
 	static int pcm_read( void*, nes_addr_t );
 	
-#if !NSF_EMU_APU_ONLY
-	// namco
-	Nes_Namco namco;
-	static int read_namco( Nsf_Emu*, nes_addr_t );
-	static void write_namco( Nsf_Emu*, nes_addr_t, int );
-	static void write_namco_addr( Nsf_Emu*, nes_addr_t, int );
-	
-	// vrc6
-	Nes_Vrc6 vrc6;
-	static void write_vrc6( Nsf_Emu*, nes_addr_t, int );
-#endif
-	
 	// sram
 	enum { sram_size = 0x2000 };
 	byte sram [sram_size];
 	static int read_sram( Nsf_Emu*, nes_addr_t );
 	static void write_sram( Nsf_Emu*, nes_addr_t, int );
-	
-	friend class Nsf_Remote_Emu; // hack
 };
 
-class Nsf_Reader : public Std_File_Reader {
-	VFSFile* file;
-public:
-	Nsf_Reader();
-	~Nsf_Reader();
-	
-	// Custom reader for SPC headers [tempfix]
-	blargg_err_t read_head( Nsf_Emu::header_t* );
-};
 #endif
 
--- a/Plugins/Input/console/Panning_Buffer.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
-
-#include "Panning_Buffer.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-typedef long fixed_t;
-
-#define TO_FIXED( f )   fixed_t ((f) * (1L << 15) + 0.5)
-#define FMUL( x, y )    (((x) * (y)) >> 15)
-
-Panning_Buffer::Panning_Buffer()
-{
-	bufs = NULL;
-	buf_count = 0;
-	bass_freq_ = -1;
-	clock_rate_ = -1;
-}
-
-Panning_Buffer::~Panning_Buffer()
-{
-	delete [] bufs;
-}
-	
-blargg_err_t Panning_Buffer::sample_rate( long rate, int msec )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
-	sample_rate_ = rate;
-	length_ = buf_count ? bufs [0].length() : msec;
-	return blargg_success;
-}
-
-void Panning_Buffer::bass_freq( int freq )
-{
-	bass_freq_ = freq;
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].bass_freq( freq );
-}
-
-void Panning_Buffer::clock_rate( long rate )
-{
-	clock_rate_ = rate;
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].clock_rate( clock_rate_ );
-}
-
-void Panning_Buffer::clear()
-{
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].clear();
-}
-
-blargg_err_t Panning_Buffer::set_channel_count( int count )
-{
-	count += 2;
-	if ( count != buf_count )
-	{
-		delete [] bufs;
-		bufs = NULL;
-		
-		bufs = new buf_t [count];
-		if ( !bufs )
-			return "Out of memory";
-		
-		buf_count = count;
-		
-		if ( sample_rate_ )
-			BLARGG_RETURN_ERR( sample_rate( sample_rate_, length_ ) );
-		
-		if ( clock_rate_ >= 0 )
-			clock_rate( clock_rate_ );
-		
-		if ( bass_freq_ >= 0 )
-			bass_freq( bass_freq_ );
-		
-		set_pan( left_chan, 1.0, 0.0 );
-		set_pan( right_chan, 0.0, 1.0 );
-		for ( int i = 0; i < buf_count - 2; i++ )
-			set_pan( i, 1.0, 1.0 );
-	}
-	return blargg_success;
-}
-
-Panning_Buffer::channel_t Panning_Buffer::channel( int i )
-{
-	i += 2;
-	require( i < buf_count );
-	channel_t ch;
-	ch.center = &bufs [i];
-	ch.left   = &bufs [buf_count];
-	ch.right  = &bufs [buf_count];
-	return ch;
-}
-
-
-void Panning_Buffer::set_pan( int i, double left, double right )
-{
-	i += 2;
-	require( i < buf_count );
-	bufs [i].left_gain  = TO_FIXED( left  );
-	bufs [i].right_gain = TO_FIXED( right );
-}
-
-
-void Panning_Buffer::end_frame( blip_time_t time, bool )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		bufs [i].end_frame( time );
-}
-
-long Panning_Buffer::read_samples( blip_sample_t* out, long count )
-{
-	require( count % 2 == 0 ); // count must be even
-	
-	long avail = bufs [0].samples_avail() * 2;
-	if ( count > avail )
-		count = avail;
-	
-	if ( count )
-	{
-		memset( out, 0, count * sizeof *out );
-		
-		int pair_count = count >> 1;
-		
-		int i;
-		for ( i = 0; i < buf_count; i++ )
-			add_panned( bufs [i], out, pair_count );
-		
-		for ( i = 0; i < buf_count; i++ )
-			bufs [i].remove_samples( pair_count );
-	}
-	return count;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Panning_Buffer::add_panned( buf_t& buf, blip_sample_t* out, long count )
-{
-	Blip_Reader in; 
-	
-	fixed_t left_gain = buf.left_gain;
-	fixed_t right_gain = buf.right_gain;
-	int bass = in.begin( buf );
-	
-	while ( count-- )
-	{
-		long s = in.read();
-		long l = out [0] + FMUL( s, left_gain );
-		long r = out [1] + FMUL( s, right_gain );
-		in.next();
-		
-		if ( (BOOST::int16_t) l != l )
-			l = 0x7FFF - (l >> 24);
-		
-		out [0] = l;
-		out [1] = r;
-		out += 2;
-		
-		if ( (BOOST::int16_t) r != r )
-			out [-1] = 0x7FFF - (r >> 24);
-	}
-	
-	in.end( buf );
-}
-
--- a/Plugins/Input/console/Panning_Buffer.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-
-// Multi-channel buffer with pan control for each buffer
-
-// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
-
-#ifndef PANNING_BUFFER_H
-#define PANNING_BUFFER_H
-
-#include "Multi_Buffer.h"
-
-// Panning_Buffer uses several buffers and outputs stereo sample pairs.
-class Panning_Buffer : public Multi_Buffer {
-public:
-	Panning_Buffer();
-	~Panning_Buffer();
-	
-	// Set pan of a channel, using left and right gain values (1.0 = normal).
-	// Use left_chan and right_chan for the common left and right buffers used
-	// by all channels.
-	enum { left_chan = -2 };
-	enum { right_chan = -1 };
-	void set_pan( int channel, double left, double right );
-	
-	// See Multi_Buffer.h
-	blargg_err_t sample_rate( long rate, int msec );
-	void clock_rate( long );
-	void bass_freq( int );
-	void clear();
-	blargg_err_t set_channel_count( int );
-	channel_t channel( int );
-	void end_frame( blip_time_t, bool unused = true );
-	long read_samples( blip_sample_t*, long );
-	
-private:
-	typedef long fixed_t;
-	
-	struct buf_t : Blip_Buffer {
-		fixed_t left_gain;
-		fixed_t right_gain;
-	};
-	buf_t* bufs;
-	int buf_count;
-	long clock_rate_;
-	int bass_freq_;
-	
-	void add_panned( buf_t&, blip_sample_t*, long );
-};
-
-#endif
-
--- a/Plugins/Input/console/Sms_Apu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Sms_Apu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,9 +1,9 @@
 
-// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/
 
 #include "Sms_Apu.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -38,10 +38,7 @@
 
 // Sms_Square
 
-Sms_Square::Sms_Square() {
-}
-
-void Sms_Square::reset()
+inline void Sms_Square::reset()
 {
 	period = 0;
 	phase = 0;
@@ -53,15 +50,18 @@
 	if ( !volume || period <= 128 )
 	{
 		// ignore 16kHz and higher
-		if ( last_amp ) {
+		if ( last_amp )
+		{
 			synth->offset( time, -last_amp, output );
 			last_amp = 0;
 		}
 		time += delay;
-		if ( !period ) {
+		if ( !period )
+		{
 			time = end_time;
 		}
-		else if ( time < end_time ) {
+		else if ( time < end_time )
+		{
 			// keep calculating phase
 			int count = (end_time - time + period - 1) / period;
 			phase = (phase + count) & 1;
@@ -71,19 +71,22 @@
 	else
 	{
 		int amp = phase ? volume : -volume;
-		if ( amp != last_amp ) {
-			synth->offset( time, amp - last_amp, output );
+		int delta = amp - last_amp;
+		if ( delta )
+		{
 			last_amp = amp;
+			synth->offset( time, delta, output );
 		}
 		
 		time += delay;
 		if ( time < end_time )
 		{
 			Blip_Buffer* const output = this->output;
-			amp *= 2;
-			do {
-				amp = -amp; // amp always alternates
-				synth->offset_inline( time, amp, output );
+			int delta = amp * 2;
+			do
+			{
+				delta = -delta;
+				synth->offset_inline( time, delta, output );
 				time += period;
 				phase ^= 1;
 			}
@@ -98,9 +101,6 @@
 
 static const int noise_periods [3] = { 0x100, 0x200, 0x400 };
 
-inline Sms_Noise::Sms_Noise() {
-}
-
 inline void Sms_Noise::reset()
 {
 	period = &noise_periods [0];
@@ -111,55 +111,55 @@
 
 void Sms_Noise::run( sms_time_t time, sms_time_t end_time )
 {
-	int cur_amp = 0;
-	int period = *this->period * 2;
-	if ( !volume ) {
-		if ( last_amp ) {
-			synth.offset( time, -last_amp, output );
-			last_amp = 0;
-		}
-		delay = 0;
+	int amp = volume;
+	if ( shifter & 1 )
+		amp = -amp;
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth.offset( time, delta, output );
 	}
-	else
+	
+	time += delay;
+	if ( !volume )
+		time = end_time;
+	
+	if ( time < end_time )
 	{
-		int amp = (shifter & 1) ? -volume : volume;
+		Blip_Buffer* const output = this->output;
+		unsigned shifter = this->shifter;
+		int delta = amp * 2;
+		int period = *this->period * 2;
 		if ( !period )
 			period = 16;
-		if ( amp != last_amp ) {
-			synth.offset( time, amp - last_amp, output );
-			last_amp = amp;
-		}
 		
-		time += delay;
-		if ( time < end_time )
+		do
 		{
-			Blip_Buffer* const output = this->output;
-			unsigned shifter = this->shifter;
-			amp *= 2;
-			
-			do {
-				int changed = (shifter + 1) & 2;
-				shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
-				if ( changed ) { // prev and next bits differ
-					amp = -amp;
-					synth.offset_inline( time, amp, output );
-				}
-				time += period;
+			int changed = (shifter + 1) & 2; // set if prev and next bits differ
+			shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
+			if ( changed )
+			{
+				delta = -delta;
+				synth.offset_inline( time, delta, output );
 			}
-			while ( time < end_time );
-			
-			this->shifter = shifter;
-			this->last_amp = amp >> 1;
+			time += period;
 		}
-		delay = time - end_time;
+		while ( time < end_time );
+		
+		this->shifter = shifter;
+		this->last_amp = delta >> 1;
 	}
+	delay = time - end_time;
 }
 
 // Sms_Apu
 
 Sms_Apu::Sms_Apu()
 {
-	for ( int i = 0; i < 3; i++ ) {
+	for ( int i = 0; i < 3; i++ )
+	{
 		squares [i].synth = &square_synth;
 		oscs [i] = &squares [i];
 	}
@@ -169,7 +169,15 @@
 	reset();
 }
 
-Sms_Apu::~Sms_Apu() {
+Sms_Apu::~Sms_Apu()
+{
+}
+
+void Sms_Apu::volume( double vol )
+{
+	vol *= 0.85 / (osc_count * 64 * 2);
+	square_synth.volume( vol );
+	noise.synth.volume( vol );
 }
 
 void Sms_Apu::treble_eq( const blip_eq_t& eq )
@@ -178,11 +186,15 @@
 	noise.synth.treble_eq( eq );
 }
 
-void Sms_Apu::volume( double vol )
+void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
 {
-	vol *= 0.85 / osc_count;
-	square_synth.volume( vol );
-	noise.synth.volume( vol );
+	require( (unsigned) index < osc_count );
+	require( (center && left && right) || (!center && !left && !right) );
+	Sms_Osc& osc = *oscs [index];
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
 }
 
 void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
@@ -191,29 +203,6 @@
 		osc_output( i, center, left, right );
 }
 
-void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left,
-		Blip_Buffer* right )
-{
-	require( (unsigned) index < osc_count );
-	
-	Sms_Osc& osc = *oscs [index];
-	if ( center && !left && !right )
-	{
-		// mono
-		left = center;
-		right = center;
-	}
-	else
-	{
-		// must be silenced or stereo
-		require( (!left && !right) || (left && right) );
-	}
-	osc.outputs [1] = right;
-	osc.outputs [2] = left;
-	osc.outputs [3] = center;
-	osc.output = osc.outputs [osc.output_select];
-}
-
 void Sms_Apu::reset()
 {
 	stereo_found = false;
@@ -233,12 +222,18 @@
 	if ( end_time > last_time )
 	{
 		// run oscillators
-		for ( int i = 0; i < osc_count; ++i ) {
+		for ( int i = 0; i < osc_count; ++i )
+		{
 			Sms_Osc& osc = *oscs [i];
-			if ( osc.output ) {
+			if ( osc.output )
+			{
 				if ( osc.output != osc.outputs [3] )
 					stereo_found = true; // playing on side output
-				osc.run( last_time, end_time );
+				
+				if ( i < 3 )
+					squares [i].run( last_time, end_time );
+				else
+					noise.run( last_time, end_time );
 			}
 		}
 		
@@ -248,8 +243,11 @@
 
 bool Sms_Apu::end_frame( sms_time_t end_time )
 {
-	run_until( end_time );
-	last_time = 0;
+	if ( end_time > last_time )
+		run_until( end_time );
+	
+	assert( last_time >= end_time );
+	last_time -= end_time;
 	
 	bool result = stereo_found;
 	stereo_found = false;
@@ -258,19 +256,19 @@
 
 void Sms_Apu::write_ggstereo( sms_time_t time, int data )
 {
-	require( (unsigned) data <= 0xff );
+	require( (unsigned) data <= 0xFF );
 	
 	run_until( time );
 	
-	// left/right assignments
 	for ( int i = 0; i < osc_count; i++ )
 	{
 		Sms_Osc& osc = *oscs [i];
 		int flags = data >> i;
 		Blip_Buffer* old_output = osc.output;
-		osc.output_select = ((flags >> 3) & 2) | (flags & 1);
+		osc.output_select = (flags >> 3 & 2) | (flags & 1);
 		osc.output = osc.outputs [osc.output_select];
-		if ( osc.output != old_output && osc.last_amp ) {
+		if ( osc.output != old_output && osc.last_amp )
+		{
 			if ( old_output )
 				square_synth.offset( time, -osc.last_amp, old_output );
 			osc.last_amp = 0;
@@ -278,14 +276,14 @@
 	}
 }
 
-static const char volumes [16] = {
+static const unsigned char volumes [16] = {
 	// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
 	64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
 };
 
 void Sms_Apu::write_data( sms_time_t time, int data )
 {
-	require( (unsigned) data <= 0xff );
+	require( (unsigned) data <= 0xFF );
 	
 	run_until( time );
 	
@@ -295,28 +293,26 @@
 	int index = (latch >> 5) & 3;
 	if ( latch & 0x10 )
 	{
-		// volume
 		oscs [index]->volume = volumes [data & 15];
 	}
 	else if ( index < 3 )
 	{
-		// square period
 		Sms_Square& sq = squares [index];
 		if ( data & 0x80 )
-			sq.period = (sq.period & ~0xff) | ((data << 4) & 0xff);
+			sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
 		else
-			sq.period = (sq.period & 0xff) | ((data << 8) & 0x3f00);
+			sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
 	}
 	else
 	{
-		// noise period/mode
 		int select = data & 3;
 		if ( select < 3 )
 			noise.period = &noise_periods [select];
 		else
 			noise.period = &squares [2].period;
 		
-		noise.tap = (data & 0x04) ? 12 : 16; // 16 disables tap
+		int const tap_disabled = 16;
+		noise.tap = (data & 0x04) ? 12 : tap_disabled;
 		noise.shifter = 0x8000;
 	}
 }
--- a/Plugins/Input/console/Sms_Apu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Sms_Apu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
 // Sega Master System SN76489 PSG sound chip emulator
 
-// Sms_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Sms_Snd_Emu 0.1.3
 
 #ifndef SMS_APU_H
 #define SMS_APU_H
@@ -12,28 +12,28 @@
 
 class Sms_Apu {
 public:
-	Sms_Apu();
-	~Sms_Apu();
-	
-	// Overall volume of all oscillators, where 1.0 is full volume.
+	// Set overall volume of all oscillators, where 1.0 is full volume
 	void volume( double );
 	
-	// Treble equalization (see notes.txt).
+	// Set treble equalization
 	void treble_eq( const blip_eq_t& );
 	
+	// Outputs can be assigned to a single buffer for mono output, or to three
+	// buffers for stereo output (using Stereo_Buffer to do the mixing).
+	
 	// Assign all oscillator outputs to specified buffer(s). If buffer
-	// is NULL, silence all oscillators.
+	// is NULL, silences all oscillators.
 	void output( Blip_Buffer* mono );
 	void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
 	
-	// Assign oscillator output to buffer(s). Valid indicies are 0 to
-	// osc_count - 1, which refer to Square 1, Square 2, Square 3, and
-	// Noise, respectively. If buffer is NULL, silence oscillator.
+	// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3,
+	// which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL,
+	// silences oscillator.
 	enum { osc_count = 4 };
 	void osc_output( int index, Blip_Buffer* mono );
 	void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
 	
-	// Reset oscillators
+	// Reset oscillators and internal state
 	void reset();
 	
 	// Write GameGear left/right assignment byte
@@ -43,13 +43,14 @@
 	void write_data( sms_time_t, int );
 	
 	// Run all oscillators up to specified time, end current frame, then
-	// start a new frame at time 0. Return true if any oscillators added
+	// start a new frame at time 0. Returns true if any oscillators added
 	// sound to one of the left/right buffers, false if they only added
 	// to the center buffer.
 	bool end_frame( sms_time_t );
-	
-	
-	// End of public interface
+
+public:
+	Sms_Apu();
+	~Sms_Apu();
 private:
 	// noncopyable
 	Sms_Apu( const Sms_Apu& );
@@ -57,22 +58,24 @@
 	
 	Sms_Osc*    oscs [osc_count];
 	Sms_Square  squares [3];
-	Sms_Noise   noise;
-	Sms_Square::Synth square_synth; // shared between squares
+	Sms_Square::Synth square_synth; // used by squares
 	sms_time_t  last_time;
 	int         latch;
 	bool        stereo_found;
+	Sms_Noise   noise;
 	
 	void run_until( sms_time_t );
 };
 
-inline void Sms_Apu::output( Blip_Buffer* mono ) {
-	output( mono, NULL, NULL );
-}
+struct sms_apu_state_t
+{
+	unsigned char regs [8] [2];
+	unsigned char latch;
+};
 
-inline void Sms_Apu::osc_output( int index, Blip_Buffer* mono ) {
-	osc_output( index, mono, NULL, NULL );
-}
+inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); }
+
+inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); }
 
 #endif
 
--- a/Plugins/Input/console/Sms_Oscs.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Sms_Oscs.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,11 +1,12 @@
 
 // Private oscillators used by Sms_Apu
 
-// Sms_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Sms_Snd_Emu 0.1.3
 
 #ifndef SMS_OSCS_H
 #define SMS_OSCS_H
 
+#include "blargg_common.h"
 #include "Blip_Buffer.h"
 
 struct Sms_Osc
@@ -20,7 +21,6 @@
 	
 	Sms_Osc();
 	void reset();
-	virtual void run( sms_time_t start, sms_time_t end ) = 0;
 };
 
 struct Sms_Square : Sms_Osc
@@ -28,10 +28,9 @@
 	int period;
 	int phase;
 	
-	typedef Blip_Synth<blip_good_quality,64 * 2> Synth;
+	typedef Blip_Synth<blip_good_quality,1> Synth;
 	const Synth* synth;
 	
-	Sms_Square();
 	void reset();
 	void run( sms_time_t, sms_time_t );
 };
@@ -42,10 +41,9 @@
 	unsigned shifter;
 	unsigned tap;
 	
-	typedef Blip_Synth<blip_med_quality,64 * 2> Synth;
+	typedef Blip_Synth<blip_med_quality,1> Synth;
 	Synth synth;
 	
-	Sms_Noise();
 	void reset();
 	void run( sms_time_t, sms_time_t );
 };
--- a/Plugins/Input/console/Snes_Spc.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Snes_Spc.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,13 +1,12 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Snes_Spc.h"
 
 #include <assert.h>
 #include <string.h>
-#include <cstdio>
 
-/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2004-2006 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
@@ -20,7 +19,10 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-Snes_Spc::Snes_Spc() : cpu( ram, this ), dsp( ram )
+// always in the future (CPU time can go over 0, but not by this much)
+int const timer_disabled_time = 127;
+
+Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, ram )
 {
 	timer [0].shift = 7; // 8 kHz
 	timer [1].shift = 7; // 8 kHz
@@ -32,7 +34,7 @@
 
 // Load
 
-const char* Snes_Spc::load_spc( const void* data, long size, int clear_echo_ )
+blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool clear_echo_ )
 {
 	struct spc_file_t {
 		char    signature [27];
@@ -88,7 +90,7 @@
 
 // Handle other file formats (emulator save states) in user code, not here.
 
-const char* Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram,
+blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram,
 		const void* dsp_state )
 {
 	// cpu
@@ -120,9 +122,11 @@
 	{
 		Timer& t = timer [i];
 		
+		t.next_tick = 0;
 		t.enabled = (ram [0xf1] >> i) & 1;
+		if ( !t.enabled )
+			t.next_tick = timer_disabled_time;
 		t.count = 0;
-		t.next_tick = 0;
 		t.counter = ram [0xfd + i] & 15;
 		
 		int p = ram [0xfa + i];
@@ -158,6 +162,8 @@
 
 void Snes_Spc::Timer::run_until_( spc_time_t time )
 {
+	if ( !enabled )
+		dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time );
 	assert( enabled ); // when disabled, next_tick should always be in the future
 	
 	int elapsed = ((time - next_tick) >> shift) + 1;
@@ -264,9 +270,10 @@
 	0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF
 };
 
-void Snes_Spc::enable_rom( int enable )
+void Snes_Spc::enable_rom( bool enable )
 {
-	if ( rom_enabled != enable ) {
+	if ( rom_enabled != enable )
+	{
 		rom_enabled = enable;
 		memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size );
 	}
@@ -333,7 +340,7 @@
 				Timer& t = timer [i];
 				if ( !(data & (1 << i)) ) {
 					t.enabled = 0;
-					t.next_tick = 0;
+					t.next_tick = timer_disabled_time;
 				}
 				else if ( !t.enabled ) {
 					// just enabled
@@ -424,7 +431,7 @@
 blargg_err_t Snes_Spc::play( long count, sample_t* out )
 {
 	require( count % 2 == 0 ); // output is always in pairs of samples
-
+	
 	// CPU time() runs from -duration to 0
 	spc_time_t duration = (count / 2) * clocks_per_sample;
 	
@@ -435,9 +442,11 @@
 	
 	// Localize timer next_tick times and run them to the present to prevent a running
 	// but ignored timer's next_tick from getting too far behind and overflowing.
-	for ( int i = 0; i < timer_count; i++ ) {
+	for ( int i = 0; i < timer_count; i++ )
+	{
 		Timer& t = timer [i];
-		if ( t.enabled ) {
+		if ( t.enabled )
+		{
 			t.next_tick -= duration;
 			t.run_until( -duration );
 		}
--- a/Plugins/Input/console/Snes_Spc.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Snes_Spc.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
 // Super Nintendo (SNES) SPC-700 APU Emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef SNES_SPC_H
 #define SNES_SPC_H
@@ -12,11 +12,13 @@
 
 class Snes_Spc {
 public:
+	typedef BOOST::uint8_t uint8_t;
+	
 	Snes_Spc();
 	
 	// Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true.
 	enum { spc_file_size = 0x10180 };
-	blargg_err_t load_spc( const void* spc, long spc_size, int clear_echo = 1 );
+	blargg_err_t load_spc( const void* spc, long spc_size, bool clear_echo = 1 );
 	
 	// Load copy of state into emulator.
 	typedef Spc_Cpu::registers_t registers_t;
@@ -42,13 +44,14 @@
 	// 16-bit sample range.
 	void set_gain( double );
 	
+	// If true, prevent channels and global volumes from being phase-negated
+	void disable_surround( bool disable );
 	
 // End of public interface
 private:
-	typedef BOOST::uint8_t uint8_t;
-	
 	// timers
-	struct Timer {
+	struct Timer
+	{
 		spc_time_t next_tick;
 		int period;
 		int count;
@@ -57,7 +60,8 @@
 		int enabled;
 		
 		void run_until_( spc_time_t );
-		void run_until( spc_time_t time ) {
+		void run_until( spc_time_t time )
+		{
 			if ( time >= next_tick )
 				run_until_( time );
 		}
@@ -66,21 +70,12 @@
 	Timer timer [timer_count];
 
 	// hardware
-	Spc_Cpu cpu;
 	int extra_cycles;
 	spc_time_t time() const;
 	int  read( spc_addr_t );
 	void write( spc_addr_t, int );
 	friend class Spc_Cpu;
 	
-	// boot rom
-	enum { rom_size = 64 };
-	enum { rom_addr = 0xffc0 };
-	int rom_enabled;
-	uint8_t extra_ram [rom_size];
-	static const uint8_t boot_rom [rom_size];
-	void enable_rom( int );
-	
 	// dsp
 	sample_t* sample_buf;
 	sample_t* buf_end; // to do: remove this once possible bug resolved
@@ -92,20 +87,27 @@
 	void run_dsp( spc_time_t );
 	void run_dsp_( spc_time_t );
 	bool echo_accessed;
-	void check_for_echo_access( spc_addr_t addr );
+	void check_for_echo_access( spc_addr_t );
 	
-	// 64KB RAM + padding filled with STOP instruction to catch PC overflow.
+	// boot rom
+	enum { rom_size = 64 };
+	enum { rom_addr = 0xffc0 };
+	bool rom_enabled;
+	uint8_t extra_ram [rom_size];
+	static const uint8_t boot_rom [rom_size];
+	void enable_rom( bool );
+	
+	// CPU and RAM (at end because it's large)
+	Spc_Cpu cpu;
 	enum { ram_size = 0x10000 };
-	uint8_t ram [ram_size + 0x100];
+	uint8_t ram [ram_size + 0x100]; // padding for catching jumps past end
 };
 
-inline void Snes_Spc::mute_voices( int mask ) {
-	dsp.mute_voices( mask );
-}
+inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
 
-inline void Snes_Spc::set_gain( double v ) {
-	dsp.set_gain( v );
-}
+inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); }
+
+inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); }
 
 #endif
 
--- a/Plugins/Input/console/Spc_Cpu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Cpu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,5 +1,5 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Spc_Cpu.h"
 
@@ -8,7 +8,7 @@
 #include "blargg_endian.h"
 #include "Snes_Spc.h"
 
-/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2004-2006 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
@@ -39,9 +39,7 @@
 //  2%  0xF6    MOV A,abs+Y
 // (1% and below not shown)
 
-Spc_Cpu::Spc_Cpu( uint8_t* ram_, Snes_Spc* e ) :
-	ram( ram_ ),
-	emu( *e )
+Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e )
 {
 	remain_ = 0;
 	BOOST_STATIC_ASSERT( sizeof (int) >= 4 );
@@ -68,22 +66,23 @@
 
 // Cycle table derived from text copy of SPC-700 manual (using regular expressions)
 static const unsigned char cycle_table [0x100] = {
-	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8,
-	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6,
-	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4,
-	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8,
-	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6,
-	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3,
-	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5,
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6,
-	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5,
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,
-	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4,
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4,
-	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9,
-	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3,
-	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3,
-	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3
+//  0 1 2 3 4 5 6 7 8 9 A B C D E F
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4
+	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7
+	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9
+	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B
+	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C
+	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D
+	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E
+	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3  // F
 };
 
 // The C,mem instructions are hardly used, so a non-inline function is used for
@@ -101,9 +100,7 @@
 {
 	remain_ = cycle_count;
 	
-#if BLARGG_CPU_POWERPC
 	uint8_t* const ram = this->ram; // cache
-#endif
 	
 	// Stack pointer is kept one greater than usual SPC stack pointer to allow
 	// common pre-decrement and post-increment memory instructions that some
@@ -134,30 +131,27 @@
 	const int st_z = 0x02;
 	const int st_c = 0x01;
 	
-	// Special encoding for negative and zero being set simultaneously (by POP PSW).
-	// To do: be sure this works properly (I copied it from my NES 6502 emulator).
-	#define IS_NEG (int ((nz + 0x800) | (nz << (CHAR_BIT * sizeof (int) - 8))) < 0)
+	#define IS_NEG (nz & 0x880)
 	
 	#define CALC_STATUS( out ) do {                 \
 		out = status & ~(st_n | st_z | st_c);       \
 		out |= (c >> 8) & st_c;                     \
 		out |= (dp >> 3) & st_p;                    \
 		if ( IS_NEG ) out |= st_n;                  \
-		if ( !(uint8_t) nz ) out |= st_z;           \
+		if ( !(nz & 0xFF) ) out |= st_z;            \
 	} while ( 0 )       
 
 	#define SET_STATUS( in ) do {                   \
 		status = in & ~(st_n | st_z | st_c | st_p); \
 		c = in << 8;                                \
-		nz = in << 4;                               \
-		nz &= 0x820;                                \
-		nz ^= ~0xDF;                                \
+		nz = (in << 4) & 0x800;                     \
+		nz |= ~in & st_z;                           \
 		dp = (in << 3) & 0x100;                     \
 	} while ( 0 )
 	
 	uint8_t status;
 	int c;  // store C as 'c' & 0x100.
-	int nz; // store Z as 'nz' & 0xFF == 0 (see above for encoding of N)
+	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
 	unsigned dp; // direct page base
 	{
 		int temp = r.status;
@@ -170,43 +164,24 @@
 	
 	// Common endings for instructions
 cbranch_taken_loop: // compare and branch
-	data = READ_PROG( pc );
-branch_taken_loop: // taken branch (displacement already in 'data')
-	pc += (BOOST::int8_t) data; // sign-extend
+	pc += (BOOST::int8_t) READ_PROG( pc );
 	remain_ -= 2;
 inc_pc_loop: // end of instruction with an operand
 	pc++;
 loop:
 	
-	// Be sure all registers are in range. PC and SP wrap-around isn't handled so
-	// those checks might fail, but a, x, and y should always be in range.
 	check( (unsigned) pc < 0x10000 );
 	check( (unsigned) GET_SP() < 0x100 );
-	check( (unsigned) a < 0x100 );
-	check( (unsigned) x < 0x100 );
-	check( (unsigned) y < 0x100 );
 	
-	// Read opcode and first operand. Optimize if processor's byte order is known
-	// and non-portable constructs are allowed.
-#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
-	pc++;
-	unsigned opcode = data >> 8;
-	data = (uint8_t) data;
-
-#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
-	data = *(BOOST::uint16_t*) &READ_PROG( pc );
-	pc++;
-	unsigned opcode = (uint8_t) data;
-	data >>= 8;
-
-#else
+	assert( (unsigned) a < 0x100 );
+	assert( (unsigned) x < 0x100 );
+	assert( (unsigned) y < 0x100 );
+	
 	unsigned opcode = READ_PROG( pc );
 	pc++;
+	// to do: if pc is at end of memory, this will get wrong byte
 	data = READ_PROG( pc );
 	
-#endif
-	
 	if ( remain_ <= 0 )
 		goto stop;
 	
@@ -217,11 +192,16 @@
 	switch ( opcode )
 	{
 	
-	#define BRANCH( cond )      \
-		if ( cond )             \
-			goto branch_taken_loop; \
-		goto inc_pc_loop;
-
+	#define BRANCH( cond ) {        \
+		pc++;                       \
+		int offset = (BOOST::int8_t) data; \
+		if ( cond ) {               \
+			pc += offset;           \
+			remain_ -= 2;           \
+		}                           \
+		goto loop;                  \
+	}
+	
 // Most-Common
 
 	case 0xF0: // BEQ (most common)
@@ -319,7 +299,7 @@
 		nz = data;
 		goto inc_pc_loop;
 
-// 2. 8-BIT DATA TRANSMISSION COMMANDS. GROUP 2.
+// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
 
 	ADDR_MODES( 0xC8 ) // MOV addr,A
 		WRITE( data, a );
@@ -394,7 +374,7 @@
 		x++;
 		goto loop;
 	
-// 5. 8-BIT LOGIC OPERATION COMMANDS.
+// 5. 8-BIT LOGIC OPERATION COMMANDS
 	
 #define LOGICAL_OP( op, func )  \
 	ADDR_MODES( op ) /* addr */ \
@@ -425,19 +405,21 @@
 	
 	LOGICAL_OP( 0x48, ^ ); // EOR
 	
-// 4. 8-BIT ARITHMETIC OPERATION COMMANDS.
+// 4. 8-BIT ARITHMETIC OPERATION COMMANDS
 
 	ADDR_MODES( 0x68 ) // CMP addr
 		data = READ( data );
 	case 0x68: // CMP imm
 		nz = a - data;
 		c = ~nz;
+		nz &= 0xff;
 		goto inc_pc_loop;
 	
 	case 0x79: // CMP (X),(Y)
 		data = READ_DP( x );
 		nz = data - READ_DP( y );
 		c = ~nz;
+		nz &= 0xff;
 		goto loop;
 	
 	case 0x69: // CMP (dp),(dp)
@@ -446,6 +428,7 @@
 		pc++;
 		nz = READ_DP( READ_PROG( pc ) ) - data;
 		c = ~nz;
+		nz &= 0xff;
 		goto inc_pc_loop;
 	
 	case 0x3E: // CMP X,dp
@@ -459,6 +442,7 @@
 	case 0xC8: // CMP X,imm
 		nz = x - data;
 		c = ~nz;
+		nz &= 0xff;
 		goto inc_pc_loop;
 	
 	case 0x7E: // CMP Y,dp
@@ -472,6 +456,7 @@
 	case 0xAD: // CMP Y,imm
 		nz = y - data;
 		c = ~nz;
+		nz &= 0xff;
 		goto inc_pc_loop;
 	
 	{
@@ -521,7 +506,7 @@
 	
 	}
 	
-// 6. ADDITION & SUBTRACTION COMMANDS.
+// 6. ADDITION & SUBTRACTION COMMANDS
 
 #define INC_DEC_REG( reg, n )   \
 		nz = reg + n;           \
@@ -638,7 +623,7 @@
 		WRITE_DP( uint8_t (data + 1), y );
 		goto inc_pc_loop;
 	
-// 9. 16-BIT OPERATION COMMANDS.
+// 9. 16-BIT OPERATION COMMANDS
 
 	case 0x3A: // INCW dp
 	case 0x1A:{// DECW dp
@@ -685,7 +670,7 @@
 		// add high byte (Y)
 		temp >>= 8;
 		c = y + temp;
-		nz |= c;
+		nz = (nz | c) & 0xff;
 		
 		// half-carry (temporary avoids CodeWarrior optimizer bug)
 		unsigned hc = (c & 15) - (y & 15);
@@ -706,10 +691,11 @@
 		temp -= READ_DP( uint8_t (data + 1) );
 		nz |= temp;
 		c = ~temp;
+		nz &= 0xff;
 		goto inc_pc_loop;
 	}
 	
-// 10. MULTIPLICATION & DIVISON COMMANDS.
+// 10. MULTIPLICATION & DIVISON COMMANDS
 
 	case 0xCF: { // MUL YA
 		unsigned temp = y * a;
@@ -726,22 +712,22 @@
 		
 		status &= ~(st_h | st_v);
 		
-		if ( y >= x )
-			status |= st_v;
-		
 		if ( (y & 15) >= (x & 15) )
 			status |= st_h;
 		
-		unsigned temp = y * 0x100 + a;
-		if ( y < x * 2 ) {
-			a = temp / x;
-			y = temp - a * x;
+		if ( y >= x )
+			status |= st_v;
+		
+		unsigned ya = y * 0x100 + a;
+		if ( y < x * 2 )
+		{
+			a = ya / x;
+			y = ya - a * x;
 		}
-		else {
-			temp -= x * 0x200;
-			a = temp / (256 - x);
-			y = temp - a * (256 - x) + x;
-			a = 255 - a;
+		else
+		{
+			a = 255 - (ya - x * 0x200) / (256 - x);
+			y = x   + (ya - x * 0x200) % (256 - x);
 		}
 		
 		nz = (uint8_t) a;
@@ -750,16 +736,17 @@
 		goto loop;
 	}
 	
-// 11. DECIMAL COMPENSATION COMMANDS.
+// 11. DECIMAL COMPENSATION COMMANDS
 	
 	// seem unused
 	// case 0xDF: // DAA
 	// case 0xBE: // DAS
 	
-// 12. BRANCHING COMMANDS.
+// 12. BRANCHING COMMANDS
 
 	case 0x2F: // BRA rel
-		goto branch_taken_loop;
+		pc += (BOOST::int8_t) data;
+		goto inc_pc_loop;
 	
 	case 0x30: // BMI
 		BRANCH( IS_NEG )
@@ -816,9 +803,7 @@
 	
 	case 0xFE: // DBNZ Y,rel
 		y = uint8_t (y - 1);
-		if ( y )
-			goto branch_taken_loop;
-		goto inc_pc_loop;
+		BRANCH( y )
 	
 	case 0x6E: { // DBNZ dp,rel
 		pc++;
@@ -836,11 +821,10 @@
 		pc = READ_PROG16( pc );
 		goto loop;
 	
-// 13. SUB-ROUTINE CALL RETURN COMMANDS.
+// 13. SUB-ROUTINE CALL RETURN COMMANDS
 	
-	/*
-	// seems unused
 	case 0x0F: // BRK
+		check( false ); // untested
 		PUSH16( pc + 1 );
 		pc = READ_PROG16( 0xffde ); // vector address verified
 		int temp;
@@ -848,7 +832,6 @@
 		PUSH( temp );
 		status = (status | st_b) & ~st_i;
 		goto loop;
-	*/
 	
 	case 0x4F: // PCALL offset
 		pc++;
@@ -876,7 +859,7 @@
 		pc = READ_PROG16( 0xffde - (opcode >> 3) );
 		goto loop;
 	
-// 14. STACK OPERATION COMMANDS.
+// 14. STACK OPERATION COMMANDS
 
 	{
 		int temp;
@@ -923,7 +906,7 @@
 		y = POP();
 		goto loop;
 	
-// 15. BIT OPERATION COMMANDS.
+// 15. BIT OPERATION COMMANDS
 
 	case 0x02: // SET1
 	case 0x22:
@@ -967,30 +950,31 @@
 		c &= mem_bit( pc );
 		pc += 2;
 		goto loop;
-	/*
-	 // seem unused
+	
 	case 0x6A: // AND1 C,/mem.bit
+		check( false ); // untested
 		c &= ~mem_bit( pc );
 		pc += 2;
 		goto loop;
 	
 	case 0x0A: // OR1 C,mem.bit
+		check( false ); // untested
 		c |= mem_bit( pc );
 		pc += 2;
 		goto loop;
 	
 	case 0x2A: // OR1 C,/mem.bit
+		check( false ); // untested
 		c |= ~mem_bit( pc );
 		pc += 2;
 		goto loop;
-	*/
 	
 	case 0x8A: // EOR1 C,mem.bit
 		c ^= mem_bit( pc );
 		pc += 2;
 		goto loop;
 	
-	case 0xEA: { // NOT1 C,mem.bit
+	case 0xEA: { // NOT1 mem.bit
 		data = READ_PROG16( pc );
 		pc += 2;
 		unsigned temp = READ( data & 0x1fff );
@@ -1004,7 +988,7 @@
 		pc += 2;
 		unsigned temp = READ( data & 0x1fff );
 		unsigned bit = data >> 13;
-		temp = (temp & ~(1 << bit)) | ((c >> (8 - bit)) & 1);
+		temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit);
 		WRITE( data & 0x1fff, temp );
 		goto loop;
 	}
@@ -1014,7 +998,7 @@
 		pc += 2;
 		goto loop;
 	
-// 16. PROGRAM STATUS FLAG OPERATION COMMANDS.
+// 16. PROGRAM STATUS FLAG OPERATION COMMANDS
 
 	case 0x60: // CLRC
 		c = 0;
@@ -1040,17 +1024,17 @@
 		dp = 0x100;
 		goto loop;
 	
-	/* // seem unused
 	case 0xA0: // EI
+		check( false ); // untested
 		status |= st_i;
 		goto loop;
 	
 	case 0xC0: // DI
+		check( false ); // untested
 		status &= ~st_i;
 		goto loop;
-	*/
 	
-// 17. OTHER COMMANDS.
+// 17. OTHER COMMANDS
 
 	case 0x00: // NOP
 		goto loop;
--- a/Plugins/Input/console/Spc_Cpu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Cpu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
 // Super Nintendo (SNES) SPC-700 CPU emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef SPC_CPU_H
 #define SPC_CPU_H
@@ -9,22 +9,20 @@
 #include "blargg_common.h"
 
 typedef unsigned spc_addr_t;
-typedef long spc_time_t;
+typedef long     spc_time_t;
 
 class Snes_Spc;
 
 class Spc_Cpu {
 	typedef BOOST::uint8_t uint8_t;
 	uint8_t* const ram;
-	spc_time_t remain_;
-	Snes_Spc& emu;
 public:
-	// Keeps pointer to ram and spc
-	Spc_Cpu( uint8_t ram [0x10000], Snes_Spc* spc );
+	// Keeps pointer to 64K RAM
+	Spc_Cpu( Snes_Spc* spc, uint8_t* ram );
 	
 	// SPC-700 registers. *Not* kept updated during a call to run().
 	struct registers_t {
-		unsigned short pc;
+		long pc; // more than 16 bits to allow overflow detection
 		uint8_t a;
 		uint8_t x;
 		uint8_t y;
@@ -51,11 +49,12 @@
 	Spc_Cpu( const Spc_Cpu& );
 	Spc_Cpu& operator = ( const Spc_Cpu& );
 	unsigned mem_bit( spc_addr_t );
+	
+	spc_time_t remain_;
+	Snes_Spc& emu;
 };
 
-inline spc_time_t Spc_Cpu::remain() const {
-	return remain_;
-}
+inline spc_time_t Spc_Cpu::remain() const { return remain_; }
 
 #endif
 
--- a/Plugins/Input/console/Spc_Dsp.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Dsp.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
-// Based on Brad Martin's OpenSPC DSP emulator.
+// Based on Brad Martin's OpenSPC DSP emulator
 
 #include "Spc_Dsp.h"
 
@@ -10,7 +10,7 @@
 #include "blargg_endian.h"
 
 /* Copyright (C) 2002 Brad Martin */
-/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2004-2006 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
@@ -25,12 +25,19 @@
 
 Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ )
 {
-	voices_muted = 0;
 	set_gain( 1.0 );
+	mute_voices( 0 );
+	disable_surround( false );
 	
 	BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count );
 }
 
+void Spc_Dsp::mute_voices( int mask )
+{
+	for ( int i = 0; i < voice_count; i++ )
+		voice_state [i].enabled = (mask >> i & 1) ? 31 : 7;
+}
+
 void Spc_Dsp::reset()
 {
 	keys = 0;
@@ -42,13 +49,16 @@
 	g.flags = 0xE0; // reset, mute, echo off
 	g.key_ons = 0;
 	
-	for ( int i = 0; i < voice_count; i++ ) {
-		voice_state [i].on_cnt = 0;
-		voice_state [i].envstate = state_release;
+	for ( int i = 0; i < voice_count; i++ )
+	{
+		voice_t& v = voice_state [i];
+		v.on_cnt = 0;
+		v.volume [0] = 0;
+		v.volume [1] = 0;
+		v.envstate = state_release;
 	}
 	
 	memset( fir_buf, 0, sizeof fir_buf );
-	memset( voice_vol, 0, sizeof voice_vol );
 }
 
 void Spc_Dsp::write( int i, int data )
@@ -62,17 +72,18 @@
 		// voice volume
 		case 0:
 		case 1: {
-			int left = (int8_t) reg [i & ~1];
+			short* volume = voice_state [high].volume;
+			int left  = (int8_t) reg [i & ~1];
 			int right = (int8_t) reg [i | 1];
-			voice_vol [high] [0] = left; 
-			voice_vol [high] [1] = right; 
+			volume [0] = left;
+			volume [1] = right;
 			// kill surround only if signs of volumes differ
-			if ( left * right < 0 )
+			if ( left * right < surround_threshold )
 			{
 				if ( left < 0 )
-					voice_vol [high] [0] = -left;
+					volume [0] = -left;
 				else
-					voice_vol [high] [1] = -right;
+					volume [1] = -right;
 			}
 			break;
 		}
@@ -89,7 +100,8 @@
 // The counter starts at 30720 (0x7800). Each count divides exactly into
 // 0x7800 without remainder.
 const int env_rate_init = 0x7800;
-static const short env_rates [0x20] = {
+static const short env_rates [0x20] =
+{
 	0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
 	0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
 	0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
@@ -98,7 +110,7 @@
 
 const int env_range = 0x800;
 
-int Spc_Dsp::clock_envelope( int v )
+inline int Spc_Dsp::clock_envelope( int v )
 {                               /* Return value is current 
 								 * ENVX */
 	raw_voice_t& raw_voice = this->voice [v];
@@ -116,7 +128,8 @@
 		 * no need for a count because it always happens every update. 
 		 */
 		envx -= env_range / 256;
-		if ( envx <= 0 ) {
+		if ( envx <= 0 )
+		{
 			envx = 0;
 			keys &= ~(1 << v);
 			return -1;
@@ -135,17 +148,20 @@
 			case state_attack: {
 				// increase envelope by 1/64 each step
 				int t = adsr1 & 15;
-				if ( t == 15 ) {
+				if ( t == 15 )
+				{
 					envx += env_range / 2;
 				}
-				else {
+				else
+				{
 					cnt -= env_rates [t * 2 + 1];
 					if ( cnt > 0 )
 						break;
 					envx += env_range / 64;
 					cnt = env_rate_init;
 				}
-				if ( envx >= env_range ) {
+				if ( envx >= env_range )
+				{
 					envx = env_range - 1;
 					voice.envstate = state_decay;
 				}
@@ -159,7 +175,8 @@
 				// Multiplying ENVX by 255/256 every time DECAY is
 				// updated. 
 				cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10];
-				if ( cnt <= 0 ) {
+				if ( cnt <= 0 )
+				{
 					cnt = env_rate_init;
 					envx -= ((envx - 1) >> 8) + 1;
 					voice.envx = envx;
@@ -176,12 +193,17 @@
 				// Multiplying ENVX by 255/256 every time SUSTAIN is
 				// updated. 
 				cnt -= env_rates [raw_voice.adsr [1] & 0x1f];
-				if ( cnt <= 0 ) {
+				if ( cnt <= 0 )
+				{
 					cnt = env_rate_init;
 					envx -= ((envx - 1) >> 8) + 1;
 					voice.envx = envx;
 				}
 				break;
+			
+			case state_release:
+				// handled above
+				break;
 		}
 	}
 	else
@@ -210,7 +232,8 @@
 				break;
 			cnt = env_rate_init;
 			envx -= env_range / 64;
-			if ( envx < 0 ) {
+			if ( envx < 0 )
+			{
 				envx = 0;
 				if ( voice.envstate == state_attack )
 					voice.envstate = state_decay;
@@ -225,7 +248,8 @@
 				break;
 			cnt = env_rate_init;
 			envx -= ((envx - 1) >> 8) + 1;
-			if ( envx < 0 ) {
+			if ( envx < 0 )
+			{
 				envx = 0;
 				if ( voice.envstate == state_attack )
 					voice.envstate = state_decay;
@@ -275,7 +299,7 @@
 
 void Spc_Dsp::run( long count, short* out_buf )
 {
-	// to do: make clock_envelope() inline to avoid out-of-line calls?
+	// to do: make clock_envelope() inline so that this becomes a leaf function?
 	
 	// Should we just fill the buffer with silence? Flags won't be cleared
 	// during this run so it seems it should keep resetting every sample.
@@ -289,10 +313,12 @@
 	
 	const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100];
 	
-	int const left_volume = g.left_volume * emu_gain;
-	int right_volume = g.right_volume * emu_gain;
-	if ( left_volume * right_volume < 0 )
+	int left_volume  = g.left_volume;
+	int right_volume = g.right_volume;
+	if ( left_volume * right_volume < surround_threshold )
 		right_volume = -right_volume; // kill global surround
+	left_volume  *= emu_gain;
+	right_volume *= emu_gain;
 	
 	while ( --count >= 0 )
 	{
@@ -305,15 +331,14 @@
 		
 		g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX.
 		
-		if ( g.noise_enables ) {
+		if ( g.noise_enables )
+		{
 			noise_count -= env_rates [g.flags & 0x1F];
-			if ( noise_count <= 0 ) {
+			if ( noise_count <= 0 )
+			{
 				noise_count = env_rate_init;
 				
-				if ( noise & 0x4000 )
-					noise_amp += (noise & 1) ? noise : -noise;
-				else
-					noise_amp >>= 1;
+				noise_amp = BOOST::int16_t (noise * 2);
 				
 				int feedback = (noise << 13) ^ (noise << 14);
 				noise = (feedback & 0x4000) | (noise >> 1);
@@ -356,20 +381,23 @@
 				voice.envstate = state_attack;
 			}
 			
-			if ( g.key_ons & vbit & ~g.key_offs ) {
+			if ( g.key_ons & vbit & ~g.key_offs )
+			{
 				// voice doesn't come on if key off is set
 				g.key_ons &= ~vbit;
 				voice.on_cnt = 8;
 			}
 			
-			if ( keys & g.key_offs & vbit ) {
+			if ( keys & g.key_offs & vbit )
+			{
 				// key off
 				voice.envstate = state_release;
 				voice.on_cnt = 0;
 			}
 			
 			int envx;
-			if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) {
+			if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 )
+			{
 				raw_voice.envx = 0;
 				raw_voice.outx = 0;
 				prev_outx = 0;
@@ -385,11 +413,13 @@
 					{
 						g.wave_ended |= vbit;
 					
-						if ( voice.block_header & 2 ) {
+						if ( voice.block_header & 2 )
+						{
 							// verified (played endless looping sample and ENDX was set)
 							voice.addr = GET_LE16( sd [raw_voice.waveform].loop );
 						}
-						else {
+						else
+						{
 							// first block was end block; don't play anything (verified)
 							goto sample_ended; // to do: find alternative to goto
 						}
@@ -409,7 +439,8 @@
 					raw_voice.envx = 0;
 					voice.envx = 0;
 					// add silence samples to interpolation buffer
-					do {
+					do
+					{
 						voice.interp3 = voice.interp2;
 						voice.interp2 = voice.interp1;
 						voice.interp1 = voice.interp0;
@@ -420,7 +451,8 @@
 				}
 				
 				int delta = ram [voice.addr];
-				if ( voice.block_remain & 1 ) {
+				if ( voice.block_remain & 1 )
+				{
 					delta <<= 4; // use lower nybble
 					voice.addr++;
 				}
@@ -474,50 +506,45 @@
 			}
 			
 			// rate (with possible modulation)
-			int rate = GET_LE16( raw_voice.rate ) & 0x3fff;
+			int rate = GET_LE16( raw_voice.rate ) & 0x3FFF;
 			if ( g.pitch_mods & vbit )
 				rate = (rate * (prev_outx + 32768)) >> 15;
 			
-			// fraction
-			int fraction = voice.fraction & 0xfff;
-			voice.fraction = fraction + rate;
-			fraction >>= 4;
-			
 			// Gaussian interpolation using most recent 4 samples
-			const short* table = gauss [fraction];
-			const short* table2 = gauss [255 - fraction];
+			int index = voice.fraction >> 2 & 0x3FC;
+			voice.fraction = (voice.fraction & 0x0FFF) + rate;
+			const BOOST::int16_t* table  = (BOOST::int16_t*) ((char*) gauss + index);
+			const BOOST::int16_t* table2 = (BOOST::int16_t*) ((char*) gauss + (255*4 - index));
 			int s = ((table  [0] * voice.interp3) >> 12) +
-					((table  [1] * voice.interp2) >> 12) +
-					((table2 [1] * voice.interp1) >> 12) +
+					((table  [1] * voice.interp2) >> 12);
+			s +=    ((table2 [1] * voice.interp1) >> 12) +
+			// to do: should clamp here
 					((table2 [0] * voice.interp0) >> 12);
-			int output = noise_amp; // noise is almost never used
+			int output = noise_amp; // noise is rarely used
 			if ( !(g.noise_enables & vbit) )
 				output = clamp_16( s * 2 );
 			
-			int muted = vbit & voices_muted;
+			// scale output and set outx values
+			output = (output * envx) >> 11 & ~1;
 			
-			// scale output and set outx values
-			output = ((output * envx) >> 11) & ~1;
+			// output and apply muting (by setting voice.enabled to 31)
+			// if voice is externally disabled (not a SNES feature)
+			int l = (voice.volume [0] * output) >> voice.enabled;
+			int r = (voice.volume [1] * output) >> voice.enabled;
 			prev_outx = output;
 			raw_voice.outx = output >> 8;
-			
-			// apply muting if voice is externally disabled (not a SNES feature)
-			if ( muted )
-				output = 0;
-			
-			// output
-			int l = (voice_vol [vidx] [0] * output) >> 7;
-			int r = (voice_vol [vidx] [1] * output) >> 7;
-			if ( g.echo_ons & vbit ) {
+			if ( g.echo_ons & vbit )
+			{
 				echol += l;
 				echor += r;
 			}
 			left += l;
 			right += r;
-		} // end of channel loop
+		}
+		// end of channel loop
 		
 		// main volume control
-		left = (left * left_volume) >> (7 + emu_gain_bits);
+		left  = (left  * left_volume ) >> (7 + emu_gain_bits);
 		right = (right * right_volume) >> (7 + emu_gain_bits);
 		
 		// Echo FIR filter
@@ -528,7 +555,7 @@
 		echo_ptr += 4;
 		if ( echo_ptr >= (g.echo_delay & 15) * 0x800 )
 			echo_ptr = 0;
-		int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend
+		int fb_left  = (BOOST::int16_t) GET_LE16( echo_buf     ); // sign-extend
 		int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend
 		this->echo_ptr = echo_ptr;
 		
@@ -560,24 +587,23 @@
 				fir_pos [6] [1] * fir_coeff [1] +
 				fir_pos [7] [1] * fir_coeff [0];
 		
-		// overlap calculations with tests
-		left += (fb_left * g.left_echo_volume) >> 14;
+		left  += (fb_left  * g.left_echo_volume ) >> 14;
+		right += (fb_right * g.right_echo_volume) >> 14;
 		
 		// echo buffer feedback
-		if ( !(g.flags & 0x20) ) {
-			echol += (fb_left * g.echo_feedback) >> 14;
+		if ( !(g.flags & 0x20) )
+		{
+			echol += (fb_left  * g.echo_feedback) >> 14;
 			echor += (fb_right * g.echo_feedback) >> 14;
-			SET_LE16( echo_buf, clamp_16( echol ) );
+			SET_LE16( echo_buf    , clamp_16( echol ) );
 			SET_LE16( echo_buf + 2, clamp_16( echor ) );
 		}
 		
-		right += (fb_right * g.right_echo_volume) >> 14;
-		
 		if ( out_buf )
 		{
 			// write final samples
 			
-			left = clamp_16( left );
+			left  = clamp_16( left  );
 			right = clamp_16( right );
 			
 			int mute = g.flags & 0x40;
@@ -587,7 +613,8 @@
 			out_buf += 2;
 			
 			// muting
-			if ( mute ) {
+			if ( mute )
+			{
 				out_buf [-2] = 0;
 				out_buf [-1] = 0;
 			}
@@ -595,19 +622,14 @@
 	}
 }
 
-// Base normal_gauss table is very close to the following:
-#if 0
-double e = 2.718281828;
-for ( int i = 0; i < 512; i++ ) {
-	double x = i / 511.0 * 2.31 - 0.05;
-	double y = pow( e, -x * x ) * 1305.64;
-	normal_gauss [i] = y - 16.54;
-}
-#endif
+// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry):
+// int normal_gauss [512];
+// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45
 
 // Interleved gauss table (to improve cache coherency).
-// gauss [i] [j] = normal_gauss [(1 - j) * 256 + i]
-const short Spc_Dsp::gauss [256] [2] = {
+// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i]
+const BOOST::int16_t Spc_Dsp::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,
--- a/Plugins/Input/console/Spc_Dsp.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Dsp.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,24 +1,20 @@
 
 // Super Nintendo (SNES) SPC DSP emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
-// Copyright (C) 2002 Brad Martin
+// Game_Music_Emu 0.3.0
 
 #ifndef SPC_DSP_H
 #define SPC_DSP_H
 
 #include "blargg_common.h"
 
-// Surround effects using opposite volumes for left and right are currently removed
-// by making left and right volume positive if their signs differ.
-
 class Spc_Dsp {
 	typedef BOOST::int8_t int8_t;
 	typedef BOOST::uint8_t uint8_t;
 public:
 	
-	// Keeps pointer to ram
-	Spc_Dsp( uint8_t ram [0x10000] );
+	// Keeps pointer to 64K ram
+	Spc_Dsp( uint8_t* ram );
 	
 	// Mute voice n if bit n (1 << n) of mask is clear.
 	enum { voice_count = 8 };
@@ -31,6 +27,9 @@
 	// the 16-bit sample range.
 	void set_gain( double );
 	
+	// If true, prevent channels and global volumes from being phase-negated
+	void disable_surround( bool disable );
+	
 	// Read/write register 'n', where n ranges from 0 to register_count - 1.
 	enum { register_count = 128 };
 	int  read ( int n );
@@ -97,8 +96,6 @@
 	short fir_buf [16] [2];
 	int fir_offset; // (0 to 7)
 	
-	short voice_vol [voice_count] [2];
-	
 	enum { emu_gain_bits = 8 };
 	int emu_gain;
 	
@@ -109,11 +106,10 @@
 	int noise_amp;
 	int noise;
 	int noise_count;
-
-	int voices_muted;
-	int disable_surround_; // set to sign bit (0x80) when disabled
 	
-	static const short gauss [] [2];
+	int surround_threshold;
+	
+	static const BOOST::int16_t gauss [];
 	
 	enum state_t {
 		state_attack,
@@ -123,18 +119,21 @@
 	};
 	
 	struct voice_t {
+		short volume [2];
 		short fraction;// 12-bit fractional position
-		short interp0; // most recent four decoded samples
+		short interp3; // most recent four decoded samples
+		short interp2;
 		short interp1;
-		short interp2;
-		short interp3;
+		short interp0;
 		short block_remain; // number of nybbles remaining in current block
 		unsigned short addr;
 		short block_header; // header byte from current block
 		short envcnt;
 		short envx;
 		short on_cnt;
-		state_t envstate;
+		short enabled; // 7 if enabled, 31 if disabled
+		short envstate;
+		short unused; // pad to power of 2
 	};
 	
 	voice_t voice_state [voice_count];
@@ -142,18 +141,15 @@
 	int clock_envelope( int );
 };
 
-inline int Spc_Dsp::read( int i ) {
+inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; }
+
+inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); }
+
+inline int Spc_Dsp::read( int i )
+{
 	assert( (unsigned) i < register_count );
 	return reg [i];
 }
 
-inline void Spc_Dsp::mute_voices( int mask ) {
-	voices_muted = mask;
-}
-
-inline void Spc_Dsp::set_gain( double v ) {
-	emu_gain = (int) (v * (1 << emu_gain_bits));
-}
-
 #endif
 
--- a/Plugins/Input/console/Spc_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,12 +1,13 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Spc_Emu.h"
 
+#include <stdlib.h>
 #include <string.h>
-#include "abstract_file.h"
+#include "blargg_endian.h"
 
-/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2004-2006 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
@@ -19,11 +20,9 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-Spc_Emu::Spc_Emu()
+Spc_Emu::Spc_Emu( double gain )
 {
-	resample_ratio = 1.0;
-	use_resampler = false;
-	track_count_ = 0;
+	apu.set_gain( gain );
 }
 
 Spc_Emu::~Spc_Emu()
@@ -38,62 +37,83 @@
 	return names;
 }
 
-blargg_err_t Spc_Emu::init( long sample_rate, double gain )
+void Spc_Emu::mute_voices( int m )
 {
-	apu.set_gain( gain );
-	use_resampler = false;
-	resample_ratio = (double) native_sample_rate / sample_rate;
+	Music_Emu::mute_voices( m );
+	apu.mute_voices( m );
+}
+
+blargg_err_t Spc_Emu::set_sample_rate( long sample_rate )
+{
 	if ( sample_rate != native_sample_rate )
 	{
 		BLARGG_RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) );
-		resampler.time_ratio( resample_ratio, 0.9965 );
-		use_resampler = true;
+		resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 );
 	}
-	
-	return blargg_success;
+	return Music_Emu::set_sample_rate( sample_rate );
 }
 
-blargg_err_t Spc_Emu::load( const header_t& h, Emu_Reader& in )
+blargg_err_t Spc_Emu::load( Data_Reader& in )
 {
-	if ( in.remain() < sizeof file.data )
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Spc_Emu::load( const header_t& h, Data_Reader& in )
+{
+	if ( in.remain() < Snes_Spc::spc_file_size - (int) sizeof h )
 		return "Not an SPC file";
 	
 	if ( strncmp( h.tag, "SNES-SPC700 Sound File Data", 27 ) != 0 )
 		return "Not an SPC file";
 	
-	track_count_ = 1;
-	voice_count_ = Snes_Spc::voice_count;
+	long remain = in.remain();
+	long size = remain + sizeof h;
+	if ( size < trailer_offset )
+		size = trailer_offset;
+	BLARGG_RETURN_ERR( spc_data.resize( size ) );
 	
-	memcpy( &file.header, &h, sizeof file.header );
-	return in.read( file.data, sizeof file.data );
+	set_track_count( 1 );
+	set_voice_count( Snes_Spc::voice_count );
+	
+	memcpy( spc_data.begin(), &h, sizeof h );
+	return in.read( &spc_data [sizeof h], remain );
 }
 
-blargg_err_t Spc_Emu::start_track( int )
+void Spc_Emu::start_track( int track )
 {
+	Music_Emu::start_track( track );
+	
 	resampler.clear();
-	return apu.load_spc( &file, sizeof file );
+	if ( apu.load_spc( spc_data.begin(), spc_data.size() ) )
+		check( false );
 }
 
-blargg_err_t Spc_Emu::skip( long count )
+void Spc_Emu::skip( long count )
 {
-	count = long (count * resample_ratio) & ~1;
+	count = long (count * resampler.ratio()) & ~1;
 	
 	count -= resampler.skip_input( count );
 	if ( count > 0 )
-		BLARGG_RETURN_ERR( apu.skip( count ) );
+		apu.skip( count );
 	
 	// eliminate pop due to resampler
 	const int resampler_latency = 64;
 	sample_t buf [resampler_latency];
-	return play( resampler_latency, buf );
+	play( resampler_latency, buf );
 }
 
-blargg_err_t Spc_Emu::play( long count, sample_t* out )
+void Spc_Emu::play( long count, sample_t* out )
 {
-	require( track_count_ ); // file must be loaded
+	require( track_count() ); // file must be loaded
 	
-	if ( !use_resampler )
-		return apu.play( count, out );
+	if ( sample_rate() == native_sample_rate )
+	{
+		if ( apu.play( count, out ) )
+			log_error();
+		return;
+	}
 	
 	long remain = count;
 	while ( remain > 0 )
@@ -102,43 +122,12 @@
 		if ( remain > 0 )
 		{
 			long n = resampler.max_write();
-			BLARGG_RETURN_ERR( apu.play( n, resampler.buffer() ) );
+			if ( apu.play( n, resampler.buffer() ) )
+				log_error();
 			resampler.write( n );
 		}
 	}
 	
 	assert( remain == 0 );
-	
-	return blargg_success;
-}
-
-Spc_Reader::Spc_Reader() : file( NULL ) {
-}
-
-Spc_Reader::~Spc_Reader() {
-	close();
 }
 
-blargg_err_t Spc_Reader::read_head(Spc_Emu::header_t *header) {
-	vfs_fread(&header->tag,     1,35,file);
-	vfs_fread(&header->format,  1, 1,file);
-	vfs_fread(&header->version, 1, 1,file);
-	vfs_fread(&header->pc,      1, 2,file);
-	vfs_fread(&header->a,       1, 1,file);
-	vfs_fread(&header->x,       1, 1,file);
-	vfs_fread(&header->y,       1, 1,file);
-	vfs_fread(&header->psw,     1, 1,file);
-	vfs_fread(&header->sp,      1, 1,file);
-	vfs_fread(&header->unused,  1, 2,file);
-	vfs_fread(&header->song,    1,32,file);
-	vfs_fread(&header->game,    1,32,file);
-	vfs_fread(&header->dumper,  1,16,file);
-	vfs_fread(&header->comment, 1,32,file);
-	vfs_fread(&header->date,    1,11,file);
-	vfs_fread(&header->len_secs,1, 3,file);
-	vfs_fread(&header->fade_msec,1,5,file);
-	vfs_fread(&header->author,  1,32,file);
-	vfs_fread(&header->mute_mask,1,1,file);
-	vfs_fread(&header->emulator,1, 1,file);
-	vfs_fread(&header->unused2, 1,45,file);
-}
--- a/Plugins/Input/console/Spc_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Spc_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,7 +1,7 @@
 
 // Super Nintendo (SNES) SPC music file emulator
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef SPC_EMU_H
 #define SPC_EMU_H
@@ -11,19 +11,19 @@
 #include "Snes_Spc.h"
 
 class Spc_Emu : public Music_Emu {
+	enum { trailer_offset = 0x10200 };
 public:
-	Spc_Emu();
-	~Spc_Emu();
+	// A gain of 1.0 results in almost no clamping. Default gain roughly
+	// matches volume of other emulators.
+	Spc_Emu( double gain = 1.4 );
 	
-	// The Super Nintendo hardware samples at 32kHz
+	// The Super Nintendo hardware samples at 32kHz. Other sample rates are
+	// handled by resampling the 32kHz output; emulation accuracy is not affected.
 	enum { native_sample_rate = 32000 };
 	
-	// Initialize emulator with given sample rate and gain. A sample rate different than
-	// the native 32kHz results in internal resampling to the desired rate. A gain of 1.0
-	// results in almost no clamping. Default gain roughly matches volume of other emulators.
-	blargg_err_t init( long sample_rate, double gain = 1.4 );
-	
-	struct header_t {
+	// SPC file header
+	struct header_t
+	{
 		char tag [35];
 		byte format;
 		byte version;
@@ -42,50 +42,45 @@
 		byte emulator;
 		byte unused2 [45];
 		
+		enum { track_count = 1 };
 		enum { copyright = 0 }; // no copyright field
 	};
-
-	int length;
+	BOOST_STATIC_ASSERT( sizeof (header_t) == 0x100 );
 	
-	// Load SPC, given its header and reader for remaining data
-	blargg_err_t load( const header_t&, Emu_Reader& );
+	// Load SPC data
+	blargg_err_t load( Data_Reader& );
 	
-	void mute_voices( int );
-	blargg_err_t start_track( int );
-	blargg_err_t play( long count, sample_t* );
-	blargg_err_t skip( long );
-	const char** voice_names() const;
-
+	// Load SPC using already-loaded header and remaining data
+	blargg_err_t load( header_t const&, Data_Reader& );
+	
+	// Header for currently loaded SPC
+	header_t const& header() const { return *(header_t*) spc_data.begin(); }
+	
+	// Pointer and size for trailer data
+	byte const* trailer() const { return &spc_data [trailer_offset]; }
+	long trailer_size() const { return spc_data.size() - trailer_offset; }
 	
-// End of public interface
-private:
-	Snes_Spc apu;
-	Fir_Resampler resampler;
-	double resample_ratio;
-	bool use_resampler;
+	// If true, prevents channels and global volumes from being phase-negated
+	void disable_surround( bool disable = true );
 	
-	struct spc_file_t {
-		header_t header;
-		char data [0x10080];
-	};
-	BOOST_STATIC_ASSERT( sizeof (spc_file_t) == 0x10180 );
-	
-	spc_file_t file;
+public:
+	~Spc_Emu();
+	blargg_err_t set_sample_rate( long );
+	void mute_voices( int );
+	void start_track( int );
+	void play( long, sample_t* );
+	void skip( long );
+	const char** voice_names() const;
+public:
+	// deprecated
+	blargg_err_t init( long r, double gain = 1.4 ) { return set_sample_rate( r ); }
+private:
+	blargg_vector<byte> spc_data;
+	Fir_Resampler<24> resampler;
+	Snes_Spc apu;
 };
 
-inline void Spc_Emu::mute_voices( int m ) {
-	apu.mute_voices( m );
-}
-
-class Spc_Reader : public Std_File_Reader {
-	VFSFile* file;
-public:
-	Spc_Reader();
-	~Spc_Reader();
-	
-	// Custom reader for SPC headers [tempfix]
-	blargg_err_t read_head( Spc_Emu::header_t* );
-};
+inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); }
 
 #endif
 
--- a/Plugins/Input/console/Tagged_Data.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-
-// Stubs to disable tagged data reflection functions
-
-// Game_Music_Emu 0.2.4. Copyright (C) 2005 Shay Green. GNU LGPL license.
-
-#ifndef TAGGED_DATA_H
-#define TAGGED_DATA_H
-
-typedef long data_tag_t;
-
-class Tagged_Data {
-public:
-	Tagged_Data( Tagged_Data& parent, data_tag_t ) { }
-	int reading() const { return false; }
-	int not_found() const { return true; }
-	int reflect_int8( data_tag_t, int n ) { return n; }
-	int reflect_int16( data_tag_t, int n ) { return n; }
-	long reflect_int32( data_tag_t, long n ) { return n; }
-};
-
-template<class T>
-inline void reflect_int8( Tagged_Data&, data_tag_t, T* ) { }
-
-template<class T>
-inline void reflect_int16( Tagged_Data&, data_tag_t, T* ) { }
-
-template<class T>
-inline void reflect_int32( Tagged_Data&, data_tag_t, T* ) { }
-
-#endif
-
--- a/Plugins/Input/console/Vgm_Emu.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Vgm_Emu.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -1,12 +1,13 @@
 
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
 
 #include "Vgm_Emu.h"
 
+#include <math.h>
 #include <string.h>
-#include <math.h>
+#include "blargg_endian.h"
 
-/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+/* Copyright (C) 2003-2006 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
@@ -19,16 +20,21 @@
 
 #include BLARGG_SOURCE_BEGIN
 
-const long vgm_sample_rate = 44100;
+double const gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
+double const rolloff = 0.990;
+double const oversample_factor = 1.5;
 
-Vgm_Emu::Vgm_Emu( double gain )
+Vgm_Emu::Vgm_Emu( bool os, double tempo )
 {
+	oversample = os;
+	pos = NULL;
 	data = NULL;
-	pos = NULL;
-	apu.volume( gain );
+	uses_fm = false;
+	vgm_rate = (long) (header_t::time_rate * tempo + 0.5);
 	
-	// to do: decide on equalization parameters
-	set_equalizer( equalizer_t( -32, 8000, 66 ) );
+	static equalizer_t const eq = { -14.0, 80 };
+	set_equalizer( eq );
+	psg.volume( 1.0 );
 }
 
 Vgm_Emu::~Vgm_Emu()
@@ -38,269 +44,255 @@
 
 void Vgm_Emu::unload()
 {
-	delete [] data;
 	data = NULL;
 	pos = NULL;
-	track_ended_ = false;
+	set_track_ended( false );
+	mem.clear();
+}
+
+blargg_err_t Vgm_Emu::set_sample_rate( long sample_rate )
+{
+	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
+	return Classic_Emu::set_sample_rate( sample_rate );
 }
 
-const char** Vgm_Emu::voice_names() const
+BOOST::uint8_t const* Vgm_Emu::gd3_data( int* size ) const
 {
-	static const char* names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
-	return names;
-}
-
-void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
-{
-	apu.osc_output( i, c, l, r );
+	if ( size )
+		*size = 0;
+	
+	long gd3_offset = get_le32( header_.gd3_offset );
+	if ( !gd3_offset )
+		return NULL;
+	
+	gd3_offset -= 0x40 - offsetof (header_t,gd3_offset);
+	if ( gd3_offset < 0 )
+		return NULL;
+	
+	byte const* gd3 = data + gd3_offset;
+	if ( data_end - gd3 < 16 || 0 != memcmp( gd3, "Gd3 ", 4 ) || get_le32( gd3 + 4 ) >= 0x200 )
+		return NULL;
+	
+	long gd3_size = get_le32( gd3 + 8 );
+	if ( data_end - gd3 < gd3_size - 12 )
+		return NULL;
+	
+	if ( size )
+		*size = data_end - gd3;
+	return gd3;
 }
 
 void Vgm_Emu::update_eq( blip_eq_t const& eq )
 {
-	apu.treble_eq( eq );
+	psg.treble_eq( eq );
+	dac_synth.treble_eq( eq );
+}
+
+void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+	if ( i < psg.osc_count )
+		psg.osc_output( i, c, l, r );
+}
+
+const char** Vgm_Emu::voice_names() const
+{
+	static const char* fm_names [] = {
+		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
+	};
+	if ( uses_fm )
+		return fm_names;
+	
+	static const char* psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
+	return psg_names;
+}
+
+void Vgm_Emu::mute_voices( int mask )
+{
+	Classic_Emu::mute_voices( mask );
+	dac_synth.output( &blip_buf );
+	if ( uses_fm )
+	{
+		psg.output( (mask & 0x80) ? 0 : &blip_buf );
+		if ( ym2612.enabled() )
+		{
+			dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * gain );
+			ym2612.mute_voices( mask );
+		}
+		
+		if ( ym2413.enabled() )
+		{
+			int m = mask & 0x3f;
+			if ( mask & 0x20 )
+				m |= 0x01e0; // channels 5-8
+			if ( mask & 0x40 )
+				m |= 0x3e00;
+			ym2413.mute_voices( m );
+		}
+	}
 }
 
-const int time_bits = 12;
-
-inline sms_time_t Vgm_Emu::clocks_from_samples( int samples ) const
+blargg_err_t Vgm_Emu::load_( const header_t& h, void const* new_data, long new_size )
 {
-	const long round_up = 1L << (time_bits - 1);
-	return (samples * time_factor + round_up) >> time_bits;
+	header_ = h;
+	
+	// compatibility
+	if ( 0 != memcmp( header_.tag, "Vgm ", 4 ) )
+		return "Not a VGM file";
+	check( get_le32( header_.version ) <= 0x150 );
+	
+	// psg rate
+	long psg_rate = get_le32( header_.psg_rate );
+	if ( !psg_rate )
+		psg_rate = 3579545;
+	blip_time_factor = (long) floor( (double) (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 );
+	blip_buf.clock_rate( psg_rate );
+	
+	data = (byte*) new_data;
+	data_end = data + new_size;
+	
+	// get loop
+	loop_begin = data_end;
+	if ( get_le32( header_.loop_offset ) )
+		loop_begin = &data [get_le32( header_.loop_offset ) + offsetof (header_t,loop_offset) - 0x40];
+	
+	set_voice_count( psg.osc_count );
+	set_track_count( 1 );
+	
+	BLARGG_RETURN_ERR( setup_fm() );
+	
+	// do after FM in case output buffer is changed
+	BLARGG_RETURN_ERR( Classic_Emu::setup_buffer( psg_rate ) );
+	
+	return blargg_success;
 }
 
-static long get_le32( const BOOST::uint8_t b [4] )
+blargg_err_t Vgm_Emu::setup_fm()
 {
-	return b [3] * 0x1000000L + b [2] * 0x10000L + b [1] * 0x100L + b [0];
+	long ym2612_rate = get_le32( header_.ym2612_rate );
+	long ym2413_rate = get_le32( header_.ym2413_rate );
+	if ( ym2413_rate && get_le32( header_.version ) < 0x110 )
+		update_fm_rates( &ym2413_rate, &ym2612_rate );
+	
+	uses_fm = false;
+	
+	double fm_rate = blip_buf.sample_rate() * oversample_factor;
+	
+	if ( ym2612_rate )
+	{
+		uses_fm = true;
+		if ( !oversample )
+			fm_rate = ym2612_rate / 144.0;
+		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
+		BLARGG_RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) );
+		ym2612.enable( true );
+		set_voice_count( 8 );
+	}
+	
+	if ( !uses_fm && ym2413_rate )
+	{
+		uses_fm = true;
+		if ( !oversample )
+			fm_rate = ym2413_rate / 72.0;
+		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
+		int result = ym2413.set_rate( fm_rate, ym2413_rate );
+		if ( result == 2 )
+			return "YM2413 FM sound isn't supported";
+		BLARGG_CHECK_ALLOC( !result );
+		ym2413.enable( true );
+		set_voice_count( 8 );
+	}
+	
+	if ( uses_fm )
+	{
+		//dprintf( "fm_rate: %f\n", fm_rate );
+		fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
+		BLARGG_RETURN_ERR( Dual_Resampler::resize( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
+		psg.volume( 0.135 * gain );
+	}
+	else
+	{
+		ym2612.enable( false );
+		ym2413.enable( false );
+		psg.volume( 1.0 );
+	}
+	
+	return blargg_success;
 }
 
-blargg_err_t Vgm_Emu::load( const header_t& h, Emu_Reader& in )
+blargg_err_t Vgm_Emu::load( Data_Reader& reader )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( reader.read( &h, sizeof h ) );
+	return load( h, reader );
+}
+
+blargg_err_t Vgm_Emu::load( const header_t& h, Data_Reader& reader )
 {
 	unload();
 	
-	// compatibility
-	if ( 0 != memcmp( h.tag, "Vgm ", 4 ) )
-		return "Not a VGM file";
-	if ( get_le32( h.vers ) > 0x0101 )
-		return "Unsupported VGM format";
-	
-	// clock rate
-	long clock_rate = get_le32( h.psg_rate );
-	if ( !clock_rate )
-		return "Only PSG sound chip is supported";
-	time_factor = (long) floor( clock_rate * ((1L << time_bits) /
-			(double) vgm_sample_rate) + 0.5 );
-	
-	// data
-	long data_size = in.remain();
-	data = new byte [data_size + 3]; // allow pointer to go past end
-	if ( !data )
-		return "Out of memory";
-	end = data + data_size;
-	data [data_size] = 0;
-	data [data_size + 1] = 0;
-	data [data_size + 2] = 0;
-	blargg_err_t err = in.read( data, data_size );
+	// allocate and read data
+	long data_size = reader.remain();
+	int const padding = 8;
+	BLARGG_RETURN_ERR( mem.resize( data_size + padding ) );
+	blargg_err_t err = reader.read( mem.begin(), data_size );
 	if ( err ) {
 		unload();
 		return err;
 	}
-	long loop_offset = get_le32( h.loop_offset );
-	loop_begin = end;
-	loop_duration = 0;
-	if ( loop_offset )
-	{
-		loop_duration = get_le32( h.loop_duration );
-		if ( loop_duration )
-			loop_begin = &data [loop_offset + 0x1c - sizeof (header_t)];
-	}
+	memset( &mem [data_size], 0x66, padding ); // pad with end command
 	
-	voice_count_ = Sms_Apu::osc_count;
-	track_count_ = 1;
-	
-	return setup_buffer( clock_rate );
+	return load_( h, mem.begin(), data_size );
 }
 
-int Vgm_Emu::track_length( const byte** end_out, int* remain_out ) const
+void Vgm_Emu::start_track( int track )
 {
 	require( data ); // file must have been loaded
 	
-	long time = 0;
+	Classic_Emu::start_track( track );
+	psg.reset();
 	
-	if ( end_out || !loop_duration )
+	dac_disabled = -1;
+	pcm_data = data;
+	pcm_pos = data;
+	dac_amp = -1;
+	vgm_time = 0;
+	pos = data;
+	if ( get_le32( header_.version ) >= 0x150 )
 	{
-		const byte* p = data;
-		while ( p < end )
-		{
-			int cmd = *p++;
-			switch ( cmd )
-			{
-				case 0x4f:
-				case 0x50:
-					p += 1;
-					break;
-				
-				case 0x61:
-					if ( p + 1 < end ) {
-						time += p [1] * 0x100L + p [0];
-						p += 2;
-					}
-					break;
-				
-				case 0x62:
-					time += 735; // ntsc frame
-					break;
-				
-				case 0x63:
-					time += 882; // pal frame
-					break;
-				
-				default:
-					if ( (p [-1] & 0xf0) == 0x50 ) {
-						p += 2;
-						break;
-					}
-					dprintf( "Bad command in VGM stream: %02X\n", (int) cmd );
-					break;
-				
-				case 0x66:
-					if ( end_out )
-						*end_out = p;
-					if ( remain_out )
-						*remain_out = end - p;
-					p = end;
-					break;
-			}
-		}
+		long data_offset = get_le32( header_.data_offset );
+		check( data_offset );
+		if ( data_offset )
+			pos += data_offset + offsetof (header_t,data_offset) - 0x40;
 	}
 	
-	// i.e. ceil( exact_length + 0.5 )
-	return loop_duration ? 0 :
-			(time + (vgm_sample_rate >> 1) + vgm_sample_rate - 1) / vgm_sample_rate;
+	if ( uses_fm )
+	{
+		if ( ym2413.enabled() )
+			ym2413.reset();
+		
+		if ( ym2612.enabled() )
+			ym2612.reset();
+		
+		fm_time_offset = 0;
+		blip_buf.clear();
+		Dual_Resampler::clear();
+	}
 }
 
-blargg_err_t Vgm_Emu::start_track( int )
+long Vgm_Emu::run( int msec, bool* added_stereo )
 {
-	require( data ); // file must have been loaded
-	
-	pos = data;
-	loop_remain = 0;
-	delay = 0;
-	track_ended_ = false;
-	apu.reset();
-	starting_track();
-	return blargg_success;
+	blip_time_t psg_end = run_commands( msec * vgm_rate / 1000 );
+	*added_stereo = psg.end_frame( psg_end );
+	return psg_end;
 }
 
-blip_time_t Vgm_Emu::run( int msec, bool* added_stereo )
+void Vgm_Emu::play( long count, sample_t* out )
 {
 	require( pos ); // track must have been started
 	
-	const int duration = vgm_sample_rate / 100 * msec / 10;
-	int time = delay;
-	while ( time < duration && pos < end )
-	{
-		if ( !loop_remain && pos >= loop_begin )
-			loop_remain = loop_duration;
-		
-		int cmd = *pos++;
-		int delay = 0;
-		switch ( cmd )
-		{
-			case 0x66:
-				pos = end; // end
-				if ( loop_duration ) {
-					pos = loop_begin;
-					loop_remain = loop_duration;
-				} 
-				break;
-			
-			case 0x62:
-				delay = 735; // ntsc frame
-				break;
-			
-			case 0x63:
-				delay = 882; // pal frame
-				break;
-			
-			case 0x4f:
-				if ( pos == end ) {
-					check( false ); // missing data
-					break;
-				}
-				apu.write_ggstereo( clocks_from_samples( time ), *pos++ );
-				break;
-			
-			case 0x50:
-				if ( pos == end ) {
-					check( false ); // missing data
-					break;
-				}
-				apu.write_data( clocks_from_samples( time ), *pos++ );
-				break;
-			
-			case 0x61:
-				if ( end - pos < 1 ) {
-					check( false ); // missing data
-					break;
-				}
-				delay = pos [1] * 0x100L + pos [0];
-				pos += 2;
-				break;
-			
-			default:
-				if ( (cmd & 0xf0) == 0x50 )
-				{
-					if ( end - pos < 1 ) {
-						check( false ); // missing data
-						break;
-					}
-					pos += 2;
-					break;
-				}
-				dprintf( "Bad command in VGM stream: %02X\n", (int) cmd );
-				break;
-			
-		}
-		time += delay;
-		if ( loop_remain && (loop_remain -= delay) <= 0 )
-		{
-			pos = loop_begin;
-			loop_remain = 0;
-		}
-	}
-	
-	blip_time_t end_time = clocks_from_samples( duration );
-	if ( pos < end )
-	{
-		delay = time - duration;
-		if ( apu.end_frame( end_time ) && added_stereo )
-			*added_stereo = true;
-	}
-	else {
-		delay = 0;
-		track_ended_ = true;
-	}
-	
-	return end_time;
+	if ( uses_fm )
+		Dual_Resampler::play( count, out, blip_buf );
+	else
+		Classic_Emu::play( count, out );
 }
 
-Vgm_Reader::Vgm_Reader() : file( NULL ) {
-}
-
-Vgm_Reader::~Vgm_Reader() {
-	close();
-}
-
-blargg_err_t Vgm_Reader::read_head(Vgm_Emu::header_t *header) {
-	vfs_fread(&header->tag,         1, 4,file);
-	vfs_fread(&header->data_size,   1, 4,file);
-	vfs_fread(&header->vers,        1, 4,file);
-	vfs_fread(&header->psg_rate,    1, 4,file);
-	vfs_fread(&header->fm_rate,     1, 4,file);
-	vfs_fread(&header->g3d_offset,  1, 4,file);
-	vfs_fread(&header->sample_count,1, 4,file);
-	vfs_fread(&header->loop_offset, 1, 4,file);
-	vfs_fread(&header->loop_duration,1,4,file);
-	vfs_fread(&header->frame_rate,  1, 4,file);
-	vfs_fread(&header->unused,      1,0x18,file);
-}
--- a/Plugins/Input/console/Vgm_Emu.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/Vgm_Emu.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,85 +1,118 @@
 
-// Sega Master System VGM-format game music file emulator (PSG chip only)
+// Multi-format VGM music emulator with support for SMS PSG and Mega Drive FM
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+// Game_Music_Emu 0.3.0
 
 #ifndef VGM_EMU_H
 #define VGM_EMU_H
 
-#include "Classic_Emu.h"
-#include "Sms_Apu.h"
+#include "abstract_file.h"
+#include "Vgm_Emu_Impl.h"
 
-class Vgm_Emu : public Classic_Emu {
+// Emulates VGM music using SN76489/SN76496 PSG, YM2612, and YM2413 FM sound chips.
+// Supports custom sound buffer and frequency equalization when VGM uses just the PSG.
+// FM sound chips can be run at their proper rates, or slightly higher to reduce
+// aliasing on high notes. Currently YM2413 support requires that you supply a
+// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
+class Vgm_Emu : public Vgm_Emu_Impl {
 public:
-	// Set internal gain, where 1.0 results in almost no clamping. Default gain
-	// roughly matches volume of other emulators.
-	Vgm_Emu( double gain = 1.0 );
-	~Vgm_Emu();
 	
-	struct header_t {
+	// Oversample runs FM chips at higher than normal rate. Tempo adjusts speed of
+	// music, but not pitch.
+	Vgm_Emu( bool oversample = true, double tempo = 1.0 );
+	
+	// VGM header format
+	struct header_t
+	{
 		char tag [4];
 		byte data_size [4];
-		byte vers [4];
+		byte version [4];
 		byte psg_rate [4];
-		byte fm_rate [4];
-		byte g3d_offset [4];
-		byte sample_count [4];
+		byte ym2413_rate [4];
+		byte gd3_offset [4];
+		byte track_duration [4];
 		byte loop_offset [4];
 		byte loop_duration [4];
 		byte frame_rate [4];
-		char unused [0x18];
+		byte noise_feedback [2];
+		byte noise_width;
+		byte unused1;
+		byte ym2612_rate [4];
+		byte ym2151_rate [4];
+		byte data_offset [4];
+		byte unused2 [8];
 		
 	    enum { track_count = 1 }; // one track per file
+		enum { time_rate = 44100 }; // all times specified at this rate
 		
-		// no text fields
+		// track information is in gd3 data
 		enum { game = 0 };
 		enum { song = 0 };
 		enum { author = 0 };
 		enum { copyright = 0 };
 	};
 	BOOST_STATIC_ASSERT( sizeof (header_t) == 64 );
+
+	// Load VGM data
+	blargg_err_t load( Data_Reader& );
 	
-	// Load VGM, given its header and reader for remaining data
-	blargg_err_t load( const header_t&, Emu_Reader& );
+	// Load VGM using already-loaded header and remaining data
+	blargg_err_t load( header_t const&, Data_Reader& );
+	
+	// Load VGM using pointer to file data. Keeps pointer to data.
+	blargg_err_t load( void const* data, long size );
+	
+	// Header for currently loaded VGM
+	header_t const& header() const { return header_; }
+	
+	// Pointer to gd3 data, or NULL if none. Optionally returns size of data.
+	// Checks for GD3 header and that version is less than 2.0.
+	byte const* gd3_data( int* size_out = NULL ) const;
 	
-	// Determine length of track, in seconds (0 if track is endless).
-	// Optionally returns pointer and size of data past end of sequence data
-	// (i.e. any tagging information).
-	int track_length( const byte** end_out = NULL, int* remain_out = NULL ) const;
+	// to do: find better name for this
+	// True if Classic_Emu operations are supported
+	bool is_classic_emu() const { return !uses_fm; }
 	
-	blargg_err_t start_track( int );
+public:
+	~Vgm_Emu();
+	blargg_err_t set_sample_rate( long sample_rate );
+	void start_track( int );
+	void mute_voices( int mask );
 	const char** voice_names() const;
-	
-
-// End of public interface
+	void play( long count, sample_t* );
+public:
+	// deprecated
+	int track_length( const byte** end_out = NULL, int* remain_out = NULL ) const
+	{
+		return  (header().track_duration [3]*0x1000000L +
+				header().track_duration [2]*0x0010000L + 
+				header().track_duration [1]*0x0000100L + 
+				header().track_duration [0]) / header_t::time_rate;
+	}
 protected:
+	// Classic_Emu
 	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
 	void update_eq( blip_eq_t const& );
 	blip_time_t run( int, bool* );
 private:
-	byte* data;
-	const byte* pos;
-	const byte* end;
-	const byte* loop_begin;
-	long loop_duration;
-	long loop_remain;
-	long time_factor;
-	int delay;
-	Sms_Apu apu;
+	header_t header_;
+	blargg_vector<byte> mem;
+	long vgm_rate;
+	bool oversample;
+	bool uses_fm;
 	
-	sms_time_t clocks_from_samples( int ) const;
+	blargg_err_t init_( long sample_rate );
+	blargg_err_t load_( const header_t&, void const* data, long size );
+	blargg_err_t setup_fm();
 	void unload();
 };
 
-class Vgm_Reader : public Std_File_Reader {
-	VFSFile* file;
-public:
-	Vgm_Reader();
-	~Vgm_Reader();
-	
-	// Custom reader for SPC headers [tempfix]
-	blargg_err_t read_head( Vgm_Emu::header_t* );
-};
+inline blargg_err_t Vgm_Emu::load( void const* data, long size )
+{
+	unload();
+	return load_( *(header_t*) data, (char*) data + sizeof (header_t),
+			size - sizeof (header_t) );
+}
 
 #endif
 
--- a/Plugins/Input/console/abstract_file.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/abstract_file.cpp	Tue Jan 24 19:10:07 2006 -0800
@@ -4,8 +4,9 @@
 #include <assert.h>
 #include <string.h>
 #include <stddef.h>
+#include <stdlib.h>
 
-/* Copyright (C) 2005 by Shay Green. Permission is hereby granted, free of
+/* Copyright (C) 2005 Shay Green. Permission is hereby granted, free of
 charge, to any person obtaining a copy of this software module and associated
 documentation files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use, copy, modify,
@@ -20,12 +21,17 @@
 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. */
 
+// to do: remove?
 #ifndef RAISE_ERROR
 	#define RAISE_ERROR( str ) return str
 #endif
 
 typedef Data_Reader::error_t error_t;
 
+error_t Data_Writer::write( const void*, long ) { return NULL; }
+
+void Data_Writer::satisfy_lame_linker_() { }
+
 error_t Data_Reader::read( void* p, long s )
 {
 	long result = read_avail( p, s );
@@ -40,11 +46,6 @@
 	return NULL;
 }
 
-long File_Reader::remain() const
-{
-	return size() - tell();
-}
-
 error_t Data_Reader::skip( long count )
 {
 	char buf [512];
@@ -59,6 +60,11 @@
 	return NULL;
 }
 
+long File_Reader::remain() const
+{
+	return size() - tell();
+}
+
 error_t File_Reader::skip( long n )
 {
 	assert( n >= 0 );
@@ -128,7 +134,7 @@
 
 // Std_File_Reader
 
-Std_File_Reader::Std_File_Reader() : file( NULL ) {
+Std_File_Reader::Std_File_Reader() : file_( NULL ) {
 }
 
 Std_File_Reader::~Std_File_Reader() {
@@ -137,8 +143,8 @@
 
 error_t Std_File_Reader::open( const char* path )
 {
-	file = vfs_fopen( path, "rb" );
-	if ( !file )
+	file_ = fopen( path, "rb" );
+	if ( !file_ )
 		RAISE_ERROR( "Couldn't open file" );
 	return NULL;
 }
@@ -146,38 +152,38 @@
 long Std_File_Reader::size() const
 {
 	long pos = tell();
-	vfs_fseek( file, 0, SEEK_END );
+	fseek( file_, 0, SEEK_END );
 	long result = tell();
-	vfs_fseek( file, pos, SEEK_SET );
+	fseek( file_, pos, SEEK_SET );
 	return result;
 }
 
 long Std_File_Reader::read_avail( void* p, long s ) {
-	return vfs_fread( p, 1, s, file );
+	return (long) fread( p, 1, s, file_ );
 }
 
 long Std_File_Reader::tell() const {
-	return vfs_ftell( file );
+	return ftell( file_ );
 }
 
 error_t Std_File_Reader::seek( long n )
 {
-	if ( vfs_fseek( file, n, SEEK_SET ) != 0 )
+	if ( fseek( file_, n, SEEK_SET ) != 0 )
 		RAISE_ERROR( "Error seeking in file" );
 	return NULL;
 }
 
 void Std_File_Reader::close()
 {
-	if ( file ) {
-		vfs_fclose( file );
-		file = NULL;
+	if ( file_ ) {
+		fclose( file_ );
+		file_ = NULL;
 	}
 }
 
 // Std_File_Writer
 
-Std_File_Writer::Std_File_Writer() : file( NULL ) {
+Std_File_Writer::Std_File_Writer() : file_( NULL ) {
 }
 
 Std_File_Writer::~Std_File_Writer() {
@@ -186,19 +192,19 @@
 
 error_t Std_File_Writer::open( const char* path )
 {
-	file = vfs_fopen( path, "wb" );
-	if ( !file )
+	file_ = fopen( path, "wb" );
+	if ( !file_ )
 		RAISE_ERROR( "Couldn't open file for writing" );
 		
 	// to do: increase file buffer size
-	//setvbuf( file, NULL, _IOFBF, 32 * 1024L );
+	//setvbuf( file_, NULL, _IOFBF, 32 * 1024L );
 	
 	return NULL;
 }
 
 error_t Std_File_Writer::write( const void* p, long s )
 {
-	long result = vfs_fwrite( p, 1, s, file );
+	long result = (long) fwrite( p, 1, s, file_ );
 	if ( result != s )
 		RAISE_ERROR( "Couldn't write to file" );
 	return NULL;
@@ -206,42 +212,71 @@
 
 void Std_File_Writer::close()
 {
-	if ( file ) {
-		vfs_fclose( file );
-		file = NULL;
+	if ( file_ ) {
+		fclose( file_ );
+		file_ = NULL;
 	}
 }
 
 // Mem_Writer
 
-Mem_Writer::Mem_Writer( void* p, long s, int b ) :
-	out( p ),
-	remain_( s ),
-	ignore_excess( b )
+Mem_Writer::Mem_Writer( void* p, long s, int b )
+{
+	data_ = (char*) p;
+	size_ = 0;
+	allocated = s;
+	mode = b ? ignore_excess : fixed;
+}
+
+Mem_Writer::Mem_Writer()
 {
+	data_ = NULL;
+	size_ = 0;
+	allocated = 0;
+	mode = expanding;
+}
+
+Mem_Writer::~Mem_Writer()
+{
+	if ( mode == expanding )
+		free( data_ );
 }
 
 error_t Mem_Writer::write( const void* p, long s )
 {
-	if ( s > remain_ )
+	long remain = allocated - size_;
+	if ( s > remain )
 	{
-		if ( !ignore_excess )
+		if ( mode == fixed )
 			RAISE_ERROR( "Tried to write more data than expected" );
-		s = remain_;
+		
+		if ( mode == ignore_excess )
+		{
+			s = remain;
+		}
+		else // expanding
+		{
+			long new_allocated = size_ + s;
+			new_allocated += (new_allocated >> 1) + 2048;
+			void* p = realloc( data_, new_allocated );
+			if ( !p )
+				RAISE_ERROR( "Out of memory" );
+			data_ = (char*) p;
+			allocated = new_allocated;
+		}
 	}
-	remain_ -= s;
-	memcpy( out, p, s );
-	out = (char*) out + s;
+	
+	assert( size_ + s <= allocated );
+	memcpy( data_ + size_, p, s );
+	size_ += s;
+	
 	return NULL;
 }
 
-long Mem_Writer::remain() const {
-	return remain_;
-}
-
 // Null_Writer
 
-error_t Null_Writer::write( const void*, long ) {
+error_t Null_Writer::write( const void*, long )
+{
 	return NULL;
 }
 
--- a/Plugins/Input/console/abstract_file.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/abstract_file.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,19 +1,13 @@
 
 // Abstract file access interfaces
 
-// Copyright (C) 2005 Shay Green. MIT license.
-
 #ifndef ABSTRACT_FILE_H
 #define ABSTRACT_FILE_H
 
-#include "libaudacious/vfs.h"
-
-// to do: built-in buffering?
+#include <stdio.h>
 
+// Supports reading and finding out how many bytes are remaining
 class Data_Reader {
-	// noncopyable
-	Data_Reader( const Data_Reader& );
-	Data_Reader& operator = ( const Data_Reader& );
 public:
 	Data_Reader() { }
 	virtual ~Data_Reader() { }
@@ -21,12 +15,11 @@
 	// NULL on success, otherwise error string
 	typedef const char* error_t;
 	
-	// Read at most 'n' bytes. Return number of bytes read, negative
-	// value if error.
+	// Read at most 'n' bytes. Return number of bytes read, zero or negative
+	// if error.
 	virtual long read_avail( void*, long n ) = 0;
 	
-	// Read exactly 'n' bytes (error if fewer are available). NULL on success,
-	// otherwise error string.
+	// Read exactly 'n' bytes (error if fewer are available).
 	virtual error_t read( void*, long );
 	
 	// Number of bytes remaining
@@ -36,8 +29,14 @@
 	virtual error_t skip( long n );
 	
 	// to do: bytes remaining = LONG_MAX when unknown?
+	
+private:
+	// noncopyable
+	Data_Reader( const Data_Reader& );
+	Data_Reader& operator = ( const Data_Reader& );
 };
 
+// Adds seeking operations
 class File_Reader : public Data_Reader {
 public:
 	// Size of file
@@ -54,6 +53,7 @@
 	error_t skip( long n );
 };
 
+// Limit access to a subset of data
 class Subset_Reader : public Data_Reader {
 	Data_Reader* in;
 	long remain_;
@@ -63,6 +63,7 @@
 	long read_avail( void*, long );
 };
 
+// Treat range of memory as a file
 class Mem_File_Reader : public File_Reader {
 	const char* const begin;
 	long pos;
@@ -77,14 +78,23 @@
 	error_t seek( long );
 };
 
+// File reader based on C FILE
 class Std_File_Reader : public File_Reader {
-	VFSFile* file;
+	FILE* file_;
+protected:
+	void reset( FILE* f ) { file_ = f; }
+	//FILE* owned_file;
 public:
 	Std_File_Reader();
 	~Std_File_Reader();
 	
 	error_t open( const char* );
 	
+	FILE* file() const { return file_; }
+	
+	// Forward read requests to file. Caller must close file later.
+	//void forward( FILE* );
+	
 	long size() const;
 	long read_avail( void*, long );
 	
@@ -94,10 +104,8 @@
 	void close();
 };
 
+// Supports writing
 class Data_Writer {
-	// noncopyable
-	Data_Writer( const Data_Writer& );
-	Data_Writer& operator = ( const Data_Writer& );
 public:
 	Data_Writer() { }
 	virtual ~Data_Writer() { }
@@ -106,21 +114,26 @@
 	
 	// Write 'n' bytes. NULL on success, otherwise error string.
 	virtual error_t write( const void*, long n ) = 0;
-};
-
-class Null_Writer : public Data_Writer {
-public:
-	error_t write( const void*, long );
+	
+	void satisfy_lame_linker_();
+private:
+	// noncopyable
+	Data_Writer( const Data_Writer& );
+	Data_Writer& operator = ( const Data_Writer& );
 };
 
 class Std_File_Writer : public Data_Writer {
-	VFSFile* file;
+	FILE* file_;
+protected:
+	void reset( FILE* f ) { file_ = f; }
 public:
 	Std_File_Writer();
 	~Std_File_Writer();
 	
 	error_t open( const char* );
 	
+	FILE* file() const { return file_; }
+	
 	// Forward writes to file. Caller must close file later.
 	//void forward( FILE* );
 	
@@ -129,18 +142,35 @@
 	void close();
 };
 
-// to do: mem file writer
-
-// Write to block of memory
+// Write data to memory
 class Mem_Writer : public Data_Writer {
-	void* out;
-	long remain_;
-	int ignore_excess;
+	char* data_;
+	long size_;
+	long allocated;
+	enum { expanding, fixed, ignore_excess } mode;
 public:
-	// to do: automatic allocation and expansion of memory?
-	Mem_Writer( void*, long size, int ignore_excess = 1 );
+	// Keep all written data in expanding block of memory
+	Mem_Writer();
+	
+	// Write to fixed-size block of memory. If ignore_excess is false, returns
+	// error if more than 'size' data is written, otherwise ignores any excess.
+	Mem_Writer( void*, long size, int ignore_excess = 0 );
+	
 	error_t write( const void*, long );
-	long remain() const;
+	
+	// Pointer to beginning of written data
+	char* data() { return data_; }
+	
+	// Number of bytes written
+	long size() const { return size_; }
+	
+	~Mem_Writer();
+};
+
+// Written data is ignored
+class Null_Writer : public Data_Writer {
+public:
+	error_t write( const void*, long );
 };
 
 #endif
--- a/Plugins/Input/console/blargg_common.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/blargg_common.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,167 +1,241 @@
 
-// Common headers used by Shay Green's libraries
-
-// Copyright (C) 2004-2005 Shay Green.
+// Sets up common environment for Shay Green's libraries.
+//
+// To change configuration options, modify blargg_config.h, not this file.
 
 #ifndef BLARGG_COMMON_H
 #define BLARGG_COMMON_H
 
-// Allow prefix configuration file *which can re-include blargg_common.h*
-// (probably indirectly).
+// HAVE_CONFIG_H: If defined, include user's "config.h" first (which *can*
+// re-include blargg_common.h if it needs to)
 #ifdef HAVE_CONFIG_H
 	#undef BLARGG_COMMON_H
 	#include "config.h"
 	#define BLARGG_COMMON_H
 #endif
 
-#ifdef WORDS_BIGENDIAN
-# define BLARGG_BIG_ENDIAN	1
-#else
-# define BLARGG_LITTLE_ENDIAN	1
+// BLARGG_NONPORTABLE: If defined to 1, platform-specific (and possibly non-portable)
+// optimizations are used. Defaults to off. Report any problems that occur only when
+// this is enabled.
+#ifndef BLARGG_NONPORTABLE
+	#define BLARGG_NONPORTABLE 0
 #endif
 
-// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code
-#ifndef BLARGG_ENABLE_OPTIMIZER
-	#define BLARGG_ENABLE_OPTIMIZER "blargg_common.h"
-#endif
-
-// Source files have #include BLARGG_SOURCE_BEGIN at the beginning
-#ifndef BLARGG_SOURCE_BEGIN
-	#define BLARGG_SOURCE_BEGIN "blargg_source.h"
+// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
+// one must be #defined to 1. Only needed if something actually depends on byte order.
+#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
+	#if defined (MSB_FIRST) || defined (__powerc) || defined (macintosh) || defined (WORDS_BIGENDIAN)
+		#define BLARGG_BIG_ENDIAN 1
+	#else
+		#define BLARGG_LITTLE_ENDIAN 1
+	#endif
 #endif
 
 // Determine compiler's language support
 
+// Metrowerks CodeWarrior
 #if defined (__MWERKS__)
-	// Metrowerks CodeWarrior
 	#define BLARGG_COMPILER_HAS_NAMESPACE 1
 	#if !__option(bool)
 		#define BLARGG_COMPILER_HAS_BOOL 0
 	#endif
+	#define STATIC_CAST(T,expr) static_cast< T > (expr)
 
+// Microsoft Visual C++
 #elif defined (_MSC_VER)
-	// Microsoft Visual C++
 	#if _MSC_VER < 1100
 		#define BLARGG_COMPILER_HAS_BOOL 0
 	#endif
 
+// GNU C++
 #elif defined (__GNUC__)
-	// GNU C++
-	#define BLARGG_COMPILER_HAS_BOOL 1
+	#if __GNUC__ > 2
+		#define BLARGG_COMPILER_HAS_NAMESPACE 1
+	#endif
 
+// Mingw
 #elif defined (__MINGW32__)
-	// Mingw?
-	#define BLARGG_COMPILER_HAS_BOOL 1
+	// empty
 
+// Pre-ISO C++ compiler
 #elif __cplusplus < 199711
-	// Pre-ISO C++ compiler
-	#define BLARGG_COMPILER_HAS_BOOL 0
+	#ifndef BLARGG_COMPILER_HAS_BOOL
+		#define BLARGG_COMPILER_HAS_BOOL 0
+	#endif
 
 #endif
 
-// Set up boost
-#include "boost/config.hpp"
-#ifndef BOOST_MINIMAL
-	#define BOOST boost
-	#ifndef BLARGG_COMPILER_HAS_NAMESPACE
-		#define BLARGG_COMPILER_HAS_NAMESPACE 1
-	#endif
-	#ifndef BLARGG_COMPILER_HAS_BOOL
-		#define BLARGG_COMPILER_HAS_BOOL 1
-	#endif
-#endif
-
-// Bool support
-#ifndef BLARGG_COMPILER_HAS_BOOL
-	#define BLARGG_COMPILER_HAS_BOOL 1
-#elif !BLARGG_COMPILER_HAS_BOOL
+/* BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compilers.
+   If errors occur here, add the following line to your config.h file:
+	#define BLARGG_COMPILER_HAS_BOOL 0
+*/
+#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
 	typedef int bool;
 	const bool true  = 1;
 	const bool false = 0;
 #endif
 
-// Set up namespace support
-
-#ifndef BLARGG_COMPILER_HAS_NAMESPACE
-	#define BLARGG_COMPILER_HAS_NAMESPACE 0
-#endif
-
-#ifndef BLARGG_USE_NAMESPACE
-	#define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE
-#endif
-
-#ifndef BOOST
-	#if BLARGG_USE_NAMESPACE
-		#define BOOST boost
-	#else
-		#define BOOST
-	#endif
-#endif
-
-#undef BLARGG_BEGIN_NAMESPACE
-#undef BLARGG_END_NAMESPACE
-#if BLARGG_USE_NAMESPACE
-	#define BLARGG_BEGIN_NAMESPACE( name ) namespace name {
-	#define BLARGG_END_NAMESPACE }
-#else
-	#define BLARGG_BEGIN_NAMESPACE( name )
-	#define BLARGG_END_NAMESPACE
-#endif
-
-#if BLARGG_USE_NAMESPACE
+// BLARGG_USE_NAMESPACE: If 1, use <cxxx> headers rather than <xxxx.h>
+#if BLARGG_USE_NAMESPACE || (!defined (BLARGG_USE_NAMESPACE) && BLARGG_COMPILER_HAS_NAMESPACE)
+	#include <cstddef>
+	#include <cstdlib>
+	#include <cassert>
+	#include <climits>
 	#define STD std
 #else
+	#include <stddef.h>
+	#include <stdlib.h>
+	#include <assert.h>
+	#include <limits.h>
 	#define STD
 #endif
 
-// BOOST::uint8_t, BOOST::int16_t, etc.
-#include "boost/cstdint.hpp"
+// BLARGG_NEW is used in place of 'new' to create objects. By default, plain new is used.
+// To prevent an exception if out of memory, #define BLARGG_NEW new (std::nothrow)
+#ifndef BLARGG_NEW
+	#define BLARGG_NEW new
+#endif
+
+// BOOST::int8_t etc.
 
-// BOOST_STATIC_ASSERT( expr )
-#include "boost/static_assert.hpp"
+// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
+#if defined (HAVE_STDINT_H)
+	#include <stdint.h>
+	#define BOOST
+
+// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_INTTYPES_H)
+	#include <inttypes.h>
+	#define BOOST
 
-// Common standard headers
-#if BLARGG_COMPILER_HAS_NAMESPACE
-	#include <cstddef>
-	#include <cassert>
 #else
-	#include <stddef.h>
-	#include <assert.h>
+	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
+
+// BLARGG_SOURCE_BEGIN: Library sources #include this after other #includes.
+#ifndef BLARGG_SOURCE_BEGIN
+	#define BLARGG_SOURCE_BEGIN "blargg_source.h"
+#endif
+
+// BLARGG_ENABLE_OPTIMIZER: Library sources #include this for speed-critical code
+#ifndef BLARGG_ENABLE_OPTIMIZER
+	#define BLARGG_ENABLE_OPTIMIZER "blargg_common.h"
+#endif
+
+// BLARGG_CPU_*: Used to select between some optimizations
+#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86)
+	#if defined (__powerc)
+		#define BLARGG_CPU_POWERPC 1
+	#elif defined (_MSC_VER) && defined (_M_IX86)
+		#define BLARGG_CPU_X86 1
+	#endif
+#endif
+
+// 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 / ((expr) ? 1 : 0) - 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 : 0) - 1] [__LINE__] )
+	#endif
+#endif
+
+// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
+#ifndef STATIC_CAST
+	#define STATIC_CAST(T,expr) ((T) (expr))
 #endif
 
 // blargg_err_t (NULL on success, otherwise error string)
-typedef const char* blargg_err_t;
-const blargg_err_t blargg_success = 0;
-
-// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN
-#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
-	#if defined (__powerc) || defined (macintosh)
-		#define BLARGG_BIG_ENDIAN 1
-	
-	#elif defined (_MSC_VER) && defined (_M_IX86)
-		#define BLARGG_LITTLE_ENDIAN 1
-	
-	#endif
+#ifndef blargg_err_t
+	typedef const char* blargg_err_t;
 #endif
+const char* const blargg_success = 0;
 
-// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features)
-#ifndef BLARGG_NONPORTABLE
-	#define BLARGG_NONPORTABLE 0
-#endif
-#ifdef BLARGG_MOST_PORTABLE
-	#error "BLARGG_MOST_PORTABLE has been removed; see BLARGG_NONPORTABLE."
-#endif
-
-// BLARGG_CPU_*
-#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86)
-	#if defined (__powerc)
-		#define BLARGG_CPU_POWERPC 1
+// blargg_vector: Simple array that does *not* work for types with a constructor (non-POD).
+template<class T>
+class blargg_vector {
+	T* begin_;
+	STD::size_t size_;
+public:
+	blargg_vector() : begin_( 0 ), size_( 0 ) { }
+	~blargg_vector() { STD::free( begin_ ); }
+	
+	typedef STD::size_t size_type;
+	
+	blargg_err_t resize( size_type n )
+	{
+		void* p = STD::realloc( begin_, n * sizeof (T) );
+		if ( !p && n )
+			return "Out of memory";
+		begin_ = (T*) p;
+		size_ = n;
+		return 0;
+	}
 	
-	#elif defined (_MSC_VER) && defined (_M_IX86)
-		#define BLARGG_CPU_X86 1
+	void clear()
+	{
+		void* p = begin_;
+		begin_ = 0;
+		size_ = 0;
+		STD::free( p );
+	}
+	
+	size_type size() const { return size_; }
+	
+	T* begin() { return begin_; }
+	T* end()   { return begin_ + size_; }
 	
-	#endif
-#endif
+	const T* begin() const { return begin_; }
+	const T* end() const   { return begin_ + size_; }
+	
+	T& operator [] ( size_type n )
+	{
+		assert( n <= size_ ); // allow for past-the-end value
+		return begin_ [n];
+	}
+	
+	const T& operator [] ( size_type n ) const
+	{
+		assert( n <= size_ ); // allow for past-the-end value
+		return begin_ [n];
+	}
+};
 
 #endif
 
--- a/Plugins/Input/console/blargg_endian.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/blargg_endian.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,47 +1,156 @@
 
 // CPU Byte Order Utilities
 
-// Game_Music_Emu 0.2.4. Copyright (C) 2005 Shay Green. BSD license.
+// Game_Music_Emu 0.3.0
 
 #ifndef BLARGG_ENDIAN
 #define BLARGG_ENDIAN
 
-inline unsigned get_le16( const void* p ) {
-	return *((unsigned char*) p + 1) * 0x100u + *(unsigned char*) p;
+#include "blargg_common.h"
+
+#if 0
+	// Read 16/32-bit little-endian integer from memory
+	unsigned      GET_LE16( void const* );
+	unsigned long GET_LE32( void const* );
+
+	// Read 16/32-bit big-endian integer from memory
+	unsigned      GET_BE16( void const* );
+	unsigned long GET_BE32( void const* );
+
+	// Write 16/32-bit integer to memory in little-endian format
+	void SET_LE16( void*, unsigned );
+	void SET_LE32( void*, unsigned );
+	
+	// Write 16/32-bit integer to memory in big-endian format
+	void SET_BE16( void*, unsigned long );
+	void SET_BE32( void*, unsigned long );
+#endif
+
+inline unsigned get_le16( void const* p )
+{
+	return  ((unsigned char*) p) [1] * 0x100 +
+			((unsigned char*) p) [0];
+}
+
+inline unsigned get_be16( void const* p )
+{
+	return  ((unsigned char*) p) [0] * 0x100 +
+			((unsigned char*) p) [1];
 }
 
-inline void set_le16( void* p, unsigned n ) {
-	*(unsigned char*) p = n;
-	*((unsigned char*) p + 1) = n >> 8;
+inline unsigned long get_le32( void const* p )
+{
+	return  ((unsigned char*) p) [3] * 0x01000000 +
+			((unsigned char*) p) [2] * 0x00010000 +
+			((unsigned char*) p) [1] * 0x00000100 +
+			((unsigned char*) p) [0];
+}
+
+inline unsigned long get_be32( void const* p )
+{
+	return  ((unsigned char*) p) [0] * 0x01000000 +
+			((unsigned char*) p) [1] * 0x00010000 +
+			((unsigned char*) p) [2] * 0x00000100 +
+			((unsigned char*) 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, unsigned long n )
+{
+	((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [0] = (unsigned char) n;
+}
+
+inline void set_be32( void* p, unsigned long n )
+{
+	((unsigned char*) p) [0] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [3] = (unsigned char) n;
 }
 
 #ifndef GET_LE16
-
-	#if 0
-		// Read 16-bit little-endian unsigned integer from memory
-		unsigned GET_LE16( const void* );
-
-		// Write 16-bit little-endian integer to memory
-		void SET_LE16( void*, unsigned );
-	#endif
-
 	// Optimized implementation if byte order is known
 	#if BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
-		#define GET_LE16( addr )        (*(unsigned short*) (addr))
-		#define SET_LE16( addr, data )  (void (*(unsigned short*) (addr) = (data)))
+		#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_NONPORTABLE && BLARGG_CPU_POWERPC
 		// PowerPC has special byte-reversed instructions
-		#define GET_LE16( addr )        ((unsigned) __lhbrx( (addr), 0 ))
+		// to do: assumes that PowerPC is running in big-endian mode
+		#define GET_LE16( addr )        (__lhbrx( (addr), 0 ))
+		#define GET_LE32( addr )        (__lwbrx( (addr), 0 ))
 		#define SET_LE16( addr, data )  (__sthbrx( (data), (addr), 0 ))
+		#define SET_LE32( addr, data )  (__stwbrx( (data), (addr), 0 ))
+
+		#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)))
+		
+	#endif
+#endif
+
+#ifndef GET_LE16
+	#define GET_LE16( addr )        get_le16( addr )
+#endif
+
+#ifndef GET_LE32
+	#define GET_LE32( addr )        get_le32( addr )
+#endif
+
+#ifndef SET_LE16
+	#define SET_LE16( addr, data )  set_le16( addr, data )
+#endif
+
+#ifndef SET_LE32
+	#define SET_LE32( addr, data )  set_le32( addr, data )
+#endif
 
-	#else
-		#define GET_LE16( addr )        get_le16( addr )
-		#define SET_LE16( addr, data )  set_le16( addr, data )
+#ifndef GET_BE16
+	#define GET_BE16( addr )        get_be16( addr )
+#endif
+
+#ifndef GET_BE32
+	#define GET_BE32( addr )        get_be32( addr )
+#endif
+
+#ifndef SET_BE16
+	#define SET_BE16( addr, data )  set_be16( addr, data )
+#endif
 
-	#endif
+#ifndef SET_BE32
+	#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, unsigned long 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, unsigned long n ) { SET_BE32( p, n ); }
+
+inline unsigned      get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); }
+inline unsigned long get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); }
+
+inline unsigned      get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); }
+inline unsigned long get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); }
 
 #endif
 
-#endif
-
--- a/Plugins/Input/console/blargg_source.h	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/blargg_source.h	Tue Jan 24 19:10:07 2006 -0800
@@ -1,5 +1,6 @@
 
-// By default, #included at beginning of library source files
+// By default, #included at beginning of library source files.
+// Can be overridden by #defining BLARGG_SOURCE_BEGIN to path of alternate file.
 
 // Copyright (C) 2005 Shay Green.
 
@@ -16,25 +17,60 @@
 // module. A failed requirement indicates a bug outside the module.
 // void require( bool expr );
 #undef require
-#define require( expr ) assert(( "unmet requirement", expr ))
+#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.
+// nothing (not even evaluate its arguments).
 // void dprintf( const char* format, ... );
 #undef dprintf
-#define dprintf (1) ? ((void) 0) : (void)
+#ifdef BLARGG_DPRINTF
+	#define dprintf BLARGG_DPRINTF
+#else
+	inline void blargg_dprintf_( const char*, ... ) { }
+	#define dprintf (1) ? (void) 0 : blargg_dprintf_
+#endif
 
 // 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)
+#ifdef BLARGG_CHECK
+	#define check( expr ) BLARGG_CHECK( expr )
+#else
+	#define check( expr ) ((void) 0)
+#endif
 
-// If expr returns error string, return it from current function, otherwise continue.
+// If expr returns non-NULL error string, return it from current function, otherwise continue.
 #define BLARGG_RETURN_ERR( expr ) do {                          \
 		blargg_err_t blargg_return_err_ = (expr);               \
 		if ( blargg_return_err_ ) return blargg_return_err_;    \
 	} while ( 0 )
 
+// If ptr is NULL, return out of memory error string.
+#define BLARGG_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
+
+// using const references generates crappy code, and I am currenly only using these
+// for built-in types, so they take arguments by value
+
+template<class T>
+inline T min( T x, T y )
+{
+	if ( x < y )
+		return x;
+	return y;
+}
+
+template<class T>
+inline T max( T x, T y )
+{
+	if ( x < y )
+		return y;
+	return x;
+}
+
 #endif
 
--- a/Plugins/Input/console/boost/config.hpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-
-// Boost substitute. For full boost library see http://boost.org
-
-#ifndef BOOST_CONFIG_HPP
-#define BOOST_CONFIG_HPP
-
-#define BOOST_MINIMAL 1
-
-#define BLARGG_BEGIN_NAMESPACE( name )
-#define BLARGG_END_NAMESPACE
-
-#endif
-
--- a/Plugins/Input/console/boost/cstdint.hpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-
-// Boost substitute. For full boost library see http://boost.org
-
-#ifndef BOOST_CSTDINT_HPP
-#define BOOST_CSTDINT_HPP
-
-#if BLARGG_USE_NAMESPACE
-#	include <climits>
-#else
-#	include <limits.h>
-#endif
-
-BLARGG_BEGIN_NAMESPACE (boost)
-
-#include <inttypes.h>
-
-BLARGG_END_NAMESPACE
-
-#endif
-
--- a/Plugins/Input/console/boost/static_assert.hpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-
-// Boost substitute. For full boost library see http://boost.org
-
-#ifndef BOOST_STATIC_ASSERT_HPP
-#define BOOST_STATIC_ASSERT_HPP
-
-#if defined (_MSC_VER) && _MSC_VER <= 1200
-	// MSVC6 can't handle the ##line concatenation
-	#define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; }
-
-#else
-	#define BOOST_STATIC_ASSERT3( expr, line ) \
-				typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)]
-
-	#define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line )
-
-	#define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ )
-
-#endif
-
-#endif
-
--- a/Plugins/Input/console/changes.txt	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-Game_Music_Emu Change Log
-
-
-Game_Music_Emu 0.2.4
---------------------
-
-- Created a discussion forum for problems and feedback:
-http://groups-beta.google.com/group/blargg-sound-libs
-
-- Added to-do list and design notes
-
-- Added Music_Emu::skip( long sample_count ) to skip ahead in current track
-
-- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for determining the
-length of non-looped GYM and VGM files
-
-- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly using
-abs() instead of fabs()...argh)
-
-- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and now stops
-sample slightly earlier than the end, as the SNES does. Fixed a totally broken
-CPU addressing mode.
-
-- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music sounds
-decent
-
-- Fixed a minor GBS emulation bug
-
-- Fixed GYM loop point bug when track was restarted before loop point had been
-reached
-
-- Made default GBS frequency equalization less muffled
-
-- Added pseudo-surround effect removal for SPC files
-
-- Added Music_Emu::voice_names() which returns names for each voice.
-
-- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be easily
-set for library sources
-
-- Changed assignment of expansion sound chips in Nsf_Emu to be spread more
-evenly when using Effects_Buffer
-
-
-Game_Music_Emu 0.2.0
---------------------
-
-- Redid framework and rewrote/cleaned up emulators
-
-- Changed licensing to GNU Lesser General Public License (LGPL)
-
-- Added Sega Genesis GYM and Super Nintendo SPC emulators
-
-- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator
-
-- Eliminated use of static mutable data in emulators, allowing multi-instance
-safety
-
-
-Game_Music_Emu 0.1.0
---------------------
-
-- First release
--- a/Plugins/Input/console/design.txt	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-Game_Music_Emu Design Notes
-
-
-Managing Complexity
--------------------
-Complexity has been a factor in most library decisions. Many features
-have been passed by due to the complexity they would add. Once past a
-certain level, complexity prevents mentally grasping the library in its
-entirety, at which point more defects will occur and be hard to find.
-
-I chose 16-bit signed samples because it seems to be the most common
-format. Supporting multiple formats would add too much complexity to be
-worth it. Other formats can be obtained via conversion.
-
-I've kept interfaces fairly lean, leaving many possible features
-untapped but easy to add if necessary. For example the classic emulators
-could have volume and frequency equalization adjusted separately for
-each channel, since they each have an associated Blip_Synth.
-
-Source files of 400 lines or less seem to be the best size to limit
-complexity. In a few cases there is no reasonable way to split longer
-files, or there is benefit from having the source together in one file.
-
-
-Library Configuration
----------------------
-Library optimizations can be configured through macros defined in
-config.h. By default, the library is configured to be most likely to
-compile and work on any platform, rather than be most optimal with
-increased chance of problems. It's easier to track down optimiztation
-problems if the library can first be shown to work correctly without
-them.
-
-
-Flexibility through indirection
--------------------------------
-I've tried to allow the most flexibility of modules by using indirection
-to allow extension by the user. This keeps each module simple and more
-focused on its unique task.
-
-The classic emulators use Multi_Buffer, which potentially allows a
-separate Blip_Buffer for each channel. This keeps emulators free of
-typical code to allow output in mono, stereo, panning, etc.
-
-All emulators use a reader object to access file data, allowing it to be
-stored in a regular file, compressed archive, memory, or generated
-on-the-fly. Again, the library can be kept free of the particulars of
-file access and changes required to support new formats.
-
-
-Choice of pre-ISO (ARM) C++
----------------------------
-The library started out as an unreleased NSF player written using ISO
-C++. The code was clean enough that I decided to release it as a player
-library. Before release I evaluated its use of C++ features to determine
-the important ones.
-
-Namespaces and exceptions weren't essential, so I compared a version of
-the library with and without them. I decided that I could do without and
-get better compatibility with older compilers or newer ones with buggy
-namespace and exception implementations.
-
-Templates are the worst area for most compilers, due to their inherent
-complexity, but they are too useful to avoid entirely. I've used them
-sparingly and in ways that compilers are more likely to work with.
-
-Sticking to ARM C++ has helped keep the library simpler to understand.
-
-
-Platform-specific optimization
-------------------------------
-Performance profiling doesn't shown any big bottlenecks that warrant
-heavy platform-specific optimization. The main bottlenecks are CPU
-emulation, Blip_Buffer synthesis and sample reading, Super NES DSP, Sega
-Genesis FM, and Fir_Resampler.
-
-Further optimization of the CPU emulators can probably only be achieved
-by writing them in assembly or using dynamic recompilation. Blip_Buffer
-might benefit somewhat from vector instructions. Sega Genesis FM
-synthesis can probably be made twice as fast by someone who fully
-understands its operation (I am almost clueless about its internals).
-The Super NES DSP might have some room for optimization. Fir_Resampler
-would benefit greatly from vector operations.
-
-Most of the above optimizations add more complexity than they are worth.
-All that seems worthwhile is optimization of Sega Genesis FM emulation
-and Fir_Resampler.
-
-
-Preventing Bugs
----------------
-I've done many things to reduce the opportunity for defects. A general
-principle is to write code so that defects will be as visible as
-possible. I've used several techniques to achieve this.
-
-I put assertions at key points where defects seem likely or where
-corruption due to a defect is likely to be visible. I've also put
-assetions where violations of the interface are likely. In emulators
-where I am unsure of exact hardware operation in a particular case, I
-output a debug-only message noting that this has occurred; many times I
-haven't implemented a hardware feature because nothing uses it. I've
-made code brittle where there is no clear reason flexibility; code
-written to handle every possibility sacrifices quality and reliability
-to handle vaguely defined situations.
-
-
-Miscellaneous
--------------
-I don't like naming header files with a ".hpp" suffix for some reason.
-They aren't as visually distinct from ".cpp" source files. The ".cpp"
-suffix is useful to inform the compiler that it's a C++ file rather than
-a C file, but I don't know of significant practical benefits the ".hpp"
-suffix gives over ".h" (I used to use *no* suffix for header files, but
-this does cause problems).
-
-When implementation has to be put in a header file, it's as far near the
-end as possible to prevent distraction from the public interface.
-
-
-CPU Cores
----------
-I've spent lots of time coming up with techniques to optimize the CPU
-cores. Some of the most important: execute multiple instructions during
-an emulation call, keep state in local variables to allow register
-assignment, optimize state representation for most common instructions,
-defer status flag calculation until actually needed, read program code
-directly without a call to the memory read function, always pre-fetch
-the operand byte before decoding instruction, and emulate instructions
-using common blocks of code.
-
-I've successfully used Nes_Cpu in a simple NES emulator, and I'd like to
-make all the CPU emulators suitable for use in emulators. It seems a
-waste for them to be used only for the small amount of emulation
-necessary for game music files.
-
-I debugged the CPU cores by writing a test shell that ran them in
-parallel with other CPU cores and compared all memory accesses and
-processor states at each step. This provided good value at little cost.
-
-The CPU mapping page size is adjustable to allow the best tradeoff
-between memory/cache usage and handler granularity. The interface allows
-code to be somewhat independent of the page size.
-
-I optimize program memory accesses to to direct reads rather than calls
-to the memory read function. My assumption is that it would be difficult
-to get useful code out of hardware I/O addresses, so no software will
-intentionally execute out of I/O space. Since the page size can be
-changed easily, most program memory mapping schemes can be accommodated.
-This greatly reduces memory access function calls.
-
-
-Sub-Libraries
--------------
-I've also released the sound cores as individual libraries to reduce
-complexity for emulator authors who just want a single sound core. These
-authors will be using the sound cores directly, while users of this
-music emulator library won't even see them, so documentation and demos
-can be specific to each library.
-
-
-Documentation
--------------
-I started out with separate documentation in HTML and found that it
-wasn't going to be easy to maintain. I switched to putting descriptions
-of function behavior in header files before the function declarations.
-This has worked well so far.
-
-I think the concrete executable demo code helps the most when someone is
-using the library for the first time, since it shows a complete program
-and provides a framework for using the library. By recording to a sound
-file, I can keep the code portable, and the result can be listened to or
-examined closely, which is important for something real-time like sound.
-
-
-I want to write some tutorials to complement the demo code, desribing
-the basic framework and operation of the modules.
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/gme_notes.txt	Tue Jan 24 19:10:07 2006 -0800
@@ -0,0 +1,181 @@
+Game_Music_Emu 0.3.0 Notes
+--------------------------
+Author : Shay Green <hotpop.com@blargg>
+Website: http://www.slack.net/~ant/
+Forum  : http://groups.google.com/group/blargg-sound-libs
+
+
+Overview
+--------
+This library is composed of several independent game music emulators
+derived from the common Music_Emu interface. Each emulator can load a
+game music file and play from any track. To play a game music file, do
+the following:
+
+- Determine file's type
+- Create appropriate emulator
+- Set sample rate
+- Load file into emulator
+- Start desired track
+- When samples are needed, call play()
+- When done, delete emulator
+
+See Music_Emu.h for reference.
+
+
+Information Fields
+------------------
+Game music files include text fields with information about the game and
+track. These are stored in the file's header or in an embedded block.
+Text fields in most game music formats do *not* have a nul terminator if
+the string completely fills the field. The simplest way to handle this
+is to copy the string out and manually add a nul terminator at the end.
+
+This library is currently focused only on actual emulation, so it
+doesn't provide a common interface to the different schemes each game
+music file format uses. Refer to the file's official specification for
+further information.
+
+
+Modular Construction
+--------------------
+The library is made of many fairly independent modules. If you're using
+only one music file emulator, you can eliminate many of the library
+sources from your program. Refer to the files list in readme.txt to get
+a general idea of what can be removed. Post to the forum if you'd like
+me to put together a smaller version for a particular use, as this only
+takes me a few minutes to do.
+
+If you want to use one of the individual sound chip emulators in your
+console emulator, first check the libraries page on my website since I
+have released several of them as standalone libraries with included
+documentation and examples on their use.
+
+The "classic" sound chips use my Blip_Buffer library, which greatly
+simplifies their implementation and efficiently handles band-limited
+synthesis. It is also available as a standalone library with
+documentation and many examples.
+
+
+Sound Parameters
+----------------
+All emulators support adjustable output sampling rate, set with
+Music_Emu::set_sample_rate(). A rate of 44100 should work well on most
+systems. Since band-limited synthesis is used, a sampling rate above
+48000 Hz is not necessary. 
+
+Some emulators support adjustable treble and bass frequency equalization
+(NSF, GBS, VGM) using Music_Emu::set_equalizer(). Parameters are
+specified using Music_Emu::equalizer_t eq = { treble_dB, bass_freq }.
+Treble_dB sets the treble level (in dB), where 0.0 dB gives normal
+treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble for an
+extra crisp sound. Bass_freq sets the frequency where bass response
+starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, and 15000
+Hz removes all bass. For example, the following makes the sound
+extra-crisp but lacking bass:
+
+	Music_Emu::equalizer_t eq = { 5.0, 1000 };
+	music_emu->set_equalizer( eq );
+
+Each emulator's equalization defaults to a profile that approximates its
+particular console's sound quality; this default can be determined by
+calling Music_Emu::equalizer() just after creating the emulator. Some
+emulators include other profiles for different versions of the system.
+The Music_Emu::tv_eq profile gives sound as if coming from a TV speaker.
+For example, to use Famicom sound equalization with the NSF emulator, do
+the following:
+
+	nsf_emu->set_equalizer( Nsf_Emu::famicom_eq );
+
+
+VGM/GYM YM2413 & YM2612 FM Sound
+--------------------------------
+The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
+chip emulator based on Gens. Because this has some inaccuracies, other
+YM2612 emulators can be used in its place by reimplementing the
+interface in YM2612_Emu.h. Available on my website is a modified version
+of MAME's YM2612 emulator, which sounds better in some ways and whose
+author is still making improvements.
+
+VGM music files using the YM2413 FM sound chip are also supported, but a
+YM2413 emulator isn't included. Similar to above, I have put one of the
+available YM2413 emulators on my website that can be used directly.
+
+
+Misc
+----
+Some emulators have constructor parameters which can be specified when
+creating the object. For example, this creates a Vgm_Emu with
+oversampling off and a tempo of 83%:
+
+	Vgm_Emu* emu = new Vgm_Emu( false, 0.83 );
+
+For a full example of using Game_Music_Emu see the source code for Game
+Music Box, a full-featured game music player for Mac OS:
+
+	http://www.slack.net/~ant/game-music-box/dev.html
+
+
+Thanks
+------
+Big thanks to Chris Moeller (kode54) for help with library testing and
+feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
+for original work on openspc++ that was used when developing Spc_Emu.
+Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
+start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, and the
+Audacious team for testing and using the library in their game music
+players.
+
+
+Solving Problems
+----------------
+If you're having problems, try the following:
+
+- 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.
+
+
+Error handling
+--------------
+Functions which can fail have a return type of blargg_err_t, which is a
+pointer to an error string (const char*). If the function is successful
+it returns blargg_success (NULL), otherwise it returns a pointer to an
+error string. Errors which the caller can easily detect are only checked
+with debug assertions; blargg_err_t returns 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).
+
+To allow compatibility with older C++ compilers, no exceptions are
+thrown by any of the modules and code is generally exception-safe. Any
+exceptions thrown by the standard library or caller-supplied functions
+are allowed to propagate normally.
+
+
+Configuration
+-------------
+The header "blargg_common.h" is used to establish a common environment,
+and is #included at the beginning of all library headers and sources. It
+attempts to automatically determine the features of the environment, but
+might need help. Refer to "blargg_common.h" for descriptions of
+features.
+
+If defined HAVE_CONFIG_H in the compiler command-line, the user-provided
+"config.h" is included at the beginning of each library header file,
+allowing configuration options for the library to be set. I have
+attempted to design the library so that configuration can be done
+*without* modifying any of the library sources and header files. This
+makes it easy to upgrade to a new version without losing any
+customizations to its configuration.
+
+Post to the forum if you have problems or suggestions.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/gme_readme.txt	Tue Jan 24 19:10:07 2006 -0800
@@ -0,0 +1,136 @@
+Game_Music_Emu 0.3.0: Game Music Emulators
+------------------------------------------
+Game_Music_Emu is a collection of portable video game music emulators. Its
+modular design allows elimination of any unneeded emulators and features.
+Modules are included supporting the following file formats:
+
+GBS     Nintendo Game Boy
+VGM/VGZ Sega Master System/Genesis/Mega Drive/Mark III/BBC Micro
+GYM     Sega Genesis
+SPC     Super Nintendo
+NSF     Nintendo NES (with VRC6, N106, and FME-7 sound)
+
+This library has been used in game music players for Win32, Linux x86-32/64,
+Mac OS X, Mac OS Classic, MorphOS (Amiga), PlayStation Portable, and GP2X.
+
+Author : Shay Green <hotpop.com@blargg>
+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/basics.cpp, demo/Wave_Writer.cpp, and all
+source files in gme/ except Gzip_File.cpp. Be sure "test.nsf" is in the same
+directory. Running the program should generate a WAVE sound file "out.wav" of
+music.
+
+See notes.txt for more information, and respective header (.h) files for
+reference. Post to the discussion forum for assistance.
+
+
+Files
+-----
+notes.txt               General notes about the library
+changes.txt             Changes made since previous releases
+design.txt              Library design notes
+LGPL.txt                GNU Lesser General Public License
+
+test.nsf                Test file for NSF emulator
+
+demo/
+  basics.cpp            Loads game music file and records to wave sound file
+  info_fields.cpp       Reads information tags from files
+  multi_format.cpp      Handles multiple game music types
+  custom_reader.cpp     Loads music data from gzip file and memory block
+  stereo_effects.cpp    Uses Effects_Buffer to add stereo echo
+
+  simple_player.cpp     Uses Music_Player to make simple player
+  Music_Player.cpp      Simple game music player module using SDL sound
+  Music_Player.h
+
+  Wave_Writer.h         WAVE sound file writer used for demo output
+  Wave_Writer.cpp
+
+gme/
+  Effects_Buffer.h      Sound buffer with adjustable stereo echo and panning
+  Effects_Buffer.cpp
+
+  Gzip_File.h           Gzip reader for transparent access to gzipped files
+  Gzip_File.cpp
+
+  Music_Emu.h           Game music emulator interface
+
+  Nsf_Emu.h             Nintendo NES NSF emulator
+  Nsf_Emu.cpp
+  Nes_Apu.cpp
+  Nes_Apu.h
+  Nes_Cpu.cpp
+  Nes_Cpu.h
+  Nes_Oscs.cpp
+  Nes_Oscs.h
+  Nes_Fme7_Apu.cpp
+  Nes_Fme7_Apu.h
+  Nes_Namco_Apu.cpp
+  Nes_Namco_Apu.h
+  Nes_Vrc6_Apu.cpp
+  Nes_Vrc6_Apu.h
+
+  Gbs_Emu.h             Nintendo Game Boy GBS emulator
+  Gbs_Emu.cpp
+  Gb_Apu.cpp
+  Gb_Apu.h
+  Gb_Cpu.cpp
+  Gb_Cpu.h
+  Gb_Oscs.cpp
+  Gb_Oscs.h
+
+  Spc_Emu.h             Super Nintendo SPC emulator
+  Spc_Emu.cpp
+  Snes_Spc.cpp
+  Snes_Spc.h
+  Spc_Cpu.cpp
+  Spc_Cpu.h
+  Spc_Dsp.cpp
+  Spc_Dsp.h
+  Fir_Resampler.cpp
+  Fir_Resampler.h
+
+  Gym_Emu.h             Sega Genesis GYM emulator
+  Gym_Emu.cpp
+  Vgm_Emu.h             Sega VGM emulator
+  Vgm_Emu_Impl.cpp
+  Vgm_Emu_Impl.h
+  Vgm_Emu.cpp
+  Ym2413_Emu.cpp
+  Ym2413_Emu.h
+  Sms_Apu.cpp           Common Sega emulator files
+  Sms_Apu.h
+  Sms_Oscs.h
+  Ym2612_Emu.cpp
+  Ym2612_Emu.h
+  Dual_Resampler.cpp
+  Dual_Resampler.h
+  Fir_Resampler.cpp
+  Fir_Resampler.h
+  
+  blargg_common.h       Common files
+  blargg_endian.h
+  blargg_source.h
+  Blip_Buffer.cpp
+  Blip_Buffer.h
+  Music_Emu.cpp
+  Classic_Emu.h
+  Classic_Emu.cpp
+  Multi_Buffer.h
+  Multi_Buffer.cpp
+  abstract_file.cpp
+  abstract_file.h
+
+
+Legal
+-----
+Game_Music_Emu library copyright (C) 2003-2006 Shay Green.
+SNES SPC DSP emulator based on OpenSPC, copyright (C) 2002 Brad Martin.
+Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
--- a/Plugins/Input/console/notes.txt	Tue Jan 24 13:57:22 2006 -0800
+++ b/Plugins/Input/console/notes.txt	Tue Jan 24 19:10:07 2006 -0800
@@ -1,220 +1,77 @@
-Game_Music_Emu Notes
-
-
-Architecture
-------------
-
-This library has several emulator classes derived from common interface
-classes. Music_Emu specifies the main interface, and Classic_Emu adds
-features available only for "classic" systems (frequency equalization
-and customizable multi-channel sound buffer).
-
-To play a given game music file, do the following:
-
-- Determine its file type
-- Create and set up an appropriate emulator
-- Load file header and file data into emulator
-- Start desired track
-- When samples are needed, call play()
-- When done, delete emulator
-
-Each emulator type defines a nested header_t structure type with members
-for the file header. When loading a file, the emulators expect the file
-header to have already been loaded; this allows the caller to use header
-fields like the game name and music author.
+Audacious Console Game Music Driver
+-----------------------------------
+Contact: Shay Green <hotpop.com@blargg>
 
-See Music_Emu.h and Classic_Emu.h for reference.
-
-
-Error handling
---------------
-
-Functions which can fail have a return type of blargg_err_t, which is a
-pointer to an error string (const char*). If the function is successful
-it returns blargg_success (NULL), otherwise it returns a pointer to an
-error string.
+Notes
+-----
+- This is a fairly rough version. I'm sending it so we can decide more
+concretely on the desired features. I don't have Unix (or even Mac OS X)
+so I've only tested this lightly with a quick framework I wrote to
+simulate the Audacious environment (as best as I could determine based
+on the limited documentation).
 
-To allow compatibility with older C++ compilers, no exceptions are
-thrown by any of the modules. The library is exception-safe, and any
-exceptions which occur are not intercepted.
+- The most significant missing feature is a way to select the track
+number of multi-track formats (NSF, NSFE, GBS). I've implemented
+internal support for this and marked the places where the track number
+is needed from an external source.
 
-Due to the different ways compiler runtime libraries handle
-out-of-memory errors, if the library encounters one it will be reported
-in one of two ways: if the compiler is configured to throw an exception
-when operator new can't satisfy a request (which is the case in ISO
-C++), it is allowed to propagate normally, otherwise the error string
-"Out of memory" is returned.
-
-Significant violations of the documented interface are flagged with
-debug-only assertions. Failure of these usually indicates a caller error
-rather than a defect in the library.
-
+- Seeking should be tested carefully. It might be too slow for some
+formats.
 
-Configuration
--------------
-
-The header "blargg_common.h" is used to establish a common environment.
-It attempts to automatically determine the features of the environment,
-but might need help.
-
-If HAVE_CONFIG_H is defined, the file "config.h" is included at the
-beginning of each library header file, allowing configuration options
-for the library to be set. It's fine if other libraries also use this
-scheme, as they won't conflict.
+- Currently text fields are treated as they are already in UTF-8 format
+(which they aren't), but they should probably be converted from Windows
+charset to UTF-8. VGM files have 16-bit chars in an unknown encoding,
+currently just truncated to 8-bits.
 
-Some libraries depend on the order of bytes in multibyte types. These
-will cause a compilation error if the order can't be determined. If this
-occurs, define the appropriate symbol. For big-endian (most significant
-byte first, i.e. Motorola 68000, PowerPC), #define BLARGG_BIG_ENDIAN to
-1. For little-endian (least significant byte first, i.e. Intel x86),
-#define BLARGG_LITTLE_ENDIAN to 1.
-
-Pre-ISO C++ compilers might not support bool. Support is provided where
-bool is not available, but the compiler's support of bool might not be
-properly determined. If errors occur in "blargg_common.h" in the bool
-section, #define BLARGG_COMPILER_HAS_BOOL to 1 if your compiler supports
-bool, otherwise 0.
+- Errors in Audacious_Driver.cpp are checked and generally result in
+exit of the current operation and no overall effect. Information about
+the cause of the error is consistently lost, so it would be difficult to
+switch over to a model of actually reporting the error so the user can
+know about it and take useful action.
 
-If your compiler supports namespaces, blargg_common.h uses standard
-headers with the "c" prefix to avoid bringing names from std into the
-global namespace. If your compiler supports namespaces but this isn't
-being detected by blargg_common.h, #define BLARGG_COMPILER_HAS_NAMESPACE
-to 1 in your config.h file.
-
-If you have any problems with "blargg_common.h", contact me.
-
-
-Game Music File Handling
-------------------------
+- Each track may contain any of the following: preferred play length,
+intro length, loop length. If play length is present, it is used as the
+track time. If not present, the default play length from the config file
+is used. A more sophisticated algorithm could be used that takes into
+account the loop length.
 
-Game music files include text fields with information about the game and
-track. Each emulator's header_t defines the basic fields (game, song,
-author, copyright, track_count) supported by that music type, or has an
-enum { field = 0 } if unsupported. This allows the same code to be used
-for parsing each header if it checks that the field is non-zero.
-
-Text fields in most game music formats won't have a nul terminator if
-the string completely fills the field. The demos show one way to handle
-this.
-
-This library is focused on playback only and the emulators don't parse
-extended text fields, since this can be complex for some formats and can
-be done by the caller. To load the NSFE format, it must be parsed first
-and an NSF header must be made in memory and passed Nsf_Emu. See the
-Game Music Box source code for an example of this:
-
-    http://www.slack.net/~ant/game-music-box/dev.html 
+- File opening and reading has been significantly minimized. Bytes read
+for file identification are preserved for when the rest of header is
+read. When playing a track, file information is obtained using
+already-loaded data in the emulator, eliminating extra reading.
 
 
-Frequency equalization
-----------------------
+Change Log
+----------
+- Marked things to be addressed with "// to do:" comments
 
-The classic emulators allow frequency equalization to be adjusted;
-Classic_Emu::equalizer_t( treble, cutoff, bass ) specifies low-pass and
-high-pass filtering.
-
-Low-pass is an exponential rolloff beginning at 'cutoff' Hz and
-attenuating by 'treble' dB at 22kHz. For example, with cutoff = 8000 Hz
-and treble = -6 dB, the following results:
+- Updated to Game_Music_Emu 0.3.0 and eliminated unnecessary source
+files
 
-        cutoff = 8kHz
-  0dB -------*__                
-	            ~~--__ treble = -6dB
-	                  ~~-*__
-	                        ~~--__
-	                              ~~--__
-	                                    ~~--__
--18dB - - - - - - - - - - - - - - - - - - - - - -
-	  0      8kHz      22kHz               44kHz ...
+- Eliminated Audacious_Driver.h since it served no purpose
 
+- Added support for NSFE files
 
-High-pass is a steep rolloff which passes -3dB attenuation at
-'breakpoint' Hz, where useful frequencies range from 0 to 6000 Hz.  For
-example, with breakpoint = 1000 Hz, the following results:
+- Added Vfs_File, a wrapper for the vfs_* file functions. This allows
+all the emulators to access files in this manner.
 
-      breakpoint = 1000 Hz
- 0dB                 ___________
--3dB      ,_*---~~~~~       
-        _~
-       /
-	  /
-      |
-      |
--21dB - - - - - - - - - - - - -
-	  0   1000 Hz            4000 Hz
+- Updated Makefile.am but didn't add line to link with zlib
+
+- Added fading at end of tracks
 
-Each emulator defaults to a profile that approximates its particular
-console's sound quality; this default can be determined by getting the
-current equalization just after creating the emulator. See Classic_Emu.h
-for reference.
-
+- Added end-of-track silence detection for tracks without timing
+information. Stops track when 6 seconds of silence have passed, but
+looks ahead so that the user only experiences about 1 second of silence
+before the track ends.
 
-Emulator gain control
----------------------
-
-Each emulator allows its gain to be adjusted. The default gains are
-selected to give consistent relative volumes between emulators without
-resulting in excessive clamping of samples that would otherwise go
-beyond the 16-bit range. A gain of 1.0 results in a conservative volume
-that rarely requires any clamping to stay within the 16-bit sample
-range. Clamping samples to 16 bits is handled by the library.
+- Added beginning-of-track silence removal, as some tracks have many
+seconds of silence (Zelda Link's Awakening.gbs track 61 has 20 seconds
+of silence, making you think it's not a music track without this
+feature).
 
 
-Output sample rate
-------------------
-
-Each emulator has a way of specifying the output sample rate during
-initialization. All the emulators use internal band-limiting, so there
-is no reason to use a sample rate above 48kHz unless the sound hardware
-demands it; 44-48kHz will yield the best results. You could use the
-following code to choose a rate that is both near this range and an
-integral division of the native rate:
-
-	long adjust_rate( double native )
-	{
-		for ( double divider = 1; divider <= 4; divider++ )
-		{
-			long adjusted = native / divider + 0.5;
-			if ( adjusted <= 56000 )
-				return adjusted;
-		}
-		return 44100; // give up; CD rate probably works well enough
-	}
-
-
-Interface conventions
-----------------------
-
-If a function will keep a pointer to an object passed, to make this
-clear in source code it takes a pointer rather than a reference.
+To Do
+-----
+- Separate track info handling from Audacious_Driver.cpp, since the
+current complexity is a good source of bugs
 
-Multi-word names have an underscore '_' separator between individual
-words.
-
-Functions are named with lowercase words. Functions which perform an
-action with side-effects are named with a verb phrase (i.e. load, move,
-run). Functions which set or return the value of a piece of state are
-named using a noun phrase (i.e. loaded, moved, running).
-
-Classes are named with capitalized words. Only the first letter of an
-acronym is capitalized. Class names are nouns, sometimes suggestive of
-what they do (i.e. File_Scanner).
-
-Structure, enumeration, and typedefs to these and built-in types are
-named using lowercase words with a _t suffix.
-
-Macros are named with all-uppercase words.
-
-Internal names which can't be hidden due to technical reasons have an
-underscore '_' suffix.
-
-
-Misc
-----
-
-Special thanks to Chris Moeller (kode54) for help with library testing
-and feedback. His openspc++ library in C++ was an essential starting
-point and framework for developing the SPC emulator. Brad Martin's
-excellent SNES DSP emulator (also part of openspc++) provided an
-essential foundation for the DSP core.
-
--- a/Plugins/Input/console/readme.txt	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-Game_Music_Emu 0.2.4: Multi-Format Game Music Emulation Library
-
-
-Game_Music_Emu is a collection of portable video game music emulators for the
-following file formats: Nintendo NSF, Game Boy GBS, Sega Master System VGM,
-Sega Gensesis GYM, and Super Nintendo SPC.
-
-Licensed under the GNU Lesser General Public License (LGPL); see LGPL.txt.
-Copyright (C) 2003-2005 Shay Green. SNES SPC DSP emulator based on OpenSPC,
-Copyright (C) 2002 Brad Martin. Sega Genesis YM2612 emulator from Gens project,
-Copyright (C) 2002 Stephane Dallongeville.
-
-Website: http://www.slack.net/~ant/libs/
-Forum  : http://groups-beta.google.com/group/blargg-sound-libs
-Contact: hotpop.com@blargg (swap to e-mail)
-
-
-Getting Started
----------------
-
-This library is written in somewhat conservative C++ that should compile with
-current and older compilers (ANSI/ISO and ARM).
-
-If the Boost library is installed in your environment, delete the included
-"boost" compatibility directory, otherwise add the included "boost" directory
-to your compiler's search paths.
-
-Build a program consisting of the included source files except demo_effects.cpp
-and demo_panning.cpp, and any necessary system libraries. Be sure "test.nsf" is
-in the same directory. The program should generate a WAVE sound file "out.wav"
-of music.
-
-For a full example of using Game_Music_Emu in a music player, see the Game
-Music Box source code: http://www.slack.net/~ant/game-music-box/dev.html
-
-See notes.txt for more information, and respective header (.h) files for
-reference. Visit the discussion forum to get assistance.
-
-
-Files
------
-
-notes.txt               Collection of notes about the library
-changes.txt             Changes made since previous releases
-todo.txt                Planned improvements and fixes
-design.txt              Library design notes
-LGPL.TXT                GNU Lesser General Public License
-
-demo.cpp                Record NSF to WAVE sound file using emulator
-demo_effects.cpp        Use Effects_Buffer while recording GBS file
-demo_panning.cpp        Use Panning_Buffer while recording VGM file
-test.nsf                Test file for NSF emulator
-
-Music_Emu.h             Game music emulator interface
-Spc_Emu.h               Super NES SPC emulator
-Gym_Emu.h               Sega Genesis GYM emulator
-
-Classic_Emu.h           "Classic" game music emulator interface
-Nsf_Emu.h               Nintendo NSF emulator
-Gbs_Emu.h               Game Boy GBS emulator
-Vgm_Emu.h               Sega Master System VGM emulator
-
-Multi_Buffer.h          Mono and stereo buffers for classic emulators
-Effects_Buffer.h        Effects buffer for classic emulators
-Panning_Buffer.h        Panning buffer for classic emulators
-
-blargg_common.h         Common library source
-blargg_endian.h
-blargg_source.h
-Blip_Buffer.cpp
-Blip_Buffer.h
-Blip_Synth.h
-Music_Emu.cpp
-Classic_Emu.cpp
-Multi_Buffer.cpp
-Effects_Buffer.cpp
-Panning_Buffer.cpp
-Fir_Resampler.cpp
-Fir_Resampler.h
-abstract_file.cpp
-abstract_file.h
-Nes_Apu.cpp             NSF emulator source
-Nes_Apu.h
-Nes_Cpu.cpp
-Nes_Cpu.h
-Nes_Oscs.cpp
-Nes_Oscs.h
-Nsf_Emu.cpp
-Nes_Namco.cpp
-Nes_Namco.h
-Nes_Vrc6.cpp
-Nes_Vrc6.h
-Tagged_Data.h
-Gbs_Emu.cpp             GBS emulator source
-Gb_Apu.cpp
-Gb_Apu.h
-Gb_Cpu.cpp
-Gb_Cpu.h
-Gb_Oscs.cpp
-Gb_Oscs.h
-Sms_Apu.cpp             VGM emulator source
-Sms_Apu.h
-Sms_Oscs.h
-Vgm_Emu.cpp
-Gym_Emu.cpp             GYM emulator source
-ym2612.cpp
-ym2612.h
-Spc_Emu.cpp             SPC emulator source
-Snes_Spc.cpp
-Snes_Spc.h
-Spc_Cpu.cpp
-Spc_Cpu.h
-Spc_Dsp.cpp
-Spc_Dsp.h
-
-boost/                  Substitute for boost library if it's unavailable
-
-Wave_Writer.hpp         WAVE sound file writer used for demo output
-Wave_Writer.cpp
-
--- 
-Shay Green <hotpop.com@blargg> (swap to e-mail)
--- a/Plugins/Input/console/todo.txt	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-Game_Music_Emu 0.2.4: Problems and planned improvements
-
-
-Interface
----------
-
-- Refer to sound chip oscillators as channels, since Nes_Channel was
-renamed a long time ago.
-
-- Rename setters with set_ prefix. I decided the terseness wasn't worth
-it.
-
-- Consider putting buffer pointer before count in play() etc. Only
-reason I put count first is to allow making buffer pointer default to
-NULL for skip. But it seems better to have skip() a separate function.
-One other reason to put count first is to follow the left-to-right
-principle in organizing arguments: play( count, buf ) generates 'count'
-samples and writes them into 'buf', and count is a more significant
-parameter than the buffer to write to.
-
-- VGM, GYM: change track_length() to return length and start of loop?
-
-
-Misc
-----
-
-- NSF: Partial implementation of DAC non-linearity causes possible
-drift. Messes up saw wave demo at high frequencies.
-
-- GYM: See if ignoring PCM when it's disabled eliminates pop in some
-tracks.
-
-- All CPU emulators: what if instruction straddles page boundary? Might
-need to completely avoid reading words from code memory.
-
-- Test reloading a new file into emulator after already loading one
-
-- Optimize resampling in Sega Genesis GYM emulator
-
-- Improve GBS emulation
-
-- Improve GYM PCM channel emulation
-
-- SPC: Finish KON and KOFF reverse-engineering and incorporate into
-Spc_Dsp
-
-- GBS: Crystalis track 18 plays really fast. Does same in gbsplay 0.7.
-
-- GBS: Ultima 3 overflows stack. Messes up in gbsplay 0.7.
-
-- Set track_ended flag when emulation error occurs (log emulation error
-only in debug mode).
-
-- Change wave and noise Gb_Oscs to blip_good_quality?
-
-- VGM: Support Sega Genesis
-
-- VGM: Parse extended header
-
-- VGM: Support new version of VGM file format that might be released
-soon
-
-- GYM: Check Altered Beast tracks with samples. They cut off seemingly
-too soon.
-
-- Include example of how to use NSF non-linear handling (with note about
-limitations)
-
-- Keep SP and PC as 32-bit in CPU registers structure so emulator can
-detect overflow/underflow and halt emulation, rather than having them
-masked when written back?
-
--- a/Plugins/Input/console/ym2612.cpp	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1328 +0,0 @@
-
-// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
-
-#include "ym2612.h"
-
-#include <string.h>
-#include "libaudacious/vfs.h"
-#include <math.h>
-
-/* Copyright (C) 2002 Stéphane Dallongeville (gens@consolemul.com) */
-/* Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-// This is mostly the original source in its C style and all.
-//
-// Somewhat optimized and simplified. Uses a template to generate the many
-// variants of Update_Chan. Rewrote header file. In need of full rewrite by
-// someone more familiar with FM sound and the YM2612. Has some inaccuracies
-// compared to the Sega Genesis sound, particularly being mixed at such a
-// high sample accuracy (the Genesis sounds like it has only 8 bit samples).
-// - Shay
-
-const int max_length = 3072; // A little over 4 frames
-
-const int output_bits = 14;
-
-typedef struct YM2612_slot_t {
-	const int *DT;  // parametre detune
-	int MUL;    // parametre "multiple de frequence"
-	int TL;     // Total Level = volume lorsque l'enveloppe est au plus haut
-	int TLL;    // Total Level ajusted
-	int SLL;    // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression
-	int KSR_S;  // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
-	int KSR;    // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer
-				// sur les differents parametres de l'enveloppe comme l'attaque, le decay ...  comme dans la realite !
-	int SEG;    // Type enveloppe SSG
-	int env_xor;
-	int env_max;
-
-	const int *AR;  // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR])
-	const int *DR;  // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR])
-	const int *SR;  // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR])
-	const int *RR;  // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR])
-	int Fcnt;   // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16])
-	int Finc;   // frequency step = pas d'incrementation du compteur-frequence
-				// plus le pas est grand, plus la frequence est aïgu (ou haute)
-	int Ecurp;  // Envelope current phase = cette variable permet de savoir dans quelle phase
-				// de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
-				// en fonction de la valeur de cette variable, on va appeler une fonction permettant
-				// de mettre à jour l'enveloppe courante.
-	int Ecnt;   // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
-	int Einc;   // Envelope step courant
-	int Ecmp;   // Envelope counter limite pour la prochaine phase
-	int EincA;  // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
-				// cette valeur est egal à AR[KSR]
-	int EincD;  // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
-				// cette valeur est egal à DR[KSR]
-	int EincS;  // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
-				// cette valeur est egal à SR[KSR]
-	int EincR;  // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
-				// cette valeur est egal à RR[KSR]
-	int *OUTp;  // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree
-				// d'un autre ou carrement à la sortie de la voie
-	int INd;    // input data of the slot = donnees en entree du slot
-	int ChgEnM; // Change envelop mask.
-	int AMS;    // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO
-	int AMSon;  // AMS enable flag = drapeau d'activation de l'AMS
-} slot_t;
-
-typedef struct YM2612_channel_t {
-	int S0_OUT[4];          // anciennes sorties slot 0 (pour le feed back)
-	int LEFT;               // LEFT enable flag
-	int RIGHT;              // RIGHT enable flag
-	int ALGO;               // Algorythm = determine les connections entre les operateurs
-	int FB;                 // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree)
-	int FMS;                // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO
-	int AMS;                // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO
-	int FNUM[4];            // hauteur frequence de la voie (+ 3 pour le mode special)
-	int FOCT[4];            // octave de la voie (+ 3 pour le mode special)
-	int KC[4];              // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S)
-	slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie
-	int FFlag;              // Frequency step recalculation flag
-} channel_t;
-
-typedef struct YM2612_state_t {
-	int Clock;          // Horloge YM2612
-	int Rate;           // Sample Rate (11025/22050/44100)
-	int TimerBase;      // TimerBase calculation
-	int Status;         // YM2612 Status (timer overflow)
-	int OPNAadr;        // addresse pour l'ecriture dans l'OPN A (propre à l'emulateur)
-	int OPNBadr;        // addresse pour l'ecriture dans l'OPN B (propre à l'emulateur)
-	int LFOcnt;         // LFO counter = compteur-frequence pour le LFO
-	int TimerA;         // timerA limit = valeur jusqu'à laquelle le timer A doit compter
-	int TimerAL;
-	int TimerAcnt;      // timerA counter = valeur courante du Timer A
-	int TimerB;         // timerB limit = valeur jusqu'à laquelle le timer B doit compter
-	int TimerBL;
-	int TimerBcnt;      // timerB counter = valeur courante du Timer B
-	int Mode;           // Mode actuel des voie 3 et 6 (normal / special)
-	int DAC;            // DAC enabled flag
-	double Frequence;   // Frequence de base, se calcul par rapport à l'horlage et au sample rate
-	channel_t CHANNEL[YM2612_Emu::channel_count];   // Les 6 voies du YM2612
-	int REG[2][0x100];  // Sauvegardes des valeurs de tout les registres, c'est facultatif
-						// cela nous rend le debuggage plus facile
-} state_t;
-
-#ifndef PI
-#define PI 3.14159265358979323846
-#endif
-
-#define ATTACK    0
-#define DECAY     1
-#define SUBSTAIN  2
-#define RELEASE   3
-
-// SIN_LBITS <= 16
-// LFO_HBITS <= 16
-// (SIN_LBITS + SIN_HBITS) <= 26
-// (ENV_LBITS + ENV_HBITS) <= 28
-// (LFO_LBITS + LFO_HBITS) <= 28
-
-#define SIN_HBITS      12                               // Sinus phase counter int part
-#define SIN_LBITS      (26 - SIN_HBITS)                 // Sinus phase counter float part (best setting)
-
-#if (SIN_LBITS > 16)
-#define SIN_LBITS      16                               // Can't be greater than 16 bits
-#endif
-
-#define ENV_HBITS      12                               // Env phase counter int part
-#define ENV_LBITS      (28 - ENV_HBITS)                 // Env phase counter float part (best setting)
-
-#define LFO_HBITS      10                               // LFO phase counter int part
-#define LFO_LBITS      (28 - LFO_HBITS)                 // LFO phase counter float part (best setting)
-
-#define SIN_LENGHT     (1 << SIN_HBITS)
-#define ENV_LENGHT     (1 << ENV_HBITS)
-#define LFO_LENGHT     (1 << LFO_HBITS)
-
-#define TL_LENGHT      (ENV_LENGHT * 3)                 // Env + TL scaling + LFO
-
-#define SIN_MASK       (SIN_LENGHT - 1)
-#define ENV_MASK       (ENV_LENGHT - 1)
-#define LFO_MASK       (LFO_LENGHT - 1)
-
-#define ENV_STEP       (96.0 / ENV_LENGHT)              // ENV_MAX = 96 dB
-
-#define ENV_ATTACK     ((ENV_LENGHT * 0) << ENV_LBITS)
-#define ENV_DECAY      ((ENV_LENGHT * 1) << ENV_LBITS)
-#define ENV_END        ((ENV_LENGHT * 2) << ENV_LBITS)
-
-#define MAX_OUT_BITS   (SIN_HBITS + SIN_LBITS + 2)      // Modulation = -4 <--> +4
-#define MAX_OUT        ((1 << MAX_OUT_BITS) - 1)
-
-#define PG_CUT_OFF     ((int) (78.0 / ENV_STEP))
-#define ENV_CUT_OFF    ((int) (68.0 / ENV_STEP))
-
-#define AR_RATE        399128
-#define DR_RATE        5514396
-
-//#define AR_RATE        426136
-//#define DR_RATE        (AR_RATE * 12)
-
-#define LFO_FMS_LBITS  9    // FIXED (LFO_FMS_BASE gives somethink as 1)
-#define LFO_FMS_BASE   ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))
-
-#define S0             0    // Stupid typo of the YM2612
-#define S1             2
-#define S2             1
-#define S3             3
-
-inline void set_seg( slot_t& s, int seg ) {
-	s.env_xor = 0;
-	s.env_max = INT_MAX;
-	s.SEG = seg;
-	if ( seg & 4 ) {
-		s.env_xor = ENV_MASK;
-		s.env_max = ENV_MASK;
-	}
-}
-
-typedef struct YM2612_tables_t
-{
-	short SIN_TAB [SIN_LENGHT];                 // SINUS TABLE (offset into TL TABLE)
-	int LFOinc;         // LFO step counter = pas d'incrementation du compteur-frequence du LFO
-						// plus le pas est grand, plus la frequence est grande
-	unsigned int AR_TAB [128];                  // Attack rate table
-	unsigned int DR_TAB [96];                   // Decay rate table
-	unsigned int DT_TAB [8] [32];               // Detune table
-	unsigned int SL_TAB [16];                   // Substain level table
-	unsigned int NULL_RATE [32];                // Table for NULL rate
-	int LFO_INC_TAB [8];                        // LFO step table
-	
-	int LFO_ENV_FREQ_UP [max_length * 2];       // Temporary calculated LFO FMS, AMS (adjusted for 11.8 dB) (interleved)
-	unsigned int FINC_TAB [2048];               // Frequency step table
-	
-	short ENV_TAB [2 * ENV_LENGHT + 8];         // ENV CURVE TABLE (attack & decay)
-	
-	unsigned int DECAY_TO_ATTACK [ENV_LENGHT];  // Conversion from decay to attack phase
-	short LFO_ENV_TAB [LFO_LENGHT];             // LFO AMS TABLE (adjusted for 11.8 dB)
-	short LFO_FREQ_TAB [LFO_LENGHT];            // LFO FMS TABLE
-	int TL_TAB [TL_LENGHT * 2];                 // TOTAL LEVEL TABLE (positif and minus)
-} tables_t;
-
-static const unsigned char DT_DEF_TAB [4 * 32] = {
-// FD = 0
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-
-// FD = 1
-  0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
-  2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
-
-// FD = 2
-  1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
-  5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
-
-// FD = 3
-  2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
-  8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
-};
-
-static const unsigned char FKEY_TAB [16] = { 
-	0, 0, 0, 0,
-	0, 0, 0, 1,
-	2, 3, 3, 3,
-	3, 3, 3, 3
-};
-
-static const unsigned int LFO_AMS_TAB [4] = {
-	31, 4, 1, 0
-};
-
-static const unsigned char LFO_FMS_TAB [8] = {
-	LFO_FMS_BASE * 0, LFO_FMS_BASE * 1,
-	LFO_FMS_BASE * 2, LFO_FMS_BASE * 3,
-	LFO_FMS_BASE * 4, LFO_FMS_BASE * 6,
-	LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
-};
-
-inline void YM2612_Special_Update() { }
-
-
-struct YM2612_Impl
-{
-	enum { channel_count = YM2612_Emu::channel_count };
-	
-	state_t YM2612;
-	int mute_mask;
-	tables_t g;
-	
-	void KEY_ON( channel_t&, int );
-	void KEY_OFF( channel_t&, int );
-	int SLOT_SET( int, int );
-	int CHANNEL_SET( int, int );
-	int YM_SET( int, int );
-	
-	blargg_err_t set_rate( long sample_rate, long clock_rate );
-	void reset();
-	void write( int addr, int data );
-	void run_timer( int );
-	void run( YM2612_Emu::sample_t*, int count );
-};
-
-void YM2612_Impl::KEY_ON( channel_t& ch, int nsl)
-{
-	slot_t *SL = &(ch.SLOT [nsl]);  // on recupere le bon pointeur de slot
-	
-	if (SL->Ecurp == RELEASE)       // la touche est-elle rel'chee ?
-	{
-		SL->Fcnt = 0;
-
-		// Fix Ecco 2 splash sound
-		
-		SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM;
-		SL->ChgEnM = ~0;
-
-//      SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK;
-//      SL->Ecnt = 0;
-
-		SL->Einc = SL->EincA;
-		SL->Ecmp = ENV_DECAY;
-		SL->Ecurp = ATTACK;
-	}
-}
-
-
-void YM2612_Impl::KEY_OFF(channel_t& ch, int nsl)
-{
-	slot_t *SL = &(ch.SLOT [nsl]);  // on recupere le bon pointeur de slot
-	
-	if (SL->Ecurp != RELEASE)       // la touche est-elle appuyee ?
-	{
-		if (SL->Ecnt < ENV_DECAY)   // attack phase ?
-		{
-			SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
-		}
-
-		SL->Einc = SL->EincR;
-		SL->Ecmp = ENV_END;
-		SL->Ecurp = RELEASE;
-	}
-}
-
-
-int YM2612_Impl::SLOT_SET( int Adr, int data )
-{
-	int nch = Adr & 3;
-	if ( nch == 3 )
-		return 1;
-	
-	channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)];
-	slot_t& sl = ch.SLOT [(Adr >> 2) & 3];
-
-	switch ( Adr & 0xF0 )
-	{
-		case 0x30:
-			if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1;
-			else sl.MUL = 1;
-
-			sl.DT = (int*) g.DT_TAB [(data >> 4) & 7];
-
-			ch.SLOT [0].Finc = -1;
-
-			break;
-
-		case 0x40:
-			sl.TL = data & 0x7F;
-
-			// SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
-			YM2612_Special_Update();
-
-#if ((ENV_HBITS - 7) < 0)
-			sl.TLL = sl.TL >> (7 - ENV_HBITS);
-#else
-			sl.TLL = sl.TL << (ENV_HBITS - 7);
-#endif
-
-			break;
-
-		case 0x50:
-			sl.KSR_S = 3 - (data >> 6);
-
-			ch.SLOT [0].Finc = -1;
-
-			if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1];
-			else sl.AR = (int*) &g.NULL_RATE [0];
-
-			sl.EincA = sl.AR [sl.KSR];
-			if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA;
-			break;
-
-		case 0x60:
-			if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS;
-			else sl.AMS = 31;
-
-			if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1];
-			else sl.DR = (int*) &g.NULL_RATE [0];
-
-			sl.EincD = sl.DR [sl.KSR];
-			if (sl.Ecurp == DECAY) sl.Einc = sl.EincD;
-			break;
-
-		case 0x70:
-			if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1];
-			else sl.SR = (int*) &g.NULL_RATE [0];
-
-			sl.EincS = sl.SR [sl.KSR];
-			if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS;
-			break;
-
-		case 0x80:
-			sl.SLL = g.SL_TAB [data >> 4];
-
-			sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2];
-
-			sl.EincR = sl.RR [sl.KSR];
-			if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR;
-			break;
-
-		case 0x90:
-			// SSG-EG envelope shapes :
-			/*
-			   E  At Al H
-			  
-			   1  0  0  0  \\\\
-			   1  0  0  1  \___
-			   1  0  1  0  \/\/
-			   1  0  1  1  \
-			   1  1  0  0  ////
-			   1  1  0  1  /
-			   1  1  1  0  /\/\
-			   1  1  1  1  /___
-			  
-			   E  = SSG-EG enable
-			   At = Start negate
-			   Al = Altern
-			   H  = Hold */
-
-			set_seg( sl, (data & 8) ? (data & 0x0F) : 0 );
-			break;
-	}
-
-	return 0;
-}
-
-
-int YM2612_Impl::CHANNEL_SET( int Adr, int data )
-{
-	int num = Adr & 3;
-	if ( num == 3 )
-		return 1;
-	
-	channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)];
-	
-	switch ( Adr & 0xFC )
-	{
-		case 0xA0:
-			YM2612_Special_Update();
-
-			ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data;
-			ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
-
-			ch.SLOT [0].Finc = -1;
-			break;
-
-		case 0xA4:
-			YM2612_Special_Update();
-
-			ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8);
-			ch.FOCT [0] = (data & 0x38) >> 3;
-			ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
-
-			ch.SLOT [0].Finc = -1;
-			break;
-
-		case 0xA8:
-			if ( Adr < 0x100 ) {
-				num++;
-
-				YM2612_Special_Update();
-
-				YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data;
-				YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
-						FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
-
-				YM2612.CHANNEL [2].SLOT [0].Finc = -1;
-			}
-			break;
-
-		case 0xAC:
-			if ( Adr < 0x100 ) {
-				num++;
-
-				YM2612_Special_Update();
-
-				YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8);
-				YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3;
-				YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
-						FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
-
-				YM2612.CHANNEL [2].SLOT [0].Finc = -1;
-			}
-			break;
-
-		case 0xB0:
-			if ( ch.ALGO != (data & 7) ) {
-				// Fix VectorMan 2 heli sound (level 1)
-				YM2612_Special_Update();
-
-				ch.ALGO = data & 7;
-				
-				ch.SLOT [0].ChgEnM = 0;
-				ch.SLOT [1].ChgEnM = 0;
-				ch.SLOT [2].ChgEnM = 0;
-				ch.SLOT [3].ChgEnM = 0;
-			}
-
-			ch.FB = 9 - ((data >> 3) & 7);                              // Real thing ?
-
-//          if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB;       // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound...
-//          else ch.FB = 31;
-			break;
-
-		case 0xB4: {
-			YM2612_Special_Update();
-			
-			ch.LEFT = 0 - ((data >> 7) & 1);
-			ch.RIGHT = 0 - ((data >> 6) & 1);
-			
-			ch.AMS = LFO_AMS_TAB [(data >> 4) & 3];
-			ch.FMS = LFO_FMS_TAB [data & 7];
-			
-			for ( int i = 0; i < 4; i++ ) {
-				slot_t& sl = ch.SLOT [i];
-				sl.AMS = (sl.AMSon ? ch.AMS : 31);
-			}
-			break;
-		}
-	}
-	
-	return 0;
-}
-
-
-int YM2612_Impl::YM_SET(int Adr, int data)
-{
-	switch ( Adr )
-	{
-		case 0x22:
-			if (data & 8) // LFO enable
-			{
-				// Cool Spot music 1, LFO modified severals time which
-				// distord the sound, have to check that on a real genesis...
-
-				g.LFOinc = g.LFO_INC_TAB [data & 7];
-			}
-			else
-			{
-				g.LFOinc = YM2612.LFOcnt = 0;
-			}
-			break;
-
-		case 0x24:
-			YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2);
-
-			if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
-			{
-				YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
-			}
-			break;
-
-		case 0x25:
-			YM2612.TimerA = (YM2612.TimerA & 0x3fc) | (data & 3);
-
-			if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
-			{
-				YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
-			}
-			break;
-
-		case 0x26:
-			YM2612.TimerB = data;
-
-			if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12))
-			{
-				YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12);
-			}
-			break;
-
-		case 0x27:
-			// Parametre divers
-			// b7 = CSM MODE
-			// b6 = 3 slot mode
-			// b5 = reset b
-			// b4 = reset a
-			// b3 = timer enable b
-			// b2 = timer enable a
-			// b1 = load b
-			// b0 = load a
-
-			if ((data ^ YM2612.Mode) & 0x40)
-			{
-				// We changed the channel 2 mode, so recalculate phase step
-				// This fix the punch sound in Street of Rage 2
-
-				YM2612_Special_Update();
-
-				YM2612.CHANNEL [2].SLOT [0].Finc = -1;      // recalculate phase step
-			}
-
-//          if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL;
-//          if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL;
-
-//          YM2612.Status &= (~data >> 4);                  // Reset du Status au cas ou c'est demande
-			YM2612.Status &= (~data >> 4) & (data >> 2);    // Reset Status
-
-			YM2612.Mode = data;
-			break;
-
-		case 0x28: {
-			int nch = data & 3;
-			if ( nch == 3 )
-				return 1;
-			if ( data & 4 )
-				nch += 3;
-			channel_t& ch = YM2612.CHANNEL [nch];
-
-			YM2612_Special_Update();
-
-			if (data & 0x10) KEY_ON(ch, S0);    // On appuie sur la touche pour le slot 1
-			else KEY_OFF(ch, S0);               // On rel'che la touche pour le slot 1
-			if (data & 0x20) KEY_ON(ch, S1);    // On appuie sur la touche pour le slot 3
-			else KEY_OFF(ch, S1);               // On rel'che la touche pour le slot 3
-			if (data & 0x40) KEY_ON(ch, S2);    // On appuie sur la touche pour le slot 2
-			else KEY_OFF(ch, S2);               // On rel'che la touche pour le slot 2
-			if (data & 0x80) KEY_ON(ch, S3);    // On appuie sur la touche pour le slot 4
-			else KEY_OFF(ch, S3);               // On rel'che la touche pour le slot 4
-			break;
-		}
-		
-		case 0x2B:
-			if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update();
-
-			YM2612.DAC = data & 0x80;   // activation/desactivation du DAC
-			break;
-	}
-	
-	return 0;
-}
-
-YM2612_Emu::YM2612_Emu() {
-	impl = NULL;
-}
-
-YM2612_Emu::~YM2612_Emu() {
-	delete impl;
-}
-
-blargg_err_t YM2612_Emu::set_rate( long rate, long clock )
-{
-	if ( !impl ) {
-		impl = new YM2612_Impl;
-		if ( !impl )
-			return "Out of memory";
-		impl->mute_mask = 0;
-	}
-	return impl->set_rate( rate, clock );
-}
-
-blargg_err_t YM2612_Impl::set_rate( long Rate, long Clock )
-{
-	require( Rate );
-	require( Clock );
-	
-	int i, j;
-	double x;
-
-	memset(&YM2612, 0, sizeof(YM2612));
-
-	YM2612.Clock = Clock;
-	YM2612.Rate = Rate;
-
-	// 144 = 12 * (prescale * 2) = 12 * 6 * 2
-	// prescale set to 6 by default
-
-	YM2612.Frequence = ((double) YM2612.Clock / (double) YM2612.Rate) / 144.0;
-	YM2612.TimerBase = (int) (YM2612.Frequence * 4096.0);
-
-	// Tableau TL :
-	// [0     -  4095] = +output  [4095  - ...] = +output overflow (fill with 0)
-	// [12288 - 16383] = -output  [16384 - ...] = -output overflow (fill with 0)
-
-	for(i = 0; i < TL_LENGHT; i++)
-	{
-		if (i >= PG_CUT_OFF)    // YM2612 cut off sound after 78 dB (14 bits output ?)
-		{
-			g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0;
-		}
-		else
-		{
-			x = MAX_OUT;                                // Max output
-			x /= pow( 10.0, (ENV_STEP * i) / 20.0 );    // Decibel -> Voltage
-
-			g.TL_TAB [i] = (int) x;
-			g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i];
-		}
-	}
-	
-	// Tableau SIN :
-	// g.SIN_TAB [x] [y] = sin(x) * y; 
-	// x = phase and y = volume
-
-	g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF;
-
-	for(i = 1; i <= SIN_LENGHT / 4; i++)
-	{
-		x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT));   // Sinus
-		x = 20 * log10(1 / x);                                      // convert to dB
-
-		j = (int) (x / ENV_STEP);                       // Get TL range
-
-		if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
-
-		g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j;
-		g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j;
-	}
-
-	// Tableau LFO (LFO wav) :
-
-	for(i = 0; i < LFO_LENGHT; i++)
-	{
-		x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT));   // Sinus
-		x += 1.0;
-		x /= 2.0;                   // positive only
-		x *= 11.8 / ENV_STEP;       // ajusted to MAX enveloppe modulation
-
-		g.LFO_ENV_TAB [i] = (int) x;
-
-		x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT));   // Sinus
-		x *= (double) ((1 << (LFO_HBITS - 1)) - 1);
-
-		g.LFO_FREQ_TAB [i] = (int) x;
-
-	}
-
-	// Tableau Enveloppe :
-	// g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1]              = attack curve
-	// g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve
-
-	for(i = 0; i < ENV_LENGHT; i++)
-	{
-		// Attack curve (x^8 - music level 2 Vectorman 2)
-		x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8);
-		x *= ENV_LENGHT;
-
-		g.ENV_TAB [i] = (int) x;
-
-		// Decay curve (just linear)
-		x = pow(((double) (i) / (double) (ENV_LENGHT)), 1);
-		x *= ENV_LENGHT;
-
-		g.ENV_TAB [ENV_LENGHT + i] = (int) x;
-	}
-
-	g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1;      // for the stopped state
-
-	// Tableau pour la conversion Attack -> Decay and Decay -> Attack
-	
-	for(i = 0, j = ENV_LENGHT - 1; i < ENV_LENGHT; i++)
-	{
-		while (j && (g.ENV_TAB [j] < (unsigned) i)) j--;
-
-		g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
-	}
-
-	// Tableau pour le Substain Level
-	
-	for(i = 0; i < 15; i++)
-	{
-		x = i * 3;                  // 3 and not 6 (Mickey Mania first music for test)
-		x /= ENV_STEP;
-
-		j = (int) x;
-		j <<= ENV_LBITS;
-
-		g.SL_TAB [i] = j + ENV_DECAY;
-	}
-
-	j = ENV_LENGHT - 1;             // special case : volume off
-	j <<= ENV_LBITS;
-	g.SL_TAB [15] = j + ENV_DECAY;
-
-	// Tableau Frequency Step
-
-	for(i = 0; i < 2048; i++)
-	{
-		x = (double) (i) * YM2612.Frequence;
-
-#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0)
-		x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS));
-#else
-		x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7)));
-#endif
-
-		x /= 2.0;   // because MUL = value * 2
-
-		g.FINC_TAB [i] = (unsigned int) x;
-	}
-
-	// Tableaux Attack & Decay Rate
-
-	for(i = 0; i < 4; i++)
-	{
-		g.AR_TAB [i] = 0;
-		g.DR_TAB [i] = 0;
-	}
-
-	for(i = 0; i < 60; i++)
-	{
-		x = YM2612.Frequence;
-
-		x *= 1.0 + ((i & 3) * 0.25);                    // bits 0-1 : x1.00, x1.25, x1.50, x1.75
-		x *= (double) (1 << ((i >> 2)));                // bits 2-5 : shift bits (x2^0 - x2^15)
-		x *= (double) (ENV_LENGHT << ENV_LBITS);        // on ajuste pour le tableau g.ENV_TAB
-
-		g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE);
-		g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE);
-	}
-
-	for(i = 64; i < 96; i++)
-	{
-		g.AR_TAB [i] = g.AR_TAB [63];
-		g.DR_TAB [i] = g.DR_TAB [63];
-
-		g.NULL_RATE [i - 64] = 0;
-	}
-
-	// Tableau Detune
-
-	for(i = 0; i < 4; i++)
-	{
-		for (j = 0; j < 32; j++)
-		{
-#if ((SIN_LBITS + SIN_HBITS - 21) < 0)
-			x = (double) DT_DEF_TAB [(i << 5) + j] * YM2612.Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
-#else
-			x = (double) DT_DEF_TAB [(i << 5) + j] * YM2612.Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21));
-#endif
-
-			g.DT_TAB [i + 0] [j] = (int) x;
-			g.DT_TAB [i + 4] [j] = (int) -x;
-		}
-	}
-
-	// Tableau LFO
-
-	j = YM2612.Rate;
-	
-	g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
-	
-	reset();
-
-	return blargg_success;
-}
-
-void YM2612_Emu::reset() {
-	impl->reset();
-}
-
-void YM2612_Impl::reset()
-{
-	YM2612.LFOcnt = 0;
-	YM2612.TimerA = 0;
-	YM2612.TimerAL = 0;
-	YM2612.TimerAcnt = 0;
-	YM2612.TimerB = 0;
-	YM2612.TimerBL = 0;
-	YM2612.TimerBcnt = 0;
-	YM2612.DAC = 0;
-
-	YM2612.Status = 0;
-
-	YM2612.OPNAadr = 0;
-	YM2612.OPNBadr = 0;
-	
-	int i;
-	for ( i = 0; i < channel_count; i++ )
-	{
-		channel_t& ch = YM2612.CHANNEL [i];
-		
-		ch.LEFT = ~0;
-		ch.RIGHT = ~0;
-		ch.ALGO = 0;;
-		ch.FB = 31;
-		ch.FMS = 0;
-		ch.AMS = 0;
-
-		for ( int j = 0 ;j < 4 ; j++ )
-		{
-			ch.S0_OUT [j] = 0;
-			ch.FNUM [j] = 0;
-			ch.FOCT [j] = 0;
-			ch.KC [j] = 0;
-
-			ch.SLOT [j].Fcnt = 0;
-			ch.SLOT [j].Finc = 0;
-			ch.SLOT [j].Ecnt = ENV_END;     // Put it at the end of Decay phase...
-			ch.SLOT [j].Einc = 0;
-			ch.SLOT [j].Ecmp = 0;
-			ch.SLOT [j].Ecurp = RELEASE;
-
-			ch.SLOT [j].ChgEnM = 0;
-		}
-	}
-
-	for ( i = 0; i < 0x100; i++ ) {
-		YM2612.REG [0] [i] = -1;
-		YM2612.REG [1] [i] = -1;
-	}
-
-	for ( i = 0xB6; i >= 0xB4; i-- ) {
-		write( 0, i );
-		write( 2, i );
-		write( 1, 0xC0 );
-		write( 3, 0xC0 );
-	}
-
-	for ( i = 0xB2; i >= 0x22; i-- ) {
-		write( 0, i );
-		write( 2, i );
-		write( 1, 0 );
-		write( 3, 0 );
-	}
-	
-	write( 0, 0x2A );
-	write( 1, 0x80 );
-}
-
-inline void YM2612_Impl::write( int addr, int data )
-{
-	require( (unsigned) addr < 4 );
-	require( (unsigned) data <= 0xFF );
-	
-	switch( addr )
-	{
-		case 0:
-			YM2612.OPNAadr = data;
-			break;
-
-		case 2:
-			YM2612.OPNBadr = data;
-			break;
-		
-		case 1: {
-			int opn_addr = YM2612.OPNAadr;
-			if ( opn_addr < 0x30 )
-			{
-				YM2612.REG [0] [opn_addr] = data;
-				YM_SET( opn_addr, data );
-			}
-			else if ( YM2612.REG [0] [opn_addr] != data ) {
-				YM2612.REG [0] [opn_addr] = data;
-				
-				if ( opn_addr < 0xA0 )
-					SLOT_SET( opn_addr, data );
-				else
-					CHANNEL_SET( opn_addr, data );
-			}
-			break;
-		}
-		
-		case 3: {
-			int opn_addr = YM2612.OPNBadr;
-			if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data )
-			{
-				YM2612.REG [1] [opn_addr] = data;
-
-				if ( opn_addr < 0xA0 )
-					SLOT_SET( opn_addr + 0x100, data );
-				else
-					CHANNEL_SET( opn_addr + 0x100, data );
-			}
-			break;
-		}
-	}
-}
-
-void YM2612_Emu::write( int addr, int data ) {
-	impl->write( addr, data );
-}
-
-inline void YM2612_Impl::run_timer( int length )
-{
-	int i = YM2612.TimerBase * length;
-
-	if (YM2612.Mode & 1)                            // Timer A ON ?
-	{
-//      if ((YM2612.TimerAcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
-		if ((YM2612.TimerAcnt -= i) <= 0)
-		{
-			// timer a overflow
-			
-			YM2612.Status |= (YM2612.Mode & 0x04) >> 2;
-			YM2612.TimerAcnt += YM2612.TimerAL;
-
-			if (YM2612.Mode & 0x80) {
-				KEY_ON( YM2612.CHANNEL [2], 0 );
-				KEY_ON( YM2612.CHANNEL [2], 1 );
-				KEY_ON( YM2612.CHANNEL [2], 2 );
-				KEY_ON( YM2612.CHANNEL [2], 3 );
-			}
-		}
-	}
-
-	if (YM2612.Mode & 2)                            // Timer B ON ?
-	{
-//      if ((YM2612.TimerBcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
-		if ((YM2612.TimerBcnt -= i) <= 0)
-		{
-			// timer b overflow
-			YM2612.Status |= (YM2612.Mode & 0x08) >> 2;
-			YM2612.TimerBcnt += YM2612.TimerBL;
-		}
-	}
-}
-
-void YM2612_Emu::run_timer( int length ) {
-	impl->run_timer( length );
-}
-
-void YM2612_Emu::mute_voices( int mask ) {
-	impl->mute_mask = mask;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-static const int no_lfo [2] = { 0, 0 };
-
-static void update_env( channel_t& ch )
-{
-	slot_t* sl = ch.SLOT;
-	for ( int n = 4; n--; sl++ )
-	{
-		if ( (sl->Ecnt += sl->Einc) < sl->Ecmp )
-			continue;
-		
-		switch ( sl->Ecurp ) {
-		case 0:
-			// Env_Attack_Next
-			
-			// Verified with Gynoug even in HQ (explode SFX)
-			sl->Ecnt = ENV_DECAY;
-
-			sl->Einc = sl->EincD;
-			sl->Ecmp = sl->SLL;
-			sl->Ecurp = DECAY;
-			break;
-		
-		case 1:
-			// Env_Decay_Next
-			
-			// Verified with Gynoug even in HQ (explode SFX)
-			sl->Ecnt = sl->SLL;
-
-			sl->Einc = sl->EincS;
-			sl->Ecmp = ENV_END;
-			sl->Ecurp = SUBSTAIN;
-			break;
-		
-		case 2:
-			// Env_Substain_Next(slot_t *SL)
-			if (sl->SEG & 8)    // SSG envelope type
-			{
-				int release = sl->SEG & 1;
-				
-				if ( !release )
-				{
-					// re KEY ON
-
-					// sl->Fcnt = 0;
-					// sl->ChgEnM = ~0;
-
-					sl->Ecnt = 0;
-					sl->Einc = sl->EincA;
-					sl->Ecmp = ENV_DECAY;
-					sl->Ecurp = ATTACK;
-				}
-
-				set_seg( *sl, (sl->SEG << 1) & 4 );
-				
-				if ( !release )
-					break;
-			}
-			// fall through
-		
-		case 3:
-			// Env_Release_Next
-			sl->Ecnt = ENV_END;
-			sl->Einc = 0;
-			sl->Ecmp = ENV_END + 1;
-			break;
-		
-		// default: no op
-		}
-	}
-}
-
-template<int algo>
-struct ym2612_update_chan {
-	static void func( const tables_t& g, channel_t&, YM2612_Emu::sample_t*, int );
-};
-
-typedef void (*ym2612_update_chan_t)( const tables_t& g, channel_t&, YM2612_Emu::sample_t*, int );
-
-template<int algo>
-void ym2612_update_chan<algo>::func( const tables_t& g, channel_t& ch, YM2612_Emu::sample_t* buf, int length )
-{
-	int not_end = ch.SLOT [S3].Ecnt - ENV_END;
-	
-	// algo is a compile-time constant, so all conditions based on it are resolved during compilation
-	
-	// special cases
-	if ( algo == 7 )
-		not_end |= ch.SLOT [S0].Ecnt - ENV_END;
-	if ( algo >= 5 )
-		not_end |= ch.SLOT [S2].Ecnt - ENV_END;
-	if ( algo >= 4 )
-		not_end |= ch.SLOT [S1].Ecnt - ENV_END;
-	
-	const int CH_LEFT = ch.LEFT;
-	const int CH_RIGHT = ch.RIGHT;
-	
-	int CH_S0_OUT_1 = ch.S0_OUT [1];
-	
-	if ( !not_end )
-		return;
-	
-	int in0 = ch.SLOT [S0].Fcnt;
-	int in1 = ch.SLOT [S1].Fcnt;
-	int in2 = ch.SLOT [S2].Fcnt;
-	int in3 = ch.SLOT [S3].Fcnt;
-	
-	const int* lfo_freq_env = g.LFO_ENV_FREQ_UP;
-	int lfo_step = 2;
-	if ( !g.LFOinc ) {
-		lfo_step = 0;
-		lfo_freq_env = no_lfo;
-	}
-	
-	const int* const TL_TAB = g.TL_TAB; // cache
-	
-#define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
-	
-	const short* const ENV_TAB = g.ENV_TAB; // cache
-	
-	goto first_iter;
-	
-	while ( --length )
-	{
-		// update envelope
-		update_env( ch );
-	first_iter:
-		
-		// calc envelope
-		int const env_LFO = lfo_freq_env [0];
-		
-	#define CALC_EN( x ) \
-		int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL;  \
-		int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) &    \
-				((temp##x - ch.SLOT [S##x].env_max) >> 31);
-		
-		CALC_EN( 0 )
-		CALC_EN( 1 )
-		CALC_EN( 2 )
-		CALC_EN( 3 )
-		
-		// feedback
-		int CH_S0_OUT_0 = ch.S0_OUT [0];
-		{
-			int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB);
-			CH_S0_OUT_1 = CH_S0_OUT_0;
-			CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 );
-		}
-		
-		int CH_OUTd;
-		if ( algo == 0 ) {
-			int temp = in1 + CH_S0_OUT_1;
-			temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 );
-			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
-			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
-		}
-		else if ( algo == 1 ) {
-			int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
-			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
-			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
-		}
-		else if ( algo == 2 ) {
-			int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
-			temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
-			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
-		}
-		else if ( algo == 3 ) {
-			int temp = in1 + CH_S0_OUT_1;
-			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) +
-					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
-			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
-		}
-		else if ( algo == 4 ) {
-			int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
-			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) +
-					SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 );
-			//DO_LIMIT
-		}
-		else if ( algo == 5 ) {
-			int temp = CH_S0_OUT_1;
-			CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) +
-					SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) +
-					SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 );
-			//DO_LIMIT
-		}
-		else if ( algo == 6 ) {
-			CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
-					SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) +
-					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
-			//DO_LIMIT
-		}
-		else if ( algo == 7 ) {
-			CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
-					SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) +
-					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1;
-			//DO_LIMIT
-		}
-		
-		CH_OUTd >>= MAX_OUT_BITS - output_bits + 2;
-		
-		// update phase
-		const unsigned freq_LFO = ((ch.FMS * lfo_freq_env [1]) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1));
-		in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
-		in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
-		in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
-		in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
-		
-		int t0 = buf [0] + (CH_OUTd & CH_LEFT);
-		int t1 = buf [1] + (CH_OUTd & CH_RIGHT);
-		
-		ch.S0_OUT [0] = CH_S0_OUT_0;
-		buf [0] = t0;
-		buf [1] = t1;
-		buf += 2;
-		
-		lfo_freq_env += lfo_step;
-	}
-	
-	ch.S0_OUT [1] = CH_S0_OUT_1;
-	
-	ch.SLOT [S0].Fcnt = in0;
-	ch.SLOT [S1].Fcnt = in1;
-	ch.SLOT [S2].Fcnt = in2;
-	ch.SLOT [S3].Fcnt = in3;
-	
-	update_env( ch );
-}
-
-static const ym2612_update_chan_t UPDATE_CHAN [8] = {
-	&ym2612_update_chan<0>::func,
-	&ym2612_update_chan<1>::func,
-	&ym2612_update_chan<2>::func,
-	&ym2612_update_chan<3>::func,
-	&ym2612_update_chan<4>::func,
-	&ym2612_update_chan<5>::func,
-	&ym2612_update_chan<6>::func,
-	&ym2612_update_chan<7>::func
-};
-
-void YM2612_Impl::run( YM2612_Emu::sample_t* buf, int length )
-{
-	require( length % 2 == 0 ); // generates pairs of samples
-	require( length <= max_length * 2 );
-	
-	length >>= 1;
-	
-	// Mise à jour des pas des compteurs-frequences s'ils ont ete modifies
-	
-	for ( int chi = 0; chi < channel_count; chi++ )
-	{
-		channel_t& ch = YM2612.CHANNEL [chi];
-		if ( ch.SLOT [0].Finc != -1 )
-			continue;
-		
-		int i2 = 0;
-		if ( chi == 2 && (YM2612.Mode & 0x40) )
-			i2 = 2;
-		
-		for ( int i = 0; i < 4; i++ )
-		{
-			// static int seq [4] = { 2, 1, 3, 0 };
-			// if ( i2 ) i2 = seq [i];
-			
-			slot_t& sl = ch.SLOT [i];
-			int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]);
-			int ksr = ch.KC [i2] >> sl.KSR_S;   // keycode attenuation
-			sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL;
-			if (sl.KSR != ksr)          // si le KSR a change alors
-			{                       // les differents taux pour l'enveloppe sont mis à jour
-				sl.KSR = ksr;
-
-				sl.EincA = sl.AR [ksr];
-				sl.EincD = sl.DR [ksr];
-				sl.EincS = sl.SR [ksr];
-				sl.EincR = sl.RR [ksr];
-
-				if (sl.Ecurp == ATTACK) {
-					sl.Einc = sl.EincA;
-				}
-				else if (sl.Ecurp == DECAY) {
-					sl.Einc = sl.EincD;
-				}
-				else if (sl.Ecnt < ENV_END) {
-					if (sl.Ecurp == SUBSTAIN)
-						sl.Einc = sl.EincS;
-					else if (sl.Ecurp == RELEASE)
-						sl.Einc = sl.EincR;
-				}
-			}
-			
-			if ( i2 )
-				i2 = (i2 ^ 2) ^ (i2 >> 1);
-		}
-	}
-	
-	if ( g.LFOinc )
-	{
-		// Precalcul LFO wav
-		for(int i = 0; i < length; i++)
-		{
-			int j = ((YM2612.LFOcnt += g.LFOinc) >> LFO_LBITS) & LFO_MASK;
-
-			g.LFO_ENV_FREQ_UP [i * 2] = g.LFO_ENV_TAB [j];
-			g.LFO_ENV_FREQ_UP [i * 2 + 1] = g.LFO_FREQ_TAB [j];
-		}
-	}
-	
-	for ( int i = 0; i < channel_count; i++ )
-		if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) )
-			UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], buf, length );
-}
-
-void YM2612_Emu::run( sample_t* buf, int length ) {
-	impl->run( buf, length );
-}
-
--- a/Plugins/Input/console/ym2612.h	Tue Jan 24 13:57:22 2006 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-
-// Sega Genesis YM2612 FM Sound Chip Emulator
-
-// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
-// Copyright (C) 2002 Stéphane Dallongeville
-
-#ifndef YM2612_H
-#define YM2612_H
-
-#include "blargg_common.h"
-
-struct YM2612_Impl;
-
-class YM2612_Emu {
-public:
-	YM2612_Emu();
-	~YM2612_Emu();
-	
-	blargg_err_t set_rate( long sample_rate, long clock_rate );
-	
-	void reset();
-	
-	enum { channel_count = 6 };
-	void mute_voices( int mask );
-	
-	void write( int addr, int data );
-	
-	void run_timer( int );
-	
-	typedef BOOST::int16_t sample_t;
-	void run( sample_t*, int count );
-	
-private:
-	YM2612_Impl* impl;
-};
-
-#endif
-