diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs index 7367e3948f..2e4d2dea50 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs @@ -11,14 +11,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { void Init(int sampleRate, int tStatesPerFrame); - void ProcessPulseValue(bool fromTape, bool earPulse); + void ProcessPulseValue(bool pulse); void StartFrame(); void EndFrame(); - void SetTapeMode(bool tapeMode); - void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index c67ce41ee4..3ca036d3bc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -151,10 +151,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void StartFrame() { - //if (TapeIsPlaying && AutoPlay) - //FlashLoad(); - - _buzzer.ProcessPulseValue(true, currentState); + //_buzzer.ProcessPulseValue(currentState); } #endregion @@ -169,8 +166,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; - _buzzer.SetTapeMode(true); - _machine.Spectrum.OSD_TapePlaying(); // update the lastCycle @@ -224,8 +219,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; - _buzzer.SetTapeMode(false); - _machine.Spectrum.OSD_TapeStopped(); // sign that the tape is no longer playing @@ -384,16 +377,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region Tape Device Methods - - - + #region Tape Device Methods /// - /// Runs every frame + /// Is called every cpu cycle but runs every 50 t-states + /// This enables the tape devices to play out even if the spectrum itself is not + /// requesting tape data /// public void TapeCycle() - { + { if (TapeIsPlaying) { counter++; @@ -402,7 +394,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { counter = 0; bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); - _buzzer.ProcessPulseValue(false, state); + _buzzer.ProcessPulseValue(state); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 3a9516a63b..1f6b9ad20c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -1,10 +1,8 @@  using BizHawk.Common; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; using System; using System.Collections.Generic; -using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -14,30 +12,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// For the purposes of emulation this devices is locked to a frame /// a list of Pulses is built up over the course of the frame and outputted at the end of the frame + /// + /// ZXHawk instantiates multiples of these buzzers to achieve separation between tape and spectrum audio output /// public class Buzzer : ISoundProvider, IBeeperDevice { - /// - /// Supplied values are right for 48K spectrum - /// These will deviate for 128k and up (as there are more T-States per frame) - /// - //public int SampleRate = 44100; //35000; - //public int SamplesPerFrame = 882; //699; - //public int TStatesPerSample = 79; //100; - + #region Fields and Properties + /// /// Sample Rate /// This usually has to be 44100 for ISoundProvider /// + private int _sampleRate; public int SampleRate { get { return _sampleRate; } set { _sampleRate = value; } } - + /// /// Number of samples in one frame /// + private int _samplesPerFrame; public int SamplesPerFrame { get { return _samplesPerFrame; } @@ -47,6 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Number of TStates in each sample /// + private int _tStatesPerSample; public int TStatesPerSample { get { return _tStatesPerSample; } @@ -54,37 +51,55 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// The tape loading volume + /// Buzzer volume /// Accepts an int 0-100 value /// - private int _tapeVolume; - public int TapeVolume + private int _volume; + public int Volume { get { - return VolumeConverterOut(_tapeVolume); + return VolumeConverterOut(_volume); } set { - _tapeVolume = VolumeConverterIn(value); + _volume = VolumeConverterIn(value); } } /// - /// The EAR beeper volume + /// The number of cpu cycles per frame /// - private int _earVolume; - public int EarVolume - { - get - { - return VolumeConverterOut(_earVolume); - } - set - { - _earVolume = VolumeConverterIn(value); - } - } + private long _tStatesPerFrame; + + /// + /// The cycle at which the frame starts + /// + private long _frameStart; + + /// + /// The parent emulated machine + /// + private SpectrumBase _machine; + + /// + /// Pulses collected during the last frame + /// + public List Pulses { get; private set; } + + /// + /// The last pulse + /// + public bool LastPulse { get; set; } + + /// + /// The last T-State (cpu cycle) that the last pulse was received + /// + public long LastPulseTState { get; set; } + + #endregion + + #region Private Methods /// /// Takes an int 0-100 and returns the relevant short volume to output @@ -115,33 +130,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return shortvol / increment; } - - private SpectrumBase _machine; - - /// - /// State fields - /// - private long _frameStart; - private bool _tapeMode; - private long _tStatesPerFrame; - private int _sampleRate; - private int _samplesPerFrame; - private int _tStatesPerSample; - - /// - /// Pulses collected during the last frame - /// - public List Pulses { get; private set; } - - /// - /// The last pulse - /// - public bool LastPulse { get; set; } - - /// - /// The last T-State (cpu cycle) that the last pulse was received - /// - public long LastPulseTState { get; set; } + #endregion #region Construction & Initialisation @@ -159,75 +148,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _tStatesPerFrame = tStatesPerFrame; _tStatesPerSample = 79; _samplesPerFrame = (int)_tStatesPerFrame / _tStatesPerSample; - - /* - - // set the tstatesperframe - _tStatesPerFrame = tStatesPerFrame; - - // calculate actual refresh rate - double refresh = (double)_machine.ULADevice.ClockSpeed / (double)_tStatesPerFrame; - - // how many samples per frame are expected by ISoundProvider (at 44.1KHz) - _samplesPerFrame = 880;// (int)((double)sampleRate / (double)refresh); - - // set the sample rate - _sampleRate = sampleRate; - - // calculate samples per frame (what ISoundProvider will be expecting at 44100) - //_samplesPerFrame = (int)((double)_tStatesPerFrame / (double)refresh); - - // calculate tstates per sameple - _tStatesPerSample = 79;// _tStatesPerFrame / _samplesPerFrame; - - /* - - - - - // get divisors - var divs = from a in Enumerable.Range(2, _tStatesPerFrame / 2) - where _tStatesPerFrame % a == 0 - select a; - - // get the highest int value under 120 (this will be TStatesPerSample) - _tStatesPerSample = divs.Where(a => a < 100).Last(); - - // get _samplesPerFrame - _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; - - */ Pulses = new List(1000); } #endregion + #region IBeeperDevice + /// - /// When the pulse value from the EAR output changes it is processed here + /// When the pulse value changes it is processed here /// - /// - /// - public void ProcessPulseValue(bool fromTape, bool earPulse) + /// + public void ProcessPulseValue(bool pulse) { if (!_machine._renderSound) return; - if (!fromTape && _tapeMode) - { - // tape mode is active but the pulse value came from an OUT instruction - // do not process the value - //return; - } - - if (earPulse == LastPulse) + if (pulse == LastPulse) { // no change detected return; } // set the lastpulse - LastPulse = earPulse; + LastPulse = pulse; // get where we are in the frame var currentULACycle = _machine.CurrentFrameCycle; @@ -244,7 +189,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add the pulse Pulse p = new Pulse { - State = !earPulse, + State = !pulse, Length = length }; Pulses.Add(p); @@ -259,7 +204,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void StartFrame() { - //DiscardSamples(); Pulses.Clear(); LastPulseTState = 0; } @@ -296,11 +240,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) { - if (_tapeMode) - samples[sampleIndex++] = pulse.State ? (short)(_tapeVolume) : (short)0; - else - samples[sampleIndex++] = pulse.State ? (short)(_earVolume) : (short)0; - } + samples[sampleIndex++] = pulse.State ? (short)(_volume) : (short)0; + } currentEnd += pulse.Length; } @@ -316,16 +257,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _frameStart += _tStatesPerFrame; } - /// - /// When the spectrum is set to receive tape input, the EAR output on the ULA is disabled - /// (so no buzzer sound is emitted) - /// - /// - public void SetTapeMode(bool tapeMode) - { - _tapeMode = tapeMode; - } - + #endregion #region ISoundProvider @@ -370,24 +302,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - + #region State Serialization + public void SyncState(Serializer ser) { ser.BeginSection("Buzzer"); ser.Sync("_frameStart", ref _frameStart); - ser.Sync("_tapeMode", ref _tapeMode); ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); ser.Sync("_sampleRate", ref _sampleRate); ser.Sync("_samplesPerFrame", ref _samplesPerFrame); ser.Sync("_tStatesPerSample", ref _tStatesPerSample); - ser.Sync("soundBuffer", ref soundBuffer, false); ser.Sync("soundBufferContains", ref soundBufferContains); ser.EndSection(); } - + #endregion } - - } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs index bcebb55542..f88922132c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -158,7 +158,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); TapeDevice.WritePort(port, value); // Tape diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs index 24e7a13760..425d33f275 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -250,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs index e56c23440b..8226326b11 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -177,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs index 0cb5b9c7c1..12f8850957 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -115,7 +115,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 5f721f233d..deb5d63eaa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -33,8 +33,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } if (_machine != null && _machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = o.TapeVolume; - ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = o.EarVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = o.EarVolume; + } + if (_machine != null && _machine.TapeBuzzer != null) + { + ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = o.TapeVolume; } Settings = o; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index cd4eabe7ba..f6c97c692e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -79,8 +79,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: throw new InvalidOperationException("Machine not yet emulated"); } - - _cpu.MemoryCallbacks = MemoryCallbacks; @@ -98,37 +96,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_cpu); ser.Register(_machine.ULADevice); + // initialize sound mixer and attach the various ISoundProvider devices SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); + SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - if (_machine.TapeBuzzer != null) - SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); - //SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; - + + // set audio device settings if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) { ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; } - + if (_machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; - ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } if (_machine.TapeBuzzer != null) { - ((Buzzer)_machine.TapeBuzzer as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; - //((Buzzer)_machine.TapeBuzzer as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; } - ser.Register(SoundMixer); HardReset(); - SetupMemoryDomains(); }