using System; using System.IO; using Jellyfish.Virtu.Services; namespace Jellyfish.Virtu { public sealed class Speaker : MachineComponent { public Speaker(Machine machine) : base(machine) { _flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage } public override void Initialize() { _audioService = Machine.Services.GetService(); Volume = 0.5f; Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent); } public override void Reset() { _audioService.Reset(); _isHigh = false; _highCycles = _totalCycles = 0; } public override void LoadState(BinaryReader reader, Version version) { if (reader == null) { throw new ArgumentNullException("reader"); } Volume = reader.ReadSingle(); } public override void SaveState(BinaryWriter writer) { if (writer == null) { throw new ArgumentNullException("writer"); } writer.Write(Volume); } public void ToggleOutput() { UpdateCycles(); _isHigh ^= true; } private void FlushOutputEvent() { UpdateCycles(); _audioService.Output(_highCycles * short.MaxValue / _totalCycles); // quick and dirty decimation _highCycles = _totalCycles = 0; Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent); } private void UpdateCycles() { int delta = (int)(Machine.Cpu.Cycles - _lastCycles); if (_isHigh) { _highCycles += delta; } _totalCycles += delta; _lastCycles = Machine.Cpu.Cycles; } public float Volume { get { return _volume; } set { _volume = value; _audioService.SetVolume(_volume); } } private const int CyclesPerFlush = 23; private Action _flushOutputEvent; private AudioService _audioService; private bool _isHigh; private int _highCycles; private int _totalCycles; private long _lastCycles; private float _volume; } }