diff --git a/config.def.h b/config.def.h index 380828d102..8e8aa7777a 100644 --- a/config.def.h +++ b/config.def.h @@ -267,6 +267,7 @@ static const struct snes_keybind snes_keybinds_1[] = { { SSNES_CHEAT_TOGGLE, SK_u, NO_BTN, AXIS_NONE }, { SSNES_SCREENSHOT, SK_PRINT, NO_BTN, AXIS_NONE }, { SSNES_DSP_CONFIG, SK_c, NO_BTN, AXIS_NONE }, + { SSNES_MUTE, SK_F9, NO_BTN, AXIS_NONE }, { -1 } }; diff --git a/driver.h b/driver.h index df1088c9a6..bc66a8dee7 100644 --- a/driver.h +++ b/driver.h @@ -52,6 +52,7 @@ enum SSNES_CHEAT_TOGGLE, SSNES_SCREENSHOT, SSNES_DSP_CONFIG, + SSNES_MUTE, SSNES_BIND_LIST_END }; diff --git a/general.h b/general.h index 37ebf52497..d2da7673fb 100644 --- a/general.h +++ b/general.h @@ -231,6 +231,7 @@ struct global double src_ratio; bool use_float; + bool mute; float *outsamples; int16_t *conv_outsamples; diff --git a/gfx/py_state/py_state.c b/gfx/py_state/py_state.c index 58d466c669..d3795eeb2d 100644 --- a/gfx/py_state/py_state.c +++ b/gfx/py_state/py_state.c @@ -161,6 +161,7 @@ static void py_set_attrs(PyObject *mod) DECL_ATTR_SSNES(CHEAT_TOGGLE); DECL_ATTR_SSNES(SCREENSHOT); DECL_ATTR_SSNES(DSP_CONFIG); + DECL_ATTR_SSNES(MUTE); } static PyModuleDef SNESModule = { diff --git a/settings.c b/settings.c index 7e2503d481..3f55cae17f 100644 --- a/settings.c +++ b/settings.c @@ -492,6 +492,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE) DECLARE_BIND(screenshot, SSNES_SCREENSHOT) DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG) + DECLARE_BIND(audio_mute, SSNES_MUTE) }, { DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A) @@ -528,6 +529,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE) DECLARE_BIND(screenshot, SSNES_SCREENSHOT) DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG) + DECLARE_BIND(audio_mute, SSNES_MUTE) }, { DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A) @@ -564,6 +566,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE) DECLARE_BIND(screenshot, SSNES_SCREENSHOT) DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG) + DECLARE_BIND(audio_mute, SSNES_MUTE) }, { DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A) @@ -600,6 +603,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE) DECLARE_BIND(screenshot, SSNES_SCREENSHOT) DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG) + DECLARE_BIND(audio_mute, SSNES_MUTE) }, { DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A) @@ -636,6 +640,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = { DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE) DECLARE_BIND(screenshot, SSNES_SCREENSHOT) DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG) + DECLARE_BIND(audio_mute, SSNES_MUTE) }, }; diff --git a/ssnes.c b/ssnes.c index 161f77fcde..6e1b03cc1b 100644 --- a/ssnes.c +++ b/ssnes.c @@ -247,7 +247,7 @@ static void video_cached_frame(void) #endif } -static bool audio_flush(const int16_t *data, unsigned samples) +static bool audio_flush(const int16_t *data, size_t samples) { #ifdef HAVE_FFMPEG if (g_extern.recording) @@ -302,9 +302,16 @@ static bool audio_flush(const int16_t *data, unsigned samples) output_frames = dsp_output.frames; } + union + { + float f[0x10000]; + int16_t i[0x10000 * sizeof(float) / sizeof(int16_t)]; + } static const empty_buf; + if (g_extern.audio_data.use_float) { - if (driver.audio->write(driver.audio_data, output_data, output_frames * sizeof(float) * 2) < 0) + if (driver.audio->write(driver.audio_data, + g_extern.audio_data.mute ? empty_buf.f : output_data, output_frames * sizeof(float) * 2) < 0) { fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); return false; @@ -312,10 +319,14 @@ static bool audio_flush(const int16_t *data, unsigned samples) } else { - audio_convert_float_to_s16(g_extern.audio_data.conv_outsamples, - output_data, output_frames * 2); + if (!g_extern.audio_data.mute) + { + audio_convert_float_to_s16(g_extern.audio_data.conv_outsamples, + output_data, output_frames * 2); + } - if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, + if (driver.audio->write(driver.audio_data, + g_extern.audio_data.mute ? empty_buf.i : g_extern.audio_data.conv_outsamples, output_frames * sizeof(int16_t) * 2) < 0) { fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); @@ -1824,9 +1835,32 @@ static void check_dsp_config(void) } #endif +static void check_mute(void) +{ + if (!g_extern.audio_active) + return; + + static bool old_pressed = false; + bool pressed = driver.input->key_pressed(driver.input_data, SSNES_MUTE); + if (pressed && !old_pressed) + { + g_extern.audio_data.mute = !g_extern.audio_data.mute; + + const char *msg = g_extern.audio_data.mute ? "Audio muted!" : "Audio unmuted!"; + msg_queue_clear(g_extern.msg_queue); + msg_queue_push(g_extern.msg_queue, msg, 1, 180); + + SSNES_LOG("%s\n", msg); + } + + old_pressed = pressed; +} + static void do_state_checks(void) { check_screenshot(); + check_mute(); + if (!g_extern.netplay) { check_pause(); diff --git a/ssnes.cfg b/ssnes.cfg index d4820fa4da..141ae2c8c0 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -308,6 +308,9 @@ # input_cheat_index_minus = t # input_cheat_toggle = u +# Mute/unmute audio +# input_audio_mute = f9 + # Take screenshot # input_screenshot = print_screen