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:
Sean Fausett 2010-03-14 21:54:17 +00:00
parent 51d9e8e5f2
commit ea2a892113
14 changed files with 85 additions and 42 deletions

View File

@ -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;
} }
} }

View File

@ -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;

View File

@ -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

View File

@ -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)
{ {
} }

View File

@ -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>()
{ {

View File

@ -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>

View File

@ -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)]

View File

@ -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;
}); });
} }

View File

@ -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;
} }
} }

View File

@ -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)]

View File

@ -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);
}); });
} }

View File

@ -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;
} }

View File

@ -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)]

View File

@ -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);
}); });
} }