From 6b8c4729787daa0c584447f9014bf32320d82789 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:57:55 +1100 Subject: [PATCH] nicer volume sliders --- source/core/StarAudio.cpp | 20 ++++++++++++++++++++ source/core/StarAudio.hpp | 9 +++++++++ source/frontend/StarMainMixer.cpp | 1 + source/frontend/StarVoice.cpp | 17 ++++++++++------- source/frontend/StarVoice.hpp | 6 +++++- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/source/core/StarAudio.cpp b/source/core/StarAudio.cpp index 4f07d05..2dfdaee 100644 --- a/source/core/StarAudio.cpp +++ b/source/core/StarAudio.cpp @@ -13,6 +13,26 @@ namespace Star { +float const DefaultPerceptualRangeDb = 40.f; +float const DefaultPerceptualBoostRangeDb = 6.f; +// https://github.com/discord/perceptual +float perceptualToAmplitude(float perceptual, float normalizedMax, float range, float boostRange) { + if (perceptual == 0.f) return 0.f; + float dB = perceptual > normalizedMax + ? ((perceptual - normalizedMax) / normalizedMax) * boostRange + : (perceptual / normalizedMax) * range - range; + return normalizedMax * pow(10.f, dB / 20.f); +} + +float amplitudeToPerceptual(float amp, float normalizedMax, float range, float boostRange) { + if (amp == 0.f) return 0.f; + float const dB = 20.f * log10(amp / normalizedMax); + float perceptual = dB > 0.f + ? dB / boostRange + 1 + : (range + dB) / range; + return normalizedMax * perceptual; +} + namespace { struct WaveData { ByteArrayPtr byteArray; diff --git a/source/core/StarAudio.hpp b/source/core/StarAudio.hpp index cf8f91f..571aaf3 100644 --- a/source/core/StarAudio.hpp +++ b/source/core/StarAudio.hpp @@ -4,6 +4,15 @@ namespace Star { +extern float const DefaultPerceptualRangeDb; +extern float const DefaultPerceptualBoostRangeDb; + +float perceptualToAmplitude(float perceptual, float normalizedMax = 1.f, + float range = DefaultPerceptualRangeDb, float boostRange = DefaultPerceptualBoostRangeDb); + +float amplitudeToPerceptual(float amp, float normalizedMax = 1.f, + float range = DefaultPerceptualRangeDb, float boostRange = DefaultPerceptualBoostRangeDb); + STAR_CLASS(CompressedAudioImpl); STAR_CLASS(UncompressedAudioImpl); STAR_CLASS(Audio); diff --git a/source/frontend/StarMainMixer.cpp b/source/frontend/StarMainMixer.cpp index fe676d5..df80018 100644 --- a/source/frontend/StarMainMixer.cpp +++ b/source/frontend/StarMainMixer.cpp @@ -37,6 +37,7 @@ void MainMixer::update(float dt, bool muteSfx, bool muteMusic) { } } else if (!m_mutedGroups.contains(group)) { float volumeSetting = Root::singleton().configuration()->get(settingName).toFloat() / 100.0f; + volumeSetting = perceptualToAmplitude(volumeSetting); if (!m_groupVolumes.contains(group) || volumeSetting != m_groupVolumes[group]) { m_mixer->setGroupVolume(group, volumeSetting); m_groupVolumes[group] = volumeSetting; diff --git a/source/frontend/StarVoice.cpp b/source/frontend/StarVoice.cpp index b2c4695..0d56089 100644 --- a/source/frontend/StarVoice.cpp +++ b/source/frontend/StarVoice.cpp @@ -6,6 +6,7 @@ #include "StarRoot.hpp" #include "StarLogging.hpp" #include "StarInterpolation.hpp" +#include "StarAudio.hpp" #include "opus/opus.h" #include "SDL2/SDL.h" @@ -196,9 +197,11 @@ void Voice::loadJson(Json const& config, bool skipSave) { && change(m_deviceName, config.optString("deviceName"), changed)) resetDevice(); - m_threshold = config.getFloat("threshold", m_threshold); - m_inputVolume = config.getFloat("inputVolume", m_inputVolume); - m_outputVolume = config.getFloat("outputVolume", m_outputVolume); + m_threshold = config.getFloat("threshold", m_threshold); + m_inputAmplitude = perceptualToAmplitude( + m_inputVolume = config.getFloat("inputVolume", m_inputVolume)); + m_outputAmplitude = perceptualToAmplitude( + m_outputVolume = config.getFloat("outputVolume", m_outputVolume)); if (change(m_loopback, config.getBool("loopback", m_loopback), changed)) m_clientSpeaker->playing = false; @@ -321,7 +324,7 @@ void Voice::readAudioData(uint8_t* stream, int len) { } } - m_clientSpeaker->decibelLevel = getAudioLoudness((int16_t*)stream, sampleCount, m_inputVolume); + m_clientSpeaker->decibelLevel = getAudioLoudness((int16_t*)stream, sampleCount, m_inputAmplitude); if (!m_loopback) { if (active && !m_clientSpeaker->playing) @@ -411,7 +414,7 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) { if (mix) { finalBuffer.resize(sharedBuffer.size(), 0); - float vol = m_outputVolume; + float vol = m_outputAmplitude; for (size_t i = 0; i != sharedBuffer.size(); ++i) finalBuffer[i] = (int16_t)clamp(sharedBuffer[i] * vol, INT16_MIN, INT16_MAX); @@ -678,9 +681,9 @@ void Voice::thread() { } m_capturedChunksFrames -= VOICE_FRAME_SIZE; - if (m_inputVolume != 1.0f) { + if (m_inputAmplitude != 1.0f) { for (size_t i = 0; i != samples.size(); ++i) - samples[i] *= m_inputVolume; + samples[i] *= m_inputAmplitude; } if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) { diff --git a/source/frontend/StarVoice.hpp b/source/frontend/StarVoice.hpp index fa149b6..8059dd4 100644 --- a/source/frontend/StarVoice.hpp +++ b/source/frontend/StarVoice.hpp @@ -173,9 +173,13 @@ private: OpusEncoderPtr m_encoder; - + float m_outputVolume = 1.0f; float m_inputVolume = 1.0f; + + float m_outputAmplitude = 1.0f; + float m_inputAmplitude = 1.0f; + float m_threshold = -50.0f; int64_t m_lastSentTime = 0;