diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index d1ec2205ae..6f2bb02f78 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -210,6 +210,7 @@
+
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/BisqAPU.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/BisqAPU.cs
new file mode 100644
index 0000000000..3d38c7dd47
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/BisqAPU.cs
@@ -0,0 +1,655 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+
+using BizHawk.Emulation.Sound;
+
+//http://wiki.nesdev.com/w/index.php/APU_Mixer_Emulation
+//http://wiki.nesdev.com/w/index.php/APU
+//http://wiki.nesdev.com/w/index.php/APU_Pulse
+//sequencer ref: http://wiki.nesdev.com/w/index.php/APU_Frame_Counter
+
+//TODO - refactor length counter to be separate component
+
+namespace BizHawk.Emulation.Consoles.Nintendo
+{
+ partial class NES
+ {
+ public class BisqAPU : ISoundProvider
+ {
+ class RegBitSetStore
+ {
+ public RegBitSetStore(int numUints)
+ {
+ data = new uint[numUints];
+ }
+ public uint[] data;
+ }
+
+ class RegBitSet
+ {
+ RegBitSetStore store;
+ int mask;
+ int bitno, nbits, dim, index;
+ public RegBitSet(RegBitSetStore store, int bitno, int nbits, int dim = 1, int index = 0)
+ {
+ this.store = store;
+ this.bitno = bitno;
+ this.nbits = nbits;
+ this.dim = dim;
+ this.index = index;
+ mask = ((1 << (nbits / 2)) << (nbits - (nbits / 2))) - 1;
+ }
+
+ public static implicit operator int(RegBitSet rhs) { return (int)rhs.get(); }
+ public static implicit operator uint(RegBitSet rhs) { return rhs.get(); }
+ public static explicit operator bool(RegBitSet rhs) { return rhs.get() != 0; }
+
+ public uint Value { get { return get(); } set { set(value); } }
+
+ public uint this[int offset]
+ {
+ get
+ {
+ return get(offset);
+ }
+ set
+ {
+ set(value, offset);
+ }
+ }
+
+ public uint get(int offset = 0)
+ {
+ return (uint)((store.data[index + offset] >> bitno) & mask);
+ }
+
+ public void set(uint val, int offset = 0)
+ {
+ long temp = (store.data[index + offset] & ~(mask << bitno))
+ | ((nbits > 1 ? val & mask : ((val != 0) ? 1 : 0)) << bitno);
+ store.data[index + offset] = (uint)temp;
+ }
+
+ public static uint operator ^(RegBitSet lhs, uint rhs) { return ((uint)(lhs.get() ^ rhs)); }
+ public static uint operator |(RegBitSet lhs, uint rhs) { return ((uint)(lhs.get() | rhs)); }
+ public static uint operator &(RegBitSet lhs, uint rhs) { return ((uint)(lhs.get() & rhs)); }
+ public static uint operator +(RegBitSet lhs, uint rhs) { return ((uint)(lhs.get() + rhs)); }
+ public static uint operator -(RegBitSet lhs, uint rhs) { return ((uint)(lhs.get() - rhs)); }
+ };
+
+ class SignedRegBitSet
+ {
+ RegBitSetStore store;
+ int mask;
+ int bitno, nbits, dim, index;
+ public SignedRegBitSet(RegBitSetStore store, int bitno, int nbits, int dim = 1, int index = 0)
+ {
+ this.store = store;
+ this.bitno = bitno;
+ this.nbits = nbits;
+ this.dim = dim;
+ this.index = index;
+ mask = ((1 << (nbits / 2)) << (nbits - (nbits / 2))) - 1;
+ }
+
+ public static implicit operator int(SignedRegBitSet rhs) { return rhs.get(); }
+ public static implicit operator uint(SignedRegBitSet rhs) { return (uint)rhs.get(); }
+ public static explicit operator bool(SignedRegBitSet rhs) { return rhs.get() != 0; }
+
+ public int Value { get { return get(); } set { set(value); } }
+
+ public int this[int offset]
+ {
+ get
+ {
+ return get(offset);
+ }
+ set
+ {
+ set(value, offset);
+ }
+ }
+
+ public int get(int offset = 0)
+ {
+ return (int)((store.data[index + offset] >> bitno) & mask);
+ }
+
+ public void set(int val, int offset = 0)
+ {
+ long temp = ((int)store.data[index + offset] & (int)~(mask << bitno))
+ | ((nbits > 1 ? val & mask : ((val != 0) ? 1 : 0)) << bitno);
+ store.data[index + offset] = (uint)temp;
+ }
+
+ public static int operator ^(SignedRegBitSet lhs, int rhs) { return ((int)(lhs.get() ^ rhs)); }
+ public static int operator |(SignedRegBitSet lhs, int rhs) { return ((int)(lhs.get() | rhs)); }
+ public static int operator &(SignedRegBitSet lhs, int rhs) { return ((int)(lhs.get() & rhs)); }
+ public static int operator +(SignedRegBitSet lhs, int rhs) { return ((int)(lhs.get() + rhs)); }
+ public static int operator -(SignedRegBitSet lhs, int rhs) { return ((int)(lhs.get() - rhs)); }
+
+ };
+
+
+ public static bool CFG_USE_METASPU = true;
+
+ class APUdata
+ {
+ public APUdata()
+ {
+ bs = new RegBitSetStore(5);
+ ChannelsEnabled = new RegBitSet(bs, 15, 1, 5, 0);
+ frame_delay = new RegBitSet(bs, 0, 15, 5, 0);
+ frame = new RegBitSet(bs, 0, 8, 5, 1);
+ FiveCycleDivider = new RegBitSet(bs, 8, 1, 5, 1);
+ IRQdisable = new RegBitSet(bs, 9, 1, 5, 1);
+ DMC_CycleCost = new RegBitSet(bs, 10, 3, 5, 1);
+ IRQ_delay = new RegBitSet(bs, 0, 15, 5, 2);
+ }
+ public RegBitSetStore bs;
+ public RegBitSet ChannelsEnabled;
+ public RegBitSet frame_delay;
+ public RegBitSet frame;
+ public RegBitSet FiveCycleDivider;
+ public RegBitSet IRQdisable;
+ public RegBitSet DMC_CycleCost;
+ public RegBitSet IRQ_delay;
+ }
+ APUdata data = new APUdata();
+
+ class APUchannel
+ {
+ public APUchannel()
+ {
+ bs = new RegBitSetStore(11);
+
+ reg0 = new RegBitSet(bs,0, 8, 11, 0);
+ DutyCycle = new RegBitSet(bs,6, 2, 11, 0);
+ EnvDecayDisable = new RegBitSet(bs,4, 1, 11, 0);
+ EnvDecayRate = new RegBitSet(bs,0, 4, 11, 0);
+ EnvDecayLoopEnable = new RegBitSet(bs,5, 1, 11, 0);
+ FixedVolume = new RegBitSet(bs,0, 4, 11, 0);
+ LengthCounterDisable = new RegBitSet(bs,5, 1, 11, 0);
+ LinearCounterInit = new RegBitSet(bs,0, 7, 11, 0);
+ LinearCounterDisable = new RegBitSet(bs,7, 1, 11, 0);
+
+ reg1 = new RegBitSet(bs,8, 8, 11, 0);
+ SweepShift = new RegBitSet(bs,8, 3, 11, 0);
+ SweepDecrease = new RegBitSet(bs,11, 1, 11, 0);
+ SweepRate = new RegBitSet(bs,12, 3, 11, 0);
+ SweepEnable = new RegBitSet(bs,15, 1, 11, 0);
+ PCMlength = new RegBitSet(bs,8, 8, 11, 0);
+
+ reg2 = new RegBitSet(bs,16, 8, 11, 0);
+ NoiseFreq = new RegBitSet(bs,16, 4, 11, 0);
+ NoiseType = new RegBitSet(bs,23, 1, 11, 0);
+ WaveLength = new RegBitSet(bs,16, 11, 11, 0);
+
+ reg3 = new RegBitSet(bs,24, 8, 11, 0);
+ LengthCounterInit = new RegBitSet(bs,27, 5, 11, 0);
+ LoopEnabled = new RegBitSet(bs,30, 1, 11, 0);
+ IRQenable = new RegBitSet(bs,31, 1, 11, 0);
+
+ length_counter = new SignedRegBitSet(bs,0, 32, 11, 1);
+ linear_counter = new SignedRegBitSet(bs,0, 32, 11, 2);
+ address = new SignedRegBitSet(bs,0, 32, 11, 3);
+ envelope = new SignedRegBitSet(bs,0, 32, 11, 4);
+ sweep_delay = new SignedRegBitSet(bs,0, 32, 11, 5);
+ env_delay = new SignedRegBitSet(bs,0, 32, 11, 6);
+ wave_counter = new SignedRegBitSet(bs,0, 32, 11, 7);
+ hold = new SignedRegBitSet(bs,0, 32, 11, 8);
+ phase = new SignedRegBitSet(bs,0, 32, 11, 9);
+ level = new SignedRegBitSet(bs,0, 32, 11, 10);
+ }
+ public RegBitSetStore bs;
+ // 4000, 4004, 400C, 4012:
+ public RegBitSet reg0, DutyCycle, EnvDecayDisable, EnvDecayRate, EnvDecayLoopEnable, FixedVolume, LengthCounterDisable, LinearCounterInit, LinearCounterDisable;
+ // 4001, 4005, 4013:
+ public RegBitSet reg1, SweepShift, SweepDecrease, SweepRate, SweepEnable, PCMlength;
+ // 4002, 4006, 400A, 400E:
+ public RegBitSet reg2, NoiseFreq, NoiseType, WaveLength;
+ // 4003, 4007, 400B, 400F, 4010:
+ public RegBitSet reg3, LengthCounterInit, LoopEnabled, IRQenable;
+ // Internals:
+ public SignedRegBitSet length_counter, linear_counter, address, envelope, sweep_delay, env_delay, wave_counter, hold, phase, level;
+ }
+
+ APUchannel[] channels = new APUchannel[5] { new APUchannel(), new APUchannel(), new APUchannel(), new APUchannel(), new APUchannel() };
+
+ static readonly byte[] LengthCounters = new byte[32]
+ { 10,254,20, 2,40, 4,80, 6, 160, 8,60,10,14,12,26,14,
+ 12, 16,24,18,48,20,96,22, 192,24,72,26,16,28,32,30 };
+
+ static readonly ushort[] NoisePeriods = new ushort[16]
+ { 2,4,8,16,32,48,64,80,101,127,190,254,381,508,1017,2034 };
+
+ static readonly ushort[] DMCperiods = new ushort[16]
+ { 428,380,340,320, 286,254,226,214, 190,160,142,128, 106,84,72,54 };
+ /*
+ For PAL:
+ static const u16 DMCperiods[16] =
+ { 0x18E,0x162,0x13C,0x12A, 0x114,0x0EC,0x0D2,0x0C6,
+ 0x0B0,0x094,0x084,0x076, 0x062,0x04E,0x042,0x032 };
+ */
+
+ const int frame_period = 7458;
+
+ // Utility function for sound
+ bool count(ref int v, int reset)
+ {
+ if (--v < 0)
+ {
+ v = reset;
+ return true;
+ }
+ else return false;
+ }
+
+ bool count(ref int v, int reset, int n_at_time)
+ {
+ if ((v -= n_at_time) < 0)
+ {
+ v += reset;
+ return true;
+ }
+ else return false;
+ }
+
+
+ int tick(int c)
+ {
+ APUchannel ch = channels[c];
+ int wl = ch.WaveLength;
+ if (c != 4) ++wl;
+ if (c < 2) wl *= 2;
+
+ if (c == 3) wl = NoisePeriods[ch.NoiseFreq];
+
+ //if(c != 4) wl = wl * (IO::UISpeed);
+ // ^ Match to the UI speed (but don't for DPCM, because it would skew the timings)
+
+ int volume = (bool)ch.length_counter ? (bool)ch.EnvDecayDisable ? (int)ch.FixedVolume : ch.envelope : 0;
+ // Sample may change at wavelen intervals.
+ int ref_S = ch.level;
+ int ref_ch_wave_counter = ch.wave_counter;
+ if (!(data.ChannelsEnabled[c] != 0)
+ || !count(ref ref_ch_wave_counter, wl))
+ {
+ ch.wave_counter.Value = ref_ch_wave_counter;
+ return ref_S;
+ }
+ ch.wave_counter.Value = ref_ch_wave_counter;
+ switch (c)
+ {
+ case 0:
+ default:
+ case 1: // Square wave. With four different 8-step binary waveforms (32 bits of data total).
+ ch.phase.Value++;
+ if (wl < 8) return ref_S;
+ if ((bool)ch.SweepEnable && !(bool)ch.SweepDecrease)
+ if (wl + (wl >> ch.SweepShift) >= 0x800)
+ return ref_S;
+ return ref_S = ch.level.Value = (0xF33C0C04u & (1u << (ch.phase % 8 + ch.DutyCycle * 8))) != 0 ? volume : 0;
+ case 2: // Triangle wave
+ if ((bool)ch.length_counter && (bool)ch.linear_counter && wl >= 3) ++ch.phase.Value;
+ return ref_S = ch.level.Value = (ch.phase & 15) ^ (((ch.phase & 16) != 0) ? 15 : 0);
+
+ case 3: // Noise: Linear feedback shift register
+ if (!(bool)ch.hold) ch.hold.Value = 1;
+ ch.hold.Value = (ch.hold >> 1)
+ | (((ch.hold ^ (ch.hold >> ((bool)ch.NoiseType ? 6 : 1))) & 1) << 14);
+ return ref_S = ch.level.Value = ((ch.hold & 1) != 0) ? 0 : volume;
+
+ case 4: // Delta modulation channel (DMC)
+ // hold = 8 bit value
+ // phase = number of bits buffered
+ // length_counter =
+ if (wl == 0) return ref_S;
+ if (ch.phase == 0) // Nothing in sample buffer?
+ {
+ if (!(bool)ch.length_counter && (bool)ch.LoopEnabled) // Loop?
+ {
+ ch.length_counter.Value = ch.PCMlength * 16 + 1;
+ ch.address.Value = (int)((ch.reg0 | 0x300) << 6);
+ }
+ if (ch.length_counter > 0) // Load next 8 bits if available
+ {
+ //==========================TODO============== DMC COST=====================
+ //for(unsigned t = data.DMC_CycleCost; t > 1; --t)
+ // CPU::RB(u16(ch.address) | 0x8000); // timing
+ ch.hold.Value = nes.ReadMemory((ushort)((ch.address.Value++) | 0x8000)); // Fetch byte
+ ch.phase.Value = 8;
+ --ch.length_counter.Value;
+ }
+ else // Otherwise, disable channel or issue IRQ
+ {
+ if ((bool)ch.IRQenable)
+ {
+ //CPU::reg.APU_DMC_IRQ = true;
+ dmc_irq = true;
+ SyncIRQ();
+ }
+
+ data.ChannelsEnabled[4] = 0;
+ }
+ }
+ if (ch.phase != 0) // Update the signal if sample buffer nonempty
+ {
+ int v = ch.linear_counter;
+ if (((ch.hold << --ch.phase.Value) & 0x80)!=0) v += 2; else v -= 2;
+ if (v >= 0 && v <= 0x7F) ch.linear_counter.Value = v;
+ }
+ return ref_S = ch.level = ch.linear_counter;
+ }
+ }
+
+ bool dmc_irq;
+ bool sequencer_irq;
+ public bool irq_pending;
+ void SyncIRQ()
+ {
+ irq_pending = sequencer_irq | dmc_irq;
+ }
+
+ NES nes;
+ public BisqAPU(NES nes)
+ {
+ this.nes = nes;
+ }
+
+ public void RunOne()
+ {
+ if (data.IRQ_delay != 0x7FFF)
+ {
+ if (data.IRQ_delay > 0) --data.IRQ_delay.Value;
+ else { sequencer_irq = true; SyncIRQ(); data.IRQ_delay.Value = 0x7FFF; }
+ }
+ if (data.frame_delay > 0)
+ --data.frame_delay.Value;
+ else
+ {
+ bool Do240 = true, Do120 = false;
+ data.frame_delay.Value += frame_period;
+ switch (data.frame.Value++)
+ {
+ case 0:
+ if (!(bool)data.IRQdisable && !(bool)data.FiveCycleDivider)
+ data.IRQ_delay.Value = frame_period * 4 + 2;
+ // passthru
+ goto case 2;
+ case 2:
+ Do120 = true;
+ break;
+ case 1:
+ data.frame_delay.Value -= 2;
+ break;
+ case 3:
+ data.frame.Value = 0;
+ if ((bool)data.FiveCycleDivider)
+ data.frame_delay.Value += frame_period - 6;
+ break;
+ }
+ // Some events are invoked at 96 Hz or 120 Hz rate. Others, 192 Hz or 240 Hz.
+ for (int c = 0; c < 4; ++c)
+ {
+ APUchannel ch = channels[c];
+ int wl = ch.WaveLength;
+
+ // 96/120 Hz events:
+ if (Do120)
+ {
+ // Length tick (all channels except DMC, but different disable bit for triangle wave)
+ if ((bool)ch.length_counter
+ && !(c == 2 ? (bool)ch.LinearCounterDisable : (bool)ch.LengthCounterDisable))
+ ch.length_counter.Value -= 1;
+
+ // Sweep tick (square waves only)
+ int ref_ch_sweep_delay = ch.sweep_delay;
+ if (c < 2 && count(ref ref_ch_sweep_delay, ch.SweepRate))
+ if (wl >= 8 && (bool)ch.SweepEnable && (bool)ch.SweepShift)
+ {
+ int s = wl >> ch.SweepShift;
+ wl += ((bool)ch.SweepDecrease ? ((c != 0) ? -s : ~s) : s);
+ if (wl < 0x800) ch.WaveLength.Value = (uint)wl;
+ }
+
+ ch.sweep_delay.Value = ref_ch_sweep_delay;
+ }
+
+ // 240/192 Hz events:
+ if (Do240)
+ {
+ // Linear tick (triangle wave only) (all ticks)
+ if (c == 2)
+ ch.linear_counter.Value = (bool)ch.LinearCounterDisable
+ ? ch.LinearCounterInit
+ : (ch.linear_counter > 0 ? ch.linear_counter - 1 : 0);
+
+ // Envelope tick (square and noise channels) (all ticks)
+ int ref_ch_env_delay = ch.env_delay;
+ if (c != 2 && count(ref ref_ch_env_delay, ch.EnvDecayRate))
+ if (ch.envelope > 0 || (bool)ch.EnvDecayLoopEnable)
+ ch.envelope.Value = (ch.envelope - 1) & 15;
+ ch.env_delay.Value = ref_ch_env_delay;
+ }
+
+ }
+ }
+
+ // Mix the audio: Get the momentary sample from each channel and mix them.
+ // #define s(c) tick()
+ //v = [](float m,float n, float d) { return n!=0.f ? m/n : d; };
+ Func v = (float m,float n, float d) => ( n!=0.0f ? m/n : d );
+
+ short sample = (short)(30000 *
+ (
+ // Square 0 and 1
+ v(95.88f, (100.0f + v(8128.0f, tick(0) + tick(1), -100.0f)), 0.0f)
+ // Triangle, noise, DMC
+ + v(159.79f, (100.0f + v(1.0f, tick(2)/8227.0f + tick(3)/12241.0f + tick(4)/22638.0f, -1000.0f)), 0.0f)
+ // GamePak audio (these volume values are bogus, but sound acceptable)
+ + v(95.88f, (100.0f + v(32512.0f, /*GamePak::ExtAudio()*/ 0, -100.0f)), 0.0f)
+ - 0.5f
+ ));
+
+ EmitSample(sample);
+
+ ////this (and the similar line below) is a crude hack
+ ////we should be generating logic to suppress the $4015 clear when the assert signal is set instead
+ ////be sure to test "apu_test" if you mess with this
+ //sequencer_irq |= sequencer_irq_assert;
+ }
+
+ void WriteC(int chno, int index, byte value)
+ {
+ APUchannel ch = channels[chno];
+ switch (index)
+ {
+ case 0:
+ if ((bool)ch.LinearCounterDisable) ch.linear_counter.Value = value & 0x7F;
+ ch.reg0.Value = value;
+ break;
+ case 1:
+ ch.reg1.Value = value;
+ ch.sweep_delay.Value = ch.SweepRate;
+ break;
+ case 2:
+ ch.reg2.Value = value;
+ break;
+ case 3:
+ ch.reg3.Value = value;
+ if (data.ChannelsEnabled[chno]!=0)
+ ch.length_counter.Value = LengthCounters[ch.LengthCounterInit];
+ ch.linear_counter.Value = ch.LinearCounterInit;
+ ch.env_delay.Value = ch.EnvDecayRate;
+ ch.envelope.Value = 15;
+ if (index < 8) ch.phase.Value = 0;
+ break;
+ case 0x12:
+ ch.reg0.Value = value;
+ ch.address.Value = ((int)ch.reg0 | 0x300) << 6;
+ break;
+ case 0x10:
+ ch.reg3.Value = value;
+ ch.WaveLength.Value = (uint)(DMCperiods[value & 0x0F] - 1);
+ if (!(bool)ch.IRQenable) { dmc_irq = false; SyncIRQ(); }
+ break;
+ case 0x13: // sample length
+ ch.reg1.Value = value;
+ if (ch.length_counter == 0)
+ ch.length_counter.Value = ch.PCMlength * 16 + 1;
+ break;
+ case 0x11: // dac value
+ ch.linear_counter.Value = value & 0x7F;
+ break;
+ case 0x15:
+ for (int c = 0; c < 5; ++c)
+ data.ChannelsEnabled[c] = (uint)((value >> c) & 1); //noteworthy tweak
+ for (int c = 0; c < 5; ++c)
+ if (data.ChannelsEnabled[c]==0)
+ channels[c].length_counter.Value = 0;
+ else if (c == 4 && channels[c].length_counter == 0)
+ {
+ APUchannel chh = channels[c];
+ chh.length_counter.Value = chh.PCMlength * 16 + 1;
+ chh.address.Value = ((int)chh.reg0 | 0x300) << 6;
+ chh.phase.Value = 0;
+ }
+ //CPU::reg.APU_DMC_IRQ = false;
+ dmc_irq = false; SyncIRQ();
+ break;
+ case 0x17:
+ data.IRQdisable.Value = (uint)(value & 0x40);
+ data.FiveCycleDivider.Value = (uint)(value & 0x80);
+ // apu_test 1-len_ctr: Writing $80 to $4017 should clock length immediately
+ // But Writing $00 to $4017 shouldn't clock length immediately
+ data.frame_delay.Value &= 1;
+ data.frame.Value = 0;
+ data.IRQ_delay.Value = 0x7FFF;
+ if ((bool)data.IRQdisable) { sequencer_irq = false; SyncIRQ(); }
+ if (!(bool)data.FiveCycleDivider)
+ {
+ data.frame.Value = 1;
+ data.frame_delay.Value += frame_period;
+
+ if (!(bool)data.IRQdisable)
+ {
+ data.IRQ_delay.Value = data.frame_delay + frame_period * 3 + 1 - 3;
+ // ^ "- 3" makes apu_test "4-jitter" not complain
+ // that "Frame irq is set too late"
+ }
+ }
+ break;
+ }
+ }
+
+ public byte ReadReg(int addr)
+ {
+ byte res = 0;
+ for (int c = 0; c < 5; ++c) res |= (byte)(((bool)channels[c].length_counter ? 1 << c : 0));
+ if (sequencer_irq) res |= 0x40; sequencer_irq = false; SyncIRQ();
+ if (dmc_irq) res |= 0x80;
+ return res;
+ }
+
+ public void WriteReg(int addr, byte val)
+ {
+ int index = addr - 0x4000;
+ WriteC((index / 4) % 5, index < 0x10 ? index % 4 : index, val);
+ }
+
+ public void NESSoftReset()
+ {
+ }
+
+ public void DiscardSamples()
+ {
+ metaspu.buffer.clear();
+ }
+
+ public void SyncState(Serializer ser)
+ {
+ }
+
+ double accumulate;
+ double timer;
+ Queue squeue = new Queue();
+ int last_hwsamp;
+ int panic_sample, panic_count;
+ void EmitSample(int samp)
+ {
+ //kill the annoying hum that is a consequence of the shitty code below
+ if (samp == panic_sample)
+ panic_count++;
+ else panic_count = 0;
+ if (panic_count > 178977)
+ samp = 0;
+ else
+ panic_sample = samp;
+
+ int this_samp = samp;
+ const double kMixRate = 44100.0 / 1789772.0;
+ const double kInvMixRate = (1 / kMixRate);
+ timer += kMixRate;
+ accumulate += samp;
+ if (timer <= 1)
+ return;
+
+ accumulate -= samp;
+ timer -= 1;
+ double ratio = (timer / kMixRate);
+ double fractional = (this_samp - last_hwsamp) * ratio;
+ double factional_remainder = (this_samp - last_hwsamp) * (1 - ratio);
+ accumulate += fractional;
+
+ accumulate *= 436; //32768/(15*4) -- adjust later for other sound channels
+ int outsamp = (int)(accumulate / kInvMixRate);
+ if (CFG_USE_METASPU)
+ metaspu.buffer.enqueue_sample((short)outsamp, (short)outsamp);
+ else squeue.Enqueue(outsamp);
+ accumulate = factional_remainder;
+
+ last_hwsamp = this_samp;
+ }
+
+ MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
+ public int MaxVolume { get; set; } // not supported
+
+ void ISoundProvider.GetSamples(short[] samples)
+ {
+ if (CFG_USE_METASPU)
+ {
+ metaspu.GetSamples(samples);
+ //foreach(short sample in samples) bw.Write((short)sample);
+ }
+ else
+ MyGetSamples(samples);
+
+ //mix in the cart's extra sound circuit
+ nes.board.ApplyCustomAudio(samples);
+ }
+
+ //static BinaryWriter bw = new BinaryWriter(new FileStream("d:\\out.raw",FileMode.Create,FileAccess.Write,FileShare.Read));
+ void MyGetSamples(short[] samples)
+ {
+ //Console.WriteLine("a: {0} with todo: {1}",squeue.Count,samples.Length/2);
+
+ for (int i = 0; i < samples.Length / 2; i++)
+ {
+ int samp = 0;
+ if (squeue.Count != 0)
+ samp = squeue.Dequeue();
+
+ samples[i * 2 + 0] = (short)(samp);
+ samples[i * 2 + 1] = (short)(samp);
+ //bw.Write((short)samp);
+ }
+ }
+
+ } //class BisqAPU
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SxROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SxROM.cs
index 9256da9adb..19c4fea07f 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SxROM.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/SxROM.cs
@@ -163,7 +163,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
break;
}
//board.NES.LogLine("mapping.. chr_mode={0}, chr={1},{2}", chr_mode, chr_0, chr_1);
- board.NES.LogLine("mapping.. prg_mode={0}, prg_slot{1}, prg={2}", prg_mode, prg_slot, prg);
+ //board.NES.LogLine("mapping.. prg_mode={0}, prg_slot{1}, prg={2}", prg_mode, prg_slot, prg);
}
public int Get_PRGBank(int addr)