BizHawk/ExternalCoreProjects/Virtu/Speaker.cs

103 lines
2.4 KiB
C#

namespace Jellyfish.Virtu
{
public interface ISpeaker
{
void ToggleOutput();
// ReSharper disable once UnusedMember.Global
void Clear();
// ReSharper disable once UnusedMember.Global
void GetSamples(out short[] samples, out int nSamp);
// ReSharper disable once UnusedMember.Global
void Sync(IComponentSerializer ser);
}
// ReSharper disable once UnusedMember.Global
public sealed class Speaker : ISpeaker
{
private const int CyclesPerFlush = 23;
private readonly MachineEvents _events;
private readonly ICpu _cpu;
private bool _isHigh;
private int _highCycles;
private int _totalCycles;
private long _lastCycles;
// only relevant if trying to savestate mid-frame
private readonly short[] _buffer = new short[4096];
private int _position;
public Speaker(MachineEvents events, ICpu cpu)
{
_events = events;
_cpu = cpu;
_events.AddEventDelegate(EventCallbacks.FlushOutput, FlushOutputEvent);
_events.AddEvent(CyclesPerFlush * _cpu.Multiplier, EventCallbacks.FlushOutput);
_isHigh = false;
_highCycles = _totalCycles = 0;
}
public void Sync(IComponentSerializer ser)
{
ser.Sync(nameof(_isHigh), ref _isHigh);
ser.Sync(nameof(_highCycles), ref _highCycles);
ser.Sync(nameof(_totalCycles), ref _totalCycles);
ser.Sync(nameof(_lastCycles), ref _lastCycles);
}
public void Clear()
{
_position = 0;
}
public void GetSamples(out short[] samples, out int nSamp)
{
samples = _buffer;
nSamp = _position / 2;
_position = 0;
}
public void ToggleOutput()
{
UpdateCycles();
_isHigh ^= true;
}
private void FlushOutputEvent()
{
UpdateCycles();
// TODO: better than simple decimation here!!
Output(_highCycles * short.MaxValue / _totalCycles);
_highCycles = _totalCycles = 0;
_events.AddEvent(CyclesPerFlush * _cpu.Multiplier, EventCallbacks.FlushOutput);
}
private void UpdateCycles()
{
int delta = (int)(_cpu.Cycles - _lastCycles);
if (_isHigh)
{
_highCycles += delta;
}
_totalCycles += delta;
_lastCycles = _cpu.Cycles;
}
private void Output(int data) // machine thread
{
data = (int)(data * 0.2);
if (_position < _buffer.Length - 2)
{
_buffer[_position++] = (short)data;
_buffer[_position++] = (short)data;
}
}
}
}