mirror of
https://github.com/digital-jellyfish/Virtu.git
synced 2024-05-31 15:41:33 +00:00
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
This commit is contained in:
parent
51d9e8e5f2
commit
ea2a892113
12
Virtu/Cpu.cs
12
Virtu/Cpu.cs
|
@ -3208,17 +3208,6 @@ private void Execute65C02Tsb0C() // tsb abs
|
||||||
private void OnVideoVSync(object sender, EventArgs e)
|
private void OnVideoVSync(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateSettings();
|
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()
|
private void UpdateSettings()
|
||||||
|
@ -3240,6 +3229,5 @@ private void UpdateSettings()
|
||||||
private Memory _memory;
|
private Memory _memory;
|
||||||
|
|
||||||
private Action[] _executeOpcode;
|
private Action[] _executeOpcode;
|
||||||
private long _lastTime;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public Machine()
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_pausedEvent.Close();
|
_pauseEvent.Close();
|
||||||
_unpauseEvent.Close();
|
_unpauseEvent.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,13 +47,15 @@ public void Start()
|
||||||
_storageService.Load(MachineSettings.FileName, stream => Settings.Deserialize(stream));
|
_storageService.Load(MachineSettings.FileName, stream => Settings.Deserialize(stream));
|
||||||
|
|
||||||
State = MachineState.Starting;
|
State = MachineState.Starting;
|
||||||
|
Services.ForEach(service => service.Start());
|
||||||
|
|
||||||
Thread.Start();
|
Thread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
State = MachineState.Pausing;
|
State = MachineState.Pausing;
|
||||||
_pausedEvent.WaitOne();
|
_pauseEvent.WaitOne();
|
||||||
State = MachineState.Paused;
|
State = MachineState.Paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +68,9 @@ public void Unpause()
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
State = MachineState.Stopping;
|
State = MachineState.Stopping;
|
||||||
|
Services.ForEach(service => service.Stop());
|
||||||
|
|
||||||
|
_pauseEvent.Set();
|
||||||
_unpauseEvent.Set();
|
_unpauseEvent.Set();
|
||||||
Thread.Join();
|
Thread.Join();
|
||||||
State = MachineState.Stopped;
|
State = MachineState.Stopped;
|
||||||
|
@ -89,7 +94,7 @@ private void Run() // machine thread
|
||||||
|
|
||||||
if (State == MachineState.Pausing)
|
if (State == MachineState.Pausing)
|
||||||
{
|
{
|
||||||
_pausedEvent.Set();
|
_pauseEvent.Set();
|
||||||
_unpauseEvent.WaitOne();
|
_unpauseEvent.WaitOne();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +120,7 @@ private void Run() // machine thread
|
||||||
|
|
||||||
public Thread Thread { get; private set; }
|
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 AutoResetEvent _unpauseEvent = new AutoResetEvent(false);
|
||||||
|
|
||||||
private StorageService _storageService;
|
private StorageService _storageService;
|
||||||
|
|
|
@ -10,7 +10,7 @@ public sealed class MachineSettings
|
||||||
{
|
{
|
||||||
public MachineSettings()
|
public MachineSettings()
|
||||||
{
|
{
|
||||||
Cpu = new CpuSettings { Is65C02 = true, IsThrottled = true };
|
Cpu = new CpuSettings { Is65C02 = true, IsThrottled = true, Multiplier = 1 };
|
||||||
DiskII = new DiskIISettings
|
DiskII = new DiskIISettings
|
||||||
{
|
{
|
||||||
Disk1 = new DiskSettings { Name = string.Empty, IsWriteProtected = false },
|
Disk1 = new DiskSettings { Name = string.Empty, IsWriteProtected = false },
|
||||||
|
@ -75,7 +75,8 @@ public void Deserialize(Stream stream)
|
||||||
Cpu = new CpuSettings
|
Cpu = new CpuSettings
|
||||||
{
|
{
|
||||||
Is65C02 = (bool)cpu.Attribute("Is65C02"),
|
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 diskII = root.Element(ns + "DiskII");
|
||||||
XElement disk1 = diskII.Element(ns + "Disk1");
|
XElement disk1 = diskII.Element(ns + "Disk1");
|
||||||
|
@ -208,7 +209,8 @@ public void Serialize(Stream stream)
|
||||||
XElement xml = new XElement(ns + "MachineSettings",
|
XElement xml = new XElement(ns + "MachineSettings",
|
||||||
new XElement(ns + "Cpu",
|
new XElement(ns + "Cpu",
|
||||||
new XAttribute("Is65C02", Cpu.Is65C02),
|
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 + "DiskII",
|
||||||
new XElement(ns + "Disk1",
|
new XElement(ns + "Disk1",
|
||||||
new XAttribute("Name", DiskII.Disk1.Name),
|
new XAttribute("Name", DiskII.Disk1.Name),
|
||||||
|
@ -309,6 +311,7 @@ public sealed class CpuSettings
|
||||||
{
|
{
|
||||||
public bool Is65C02 { get; set; }
|
public bool Is65C02 { get; set; }
|
||||||
public bool IsThrottled { get; set; }
|
public bool IsThrottled { get; set; }
|
||||||
|
public int Multiplier { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class DiskSettings
|
public sealed class DiskSettings
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu.Services
|
namespace Jellyfish.Virtu.Services
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,15 @@ public void Dispose()
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void Start()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Stop")]
|
||||||
|
public virtual void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using Jellyfish.Library;
|
||||||
using Jellyfish.Virtu.Properties;
|
using Jellyfish.Virtu.Properties;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu.Services
|
namespace Jellyfish.Virtu.Services
|
||||||
|
@ -30,6 +31,11 @@ public void AddService(Type serviceType, MachineService serviceProvider)
|
||||||
_serviceProviders.Add(serviceType, serviceProvider);
|
_serviceProviders.Add(serviceType, serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ForEach(Action<MachineService> action)
|
||||||
|
{
|
||||||
|
_serviceProviders.Values.ForEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
|
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
|
||||||
public T GetService<T>()
|
public T GetService<T>()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid Cursor="None">
|
<Grid Cursor="None">
|
||||||
<Image x:Name="_image" MinWidth="560" MinHeight="384"/>
|
<Image x:Name="_image" MinWidth="560" MinHeight="384"/>
|
||||||
<MediaElement x:Name="_media" AutoPlay="true"/>
|
<MediaElement x:Name="_media"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</tk:DockPanel>
|
</tk:DockPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
||||||
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("0.7.0.0")]
|
[assembly: AssemblyVersion("0.8.0.0")]
|
||||||
[assembly: AssemblyFileVersion("0.7.0.0")]
|
[assembly: AssemblyFileVersion("0.8.0.0")]
|
||||||
[assembly: AssemblyInformationalVersion("0.7.0.0")]
|
[assembly: AssemblyInformationalVersion("0.8.0.0")]
|
||||||
|
|
||||||
[assembly: CLSCompliant(false)]
|
[assembly: CLSCompliant(false)]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
@ -21,17 +21,16 @@ public SilverlightAudioService(Machine machine, UserControl page, MediaElement m
|
||||||
_page = page;
|
_page = page;
|
||||||
_media = media;
|
_media = media;
|
||||||
|
|
||||||
_page.Loaded += (sender, e) => _media.SetSource(_mediaSource);
|
_page.Loaded += (sender, e) => { _media.SetSource(_mediaSource); _media.Play(); };
|
||||||
_mediaSource.Update += OnMediaSourceUpdate;
|
_mediaSource.Update += OnMediaSourceUpdate;
|
||||||
|
//_page.Closed += (sender, e) => _media.Stop(); // SL is missing Closed / Unloaded event
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMediaSourceUpdate(object sender, WaveMediaStreamSourceUpdateEventArgs e)
|
private void OnMediaSourceUpdate(object sender, WaveMediaStreamSourceUpdateEventArgs e)
|
||||||
{
|
{
|
||||||
int offset = 0;
|
|
||||||
Update(e.BufferSize, (source, count) =>
|
Update(e.BufferSize, (source, count) =>
|
||||||
{
|
{
|
||||||
Buffer.BlockCopy(source, 0, e.Buffer, offset, count);
|
Buffer.BlockCopy(source, 0, e.Buffer, 0, count);
|
||||||
offset += count;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Jellyfish.Virtu.Services;
|
using System;
|
||||||
|
using Jellyfish.Virtu.Services;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
@ -7,18 +8,51 @@ public sealed class Speaker : MachineComponent
|
||||||
public Speaker(Machine machine) :
|
public Speaker(Machine machine) :
|
||||||
base(machine)
|
base(machine)
|
||||||
{
|
{
|
||||||
|
_flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
_audioService = Machine.Services.GetService<AudioService>();
|
_audioService = Machine.Services.GetService<AudioService>();
|
||||||
|
|
||||||
|
Machine.Events.AddEvent(CyclesPerFlush * Machine.Settings.Cpu.Multiplier, _flushOutputEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleOutput()
|
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;
|
private AudioService _audioService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
||||||
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("0.7.0.0")]
|
[assembly: AssemblyVersion("0.8.0.0")]
|
||||||
[assembly: AssemblyFileVersion("0.7.0.0")]
|
[assembly: AssemblyFileVersion("0.8.0.0")]
|
||||||
[assembly: AssemblyInformationalVersion("0.7.0.0")]
|
[assembly: AssemblyInformationalVersion("0.8.0.0")]
|
||||||
|
|
||||||
[assembly: CLSCompliant(false)]
|
[assembly: CLSCompliant(false)]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
@ -34,11 +34,9 @@ protected override void Dispose(bool disposing)
|
||||||
|
|
||||||
private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
|
private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
|
||||||
{
|
{
|
||||||
IntPtr buffer = e.Buffer;
|
|
||||||
Update(e.BufferSize, (source, count) =>
|
Update(e.BufferSize, (source, count) =>
|
||||||
{
|
{
|
||||||
Marshal.Copy(source, 0, buffer, count);
|
Marshal.Copy(source, 0, e.Buffer, count);
|
||||||
buffer = (IntPtr)((long)buffer + count);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ public override void Update()
|
||||||
_window.SizeToContent = SizeToContent.WidthAndHeight;
|
_window.SizeToContent = SizeToContent.WidthAndHeight;
|
||||||
}
|
}
|
||||||
_isFullScreen = IsFullScreen;
|
_isFullScreen = IsFullScreen;
|
||||||
|
SetImageSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pixelsDirty)
|
if (_pixelsDirty)
|
||||||
|
@ -70,7 +71,8 @@ public override void Update()
|
||||||
|
|
||||||
private void SetImageSize()
|
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.Width = uniformScale * BitmapWidth;
|
||||||
_image.Height = uniformScale * BitmapHeight;
|
_image.Height = uniformScale * BitmapHeight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
[assembly: AssemblyCopyright("Copyright © 1995-2010 Digital Jellyfish Design Ltd")]
|
||||||
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
[assembly: AssemblyComment("Developed by Sean Fausett & Nick Westgate")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("0.7.0.0")]
|
[assembly: AssemblyVersion("0.8.0.0")]
|
||||||
#if WINDOWS
|
#if WINDOWS
|
||||||
[assembly: AssemblyFileVersion("0.7.0.0")]
|
[assembly: AssemblyFileVersion("0.8.0.0")]
|
||||||
#endif
|
#endif
|
||||||
[assembly: AssemblyInformationalVersion("0.7.0.0")]
|
[assembly: AssemblyInformationalVersion("0.8.0.0")]
|
||||||
|
|
||||||
[assembly: CLSCompliant(false)]
|
[assembly: CLSCompliant(false)]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
@ -32,11 +32,9 @@ protected override void Dispose(bool disposing)
|
||||||
|
|
||||||
private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
|
private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
|
||||||
{
|
{
|
||||||
IntPtr buffer = e.Buffer;
|
|
||||||
Update(e.BufferSize, (source, count) =>
|
Update(e.BufferSize, (source, count) =>
|
||||||
{
|
{
|
||||||
Marshal.Copy(source, 0, buffer, count);
|
Marshal.Copy(source, 0, e.Buffer, count);
|
||||||
buffer = (IntPtr)((long)buffer + count);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user