diff --git a/audio/utils.c b/audio/utils.c index 74a0e76b04..1061fe3099 100644 --- a/audio/utils.c +++ b/audio/utils.c @@ -22,10 +22,11 @@ #endif void audio_convert_s16_to_float_C(float *out, - const int16_t *in, size_t samples) + const int16_t *in, size_t samples, float gain) { + gain = gain / 0x8000; for (size_t i = 0; i < samples; i++) - out[i] = (float)in[i] / 0x8000; + out[i] = (float)in[i] * gain; } void audio_convert_float_to_s16_C(int16_t *out, @@ -40,9 +41,10 @@ void audio_convert_float_to_s16_C(int16_t *out, #if __SSE2__ void audio_convert_s16_to_float_SSE2(float *out, - const int16_t *in, size_t samples) + const int16_t *in, size_t samples, float gain) { - __m128 factor = _mm_set1_ps(1.0f / (0x7fff * 0x10000)); + float fgain = gain / (0x7fff * 0x10000); + __m128 factor = _mm_set1_ps(fgain); size_t i; for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) { @@ -61,7 +63,7 @@ void audio_convert_s16_to_float_SSE2(float *out, _mm_storeu_ps(out + 4, output[1]); } - audio_convert_s16_to_float_C(out, in, samples - i); + audio_convert_s16_to_float_C(out, in, samples - i, gain); } void audio_convert_float_to_s16_SSE2(int16_t *out, @@ -84,8 +86,9 @@ void audio_convert_float_to_s16_SSE2(int16_t *out, } #elif __ALTIVEC__ void audio_convert_s16_to_float_altivec(float *out, - const int16_t *in, size_t samples) + const int16_t *in, size_t samples, float gain) { + const vector float gain_vec = vec_splats(gain); // Unaligned loads/store is a bit expensive, so we optimize for the good path (very likely). if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0) { @@ -95,17 +98,17 @@ void audio_convert_s16_to_float_altivec(float *out, vector signed short input = vec_ld(0, in); vector signed int hi = vec_unpackh(input); vector signed int lo = vec_unpackl(input); - vector float out_hi = vec_ctf(hi, 15); - vector float out_lo = vec_ctf(lo, 15); + vector float out_hi = vec_mul(vec_ctf(hi, 15), gain_vec); + vector float out_lo = vec_mul(vec_ctf(lo, 15), gain_vec); vec_st(out_hi, 0, out); vec_st(out_lo, 16, out); } - audio_convert_s16_to_float_C(out, in, samples - i); + audio_convert_s16_to_float_C(out, in, samples - i, gain); } else - audio_convert_s16_to_float_C(out, in, samples); + audio_convert_s16_to_float_C(out, in, samples, gain); } void audio_convert_float_to_s16_altivec(int16_t *out, diff --git a/audio/utils.h b/audio/utils.h index 228bb95302..26238881cc 100644 --- a/audio/utils.h +++ b/audio/utils.h @@ -24,7 +24,7 @@ #define audio_convert_float_to_s16 audio_convert_float_to_s16_SSE2 void audio_convert_s16_to_float_SSE2(float *out, - const int16_t *in, size_t samples); + const int16_t *in, size_t samples, float gain); void audio_convert_float_to_s16_SSE2(int16_t *out, const float *in, size_t samples); @@ -34,7 +34,7 @@ void audio_convert_float_to_s16_SSE2(int16_t *out, #define audio_convert_float_to_s16 audio_convert_float_to_s16_altivec void audio_convert_s16_to_float_altivec(float *out, - const int16_t *in, size_t samples); + const int16_t *in, size_t samples, float gain); void audio_convert_float_to_s16_altivec(int16_t *out, const float *in, size_t samples); @@ -45,7 +45,7 @@ void audio_convert_float_to_s16_altivec(int16_t *out, #endif void audio_convert_s16_to_float_C(float *out, - const int16_t *in, size_t samples); + const int16_t *in, size_t samples, float gain); void audio_convert_float_to_s16_C(int16_t *out, const float *in, size_t samples); diff --git a/config.def.h b/config.def.h index dcc7267d37..2da0c9e9e9 100644 --- a/config.def.h +++ b/config.def.h @@ -280,6 +280,9 @@ static const float rate_control_delta = 0.006; static const float rate_control_delta = 0.005; #endif +// Default audio volume in dB. (0.0 dB == unity gain). +static const float audio_volume = 0.0; + ////////////// // Misc ////////////// @@ -391,6 +394,8 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_NETPLAY_FLIP, RETROK_i, NO_BTN, AXIS_NONE }, { true, RARCH_SLOWMOTION, RETROK_e, NO_BTN, AXIS_NONE }, { true, RARCH_ENABLE_HOTKEY, RETROK_UNKNOWN, NO_BTN, AXIS_NONE }, + { true, RARCH_VOLUME_UP, RETROK_KP_PLUS, NO_BTN, AXIS_NONE }, + { true, RARCH_VOLUME_DOWN, RETROK_KP_MINUS, NO_BTN, AXIS_NONE }, }; // Player 2-5 diff --git a/driver.c b/driver.c index b6918f0620..c346d9ef2b 100644 --- a/driver.c +++ b/driver.c @@ -388,6 +388,9 @@ void init_audio(void) RARCH_WARN("Audio rate control was desired, but driver does not support needed features.\n"); } + g_extern.audio_data.volume_db = g_settings.audio.volume; + g_extern.audio_data.volume_gain = db_to_gain(g_settings.audio.volume); + #ifdef HAVE_DYLIB init_dsp_plugin(); #endif diff --git a/driver.h b/driver.h index 53492c869a..9c2ee0bba7 100644 --- a/driver.h +++ b/driver.h @@ -86,6 +86,8 @@ enum // RetroArch specific bind IDs. RARCH_NETPLAY_FLIP, RARCH_SLOWMOTION, RARCH_ENABLE_HOTKEY, + RARCH_VOLUME_UP, + RARCH_VOLUME_DOWN, #ifdef RARCH_CONSOLE RARCH_CHEAT_INPUT, diff --git a/general.h b/general.h index ea5ae83e72..6c43cdf7d4 100644 --- a/general.h +++ b/general.h @@ -159,6 +159,7 @@ struct settings bool rate_control; float rate_control_delta; + float volume; // dB scale } audio; struct @@ -341,6 +342,9 @@ struct global bool rate_control; double orig_src_ratio; size_t driver_buffer_size; + + float volume_db; + float volume_gain; } audio_data; struct @@ -679,6 +683,11 @@ static inline uint16_t swap_if_little16(uint16_t val) return val; } +static inline float db_to_gain(float db) +{ + return powf(10.0f, db / 20.0f); +} + #ifdef GEKKO #include #endif diff --git a/retroarch.c b/retroarch.c index 09f46002e8..6a4fc3bad1 100644 --- a/retroarch.c +++ b/retroarch.c @@ -368,7 +368,8 @@ static bool audio_flush(const int16_t *data, size_t samples) struct resampler_data src_data = {0}; RARCH_PERFORMANCE_INIT(audio_convert_s16); RARCH_PERFORMANCE_START(audio_convert_s16); - audio_convert_s16_to_float(g_extern.audio_data.data, data, samples); + audio_convert_s16_to_float(g_extern.audio_data.data, data, samples, + g_extern.audio_data.volume_gain); RARCH_PERFORMANCE_STOP(audio_convert_s16); #if defined(HAVE_DYLIB) @@ -2405,6 +2406,37 @@ static void check_mute(void) old_pressed = pressed; } + +static void check_volume(void) +{ + if (!g_extern.audio_active) + return; + + float db_change = 0.0f; + bool pressed_up = input_key_pressed_func(RARCH_VOLUME_UP); + bool pressed_down = input_key_pressed_func(RARCH_VOLUME_DOWN); + if (!pressed_up && !pressed_down) + return; + + if (pressed_up) + db_change += 0.5f; + if (pressed_down) + db_change -= 0.5f; + + g_extern.audio_data.volume_db += db_change; + if (g_extern.audio_data.volume_db > 12.0f) + g_extern.audio_data.volume_db = 12.0f; + else if (g_extern.audio_data.volume_db < -80.0f) + g_extern.audio_data.volume_db = -80.0f; + + char msg[256]; + snprintf(msg, sizeof(msg), "Volume: %.1f dB", g_extern.audio_data.volume_db); + msg_queue_clear(g_extern.msg_queue); + msg_queue_push(g_extern.msg_queue, msg, 1, 180); + RARCH_LOG("%s\n", msg); + + g_extern.audio_data.volume_gain = db_to_gain(g_extern.audio_data.volume_db); +} #endif #ifdef HAVE_NETPLAY @@ -2441,6 +2473,7 @@ static void do_state_checks(void) #endif #if !defined(RARCH_PERFORMANCE_MODE) check_mute(); + check_volume(); #endif check_turbo(); diff --git a/retroarch.cfg b/retroarch.cfg index 9a554fd58a..3daf84e9e2 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -186,6 +186,11 @@ # Input rate = in_rate * (1.0 +/- audio_rate_control_delta) # audio_rate_control_delta = 0.005 +# Audio volume. Volume is expressed in dB. +# 0 dB is normal volume. No gain will be applied. +# Gain can be controlled in runtime with input_volume_up/input_volume_down. +# audio_volume = 0.0 + #### Input # Input driver. Depending on video driver, it might force a different input driver. @@ -352,6 +357,11 @@ # Alternatively, all hotkeys for keyboard could be disabled by the user. # input_enable_hotkey = +# Increases audio volume. +# input_volume_up = kp_plus +# Decreases audio volume. +# input_volume_down = kp_minus + #### Misc # Enable rewinding. This will take a performance hit when playing, so it is disabled by default. diff --git a/settings.c b/settings.c index 7ae6b165db..976a8a0b73 100644 --- a/settings.c +++ b/settings.c @@ -200,6 +200,7 @@ void config_set_defaults(void) g_settings.audio.sync = audio_sync; g_settings.audio.rate_control = rate_control; g_settings.audio.rate_control_delta = rate_control_delta; + g_settings.audio.volume = audio_volume; g_settings.rewind_enable = rewind_enable; g_settings.rewind_buffer_size = rewind_buffer_size; @@ -514,6 +515,7 @@ bool config_load_file(const char *path) CONFIG_GET_BOOL(audio.sync, "audio_sync"); CONFIG_GET_BOOL(audio.rate_control, "audio_rate_control"); CONFIG_GET_FLOAT(audio.rate_control_delta, "audio_rate_control_delta"); + CONFIG_GET_FLOAT(audio.volume, "audio_volume"); CONFIG_GET_STRING(video.driver, "video_driver"); CONFIG_GET_STRING(audio.driver, "audio_driver"); @@ -670,6 +672,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][RARCH_BIND_LIST_END_NULL] = DECLARE_BIND(netplay_flip_players, RARCH_NETPLAY_FLIP), DECLARE_BIND(slowmotion, RARCH_SLOWMOTION), DECLARE_BIND(enable_hotkey, RARCH_ENABLE_HOTKEY), + DECLARE_BIND(volume_up, RARCH_VOLUME_UP), + DECLARE_BIND(volume_down, RARCH_VOLUME_DOWN), }, { DECL_PLAYER(2) }, diff --git a/tools/retroarch-joyconfig.c b/tools/retroarch-joyconfig.c index 7693489c18..0fdca81d3b 100644 --- a/tools/retroarch-joyconfig.c +++ b/tools/retroarch-joyconfig.c @@ -120,6 +120,8 @@ static struct bind binds[] = { MISC_BIND("Netplay player flip", netplay_flip_players), MISC_BIND("Slow motion", slowmotion), MISC_BIND("Hotkey enable", enable_hotkey), + MISC_BIND("Volume up", volume_up), + MISC_BIND("Volume down", volume_down), }; #define MAX_BUTTONS 32