diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b1b66ac5c4..098bc564b2 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -319,7 +319,6 @@ - @@ -1583,6 +1582,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index ccd2065e66..e18c3ae68e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -16,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private SpectrumBase _machine { get; set; } private Z80A _cpu { get; set; } - private IBeeperDevice _buzzer { get; set; } + private OneBitBeeper _buzzer { get; set; } /// /// Default constructor diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs deleted file mode 100644 index 8ee5dc0067..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs +++ /dev/null @@ -1,216 +0,0 @@ -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Logical Beeper class - /// Represents the piezoelectric buzzer used in the Spectrum to produce sound - /// The beeper is controlled by rapidly toggling bit 4 of port &FE - /// It is instantiated twice, once for speccy beeper output, and once tape buzzer emulation - /// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame - /// (so that it can be mixed easily further down the line) - /// - public class Beeper : ISoundProvider, IBeeperDevice - { - #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; } - } - - /// - /// Buzzer volume - /// Accepts an int 0-100 value - /// - private int _volume; - public int Volume - { - get - { - return VolumeConverterOut(_volume); - } - set - { - var newVol = VolumeConverterIn(value); - if (newVol != _volume) - blip.Clear(); - _volume = VolumeConverterIn(value); - } - } - - /// - /// The last used volume (used to modify blipbuffer delta values) - /// - private int lastVolume; - - /// - /// The number of cpu cycles per frame - /// - private long _tStatesPerFrame; - - /// - /// The parent emulated machine - /// - private SpectrumBase _machine; - - /// - /// The last pulse - /// - private bool LastPulse; - - /// - /// The last T-State (cpu cycle) that the last pulse was received - /// - private long LastPulseTState; - - /// - /// Device blipbuffer - /// - private readonly BlipBuffer blip = new BlipBuffer(882); - - #endregion - - #region Private Methods - - /// - /// Takes an int 0-100 and returns the relevant short volume to output - /// - /// - /// - private int VolumeConverterIn(int vol) - { - int maxLimit = short.MaxValue / 3; - int increment = maxLimit / 100; - - return vol * increment; - } - - /// - /// Takes an short volume and returns the relevant int value 0-100 - /// - /// - /// - private int VolumeConverterOut(int shortvol) - { - int maxLimit = short.MaxValue / 3; - int increment = maxLimit / 100; - - if (shortvol > maxLimit) - shortvol = maxLimit; - - return shortvol / increment; - } - - #endregion - - #region Construction & Initialisation - - public Beeper(SpectrumBase machine) - { - _machine = machine; - } - - /// - /// Initialises the beeper - /// - public void Init(int sampleRate, int tStatesPerFrame) - { - blip.SetRates((tStatesPerFrame * 50), sampleRate); - _sampleRate = sampleRate; - _tStatesPerFrame = tStatesPerFrame; - } - - #endregion - - #region IBeeperDevice - - /// - /// Processes an incoming pulse value and adds it to the blipbuffer - /// - /// - public void ProcessPulseValue(bool pulse) - { - if (!_machine._renderSound) - return; - - if (LastPulse == pulse) - { - // no change - blip.AddDelta((uint)_machine.CurrentFrameCycle, 0); - } - - else - { - if (pulse) - blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume)); - else - blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume)); - - lastVolume = _volume; - } - - LastPulse = pulse; - } - - #endregion - - #region ISoundProvider - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode != SyncSoundMode.Sync) - throw new InvalidOperationException("Only Sync mode is supported."); - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - blip.Clear(); - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - blip.EndFrame((uint)_tStatesPerFrame); - nsamp = blip.SamplesAvailable(); - samples = new short[nsamp * 2]; - blip.ReadSamples(samples, nsamp, true); - for (int i = 0; i < nsamp * 2; i += 2) - { - samples[i + 1] = samples[i]; - } - } - - #endregion - - #region State Serialization - - public void SyncState(Serializer ser) - { - ser.BeginSection("Buzzer"); - ser.Sync(nameof(_tStatesPerFrame), ref _tStatesPerFrame); - ser.Sync(nameof(_sampleRate), ref _sampleRate); - ser.Sync(nameof(LastPulse), ref LastPulse); - ser.Sync(nameof(LastPulseTState), ref LastPulseTState); - ser.EndSection(); - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index eabdc7ff30..b18ad12a2c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -97,7 +97,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - _cpu.ExecuteOne(); + _cpu.ExecuteOne(); } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.Port.cs index 033ca1f29d..d4507749ff 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.Port.cs @@ -162,9 +162,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.BorderColor = value & BORDER_BIT; } - // Buzzer - BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); - TapeDevice.WritePort(port, value); + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); + TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.cs index ea76f7a511..c46e265ccf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -30,11 +31,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ScreenPentagon128(this); - BuzzerDevice = new Beeper(this); - BuzzerDevice.Init(44100, ULADevice.FrameLength); + BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); - TapeBuzzer = new Beeper(this); - TapeBuzzer.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer"); AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 8137e21ea5..56db60c5b0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -1,5 +1,6 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -40,12 +41,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The spectrum buzzer/beeper /// - public IBeeperDevice BuzzerDevice { get; set; } + public OneBitBeeper BuzzerDevice { get; set; } /// /// A second beeper for the tape /// - public IBeeperDevice TapeBuzzer { get; set; } + public OneBitBeeper TapeBuzzer { get; set; } /// /// Device representing the AY-3-8912 chip found in the 128k and up spectrums @@ -164,8 +165,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // run the CPU Monitor cycle CPUMon.ExecuteCycle(); - // cycle the tape device - if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + // clock the beepers + TapeBuzzer.SetClock((int)CurrentFrameCycle); + BuzzerDevice.SetClock((int)CurrentFrameCycle); + + // cycle the tape device + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) TapeDevice.TapeCycle(); // has frame end been reached? 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 1ec92a69f6..c1839dcaf3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -157,9 +157,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.BorderColor = value & BORDER_BIT; } - // Buzzer - BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); - TapeDevice.WritePort(port, value); + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); + TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 009c882d38..2a3884c9b2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -30,13 +31,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new Screen128(this); - BuzzerDevice = new Beeper(this); - BuzzerDevice.Init(44100, ULADevice.FrameLength); + BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); - TapeBuzzer = new Beeper(this); - TapeBuzzer.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer"); - AYDevice = new AY38912(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); 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 7d6a6a53a4..1e3af11048 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -157,11 +157,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.BorderColor = value & BORDER_BIT; } - // Buzzer - BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); - // Tape - TapeDevice.WritePort(port, value); + // Tape + TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index b38ba2ef0a..ffde2726f7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -1,5 +1,6 @@ using BizHawk.Emulation.Cores.Components.Z80A; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -30,13 +31,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new Screen128Plus2a(this); - BuzzerDevice = new Beeper(this); - BuzzerDevice.Init(44100, ULADevice.FrameLength); + BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); - TapeBuzzer = new Beeper(this); - TapeBuzzer.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer"); - AYDevice = new AY38912(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); 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 ac662665d3..eb87580a99 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -164,11 +164,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.BorderColor = value & BORDER_BIT; } - // Buzzer - BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); - // Tape - TapeDevice.WritePort(port, value); + // Tape + TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index f681ec3bd0..98576ab5a4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -1,5 +1,6 @@ using BizHawk.Emulation.Cores.Components.Z80A; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -30,13 +31,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new Screen128Plus2a(this); - BuzzerDevice = new Beeper(this); - BuzzerDevice.Init(44100, ULADevice.FrameLength); + BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); - TapeBuzzer = new Beeper(this); - TapeBuzzer.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer"); - AYDevice = new AY38912(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); 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 b6d175028b..75aa48425b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -91,7 +91,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // Buzzer - BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); // Tape TapeDevice.WritePort(port, value); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 3291f5c20a..caf5ee440f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -1,6 +1,7 @@ using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -24,13 +25,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPUMon = new CPUMonitor(this); ULADevice = new Screen48(this); - BuzzerDevice = new Beeper(this); - BuzzerDevice.Init(44100, ULADevice.FrameLength); + BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); - TapeBuzzer = new Beeper(this); - TapeBuzzer.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer"); - KeyboardDevice = new StandardKeyboard(this); + KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 096938717d..e03bbc5258 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -36,11 +37,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } if (_machine != null && _machine.BuzzerDevice != null) { - ((Beeper)_machine.BuzzerDevice as Beeper).Volume = o.EarVolume; + ((OneBitBeeper)_machine.BuzzerDevice as OneBitBeeper).Volume = o.EarVolume; } if (_machine != null && _machine.TapeBuzzer != null) { - ((Beeper)_machine.TapeBuzzer as Beeper).Volume = o.TapeVolume; + ((OneBitBeeper)_machine.TapeBuzzer as OneBitBeeper).Volume = o.TapeVolume; } Settings = o; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 4807ffbc37..5fe2b30996 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -130,12 +131,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_machine.BuzzerDevice != null) { - ((Beeper)_machine.BuzzerDevice as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((OneBitBeeper)_machine.BuzzerDevice as OneBitBeeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } if (_machine.TapeBuzzer != null) { - ((Beeper)_machine.TapeBuzzer as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; + ((OneBitBeeper)_machine.TapeBuzzer as OneBitBeeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; } DCFilter dc = new DCFilter(SoundMixer, 512); diff --git a/BizHawk.Emulation.Cores/Sound/OneBitBeeper.cs b/BizHawk.Emulation.Cores/Sound/OneBitBeeper.cs new file mode 100644 index 0000000000..09b0bb5409 --- /dev/null +++ b/BizHawk.Emulation.Cores/Sound/OneBitBeeper.cs @@ -0,0 +1,194 @@ +using System; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Sound +{ + /// + /// A simple 1-bit (mono) beeper/buzzer implementation using blipbuffer + /// Simulating the piezzo-electric buzzer found in many old computers (such as the ZX Spectrum or Amstrad CPC) + /// Sound is generated by toggling the single input line ON and OFF rapidly + /// + public sealed class OneBitBeeper : ISoundProvider + { + private int _sampleRate; + private int _clocksPerFrame; + private int _framesPerSecond; + private BlipBuffer _blip; + private readonly string _beeperId; + + /// + /// Constructor + /// + /// The sample rate to pass to blipbuffer (this should be 44100 for ISoundProvider) + /// The number of (usually CPU) clocked cycles in one frame + /// The number of frames per second (usually either 60 or 50) + /// Unique name for this instance (needed for serialization as some cores have more than one active instance of the beeper) + public OneBitBeeper(int blipSampleRate, int clocksPerFrame, int framesPerSecond, string beeperId) + { + _beeperId = beeperId; + _sampleRate = blipSampleRate; + _clocksPerFrame = clocksPerFrame; + _framesPerSecond = framesPerSecond; + _blip = new BlipBuffer(blipSampleRate / framesPerSecond); + _blip.SetRates(clocksPerFrame * 50, blipSampleRate); + } + + private int clockCounter; + + /// + /// Option to clock the beeper every CPU clock + /// + public void Clock() + { + clockCounter++; + } + + /// + /// Option to directly set the current clock position within the frame + /// + public void SetClock(int currentFrameClock) + { + clockCounter = currentFrameClock; + } + + private bool lastPulse; + + /// + /// Processes an incoming pulse value + /// + /// + public void ProcessPulseValue(bool pulse, bool renderSound = true) + { + if (!renderSound) + return; + + if (lastPulse == pulse) + { + // no change + _blip.AddDelta((uint)clockCounter, 0); + } + + else + { + if (pulse) + _blip.AddDelta((uint)clockCounter, (short)(_volume)); + else + _blip.AddDelta((uint)clockCounter, -(short)(_volume)); + + lastVolume = _volume; + } + + lastPulse = pulse; + } + + #region Volume Handling + + /// + /// Beeper volume + /// Accepts an int 0-100 value + /// + public int Volume + { + get { return VolumeConverterOut(_volume); } + set + { + var newVol = VolumeConverterIn(value); + if (newVol != _volume) + _blip.Clear(); + _volume = VolumeConverterIn(value); + } + } + private int _volume; + + /// + /// The last used volume (used to modify blipbuffer delta values) + /// + private int lastVolume; + + + /// + /// Takes an int 0-100 and returns the relevant short volume to output + /// + /// + /// + private int VolumeConverterIn(int vol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + return vol * increment; + } + + /// + /// Takes an short volume and returns the relevant int value 0-100 + /// + /// + /// + private int VolumeConverterOut(int shortvol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + if (shortvol > maxLimit) + shortvol = maxLimit; + + return shortvol / increment; + } + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + _blip.Clear(); + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + _blip.EndFrame((uint)_clocksPerFrame); + nsamp = _blip.SamplesAvailable(); + samples = new short[nsamp * 2]; + _blip.ReadSamples(samples, nsamp, true); + for (int i = 0; i < nsamp * 2; i += 2) + { + samples[i + 1] = samples[i]; + } + + clockCounter = 0; + } + + #endregion + + #region State Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("Beeper_" + _beeperId); + ser.Sync(nameof(_sampleRate), ref _sampleRate); + ser.Sync(nameof(_clocksPerFrame), ref _clocksPerFrame); + ser.Sync(nameof(_framesPerSecond), ref _framesPerSecond); + ser.Sync(nameof(clockCounter), ref clockCounter); + ser.Sync(nameof(lastPulse), ref lastPulse); + ser.EndSection(); + } + + #endregion + } +}