diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs index 5e4318dc54..2af80d8ffe 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs @@ -34,6 +34,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo bool vs_io = false; bool vs_coin1; bool vs_coin2; + /// clock speed of the main cpu in hz + public int cpuclockrate { get; private set; } //irq state management public bool irq_apu { get { return _irq_apu; } set { _irq_apu = value; } } @@ -84,7 +86,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public void GetSamples(short[] samples) { - Console.WriteLine("Sync: {0}", nes.apu.dlist.Count); + //Console.WriteLine("Sync: {0}", nes.apu.dlist.Count); int nsamp = samples.Length / 2; if (nsamp > blipbuffsize) // oh well. nsamp = blipbuffsize; @@ -107,7 +109,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public void GetSamples(out short[] samples, out int nsamp) { - Console.WriteLine("ASync: {0}", nes.apu.dlist.Count); + //Console.WriteLine("ASync: {0}", nes.apu.dlist.Count); foreach (var d in nes.apu.dlist) blip.AddDelta(d.time, d.value); nes.apu.dlist.Clear(); @@ -165,12 +167,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo ports[0] = new JoypadPortDevice(this, 0); ports[1] = new JoypadPortDevice(this, 1); - BoardSystemHardReset(); - // don't replace the magicSoundProvider on reset, as it's not needed // if (magicSoundProvider != null) magicSoundProvider.Dispose(); - // set up region switch (cart.system) { @@ -181,26 +180,23 @@ namespace BizHawk.Emulation.Consoles.Nintendo ppu.region = PPU.Region.PAL; CoreOutputComm.VsyncNum = 50; CoreOutputComm.VsyncDen = 1; + cpuclockrate = 1662607; cpu_sequence = cpu_sequence_PAL; - if (magicSoundProvider == null) - magicSoundProvider = new MagicSoundProvider(this, 1662607); break; case "NES-NTSC": case "Famicom": apu = new APU(this, apu, false); ppu.region = PPU.Region.NTSC; + cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; - if (magicSoundProvider == null) - magicSoundProvider = new MagicSoundProvider(this, 1789773); break; // there's no official name for these in bootgod, not sure what we should use //case "PC10"://TODO case "VS": apu = new APU(this, apu, false); ppu.region = PPU.Region.RGB; + cpuclockrate = 1789773; cpu_sequence = cpu_sequence_NTSC; - if (magicSoundProvider == null) - magicSoundProvider = new MagicSoundProvider(this, 1789773); vs_io = true; break; // this is in bootgod, but not used at all @@ -209,9 +205,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo ppu.region = PPU.Region.Dendy; CoreOutputComm.VsyncNum = 50; CoreOutputComm.VsyncDen = 1; + cpuclockrate = 1773448; cpu_sequence = cpu_sequence_NTSC; - if (magicSoundProvider == null) - magicSoundProvider = new MagicSoundProvider(this, 1773448); break; case null: Console.WriteLine("Unknown NES system! Defaulting to NTSC."); @@ -220,6 +215,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo Console.WriteLine("Unrecognized NES system \"{0}\"! Defaulting to NTSC.", cart.system); goto case "NES-NTSC"; } + if (magicSoundProvider == null) + magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate); + + BoardSystemHardReset(); //check fceux's PowerNES function for more information: //relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs index 489eff95f9..7cca1eaec5 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs @@ -116,7 +116,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo Cart.board_type = "FAMICOM_DISK_SYSTEM"; diskdrive = new RamAdapter(); - audio = new FDSAudio(); + audio = new FDSAudio(NES.cpuclockrate); InsertSide(0); // set mirroring?? @@ -386,5 +386,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo { audio.ApplyCustomAudio(samples); } + + public override void Dispose() + { + base.Dispose(); + if (audio != null) + { + audio.Dispose(); + audio = null; + } + } } } diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDSAudio.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDSAudio.cs index abf18d80e1..65cab075f2 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDSAudio.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDSAudio.cs @@ -6,7 +6,7 @@ using System.Text; namespace BizHawk.Emulation.Consoles.Nintendo { // http://wiki.nesdev.com/w/index.php/FDS_audio - public class FDSAudio + public class FDSAudio : IDisposable { public void SyncState(Serializer ser) { @@ -99,11 +99,25 @@ namespace BizHawk.Emulation.Consoles.Nintendo int latchedoutput; - /// - /// enough room to hold roughly one frame of final output, 0-2047 - /// - short[] samplebuff = new short[32768]; - int samplebuffpos = 0; + public FDSAudio(int m2rate) + { + // minor hack: due to the way the initialization sequence goes, this might get called + // with m2rate = 0. such an instance will never be asked for samples, though + if (m2rate > 0) + { + blip = new Sound.Utilities.BlipBuffer(blipsize); + blip.SetRates(m2rate, 44100); + } + } + + public void Dispose() + { + if (blip != null) + { + blip.Dispose(); + blip = null; + } + } void CalcMod() { @@ -134,7 +148,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo tmp *= waveramoutput; tmp *= mastervol_num; tmp /= mastervol_den; - latchedoutput = tmp; + + if (latchedoutput != tmp) + { + dlist.Add(new Delta(sampleclock, tmp - latchedoutput)); + latchedoutput = tmp; + } } /// @@ -206,9 +225,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo CalcOut(); } } - samplebuff[samplebuffpos++] = (short)latchedoutput; - // if for some reason ApplyCustomAudio() is not called, glitch up but don't crash - samplebuffpos &= 32767; + sampleclock++; } public void WriteReg(int addr, byte value) @@ -309,30 +326,54 @@ namespace BizHawk.Emulation.Consoles.Nintendo return ret; } - Sound.Utilities.DCFilter dc = Sound.Utilities.DCFilter.DetatchedMode(4096); + Sound.Utilities.BlipBuffer blip; + + struct Delta + { + public uint time; + public int value; + public Delta(uint time, int value) + { + this.time = time; + this.value = value; + } + } + List dlist = new List(); + + uint sampleclock = 0; + const int blipsize = 4096; + + short[] mixout = new short[blipsize]; public void ApplyCustomAudio(short[] samples) { - for (int i = 0; i < samples.Length; i += 2) + int nsamp = samples.Length / 2; + if (nsamp > blipsize) // oh well. + nsamp = blipsize; + uint targetclock = (uint)blip.ClocksNeeded(nsamp); + foreach (var d in dlist) { - // worst imaginable resampling - int pos = i * samplebuffpos / samples.Length; - int samp = samplebuff[pos] * 6 - 12096; - samp += samples[i]; - if (samp > 32767) - samples[i] = 32767; - else if (samp < -32768) - samples[i] = -32768; - else - samples[i] = (short)samp; - - // NES audio is mono, so this should be identical anyway - samples[i + 1] = samples[i]; + // original deltas are in -2016..2016 + blip.AddDelta(d.time * targetclock / sampleclock, d.value * 6); } - //Console.WriteLine("##{0}##", samplebuffpos); - samplebuffpos = 0; + //Console.WriteLine("sclock {0} tclock {1} ndelta {2}", sampleclock, targetclock, dlist.Count); + dlist.Clear(); + blip.EndFrame(targetclock); + sampleclock = 0; + blip.ReadSamples(mixout, nsamp, false); - dc.PushThroughSamples(samples, samples.Length); + for (int i = 0, j = 0; i < nsamp; i++, j += 2) + { + int s = mixout[i] + samples[j]; + if (s > 32767) + samples[j] = 32767; + else if (s <= -32768) + samples[j] = -32768; + else + samples[j] = (short)s; + // nes audio is mono, so we can ignore the original value of samples[j+1] + samples[j + 1] = samples[j]; + } } } }