diff --git a/bsnes/sfc/cpu/timing.cpp b/bsnes/sfc/cpu/timing.cpp index 46f80245..d7b8c056 100644 --- a/bsnes/sfc/cpu/timing.cpp +++ b/bsnes/sfc/cpu/timing.cpp @@ -242,6 +242,6 @@ auto CPU::lastCycle() -> void { if(!status.irqLock) { if(nmiTest()) status.nmiPending = true; if(irqTest()) status.irqPending = true; - status.interruptPending = (status.nmiPending || status.irqPending); + status.interruptPending = status.nmiPending || status.irqPending; } } diff --git a/bsnes/target-bsnes/input/hotkeys.cpp b/bsnes/target-bsnes/input/hotkeys.cpp index 9febe88c..5a0eb95b 100644 --- a/bsnes/target-bsnes/input/hotkeys.cpp +++ b/bsnes/target-bsnes/input/hotkeys.cpp @@ -33,7 +33,7 @@ auto InputManager::bindHotkeys() -> void { } volume = Emulator::audio.volume(); if(settings.rewind.mute) { - Emulator::audio.setVolume(0.0); + program.mute |= Program::Mute::Rewind; } else { Emulator::audio.setVolume(volume * 0.65); } @@ -41,6 +41,7 @@ auto InputManager::bindHotkeys() -> void { rewinding = false; if(!emulator->loaded()) return; program.rewindMode(Program::Rewind::Mode::Playing); + program.mute &= ~Program::Mute::Rewind; Emulator::audio.setVolume(volume); })); @@ -87,7 +88,7 @@ auto InputManager::bindHotkeys() -> void { Emulator::audio.setFrequency(frequency / settings.fastForward.limiter); } if(settings.fastForward.mute) { - Emulator::audio.setVolume(0.0); + program.mute |= Program::Mute::FastForward; } else if(settings.fastForward.limiter) { Emulator::audio.setVolume(volume * 0.65); } @@ -101,6 +102,7 @@ auto InputManager::bindHotkeys() -> void { if(settings.fastForward.limiter) { Emulator::audio.setFrequency(frequency); } + program.mute &= ~Program::Mute::FastForward; Emulator::audio.setVolume(volume); })); diff --git a/bsnes/target-bsnes/presentation/presentation.cpp b/bsnes/target-bsnes/presentation/presentation.cpp index 076078af..b0b37fc2 100644 --- a/bsnes/target-bsnes/presentation/presentation.cpp +++ b/bsnes/target-bsnes/presentation/presentation.cpp @@ -86,8 +86,12 @@ auto Presentation::create() -> void { shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader"); muteAudio.setText("Mute Audio").setChecked(settings.audio.mute).onToggle([&] { settings.audio.mute = muteAudio.checked(); - program.updateAudioEffects(); - }); + if(settings.audio.mute) { + program.mute |= Program::Mute::Always; + } else { + program.mute &= ~Program::Mute::Always; + } + }).doToggle(); //set initial mute state flag showStatusBar.setText("Show Status Bar").setChecked(settings.general.statusBar).onToggle([&] { settings.general.statusBar = showStatusBar.checked(); if(!showStatusBar.checked()) { diff --git a/bsnes/target-bsnes/program/audio.cpp b/bsnes/target-bsnes/program/audio.cpp index a0aaf80e..1644eb11 100644 --- a/bsnes/target-bsnes/program/audio.cpp +++ b/bsnes/target-bsnes/program/audio.cpp @@ -71,7 +71,7 @@ auto Program::updateAudioLatency() -> void { } auto Program::updateAudioEffects() -> void { - double volume = settings.audio.mute ? 0.0 : settings.audio.volume * 0.01; + double volume = settings.audio.volume * 0.01; Emulator::audio.setVolume(volume); double balance = max(-1.0, min(+1.0, (settings.audio.balance - 50) / 50.0)); diff --git a/bsnes/target-bsnes/program/platform.cpp b/bsnes/target-bsnes/program/platform.cpp index 9b7f5723..18062e88 100644 --- a/bsnes/target-bsnes/program/platform.cpp +++ b/bsnes/target-bsnes/program/platform.cpp @@ -229,7 +229,6 @@ auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height uint filterWidth = width, filterHeight = height; auto filterRender = filterSelect(filterWidth, filterHeight, scale); - if(auto [output, length] = video.acquire(filterWidth, filterHeight); output) { filterRender(palette, output, length, (const uint16_t*)data, pitch << 1, width, height); video.release(); @@ -256,6 +255,11 @@ auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height } auto Program::audioFrame(const float* samples, uint channels) -> void { + if(mute) { + double silence[] = {0.0, 0.0}; + return audio.output(silence); + } + double frame[] = {samples[0], samples[1]}; audio.output(frame); } diff --git a/bsnes/target-bsnes/program/program.hpp b/bsnes/target-bsnes/program/program.hpp index b2072257..2ca1185e 100644 --- a/bsnes/target-bsnes/program/program.hpp +++ b/bsnes/target-bsnes/program/program.hpp @@ -188,6 +188,14 @@ public: string statusFrameRate; bool startFullScreen = false; + + struct Mute { enum : uint { + Always = 1 << 1, + Unfocused = 1 << 2, + FastForward = 1 << 3, + Rewind = 1 << 4, + };}; + uint mute = 0; }; extern Program program; diff --git a/bsnes/target-bsnes/program/utility.cpp b/bsnes/target-bsnes/program/utility.cpp index d8b645e1..76201c0a 100644 --- a/bsnes/target-bsnes/program/utility.cpp +++ b/bsnes/target-bsnes/program/utility.cpp @@ -72,7 +72,15 @@ auto Program::inactive() -> bool { auto Program::focused() -> bool { //exclusive mode creates its own top-level window: presentation window will not have focus - if(video && video.exclusive()) return true; - if(presentation.focused()) return true; - return false; + if(video.exclusive() || presentation.focused()) { + mute &= ~Mute::Unfocused; + return true; + } else { + if(settings.audio.muteUnfocused) { + mute |= Mute::Unfocused; + } else { + mute &= ~Mute::Unfocused; + } + return false; + } } diff --git a/bsnes/target-bsnes/program/viewport.cpp b/bsnes/target-bsnes/program/viewport.cpp index 8335f5ef..e1f32394 100644 --- a/bsnes/target-bsnes/program/viewport.cpp +++ b/bsnes/target-bsnes/program/viewport.cpp @@ -78,9 +78,13 @@ auto Program::viewportRefresh() -> void { do { uint x = uint8_t(SnowData[i * 2 + 0] >> 8) * snowX; uint y = uint8_t(SnowData[i * 2 + 1] >> 8) * snowY; - if((SnowVelDist[i * 2] & 8) != 0) { - uint8_t color = 228 + (SnowVelDist[i * 2] & 0x03); - if(y) output[y * length + x] = color << 16 | color << 8 | color << 0; + if((SnowVelDist[i * 2] & 8) != 0 && y) { + uint32_t pixel = output[y * length + x]; + float a = SnowVelDist[i * 2] / 255.0; + uint8_t r = (pixel >> 16 & 0xff) * a + 255 * (1.0 - a); + uint8_t g = (pixel >> 8 & 0xff) * a + 255 * (1.0 - a); + uint8_t b = (pixel >> 0 & 0xff) * a + 255 * (1.0 - a); + output[y * length + x] = 255u << 24 | r << 16 | g << 8 | b << 0; } } while(++i != 200); diff --git a/bsnes/target-bsnes/settings/audio.cpp b/bsnes/target-bsnes/settings/audio.cpp index 18364a9f..62d0fe38 100644 --- a/bsnes/target-bsnes/settings/audio.cpp +++ b/bsnes/target-bsnes/settings/audio.cpp @@ -45,4 +45,8 @@ auto AudioSettings::create() -> void { balanceValue.setText(value); program.updateAudioEffects(); }).doChange(); + + muteUnfocused.setText("Mute when unfocused").setChecked(settings.audio.muteUnfocused).onToggle([&] { + settings.audio.muteUnfocused = muteUnfocused.checked(); + }); } diff --git a/bsnes/target-bsnes/settings/settings.cpp b/bsnes/target-bsnes/settings/settings.cpp index 9204888b..06a460f7 100644 --- a/bsnes/target-bsnes/settings/settings.cpp +++ b/bsnes/target-bsnes/settings/settings.cpp @@ -71,10 +71,11 @@ auto Settings::process(bool load) -> void { bind(natural, "Audio/Frequency", audio.frequency); bind(natural, "Audio/Latency", audio.latency); - bind(boolean, "Audio/Mute", audio.mute); - bind(integer, "Audio/Skew", audio.skew); - bind(natural, "Audio/Volume", audio.volume); - bind(natural, "Audio/Balance", audio.balance); + bind(boolean, "Audio/Mute", audio.mute); + bind(boolean, "Audio/MuteUnfocused", audio.muteUnfocused); + bind(integer, "Audio/Skew", audio.skew); + bind(natural, "Audio/Volume", audio.volume); + bind(natural, "Audio/Balance", audio.balance); bind(text, "Input/Driver", input.driver); bind(natural, "Input/Frequency", input.frequency); diff --git a/bsnes/target-bsnes/settings/settings.hpp b/bsnes/target-bsnes/settings/settings.hpp index 0e0570da..9e64eccb 100644 --- a/bsnes/target-bsnes/settings/settings.hpp +++ b/bsnes/target-bsnes/settings/settings.hpp @@ -37,6 +37,7 @@ struct Settings : Markup::Node { uint latency = 0; bool mute = false; + bool muteUnfocused = false; int skew = 0; uint volume = 100; uint balance = 50; @@ -170,6 +171,8 @@ private: Label balanceLabel{&effectsLayout, Size{0, 0}}; Label balanceValue{&effectsLayout, Size{50_sx, 0}}; HorizontalSlider balanceSlider{&effectsLayout, Size{~0, 0}}; + // + CheckLabel muteUnfocused{this, Size{~0, 0}}; }; struct InputSettings : VerticalLayout {