diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs index ae8f517a48..3c3b5d9e1a 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs @@ -922,7 +922,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo last_hwsamp = this_samp; } - MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_Z); + MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V); void ISoundProvider.GetSamples(short[] samples) { diff --git a/BizHawk.Emulation/DiscSystem/Disc.API.cs b/BizHawk.Emulation/DiscSystem/Disc.API.cs index 2ee9867568..647f014308 100644 --- a/BizHawk.Emulation/DiscSystem/Disc.API.cs +++ b/BizHawk.Emulation/DiscSystem/Disc.API.cs @@ -104,11 +104,17 @@ namespace BizHawk.DiscSystem f = (byte) (lba - (m * 75 * 60) - (s * 75)); } + // converts MSF to LBA offset + public static int ConvertMSFtoLBA(byte m, byte s, byte f) + { + return f + (s*75) + (m*75*60); + } + // gets an identifying hash. hashes the first 512 sectors of // the first data track on the disc. public string GetHash() { - byte[] buffer = new byte[512*2353]; + byte[] buffer = new byte[512*2352]; foreach (var track in TOC.Sessions[0].Tracks) { if (track.TrackType == ETrackType.Audio) diff --git a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs index f27d3ef0b8..9c1b8defa0 100644 --- a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs +++ b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs @@ -40,6 +40,7 @@ namespace BizHawk.Emulation.Sound ESynchMethod_N, //nitsuja's ESynchMethod_Z, //zero's //ESynchMethod_P, //PCSX2 spu2-x //ohno! not available yet in c# + ESynchMethod_V // vecna }; public static class Metaspu @@ -52,6 +53,8 @@ namespace BizHawk.Emulation.Sound return new ZeromusSynchronizer(); case ESynchMethod.ESynchMethod_N: return new NitsujaSynchronizer(); + case ESynchMethod.ESynchMethod_V: + return new VecnaSynchronizer(); default: return new NitsujaSynchronizer(); } @@ -522,4 +525,128 @@ namespace BizHawk.Emulation.Sound }; //NitsujaSynchronizer + class VecnaSynchronizer : ISynchronizingAudioBuffer + { + // vecna's attempt at a fully synchronous sound provider. + // It's similar in philosophy to my "BufferedAsync" provider, but BufferedAsync is not + // fully synchronous. + + // Like BufferedAsync, it tries to make most frames 100% correct and just suck it up + // periodically and have a big bad-sounding mistake frame if it has to. + + // It is significantly less ambitious and elaborate than the other methods. + // We'll see if it works better or not! + + // It has a min and maximum amount of excess buffer to deal with minor overflows. + // When fastforwarding, it will discard samples above the maximum excess buffer. + + // When underflowing, it will attempt to resample to a certain threshhold. + // If it underflows beyond that threshhold, it will give up and output silence. + // Since it has done this, it will go ahead and generate some excess silence in order + // to restock its excess buffer. + + struct Sample + { + public short left, right; + public Sample(short l, short r) + { + left = l; + right = r; + } + } + + private Queue buffer; + private Sample[] resampleBuffer; + + private const int SamplesInOneFrame = 735; + private const int MaxExcessSamples = 2048; + + public VecnaSynchronizer() + { + buffer = new Queue(2048); + resampleBuffer = new Sample[2730]; // 2048 * 1.25 + + // Give us a little buffer wiggle-room + for (int i=0; i<367; i++) + buffer.Enqueue(new Sample(0,0)); + } + + public void enqueue_samples(short[] buf, int samples_provided) + { + throw new Exception("bluh"); + int samplesToEnqueue = samples_provided; + if (samples_provided + buffer.Count > MaxExcessSamples) + samplesToEnqueue = MaxExcessSamples - buffer.Count; + + //for (int i = 0; i < samplesToEnqueue; i++) + //buffer.Enqueue(buf[i]); + + Console.WriteLine("enqueue {0} samples, buffer at {1}/4096 max capacity, {2} excess samples", + samplesToEnqueue, buffer.Count, buffer.Count - SamplesInOneFrame); + } + + public void enqueue_sample(short left, short right) + { + if (buffer.Count >= MaxExcessSamples - 1) + { + // if buffer is overfull, dequeue old samples to make room for new samples. + buffer.Dequeue(); + } + buffer.Enqueue(new Sample(left, right)); + } + + public void clear() + { + Console.WriteLine("clear requested... but why! it makes me sad :'("); + buffer.Clear(); + } + + public int output_samples(short[] buf, int samples_requested) + { + if (samples_requested > buffer.Count) + { + // underflow! + if (buffer.Count > samples_requested * 3 / 4) + { + // if we're within 75% of target, then I guess we suck it up and resample. + // we sample in a goofy way, we could probably do it a bit smarter, if we cared more. + + Console.WriteLine("REASONABLE UNDERFLOW, RESAMPLING."); + + if (samples_requested > 2730) + throw new Exception("something rather bad has happened"); + + int samples_available = buffer.Count; + for (int i = 0; buffer.Count > 0; i++) + resampleBuffer[i] = buffer.Dequeue(); + + int index = 0; + for (int i = 0; i 0; i++) + { + Sample sample = buffer.Dequeue(); + buf[index++] = sample.left; + buf[index++] = sample.right; + } + } + return samples_requested; + } + } } \ No newline at end of file