ZXHawk: move the beeper implementation out of the core into Cores.Sound. The CPC core will also use this and we may have other cores in the future that want to make use of a nice 1-bit buzzer/implementation (tape loading, onboard speaker etc..)
This commit is contained in:
parent
7655e5194e
commit
ae7bea226c
|
@ -319,7 +319,6 @@
|
|||
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IFDDHost.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\StandardKeyboard.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\AY38912.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\Beeper.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\FloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IJoystick.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IPortIODevice.cs" />
|
||||
|
@ -1583,6 +1582,7 @@
|
|||
</Compile>
|
||||
<Compile Include="SideBySideVideo.cs" />
|
||||
<Compile Include="Sound\DualSyncSound.cs" />
|
||||
<Compile Include="Sound\OneBitBeeper.cs" />
|
||||
<Compile Include="Sound\SN76489sms.cs" />
|
||||
<Compile Include="Waterbox\CustomSaverammer.cs" />
|
||||
<Compile Include="Waterbox\ElfRunner.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; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
|
|
|
@ -1,216 +0,0 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
public class Beeper : ISoundProvider, IBeeperDevice
|
||||
{
|
||||
#region Fields and Properties
|
||||
|
||||
/// <summary>
|
||||
/// Sample Rate
|
||||
/// This usually has to be 44100 for ISoundProvider
|
||||
/// </summary>
|
||||
private int _sampleRate;
|
||||
public int SampleRate
|
||||
{
|
||||
get { return _sampleRate; }
|
||||
set { _sampleRate = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buzzer volume
|
||||
/// Accepts an int 0-100 value
|
||||
/// </summary>
|
||||
private int _volume;
|
||||
public int Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return VolumeConverterOut(_volume);
|
||||
}
|
||||
set
|
||||
{
|
||||
var newVol = VolumeConverterIn(value);
|
||||
if (newVol != _volume)
|
||||
blip.Clear();
|
||||
_volume = VolumeConverterIn(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The last used volume (used to modify blipbuffer delta values)
|
||||
/// </summary>
|
||||
private int lastVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The number of cpu cycles per frame
|
||||
/// </summary>
|
||||
private long _tStatesPerFrame;
|
||||
|
||||
/// <summary>
|
||||
/// The parent emulated machine
|
||||
/// </summary>
|
||||
private SpectrumBase _machine;
|
||||
|
||||
/// <summary>
|
||||
/// The last pulse
|
||||
/// </summary>
|
||||
private bool LastPulse;
|
||||
|
||||
/// <summary>
|
||||
/// The last T-State (cpu cycle) that the last pulse was received
|
||||
/// </summary>
|
||||
private long LastPulseTState;
|
||||
|
||||
/// <summary>
|
||||
/// Device blipbuffer
|
||||
/// </summary>
|
||||
private readonly BlipBuffer blip = new BlipBuffer(882);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Takes an int 0-100 and returns the relevant short volume to output
|
||||
/// </summary>
|
||||
/// <param name="vol"></param>
|
||||
/// <returns></returns>
|
||||
private int VolumeConverterIn(int vol)
|
||||
{
|
||||
int maxLimit = short.MaxValue / 3;
|
||||
int increment = maxLimit / 100;
|
||||
|
||||
return vol * increment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes an short volume and returns the relevant int value 0-100
|
||||
/// </summary>
|
||||
/// <param name="vol"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises the beeper
|
||||
/// </summary>
|
||||
public void Init(int sampleRate, int tStatesPerFrame)
|
||||
{
|
||||
blip.SetRates((tStatesPerFrame * 50), sampleRate);
|
||||
_sampleRate = sampleRate;
|
||||
_tStatesPerFrame = tStatesPerFrame;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IBeeperDevice
|
||||
|
||||
/// <summary>
|
||||
/// Processes an incoming pulse value and adds it to the blipbuffer
|
||||
/// </summary>
|
||||
/// <param name="pulse"></param>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -97,7 +97,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
}
|
||||
}
|
||||
|
||||
_cpu.ExecuteOne();
|
||||
_cpu.ExecuteOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// The spectrum buzzer/beeper
|
||||
/// </summary>
|
||||
public IBeeperDevice BuzzerDevice { get; set; }
|
||||
public OneBitBeeper BuzzerDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A second beeper for the tape
|
||||
/// </summary>
|
||||
public IBeeperDevice TapeBuzzer { get; set; }
|
||||
public OneBitBeeper TapeBuzzer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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?
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Sound
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public sealed class OneBitBeeper : ISoundProvider
|
||||
{
|
||||
private int _sampleRate;
|
||||
private int _clocksPerFrame;
|
||||
private int _framesPerSecond;
|
||||
private BlipBuffer _blip;
|
||||
private readonly string _beeperId;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="blipSampleRate">The sample rate to pass to blipbuffer (this should be 44100 for ISoundProvider)</param>
|
||||
/// <param name="clocksPerFrame">The number of (usually CPU) clocked cycles in one frame</param>
|
||||
/// <param name="framesPerSecond">The number of frames per second (usually either 60 or 50)</param>
|
||||
/// <param name="beeperId">Unique name for this instance (needed for serialization as some cores have more than one active instance of the beeper)</param>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Option to clock the beeper every CPU clock
|
||||
/// </summary>
|
||||
public void Clock()
|
||||
{
|
||||
clockCounter++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Option to directly set the current clock position within the frame
|
||||
/// </summary>
|
||||
public void SetClock(int currentFrameClock)
|
||||
{
|
||||
clockCounter = currentFrameClock;
|
||||
}
|
||||
|
||||
private bool lastPulse;
|
||||
|
||||
/// <summary>
|
||||
/// Processes an incoming pulse value
|
||||
/// </summary>
|
||||
/// <param name="pulse"></param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Beeper volume
|
||||
/// Accepts an int 0-100 value
|
||||
/// </summary>
|
||||
public int Volume
|
||||
{
|
||||
get { return VolumeConverterOut(_volume); }
|
||||
set
|
||||
{
|
||||
var newVol = VolumeConverterIn(value);
|
||||
if (newVol != _volume)
|
||||
_blip.Clear();
|
||||
_volume = VolumeConverterIn(value);
|
||||
}
|
||||
}
|
||||
private int _volume;
|
||||
|
||||
/// <summary>
|
||||
/// The last used volume (used to modify blipbuffer delta values)
|
||||
/// </summary>
|
||||
private int lastVolume;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Takes an int 0-100 and returns the relevant short volume to output
|
||||
/// </summary>
|
||||
/// <param name="vol"></param>
|
||||
/// <returns></returns>
|
||||
private int VolumeConverterIn(int vol)
|
||||
{
|
||||
int maxLimit = short.MaxValue / 3;
|
||||
int increment = maxLimit / 100;
|
||||
|
||||
return vol * increment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes an short volume and returns the relevant int value 0-100
|
||||
/// </summary>
|
||||
/// <param name="vol"></param>
|
||||
/// <returns></returns>
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue