diff --git a/Library/DirectSound.cs b/Library/DirectSound.cs index 60ce476..f1e58d0 100644 --- a/Library/DirectSound.cs +++ b/Library/DirectSound.cs @@ -26,6 +26,15 @@ public void Dispose() _stopEvent.Close(); } + public void SetVolume(double volume) + { + int attenuation = (volume < 0.01) ? (int)BufferVolume.Min : (int)Math.Floor(100 * 20 * Math.Log10(volume)); // 100 db + if (_buffer != null) + { + _buffer.SetVolume(attenuation); + } + } + public void Start(IntPtr window) { _window = window; @@ -62,8 +71,6 @@ private void Initialize() }; ((IDirectSoundNotify)_buffer).SetNotificationPositions(positionEvents.Length, positionEvents); - _buffer.SetVolume(-1500); // 50 % - _buffer.Play(0, 0, BufferPlay.Looping); } @@ -113,10 +120,12 @@ private void Uninitialize() { _buffer.Stop(); Marshal.ReleaseComObject(_buffer); + _buffer = null; } if (_device != null) { Marshal.ReleaseComObject(_device); + _device = null; } } diff --git a/Library/DirectSoundInterop.cs b/Library/DirectSoundInterop.cs index 677a31e..275b5e4 100644 --- a/Library/DirectSoundInterop.cs +++ b/Library/DirectSoundInterop.cs @@ -20,6 +20,8 @@ private enum BufferPlay { Looping = 0x00000001 } [Flags] private enum BufferStatus { Playing = 0x00000001, BufferLost = 0x00000002, Looping = 0x00000004, Terminated = 0x00000020 } + private enum BufferVolume { Min = -10000, Max = 0 } + private enum CooperativeLevel { Normal = 1, Priority = 2 } [StructLayout(LayoutKind.Sequential)] diff --git a/Library/MathHelpers.cs b/Library/MathHelpers.cs index 6ea1a70..b7bcc65 100644 --- a/Library/MathHelpers.cs +++ b/Library/MathHelpers.cs @@ -7,6 +7,11 @@ public static int Clamp(int value, int min, int max) return (value < min) ? min : (value > max) ? max : value; } + public static double Clamp(double value, double min, double max) + { + return (value < min) ? min : (value > max) ? max : value; + } + public static int ClampByte(int value) { return Clamp(value, byte.MinValue, byte.MaxValue); diff --git a/Virtu/Cpu.cs b/Virtu/Cpu.cs index d08773f..40ab5d3 100644 --- a/Virtu/Cpu.cs +++ b/Virtu/Cpu.cs @@ -154,8 +154,7 @@ public override void Initialize() _memory = Machine.Memory; UpdateSettings(); - - Machine.Video.VSync += OnVideoVSync; + Machine.Video.VSync += (sender, e) => UpdateSettings(); } public override void Reset() @@ -3205,11 +3204,6 @@ private void Execute65C02Tsb0C() // tsb abs } #endregion - private void OnVideoVSync(object sender, EventArgs e) - { - UpdateSettings(); - } - private void UpdateSettings() { _executeOpCode = Machine.Settings.Cpu.Is65C02 ? ExecuteOpCode65C02 : ExecuteOpCode65N02; diff --git a/Virtu/MachineSettings.cs b/Virtu/MachineSettings.cs index b78daf4..17be26d 100644 --- a/Virtu/MachineSettings.cs +++ b/Virtu/MachineSettings.cs @@ -36,6 +36,7 @@ public MachineSettings() Button0 = 0, Button1 = 0, Button2 = 0 } }; + Audio = new AudioSettings { Volume = 0.5 }; Video = new VideoSettings { IsFullScreen = false, IsMonochrome = false, ScannerOptions = ScannerOptions.None, @@ -168,6 +169,11 @@ public void Deserialize(Stream stream) Button2 = (int)buttons.Attribute("Button2") } }; + var audio = root.Element(ns + "Audio"); + Audio = new AudioSettings + { + Volume = (double)audio.Attribute("Volume") + }; var video = root.Element(ns + "Video"); var color = video.Element(ns + "Color"); Video = new VideoSettings @@ -268,6 +274,8 @@ public void Serialize(Stream stream) new XAttribute("Button0", Keyboard.Key.Button0), new XAttribute("Button1", Keyboard.Key.Button1), new XAttribute("Button2", Keyboard.Key.Button2)))), + new XElement(ns + "Audio", + new XAttribute("Volume", Audio.Volume)), new XElement(ns + "Video", new XAttribute("IsFullScreen", Video.IsFullScreen), new XAttribute("IsMonochrome", Video.IsMonochrome), @@ -301,6 +309,7 @@ public void Serialize(Stream stream) public DiskIISettings DiskII { get; set; } public KeyboardSettings Keyboard { get; set; } public GamePortSettings GamePort { get; set; } + public AudioSettings Audio { get; set; } public VideoSettings Video { get; set; } public const string FileName = "Settings.xml"; @@ -360,6 +369,11 @@ public sealed class GamePortSettings public KeySettings Key { get; set; } } + public sealed class AudioSettings + { + public double Volume { get; set; } + }; + public sealed class ColorSettings { public uint Black { get; set; } diff --git a/Virtu/Services/AudioService.cs b/Virtu/Services/AudioService.cs index dd6b0ff..4c8434d 100644 --- a/Virtu/Services/AudioService.cs +++ b/Virtu/Services/AudioService.cs @@ -4,9 +4,9 @@ namespace Jellyfish.Virtu.Services { - public class AudioService : MachineService + public abstract class AudioService : MachineService { - public AudioService(Machine machine) : + protected AudioService(Machine machine) : base(machine) { } @@ -31,6 +31,8 @@ public void Reset() Buffer.BlockCopy(SampleZero, 0, _buffer, 0, SampleSize); } + public abstract void SetVolume(double volume); // machine thread + public override void Stop() // main thread { _readEvent.Set(); // signal events; avoids deadlock diff --git a/Virtu/Silverlight/Services/SilverlightAudioService.cs b/Virtu/Silverlight/Services/SilverlightAudioService.cs index 3e71f05..23958f6 100644 --- a/Virtu/Silverlight/Services/SilverlightAudioService.cs +++ b/Virtu/Silverlight/Services/SilverlightAudioService.cs @@ -29,6 +29,11 @@ public SilverlightAudioService(Machine machine, UserControl page, MediaElement m #endif } + public override void SetVolume(double volume) // machine thread + { + _media.Dispatcher.BeginInvoke(() => _media.Volume = volume); + } + protected override void Dispose(bool disposing) { if (disposing) diff --git a/Virtu/Speaker.cs b/Virtu/Speaker.cs index dfb58df..4e295ca 100644 --- a/Virtu/Speaker.cs +++ b/Virtu/Speaker.cs @@ -15,6 +15,9 @@ public override void Initialize() { _audioService = Machine.Services.GetService(); + UpdateSettings(); + Machine.Video.VSync += (sender, e) => UpdateSettings(); + Machine.Events.AddEvent(CyclesPerFlush * Machine.Settings.Cpu.Multiplier, _flushOutputEvent); } @@ -51,6 +54,11 @@ private void UpdateCycles() _lastCycles = Machine.Cpu.Cycles; } + private void UpdateSettings() + { + _audioService.SetVolume(Machine.Settings.Audio.Volume); + } + private const int CyclesPerFlush = 23; private Action _flushOutputEvent; diff --git a/Virtu/Wpf/Services/WpfAudioService.cs b/Virtu/Wpf/Services/WpfAudioService.cs index 117cb53..383c778 100644 --- a/Virtu/Wpf/Services/WpfAudioService.cs +++ b/Virtu/Wpf/Services/WpfAudioService.cs @@ -24,6 +24,11 @@ public WpfAudioService(Machine machine, Window window) : _window.Closed += (sender, e) => _directSound.Stop(); } + public override void SetVolume(double volume) // machine thread + { + _directSound.SetVolume(volume); + } + protected override void Dispose(bool disposing) { if (disposing) diff --git a/Virtu/Xna/Services/XnaAudioService.cs b/Virtu/Xna/Services/XnaAudioService.cs index da150fb..52a1d70 100644 --- a/Virtu/Xna/Services/XnaAudioService.cs +++ b/Virtu/Xna/Services/XnaAudioService.cs @@ -23,6 +23,11 @@ public XnaAudioService(Machine machine, GameBase game) : _dynamicSoundEffect.Play(); } + public override void SetVolume(double volume) // machine thread + { + _dynamicSoundEffect.Volume = (float)volume; + } + protected override void Dispose(bool disposing) { if (disposing)