diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4d3311b065..cef6adcab0 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -221,6 +221,35 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, return (float)loudness * scale; } +template +static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) { + + // fast path for unity gain + if (gain0 == 1.0f && gain1 == 1.0f) { + return; + } + + // cubic poly from gain0 to gain1 + float c3 = -2.0f * (gain1 - gain0); + float c2 = 3.0f * (gain1 - gain0); + float c0 = gain0; + + float t = 0.0f; + float tStep = 1.0f / numFrames; + + for (int i = 0; i < numFrames; i++) { + + // evaluate poly over t=[0,1) + float gain = (c3 * t + c2) * t * t + c0; + t += tStep; + + // apply gain to all channels + for (int ch = 0; ch < NUM_CHANNELS; ch++) { + buffer[NUM_CHANNELS*i + ch] *= gain; + } + } +} + static inline float convertToFloat(int16_t sample) { return (float)sample * (1 / 32768.0f); } @@ -2109,6 +2138,14 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int framesPopped = samplesPopped / AudioConstants::STEREO; int bytesWritten; if (samplesPopped > 0) { + + // apply output gain + float newGain = _audio->_outputGain.load(std::memory_order_acquire); + float oldGain = _audio->_lastOutputGain; + _audio->_lastOutputGain = newGain; + + applyGainSmoothing(mixBuffer, framesPopped, oldGain, newGain); + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { // limit the audio _audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7608bf5cdb..00dd7ae299 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -395,6 +395,8 @@ private: int _outputPeriod { 0 }; float* _outputMixBuffer { NULL }; int16_t* _outputScratchBuffer { NULL }; + std::atomic _outputGain { 1.0f }; + float _lastOutputGain { 1.0f }; // for local audio (used by audio injectors thread) std::atomic _localInjectorGain { 1.0f };