From a38e854a6d8266be4a6c7932756593f8df5c71cc Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:49:26 -0800 Subject: [PATCH] add method to play wav files via ISoundOutput, use this instead of System.Media.SoundPlayer --- .../BizHawk.Bizware.Audio.csproj | 1 + .../DirectSoundSoundOutput.cs | 106 ++++++++++++++- .../OpenALSoundOutput.cs | 76 +++++++++-- src/BizHawk.Bizware.Audio/SDL2WavStream.cs | 125 ++++++++++++++++++ .../XAudio2SoundOutput.cs | 41 +++++- .../Sound/Interfaces/ISoundOutput.cs | 1 + .../Sound/Output/DummySoundOutput.cs | 4 + src/BizHawk.Client.EmuHawk/MainForm.cs | 21 ++- .../RetroAchievements/RCheevos.Login.cs | 6 +- .../RetroAchievements/RCheevos.Sound.cs | 30 ++--- .../RetroAchievements/RCheevos.cs | 17 ++- .../RetroAchievements/RetroAchievements.cs | 3 +- src/BizHawk.Client.EmuHawk/Sound/Sound.cs | 13 ++ 13 files changed, 396 insertions(+), 48 deletions(-) create mode 100644 src/BizHawk.Bizware.Audio/SDL2WavStream.cs diff --git a/src/BizHawk.Bizware.Audio/BizHawk.Bizware.Audio.csproj b/src/BizHawk.Bizware.Audio/BizHawk.Bizware.Audio.csproj index a0f03231d4..f8daea5b87 100644 --- a/src/BizHawk.Bizware.Audio/BizHawk.Bizware.Audio.csproj +++ b/src/BizHawk.Bizware.Audio/BizHawk.Bizware.Audio.csproj @@ -15,6 +15,7 @@ + diff --git a/src/BizHawk.Bizware.Audio/DirectSoundSoundOutput.cs b/src/BizHawk.Bizware.Audio/DirectSoundSoundOutput.cs index 768062b9d9..c67b55f2b8 100644 --- a/src/BizHawk.Bizware.Audio/DirectSoundSoundOutput.cs +++ b/src/BizHawk.Bizware.Audio/DirectSoundSoundOutput.cs @@ -1,10 +1,12 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using BizHawk.Client.Common; +using BizHawk.Common; using SharpDX; using SharpDX.DirectSound; @@ -18,7 +20,7 @@ namespace BizHawk.Bizware.Audio private readonly IntPtr _mainWindowHandle; private bool _disposed; private DirectSound _device; - private SecondarySoundBuffer _deviceBuffer; + private SecondarySoundBuffer _deviceBuffer, _wavDeviceBuffer; private int _actualWriteOffsetBytes = -1; private int _filledBufferSizeBytes; private long _lastWriteTime; @@ -40,6 +42,7 @@ namespace BizHawk.Bizware.Audio { if (_disposed) return; + StopWav(throwOnInvalidDevice: false); _device.Dispose(); _device = null; @@ -63,6 +66,8 @@ namespace BizHawk.Bizware.Audio _deviceBuffer.Dispose(); _deviceBuffer = null; + StopWav(throwOnInvalidDevice: false); + _device.Dispose(); _device = new(); _device.SetCooperativeLevel(_mainWindowHandle, CooperativeLevel.Priority); @@ -273,5 +278,104 @@ namespace BizHawk.Bizware.Audio StartPlaying(); } } + + private bool IsWavPlaying + { + get + { + if (_wavDeviceBuffer == null) + { + return false; + } + + var status = (BufferStatus)_wavDeviceBuffer.Status; + return (status & BufferStatus.BufferLost) == 0 && + (status & BufferStatus.Playing) == BufferStatus.Playing; + } + } + + private void StopWav(bool throwOnInvalidDevice = true) + { + bool isPlaying; + try + { + isPlaying = IsWavPlaying; + } + catch (SharpDXException) + { + if (throwOnInvalidDevice) + { + throw; + } + + isPlaying = false; + } + + if (isPlaying) + { + try + { + _wavDeviceBuffer.Stop(); + } + catch (SharpDXException) + { + } + } + + _wavDeviceBuffer?.Dispose(); + _wavDeviceBuffer = null; + } + + public void PlayWavFile(string path, double volume) + { + using var wavStream = new SDL2WavStream(path); + var format = wavStream.Format == SDL2WavStream.AudioFormat.F32LSB + ? WaveFormat.CreateIeeeFloatWaveFormat(wavStream.Frequency, wavStream.Channels) + : new(wavStream.Frequency, wavStream.BitsPerSample, wavStream.Channels); + + var desc = new SoundBufferDescription + { + Format = format, + Flags = + BufferFlags.GlobalFocus | + BufferFlags.Software | + BufferFlags.GetCurrentPosition2 | + BufferFlags.ControlVolume, + BufferBytes = unchecked((int)wavStream.Length) + }; + + StopWav(); + _wavDeviceBuffer = new(_device, desc); + const int TEMP_BUFFER_LENGTH = 65536; + var tempBuffer = ArrayPool.Shared.Rent(TEMP_BUFFER_LENGTH); + try + { + var bufferOffset = 0; + while (true) + { + var numRead = wavStream.Read(tempBuffer, 0, TEMP_BUFFER_LENGTH); + if (numRead == 0) + { + break; + } + + if (wavStream.Format == SDL2WavStream.AudioFormat.S16MSB) + { + EndiannessUtils.MutatingByteSwap16(tempBuffer.AsSpan()[..numRead]); + } + + _wavDeviceBuffer.Write(tempBuffer, 0, numRead, bufferOffset, LockFlags.None); + bufferOffset += numRead; + } + } + finally + { + ArrayPool.Shared.Return(tempBuffer); + } + + const int range = Volume.Maximum - Volume.Minimum; + _wavDeviceBuffer.Volume = (int)(Math.Pow(volume, 0.1) * range) + Volume.Minimum; + _wavDeviceBuffer.Play(0, PlayFlags.None); + } } } diff --git a/src/BizHawk.Bizware.Audio/OpenALSoundOutput.cs b/src/BizHawk.Bizware.Audio/OpenALSoundOutput.cs index 81442268e3..0961a08fa4 100644 --- a/src/BizHawk.Bizware.Audio/OpenALSoundOutput.cs +++ b/src/BizHawk.Bizware.Audio/OpenALSoundOutput.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using BizHawk.Client.Common; +using BizHawk.Common; using Silk.NET.Core.Native; using Silk.NET.OpenAL; @@ -26,12 +27,14 @@ namespace BizHawk.Bizware.Audio private bool _disposed; private readonly IHostAudioManager _sound; private AudioContext _context; - private uint _sourceID; + private uint _sourceID, _wavSourceID; private BufferPool _bufferPool; + private uint _wavBufferID; private int _currentSamplesQueued; private short[] _tempSampleBuffer; private unsafe Device* _device; private Disconnect _disconnectExt; + private FloatFormat _floatExt; public OpenALSoundOutput(IHostAudioManager sound, string chosenDeviceName) { @@ -44,14 +47,18 @@ namespace BizHawk.Bizware.Audio unsafe { _device = _alc.GetContextsDevice(_alc.GetCurrentContext()); - _disconnectExt = _alc.TryGetExtension(_device, out var ext) ? ext : null; + _disconnectExt = _alc.TryGetExtension(_device, out var disconnectExt) ? disconnectExt : null; } + + _floatExt = _al.TryGetExtension(out var floatFormatExt) ? floatFormatExt : null; } public void Dispose() { if (_disposed) return; + StopWav(); + _context.Dispose(); _context = null; @@ -104,11 +111,13 @@ namespace BizHawk.Bizware.Audio } StopSound(); + StopWav(); _context.Dispose(); _context = new(device: null, _sound.SampleRate); _device = _alc.GetContextsDevice(_alc.GetCurrentContext()); _disconnectExt = _alc.TryGetExtension(_device, out var ext) ? ext : null; + _floatExt = _al.TryGetExtension(out var floatFormatExt) ? floatFormatExt : null; StartSound(); } @@ -161,6 +170,62 @@ namespace BizHawk.Bizware.Audio } } + private void StopWav() + { + if (_wavSourceID != 0) + { + _al.SourceStop(_wavSourceID); + _al.DeleteSource(_wavSourceID); + _wavSourceID = 0; + } + + if (_wavBufferID != 0) + { + _al.DeleteBuffer(_wavBufferID); + _wavBufferID = 0; + } + } + + public void PlayWavFile(string path, double volume) + { + using var wavStream = new SDL2WavStream(path); + if (wavStream.Channels > 2) + { + throw new NotSupportedException("OpenAL does not support more than 2 channels"); + } + + var format = wavStream.Format switch + { + SDL2WavStream.AudioFormat.U8 => wavStream.Channels == 1 ? BufferFormat.Mono8 : BufferFormat.Stereo8, + SDL2WavStream.AudioFormat.S16LSB or SDL2WavStream.AudioFormat.S16MSB => wavStream.Channels == 1 ? BufferFormat.Mono16 : BufferFormat.Stereo16, + SDL2WavStream.AudioFormat.S32LSB => throw new NotSupportedException("OpenAL does not support s32 samples"), + SDL2WavStream.AudioFormat.F32LSB when _floatExt == null => throw new NotSupportedException("This OpenAL implementation does not support f32 samples"), + SDL2WavStream.AudioFormat.F32LSB => (BufferFormat)(wavStream.Channels == 1 ? FloatBufferFormat.Mono : FloatBufferFormat.Stereo), + _ => throw new InvalidOperationException(), + }; + + StopWav(); + _wavSourceID = _al.GenSource(); + _wavBufferID = _al.GenBuffer(); + + var tempBuffer = new byte[wavStream.Length]; + wavStream.Read(tempBuffer); + + if (wavStream.Format == SDL2WavStream.AudioFormat.S16MSB) + { + EndiannessUtils.MutatingByteSwap16(tempBuffer); + } + + _al.BufferData(_wavBufferID, format, tempBuffer, wavStream.Frequency); + _al.SetSourceProperty(_wavSourceID, SourceFloat.Gain, (float)volume); + unsafe + { + var bid = _wavBufferID; + _al.SourceQueueBuffers(_wavSourceID, 1, &bid); + } + _al.SourcePlay(_wavSourceID); + } + private unsafe void UnqueueProcessedBuffers() { var releaseCount = GetSource(GetSourceInteger.BuffersProcessed); @@ -223,13 +288,8 @@ namespace BizHawk.Bizware.Audio public class BufferPoolItem { - public uint BufferID { get; } + public uint BufferID { get; } = _al.GenBuffer(); public int Length { get; set; } - - public BufferPoolItem() - { - BufferID = _al.GenBuffer(); - } } } } diff --git a/src/BizHawk.Bizware.Audio/SDL2WavStream.cs b/src/BizHawk.Bizware.Audio/SDL2WavStream.cs new file mode 100644 index 0000000000..ea169e4af6 --- /dev/null +++ b/src/BizHawk.Bizware.Audio/SDL2WavStream.cs @@ -0,0 +1,125 @@ +using System; +using System.IO; + +using BizHawk.Common; + +using static SDL2.SDL; + +namespace BizHawk.Bizware.Audio +{ + internal sealed class SDL2WavStream : Stream, ISpanStream + { + private IntPtr _wav; + private readonly uint _len; + private uint _pos; + + // These are the only formats SDL2's wav loadder will output + public enum AudioFormat : ushort + { + U8 = 0x0008, + S16LSB = 0x8010, + S32LSB = 0x8020, + F32LSB = 0x8120, + S16MSB = 0x9010, + } + + public int Frequency { get; } + public AudioFormat Format { get; } + public byte Channels { get; } + + public int BitsPerSample => Format switch + { + AudioFormat.U8 => 8, + AudioFormat.S16LSB or AudioFormat.S16MSB => 16, + AudioFormat.S32LSB or AudioFormat.F32LSB => 32, + _ => throw new InvalidOperationException(), + }; + + public SDL2WavStream(string path) + { + // TODO: Perhaps this should just take a Stream? + // need to update SDL2-CS since the version we're on doesn't expose SDL_LoadWAV_RW :( + if (SDL_LoadWAV(path, out var spec, out var wav, out var len) == IntPtr.Zero) + { + throw new($"Could not load WAV file! SDL error: {SDL_GetError()}"); + } + + Frequency = spec.freq; + Format = (AudioFormat)spec.format; + Channels = spec.channels; + + _wav = wav; + _len = len; + } + + protected override void Dispose(bool disposing) + { + SDL_FreeWAV(_wav); + _wav = IntPtr.Zero; + + base.Dispose(disposing); + } + + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => _len; + + public override long Position + { + get => _pos; + set + { + if (value < 0 || value > _len) + { + throw new ArgumentOutOfRangeException(paramName: nameof(value), value, message: "index out of range"); + } + + _pos = (uint)value; + } + } + + public override void Flush() + { + } + + public unsafe int Read(Span buffer) + { + if (_wav == IntPtr.Zero) + { + throw new ObjectDisposedException(nameof(SDL2WavStream)); + } + + var count = (int)Math.Min(buffer.Length, _len - _pos); + new ReadOnlySpan((void*)((nint)_wav + _pos), count).CopyTo(buffer); + _pos += (uint)count; + return count; + } + + public override int Read(byte[] buffer, int offset, int count) + => Read(new(buffer, offset, count)); + + public override long Seek(long offset, SeekOrigin origin) + { + var newpos = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.Current => _pos + offset, + SeekOrigin.End => _len + offset, + _ => offset + }; + + Position = newpos; + return newpos; + } + + public override void SetLength(long value) + => throw new NotSupportedException(); + + public void Write(ReadOnlySpan buffer) + => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + => throw new NotSupportedException(); + } +} diff --git a/src/BizHawk.Bizware.Audio/XAudio2SoundOutput.cs b/src/BizHawk.Bizware.Audio/XAudio2SoundOutput.cs index 2ea5b16ce4..cf6888e56c 100644 --- a/src/BizHawk.Bizware.Audio/XAudio2SoundOutput.cs +++ b/src/BizHawk.Bizware.Audio/XAudio2SoundOutput.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using BizHawk.Client.Common; +using BizHawk.Common; using Vortice.MediaFoundation; using Vortice.Multimedia; @@ -17,8 +18,9 @@ namespace BizHawk.Bizware.Audio private volatile bool _deviceResetRequired; private IXAudio2 _device; private IXAudio2MasteringVoice _masteringVoice; - private IXAudio2SourceVoice _sourceVoice; + private IXAudio2SourceVoice _sourceVoice, _wavVoice; private BufferPool _bufferPool; + private AudioBuffer _wavBuffer; private long _runningSamplesQueued; private static string GetDeviceId(string deviceName) @@ -49,7 +51,7 @@ namespace BizHawk.Bizware.Audio // note that this won't be called on the main thread, so we'll defer the reset to the main thread _device.CriticalError += (_, _) => _deviceResetRequired = true; _masteringVoice = _device.CreateMasteringVoice( - inputChannels: _sound.ChannelCount, + inputChannels: _sound.ChannelCount, inputSampleRate: _sound.SampleRate, deviceId: GetDeviceId(chosenDeviceName)); } @@ -58,6 +60,7 @@ namespace BizHawk.Bizware.Audio { if (_disposed) return; + StopWav(); _masteringVoice.Dispose(); _device.Dispose(); @@ -113,6 +116,7 @@ namespace BizHawk.Bizware.Audio _deviceResetRequired = false; StopSound(); + StopWav(); _masteringVoice.Dispose(); _device.Dispose(); @@ -138,6 +142,7 @@ namespace BizHawk.Bizware.Audio { _sound.HandleInitializationOrUnderrun(detectedUnderrun, ref samplesNeeded); } + return samplesNeeded; } @@ -154,6 +159,38 @@ namespace BizHawk.Bizware.Audio _runningSamplesQueued += sampleCount; } + private void StopWav() + { + _wavVoice?.Stop(); + _wavVoice?.Dispose(); + _wavVoice = null; + + _wavBuffer?.Dispose(); + _wavBuffer = null; + } + + public void PlayWavFile(string path, double volume) + { + using var wavStream = new SDL2WavStream(path); + var format = wavStream.Format == SDL2WavStream.AudioFormat.F32LSB + ? WaveFormat.CreateIeeeFloatWaveFormat(wavStream.Frequency, wavStream.Channels) + : new(wavStream.Frequency, wavStream.BitsPerSample, wavStream.Channels); + + StopWav(); + _wavVoice = _device.CreateSourceVoice(format); + _wavBuffer = new(unchecked((int)wavStream.Length)); + + wavStream.Read(_wavBuffer.AsSpan()); + if (wavStream.Format == SDL2WavStream.AudioFormat.S16MSB) + { + EndiannessUtils.MutatingByteSwap16(_wavBuffer.AsSpan()); + } + + _wavVoice.SubmitSourceBuffer(_wavBuffer); + _wavVoice.Volume = (float)volume; + _wavVoice.Start(); + } + private class BufferPool : IDisposable { private readonly List _availableItems = new(); diff --git a/src/BizHawk.Client.Common/Sound/Interfaces/ISoundOutput.cs b/src/BizHawk.Client.Common/Sound/Interfaces/ISoundOutput.cs index 2b2ab9bbe2..8e2b2ecbdb 100644 --- a/src/BizHawk.Client.Common/Sound/Interfaces/ISoundOutput.cs +++ b/src/BizHawk.Client.Common/Sound/Interfaces/ISoundOutput.cs @@ -10,5 +10,6 @@ namespace BizHawk.Client.Common int MaxSamplesDeficit { get; } int CalculateSamplesNeeded(); void WriteSamples(short[] samples, int sampleOffset, int sampleCount); + void PlayWavFile(string path, double volume); } } diff --git a/src/BizHawk.Client.Common/Sound/Output/DummySoundOutput.cs b/src/BizHawk.Client.Common/Sound/Output/DummySoundOutput.cs index 31d5839e55..9de7e51059 100644 --- a/src/BizHawk.Client.Common/Sound/Output/DummySoundOutput.cs +++ b/src/BizHawk.Client.Common/Sound/Output/DummySoundOutput.cs @@ -76,5 +76,9 @@ namespace BizHawk.Client.Common if (sampleCount == 0) return; _remainingSamples += sampleCount; } + + public void PlayWavFile(string path, double volume) + { + } } } diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index ac8049d399..872a4556cb 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -4895,13 +4895,20 @@ namespace BizHawk.Client.EmuHawk private void OpenRetroAchievements() { - RA = RetroAchievements.CreateImpl(this, InputManager, Tools, () => Config, RetroAchievementsMenuItem.DropDownItems, () => - { - RA.Dispose(); - RA = null; - RetroAchievementsMenuItem.DropDownItems.Clear(); - RetroAchievementsMenuItem.DropDownItems.Add(StartRetroAchievementsMenuItem); - }); + RA = RetroAchievements.CreateImpl( + this, + InputManager, + Tools, + () => Config, + path => Sound.PlayWavFile(path, 1), // TODO: Make this configurable + RetroAchievementsMenuItem.DropDownItems, + () => + { + RA.Dispose(); + RA = null; + RetroAchievementsMenuItem.DropDownItems.Clear(); + RetroAchievementsMenuItem.DropDownItems.Add(StartRetroAchievementsMenuItem); + }); RA?.Restart(); } diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Login.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Login.cs index 563a9e6c03..c54a4eefd0 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Login.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Login.cs @@ -68,7 +68,7 @@ namespace BizHawk.Client.EmuHawk { config.RAUsername = Username; config.RAToken = ApiToken; - if (EnableSoundEffects) _loginSound.PlayNoExceptions(); + PlaySound(_loginSound); return; } } @@ -79,9 +79,9 @@ namespace BizHawk.Client.EmuHawk config.RAUsername = Username; config.RAToken = ApiToken; - if (LoggedIn && EnableSoundEffects) + if (LoggedIn) { - _loginSound.PlayNoExceptions(); + PlaySound(_loginSound); } } diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Sound.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Sound.cs index 6c1a577d6f..0ff6655cbe 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Sound.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Sound.cs @@ -1,5 +1,5 @@ +using System; using System.IO; -using System.Media; using BizHawk.Common.PathExtensions; @@ -7,29 +7,21 @@ namespace BizHawk.Client.EmuHawk { public partial class RCheevos { - // NOTE: these are net framework only... - // this logic should probably be the main sound class - // this shouldn't be a blocker to moving to net core anyways - private static readonly SoundPlayer _loginSound = new(Path.Combine(PathUtils.ExeDirectoryPath, "overlay/login.wav")); - private static readonly SoundPlayer _unlockSound = new(Path.Combine(PathUtils.ExeDirectoryPath, "overlay/unlock.wav")); - private static readonly SoundPlayer _lboardStartSound = new(Path.Combine(PathUtils.ExeDirectoryPath, "overlay/lb.wav")); - private static readonly SoundPlayer _lboardFailedSound = new(Path.Combine(PathUtils.ExeDirectoryPath, "overlay/lbcancel.wav")); - private static readonly SoundPlayer _infoSound = new(Path.Combine(PathUtils.ExeDirectoryPath, "overlay/info.wav")); + private readonly Action _playWavFileCallback; + + private static readonly string _loginSound = Path.Combine(PathUtils.ExeDirectoryPath, "overlay/login.wav"); + private static readonly string _unlockSound = Path.Combine(PathUtils.ExeDirectoryPath, "overlay/unlock.wav"); + private static readonly string _lboardStartSound = Path.Combine(PathUtils.ExeDirectoryPath, "overlay/lb.wav"); + private static readonly string _lboardFailedSound = Path.Combine(PathUtils.ExeDirectoryPath, "overlay/lbcancel.wav"); + private static readonly string _infoSound = Path.Combine(PathUtils.ExeDirectoryPath, "overlay/info.wav"); private bool EnableSoundEffects { get; set; } - } - public static class SoundPlayerExtensions - { - public static void PlayNoExceptions(this SoundPlayer sound) + private void PlaySound(string path) { - try + if (EnableSoundEffects) { - sound.Play(); - } - catch - { - // ignored + _playWavFileCallback(path); } } } diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs index 433797f29a..1132117e07 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs @@ -214,10 +214,13 @@ namespace BizHawk.Client.EmuHawk InputManager inputManager, ToolManager tools, Func getConfig, + Action playWavFile, ToolStripItemCollection raDropDownItems, Action shutdownRACallback) : base(mainForm, inputManager, tools, getConfig, raDropDownItems, shutdownRACallback) { + _playWavFileCallback = playWavFile; + _isActive = true; _httpThread = new(HttpRequestThreadProc) { IsBackground = true, Priority = ThreadPriority.BelowNormal, Name = "RCheevos HTTP Thread" }; _httpThread.Start(); @@ -227,7 +230,6 @@ namespace BizHawk.Client.EmuHawk { throw new("rc_runtime_alloc returned NULL!"); } - Login(); _eventcb = EventHandlerCallback; _peekcb = PeekCallback; @@ -241,6 +243,7 @@ namespace BizHawk.Client.EmuHawk EnableSoundEffects = config.RASoundEffects; AllowUnofficialCheevos = config.RAAllowUnofficialCheevos; + Login(); BuildMenu(raDropDownItems); } @@ -488,7 +491,7 @@ namespace BizHawk.Client.EmuHawk var prefix = HardcoreMode ? "[HARDCORE] " : ""; _dialogParent.AddOnScreenMessage($"{prefix}Achievement Unlocked!"); _dialogParent.AddOnScreenMessage(cheevo.Description); - if (EnableSoundEffects) _unlockSound.PlayNoExceptions(); + PlaySound(_unlockSound); if (cheevo.IsOfficial) { @@ -509,7 +512,7 @@ namespace BizHawk.Client.EmuHawk var prefix = HardcoreMode ? "[HARDCORE] " : ""; _dialogParent.AddOnScreenMessage($"{prefix}Achievement Primed!"); _dialogParent.AddOnScreenMessage(cheevo.Description); - if (EnableSoundEffects) _infoSound.PlayNoExceptions(); + PlaySound(_infoSound); } break; @@ -528,7 +531,7 @@ namespace BizHawk.Client.EmuHawk CurrentLboard = lboard; _dialogParent.AddOnScreenMessage($"Leaderboard Attempt Started!"); _dialogParent.AddOnScreenMessage(lboard.Description); - if (EnableSoundEffects) _lboardStartSound.PlayNoExceptions(); + PlaySound(_lboardStartSound); } } @@ -550,7 +553,7 @@ namespace BizHawk.Client.EmuHawk _dialogParent.AddOnScreenMessage($"Leaderboard Attempt Failed! ({lboard.Score})"); _dialogParent.AddOnScreenMessage(lboard.Description); - if (EnableSoundEffects) _lboardFailedSound.PlayNoExceptions(); + PlaySound(_lboardFailedSound); } lboard.SetScore(0); @@ -588,7 +591,7 @@ namespace BizHawk.Client.EmuHawk _dialogParent.AddOnScreenMessage($"Leaderboard Attempt Complete! ({lboard.Score})"); _dialogParent.AddOnScreenMessage(lboard.Description); - if (EnableSoundEffects) _unlockSound.PlayNoExceptions(); + PlaySound(_unlockSound); } } @@ -615,7 +618,7 @@ namespace BizHawk.Client.EmuHawk var prefix = HardcoreMode ? "[HARDCORE] " : ""; _dialogParent.AddOnScreenMessage($"{prefix}Achievement Unprimed!"); _dialogParent.AddOnScreenMessage(cheevo.Description); - if (EnableSoundEffects) _infoSound.PlayNoExceptions(); + PlaySound(_infoSound); } break; diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.cs index 0fd65c939d..bc23153026 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.cs @@ -47,6 +47,7 @@ namespace BizHawk.Client.EmuHawk InputManager inputManager, ToolManager tools, Func getConfig, + Action playWavFile, ToolStripItemCollection raDropDownItems, Action shutdownRACallback) { @@ -72,7 +73,7 @@ namespace BizHawk.Client.EmuHawk } else { - return new RCheevos(mainForm, inputManager, tools, getConfig, raDropDownItems, shutdownRACallback); + return new RCheevos(mainForm, inputManager, tools, getConfig, playWavFile, raDropDownItems, shutdownRACallback); } } diff --git a/src/BizHawk.Client.EmuHawk/Sound/Sound.cs b/src/BizHawk.Client.EmuHawk/Sound/Sound.cs index 782d8f2b92..702190da21 100644 --- a/src/BizHawk.Client.EmuHawk/Sound/Sound.cs +++ b/src/BizHawk.Client.EmuHawk/Sound/Sound.cs @@ -235,5 +235,18 @@ namespace BizHawk.Client.EmuHawk _outputDevice.WriteSamples(samples, sampleOffset, sampleCount); } + + public void PlayWavFile(string path, float atten) + { + if (atten <= 0) return; + try + { + _outputDevice.PlayWavFile(path, Math.Min(atten, 1)); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } } }