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
+ }
+}