116 lines
2.5 KiB
C#
116 lines
2.5 KiB
C#
using System;
|
|
using System.Runtime.Serialization;
|
|
using Newtonsoft.Json;
|
|
|
|
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);
|
|
}
|
|
|
|
public sealed class Speaker : ISpeaker
|
|
{
|
|
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
|
private MachineEvents _events;
|
|
|
|
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
|
private ICpu _cpu;
|
|
|
|
public Speaker() { }
|
|
public Speaker(MachineEvents events, ICpu cpu)
|
|
{
|
|
_events = events;
|
|
_cpu = cpu;
|
|
_flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage
|
|
|
|
_events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent);
|
|
|
|
_isHigh = false;
|
|
_highCycles = _totalCycles = 0;
|
|
}
|
|
|
|
private const int CyclesPerFlush = 23;
|
|
|
|
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
|
private Action _flushOutputEvent;
|
|
|
|
private bool _isHigh;
|
|
private int _highCycles;
|
|
private int _totalCycles;
|
|
private long _lastCycles;
|
|
|
|
[JsonIgnore] // only relevant if trying to savestate mid-frame
|
|
private readonly short[] _buffer = new short[4096];
|
|
|
|
[JsonIgnore] // only relevant if trying to savestate mid-frame
|
|
private int _position;
|
|
|
|
#region Api
|
|
|
|
public void Clear()
|
|
{
|
|
_position = 0;
|
|
}
|
|
|
|
public void GetSamples(out short[] samples, out int nSamp)
|
|
{
|
|
samples = _buffer;
|
|
nSamp = _position / 2;
|
|
_position = 0;
|
|
}
|
|
|
|
#endregion
|
|
|
|
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, _flushOutputEvent);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
[OnDeserialized]
|
|
private void OnDeserialized(StreamingContext context)
|
|
{
|
|
_position = 0;
|
|
}
|
|
}
|
|
}
|