From ea2a8921138085355b84012ad1faa88cfece3fea Mon Sep 17 00:00:00 2001 From: Sean Fausett Date: Sun, 14 Mar 2010 21:54:17 +0000 Subject: [PATCH] Rewrote sound emulation to be much more accurate. Modified CPU throttling to sync with audio thread. Added CPU multiplier to machine settings. Bumped version to 0.8 for next release. --HG-- extra : convert_revision : svn%3Affd33b8c-2492-42e0-bdc5-587b920b7d6d/trunk%4043066 --- Virtu/Cpu.cs | 12 ------ Virtu/Machine.cs | 13 +++++-- Virtu/MachineSettings.cs | 9 +++-- Virtu/Services/MachineService.cs | 10 +++++ Virtu/Services/MachineServices.cs | 6 +++ Virtu/Silverlight/MainPage.xaml | 2 +- Virtu/Silverlight/Properties/AssemblyInfo.cs | 6 +-- .../Services/SilverlightAudioService.cs | 7 ++-- Virtu/Speaker.cs | 38 ++++++++++++++++++- Virtu/Wpf/Properties/AssemblyInfo.cs | 6 +-- Virtu/Wpf/Services/WpfAudioService.cs | 4 +- Virtu/Wpf/Services/WpfVideoService.cs | 4 +- Virtu/Xna/Properties/AssemblyInfo.cs | 6 +-- Virtu/Xna/Services/XnaAudioService.cs | 4 +- 14 files changed, 85 insertions(+), 42 deletions(-) diff --git a/Virtu/Cpu.cs b/Virtu/Cpu.cs index 15138fa..50e7b95 100644 --- a/Virtu/Cpu.cs +++ b/Virtu/Cpu.cs @@ -3208,17 +3208,6 @@ private void Execute65C02Tsb0C() // tsb abs private void OnVideoVSync(object sender, EventArgs e) { UpdateSettings(); - - if (Machine.Settings.Cpu.IsThrottled) - { - long elapsedTime = DateTime.UtcNow.Ticks - _lastTime; - long ticksPerVSync = Machine.Video.TicksPerVSync; - if (elapsedTime < ticksPerVSync) - { - Thread.Sleep(TimeSpan.FromTicks(ticksPerVSync - elapsedTime).Milliseconds); - } - _lastTime = DateTime.UtcNow.Ticks; - } } private void UpdateSettings() @@ -3240,6 +3229,5 @@ private void UpdateSettings() private Memory _memory; private Action[] _executeOpcode; - private long _lastTime; } } diff --git a/Virtu/Machine.cs b/Virtu/Machine.cs index e82a1f8..97bc8c4 100644 --- a/Virtu/Machine.cs +++ b/Virtu/Machine.cs @@ -32,7 +32,7 @@ public Machine() public void Dispose() { - _pausedEvent.Close(); + _pauseEvent.Close(); _unpauseEvent.Close(); } @@ -47,13 +47,15 @@ public void Start() _storageService.Load(MachineSettings.FileName, stream => Settings.Deserialize(stream)); State = MachineState.Starting; + Services.ForEach(service => service.Start()); + Thread.Start(); } public void Pause() { State = MachineState.Pausing; - _pausedEvent.WaitOne(); + _pauseEvent.WaitOne(); State = MachineState.Paused; } @@ -66,6 +68,9 @@ public void Unpause() public void Stop() { State = MachineState.Stopping; + Services.ForEach(service => service.Stop()); + + _pauseEvent.Set(); _unpauseEvent.Set(); Thread.Join(); State = MachineState.Stopped; @@ -89,7 +94,7 @@ private void Run() // machine thread if (State == MachineState.Pausing) { - _pausedEvent.Set(); + _pauseEvent.Set(); _unpauseEvent.WaitOne(); } } @@ -115,7 +120,7 @@ private void Run() // machine thread public Thread Thread { get; private set; } - private AutoResetEvent _pausedEvent = new AutoResetEvent(false); + private AutoResetEvent _pauseEvent = new AutoResetEvent(false); private AutoResetEvent _unpauseEvent = new AutoResetEvent(false); private StorageService _storageService; diff --git a/Virtu/MachineSettings.cs b/Virtu/MachineSettings.cs index c26d6c0..704bd7c 100644 --- a/Virtu/MachineSettings.cs +++ b/Virtu/MachineSettings.cs @@ -10,7 +10,7 @@ public sealed class MachineSettings { public MachineSettings() { - Cpu = new CpuSettings { Is65C02 = true, IsThrottled = true }; + Cpu = new CpuSettings { Is65C02 = true, IsThrottled = true, Multiplier = 1 }; DiskII = new DiskIISettings { Disk1 = new DiskSettings { Name = string.Empty, IsWriteProtected = false }, @@ -75,7 +75,8 @@ public void Deserialize(Stream stream) Cpu = new CpuSettings { Is65C02 = (bool)cpu.Attribute("Is65C02"), - IsThrottled = (bool)cpu.Attribute("IsThrottled") + IsThrottled = (bool)cpu.Attribute("IsThrottled"), + Multiplier = (int)cpu.Attribute("Multiplier") }; XElement diskII = root.Element(ns + "DiskII"); XElement disk1 = diskII.Element(ns + "Disk1"); @@ -208,7 +209,8 @@ public void Serialize(Stream stream) XElement xml = new XElement(ns + "MachineSettings", new XElement(ns + "Cpu", new XAttribute("Is65C02", Cpu.Is65C02), - new XAttribute("IsThrottled", Cpu.IsThrottled)), + new XAttribute("IsThrottled", Cpu.IsThrottled), + new XAttribute("Multiplier", Cpu.Multiplier)), new XElement(ns + "DiskII", new XElement(ns + "Disk1", new XAttribute("Name", DiskII.Disk1.Name), @@ -309,6 +311,7 @@ public sealed class CpuSettings { public bool Is65C02 { get; set; } public bool IsThrottled { get; set; } + public int Multiplier { get; set; } } public sealed class DiskSettings diff --git a/Virtu/Services/MachineService.cs b/Virtu/Services/MachineService.cs index 34ee5df..f242480 100644 --- a/Virtu/Services/MachineService.cs +++ b/Virtu/Services/MachineService.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Jellyfish.Virtu.Services { @@ -25,6 +26,15 @@ public void Dispose() GC.SuppressFinalize(this); } + public virtual void Start() + { + } + + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Stop")] + public virtual void Stop() + { + } + protected virtual void Dispose(bool disposing) { } diff --git a/Virtu/Services/MachineServices.cs b/Virtu/Services/MachineServices.cs index 38b8959..766181e 100644 --- a/Virtu/Services/MachineServices.cs +++ b/Virtu/Services/MachineServices.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Jellyfish.Library; using Jellyfish.Virtu.Properties; namespace Jellyfish.Virtu.Services @@ -30,6 +31,11 @@ public void AddService(Type serviceType, MachineService serviceProvider) _serviceProviders.Add(serviceType, serviceProvider); } + public void ForEach(Action action) + { + _serviceProviders.Values.ForEach(action); + } + [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public T GetService() { diff --git a/Virtu/Silverlight/MainPage.xaml b/Virtu/Silverlight/MainPage.xaml index 4b3bce4..2aa6080 100644 --- a/Virtu/Silverlight/MainPage.xaml +++ b/Virtu/Silverlight/MainPage.xaml @@ -12,7 +12,7 @@ - + diff --git a/Virtu/Silverlight/Properties/AssemblyInfo.cs b/Virtu/Silverlight/Properties/AssemblyInfo.cs index 4c466ab..0c8f230 100644 --- a/Virtu/Silverlight/Properties/AssemblyInfo.cs +++ b/Virtu/Silverlight/Properties/AssemblyInfo.cs @@ -11,9 +11,9 @@ [assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")] [assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")] -[assembly: AssemblyVersion("0.7.0.0")] -[assembly: AssemblyFileVersion("0.7.0.0")] -[assembly: AssemblyInformationalVersion("0.7.0.0")] +[assembly: AssemblyVersion("0.8.0.0")] +[assembly: AssemblyFileVersion("0.8.0.0")] +[assembly: AssemblyInformationalVersion("0.8.0.0")] [assembly: CLSCompliant(false)] [assembly: ComVisible(false)] diff --git a/Virtu/Silverlight/Services/SilverlightAudioService.cs b/Virtu/Silverlight/Services/SilverlightAudioService.cs index ea524d6..0fbf2c2 100644 --- a/Virtu/Silverlight/Services/SilverlightAudioService.cs +++ b/Virtu/Silverlight/Services/SilverlightAudioService.cs @@ -21,17 +21,16 @@ public SilverlightAudioService(Machine machine, UserControl page, MediaElement m _page = page; _media = media; - _page.Loaded += (sender, e) => _media.SetSource(_mediaSource); + _page.Loaded += (sender, e) => { _media.SetSource(_mediaSource); _media.Play(); }; _mediaSource.Update += OnMediaSourceUpdate; + //_page.Closed += (sender, e) => _media.Stop(); // SL is missing Closed / Unloaded event } private void OnMediaSourceUpdate(object sender, WaveMediaStreamSourceUpdateEventArgs e) { - int offset = 0; Update(e.BufferSize, (source, count) => { - Buffer.BlockCopy(source, 0, e.Buffer, offset, count); - offset += count; + Buffer.BlockCopy(source, 0, e.Buffer, 0, count); }); } diff --git a/Virtu/Speaker.cs b/Virtu/Speaker.cs index b264cdf..3e3e592 100644 --- a/Virtu/Speaker.cs +++ b/Virtu/Speaker.cs @@ -1,4 +1,5 @@ -using Jellyfish.Virtu.Services; +using System; +using Jellyfish.Virtu.Services; namespace Jellyfish.Virtu { @@ -7,18 +8,51 @@ 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(); + + Machine.Events.AddEvent(CyclesPerFlush * Machine.Settings.Cpu.Multiplier, _flushOutputEvent); } public void ToggleOutput() { - _audioService.ToggleOutput(); + UpdateCycles(); + _isHigh ^= true; } + private void FlushOutputEvent() + { + UpdateCycles(); + _audioService.Output(_highCycles * 255 / _totalCycles); // quick and dirty decimation + _highCycles = _totalCycles = 0; + + Machine.Events.AddEvent(CyclesPerFlush * Machine.Settings.Cpu.Multiplier, _flushOutputEvent); + } + + private void UpdateCycles() + { + int delta = (int)(Machine.Cpu.Cycles - _lastCycles); + if (_isHigh) + { + _highCycles += delta; + } + _totalCycles += delta; + _lastCycles = Machine.Cpu.Cycles; + } + + private const int CyclesPerFlush = 23; + + private Action _flushOutputEvent; + + private bool _isHigh; + private int _highCycles; + private int _totalCycles; + private long _lastCycles; + private AudioService _audioService; } } diff --git a/Virtu/Wpf/Properties/AssemblyInfo.cs b/Virtu/Wpf/Properties/AssemblyInfo.cs index ab776a9..9864fa4 100644 --- a/Virtu/Wpf/Properties/AssemblyInfo.cs +++ b/Virtu/Wpf/Properties/AssemblyInfo.cs @@ -12,9 +12,9 @@ [assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")] [assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")] -[assembly: AssemblyVersion("0.7.0.0")] -[assembly: AssemblyFileVersion("0.7.0.0")] -[assembly: AssemblyInformationalVersion("0.7.0.0")] +[assembly: AssemblyVersion("0.8.0.0")] +[assembly: AssemblyFileVersion("0.8.0.0")] +[assembly: AssemblyInformationalVersion("0.8.0.0")] [assembly: CLSCompliant(false)] [assembly: ComVisible(false)] diff --git a/Virtu/Wpf/Services/WpfAudioService.cs b/Virtu/Wpf/Services/WpfAudioService.cs index b92d7c6..5157dba 100644 --- a/Virtu/Wpf/Services/WpfAudioService.cs +++ b/Virtu/Wpf/Services/WpfAudioService.cs @@ -34,11 +34,9 @@ protected override void Dispose(bool disposing) private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e) { - IntPtr buffer = e.Buffer; Update(e.BufferSize, (source, count) => { - Marshal.Copy(source, 0, buffer, count); - buffer = (IntPtr)((long)buffer + count); + Marshal.Copy(source, 0, e.Buffer, count); }); } diff --git a/Virtu/Wpf/Services/WpfVideoService.cs b/Virtu/Wpf/Services/WpfVideoService.cs index c7dc49a..2ce9ddd 100644 --- a/Virtu/Wpf/Services/WpfVideoService.cs +++ b/Virtu/Wpf/Services/WpfVideoService.cs @@ -59,6 +59,7 @@ public override void Update() _window.SizeToContent = SizeToContent.WidthAndHeight; } _isFullScreen = IsFullScreen; + SetImageSize(); } if (_pixelsDirty) @@ -70,7 +71,8 @@ public override void Update() private void SetImageSize() { - int uniformScale = Math.Min((int)SystemParameters.PrimaryScreenWidth / BitmapWidth, (int)SystemParameters.PrimaryScreenHeight / BitmapHeight); + int uniformScale = IsFullScreen ? Math.Min((int)SystemParameters.PrimaryScreenWidth / BitmapWidth, (int)SystemParameters.PrimaryScreenHeight / BitmapHeight) : + Math.Min((int)SystemParameters.FullPrimaryScreenWidth / BitmapWidth, (int)SystemParameters.FullPrimaryScreenHeight / BitmapHeight); _image.Width = uniformScale * BitmapWidth; _image.Height = uniformScale * BitmapHeight; } diff --git a/Virtu/Xna/Properties/AssemblyInfo.cs b/Virtu/Xna/Properties/AssemblyInfo.cs index eb52d5e..9898b41 100644 --- a/Virtu/Xna/Properties/AssemblyInfo.cs +++ b/Virtu/Xna/Properties/AssemblyInfo.cs @@ -15,11 +15,11 @@ [assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")] [assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")] -[assembly: AssemblyVersion("0.7.0.0")] +[assembly: AssemblyVersion("0.8.0.0")] #if WINDOWS -[assembly: AssemblyFileVersion("0.7.0.0")] +[assembly: AssemblyFileVersion("0.8.0.0")] #endif -[assembly: AssemblyInformationalVersion("0.7.0.0")] +[assembly: AssemblyInformationalVersion("0.8.0.0")] [assembly: CLSCompliant(false)] [assembly: ComVisible(false)] diff --git a/Virtu/Xna/Services/XnaAudioService.cs b/Virtu/Xna/Services/XnaAudioService.cs index 32c1857..49eeace 100644 --- a/Virtu/Xna/Services/XnaAudioService.cs +++ b/Virtu/Xna/Services/XnaAudioService.cs @@ -32,11 +32,9 @@ protected override void Dispose(bool disposing) private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e) { - IntPtr buffer = e.Buffer; Update(e.BufferSize, (source, count) => { - Marshal.Copy(source, 0, buffer, count); - buffer = (IntPtr)((long)buffer + count); + Marshal.Copy(source, 0, e.Buffer, count); }); }