nicer volume sliders

This commit is contained in:
Kae 2024-03-25 01:57:55 +11:00
parent 5da4b1a4e3
commit 6b8c472978
5 changed files with 45 additions and 8 deletions

View File

@ -13,6 +13,26 @@
namespace Star { 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 { namespace {
struct WaveData { struct WaveData {
ByteArrayPtr byteArray; ByteArrayPtr byteArray;

View File

@ -4,6 +4,15 @@
namespace Star { 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(CompressedAudioImpl);
STAR_CLASS(UncompressedAudioImpl); STAR_CLASS(UncompressedAudioImpl);
STAR_CLASS(Audio); STAR_CLASS(Audio);

View File

@ -37,6 +37,7 @@ void MainMixer::update(float dt, bool muteSfx, bool muteMusic) {
} }
} else if (!m_mutedGroups.contains(group)) { } else if (!m_mutedGroups.contains(group)) {
float volumeSetting = Root::singleton().configuration()->get(settingName).toFloat() / 100.0f; float volumeSetting = Root::singleton().configuration()->get(settingName).toFloat() / 100.0f;
volumeSetting = perceptualToAmplitude(volumeSetting);
if (!m_groupVolumes.contains(group) || volumeSetting != m_groupVolumes[group]) { if (!m_groupVolumes.contains(group) || volumeSetting != m_groupVolumes[group]) {
m_mixer->setGroupVolume(group, volumeSetting); m_mixer->setGroupVolume(group, volumeSetting);
m_groupVolumes[group] = volumeSetting; m_groupVolumes[group] = volumeSetting;

View File

@ -6,6 +6,7 @@
#include "StarRoot.hpp" #include "StarRoot.hpp"
#include "StarLogging.hpp" #include "StarLogging.hpp"
#include "StarInterpolation.hpp" #include "StarInterpolation.hpp"
#include "StarAudio.hpp"
#include "opus/opus.h" #include "opus/opus.h"
#include "SDL2/SDL.h" #include "SDL2/SDL.h"
@ -196,9 +197,11 @@ void Voice::loadJson(Json const& config, bool skipSave) {
&& change(m_deviceName, config.optString("deviceName"), changed)) && change(m_deviceName, config.optString("deviceName"), changed))
resetDevice(); resetDevice();
m_threshold = config.getFloat("threshold", m_threshold); m_threshold = config.getFloat("threshold", m_threshold);
m_inputVolume = config.getFloat("inputVolume", m_inputVolume); m_inputAmplitude = perceptualToAmplitude(
m_outputVolume = config.getFloat("outputVolume", m_outputVolume); 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)) if (change(m_loopback, config.getBool("loopback", m_loopback), changed))
m_clientSpeaker->playing = false; 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 (!m_loopback) {
if (active && !m_clientSpeaker->playing) if (active && !m_clientSpeaker->playing)
@ -411,7 +414,7 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) {
if (mix) { if (mix) {
finalBuffer.resize(sharedBuffer.size(), 0); finalBuffer.resize(sharedBuffer.size(), 0);
float vol = m_outputVolume; float vol = m_outputAmplitude;
for (size_t i = 0; i != sharedBuffer.size(); ++i) for (size_t i = 0; i != sharedBuffer.size(); ++i)
finalBuffer[i] = (int16_t)clamp<int>(sharedBuffer[i] * vol, INT16_MIN, INT16_MAX); finalBuffer[i] = (int16_t)clamp<int>(sharedBuffer[i] * vol, INT16_MIN, INT16_MAX);
@ -678,9 +681,9 @@ void Voice::thread() {
} }
m_capturedChunksFrames -= VOICE_FRAME_SIZE; m_capturedChunksFrames -= VOICE_FRAME_SIZE;
if (m_inputVolume != 1.0f) { if (m_inputAmplitude != 1.0f) {
for (size_t i = 0; i != samples.size(); ++i) 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())) { if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) {

View File

@ -173,9 +173,13 @@ private:
OpusEncoderPtr m_encoder; OpusEncoderPtr m_encoder;
float m_outputVolume = 1.0f; float m_outputVolume = 1.0f;
float m_inputVolume = 1.0f; float m_inputVolume = 1.0f;
float m_outputAmplitude = 1.0f;
float m_inputAmplitude = 1.0f;
float m_threshold = -50.0f; float m_threshold = -50.0f;
int64_t m_lastSentTime = 0; int64_t m_lastSentTime = 0;