mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #4291 from Atlante45/reverb_smoothing
Reverb smoothing
This commit is contained in:
commit
f6f54809d0
3 changed files with 92 additions and 74 deletions
|
@ -479,10 +479,20 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
|||
for (int i = 0; i < _zoneReverbSettings.size(); ++i) {
|
||||
AudioMixerClientData* data = static_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||
glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition();
|
||||
if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) {
|
||||
AABox box = _audioZones[_zoneReverbSettings[i].zone];
|
||||
if (box.contains(streamPosition)) {
|
||||
hasReverb = true;
|
||||
reverbTime = _zoneReverbSettings[i].reverbTime;
|
||||
wetLevel = _zoneReverbSettings[i].wetLevel;
|
||||
|
||||
// Modulate wet level with distance to wall
|
||||
float MIN_ATTENUATION_DISTANCE = 2.0f;
|
||||
float MAX_ATTENUATION = -12; // dB
|
||||
glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter());
|
||||
float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z);
|
||||
if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) {
|
||||
wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ AudioClient::AudioClient() :
|
|||
_audioSourceInjectEnabled(false),
|
||||
_reverb(false),
|
||||
_reverbOptions(&_scriptReverbOptions),
|
||||
_gverbLocal(NULL),
|
||||
_gverb(NULL),
|
||||
_inputToNetworkResampler(NULL),
|
||||
_networkToOutputResampler(NULL),
|
||||
|
@ -126,26 +125,23 @@ AudioClient::AudioClient() :
|
|||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||
|
||||
// Initialize GVerb
|
||||
initGverb();
|
||||
|
||||
const qint64 DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
|
||||
|
||||
_inputDevices = getDeviceNames(QAudio::AudioInput);
|
||||
_outputDevices = getDeviceNames(QAudio::AudioOutput);
|
||||
|
||||
const qint64 DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
|
||||
QTimer* updateTimer = new QTimer(this);
|
||||
connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices);
|
||||
updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// create GVerb filter
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
}
|
||||
|
||||
AudioClient::~AudioClient() {
|
||||
stop();
|
||||
|
||||
if (_gverbLocal) {
|
||||
gverb_free(_gverbLocal);
|
||||
}
|
||||
if (_gverb) {
|
||||
gverb_free(_gverb);
|
||||
}
|
||||
|
@ -158,6 +154,8 @@ void AudioClient::reset() {
|
|||
_toneSource.reset();
|
||||
_sourceGain.reset();
|
||||
_inputGain.reset();
|
||||
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
|
||||
void AudioClient::audioMixerKilled() {
|
||||
|
@ -491,8 +489,8 @@ void AudioClient::start() {
|
|||
_sourceGain.initialize();
|
||||
_noiseSource.initialize();
|
||||
_toneSource.initialize();
|
||||
_sourceGain.setParameters(0.25f,0.0f);
|
||||
_inputGain.setParameters(1.0f,0.0f);
|
||||
_sourceGain.setParameters(0.25f, 0.0f);
|
||||
_inputGain.setParameters(1.0f, 0.0f);
|
||||
}
|
||||
|
||||
void AudioClient::stop() {
|
||||
|
@ -535,38 +533,24 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
|
|||
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
|
||||
}
|
||||
|
||||
void AudioClient::initGverb() {
|
||||
ty_gverb* AudioClient::createGverbFilter() {
|
||||
// Initialize a new gverb instance
|
||||
if (_gverbLocal) {
|
||||
gverb_free(_gverbLocal);
|
||||
}
|
||||
_gverbLocal = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
|
||||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
|
||||
if (_gverb) {
|
||||
gverb_free(_gverb);
|
||||
}
|
||||
_gverb = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
|
||||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
ty_gverb* filter = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
|
||||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
void AudioClient::configureGverbFilter(ty_gverb* filter) {
|
||||
// Configure the instance (these functions are not super well named - they actually set several internal variables)
|
||||
gverb_set_roomsize(_gverbLocal, _reverbOptions->getRoomSize());
|
||||
gverb_set_revtime(_gverbLocal, _reverbOptions->getReverbTime());
|
||||
gverb_set_damping(_gverbLocal, _reverbOptions->getDamping());
|
||||
gverb_set_inputbandwidth(_gverbLocal, _reverbOptions->getInputBandwidth());
|
||||
gverb_set_earlylevel(_gverbLocal, DB_CO(_reverbOptions->getEarlyLevel()));
|
||||
gverb_set_taillevel(_gverbLocal, DB_CO(_reverbOptions->getTailLevel()));
|
||||
|
||||
gverb_set_roomsize(_gverb, _reverbOptions->getRoomSize());
|
||||
gverb_set_revtime(_gverb, _reverbOptions->getReverbTime());
|
||||
gverb_set_damping(_gverb, _reverbOptions->getDamping());
|
||||
gverb_set_inputbandwidth(_gverb, _reverbOptions->getInputBandwidth());
|
||||
gverb_set_earlylevel(_gverb, DB_CO(_reverbOptions->getEarlyLevel()));
|
||||
gverb_set_taillevel(_gverb, DB_CO(_reverbOptions->getTailLevel()));
|
||||
gverb_set_roomsize(filter, _reverbOptions->getRoomSize());
|
||||
gverb_set_revtime(filter, _reverbOptions->getReverbTime());
|
||||
gverb_set_damping(filter, _reverbOptions->getDamping());
|
||||
gverb_set_inputbandwidth(filter, _reverbOptions->getInputBandwidth());
|
||||
gverb_set_earlylevel(filter, DB_CO(_reverbOptions->getEarlyLevel()));
|
||||
gverb_set_taillevel(filter, DB_CO(_reverbOptions->getTailLevel()));
|
||||
}
|
||||
|
||||
void AudioClient::updateGverbOptions() {
|
||||
|
@ -579,7 +563,7 @@ void AudioClient::updateGverbOptions() {
|
|||
}
|
||||
if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
|
||||
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
||||
reverbChanged = true;
|
||||
// Not part of actual filter config, no need to set reverbChanged to true
|
||||
}
|
||||
|
||||
if (_reverbOptions != &_zoneReverbOptions) {
|
||||
|
@ -592,7 +576,17 @@ void AudioClient::updateGverbOptions() {
|
|||
}
|
||||
|
||||
if (reverbChanged) {
|
||||
initGverb();
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::setReverb(bool reverb) {
|
||||
_reverb = reverb;
|
||||
|
||||
if (!_reverb) {
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,14 +605,17 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
|
|||
_scriptReverbOptions.setWetLevel(options->getWetLevel());
|
||||
|
||||
if (_reverbOptions == &_scriptReverbOptions) {
|
||||
// Apply them to the reverb instance(s)
|
||||
initGverb();
|
||||
// Apply them to the reverb instances
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSamples, QAudioFormat& audioFormat, bool noEcho) {
|
||||
void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reverbAlone, int numSamples,
|
||||
QAudioFormat& audioFormat, bool noEcho) {
|
||||
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
|
||||
float dryFraction = (noEcho) ? 0.0f : (1.0f - wetFraction);
|
||||
float dryFraction = 1.0f - wetFraction;
|
||||
|
||||
float lValue,rValue;
|
||||
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
|
||||
|
@ -633,11 +630,19 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSample
|
|||
int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)lResult;
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)lValue * wetFraction;
|
||||
}
|
||||
} else if (j == (sample + 1)) {
|
||||
// right channel
|
||||
int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)rResult;
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)rValue * wetFraction;
|
||||
}
|
||||
} else {
|
||||
// ignore channels above 2
|
||||
}
|
||||
|
@ -647,9 +652,8 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSample
|
|||
|
||||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
|
||||
bool hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) &&
|
||||
!_shouldEchoToServer;
|
||||
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasLocalReverb)) {
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -659,6 +663,10 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
if (!_loopbackOutputDevice && _loopbackAudioOutput) {
|
||||
// we didn't have the loopback output device going so set that up now
|
||||
_loopbackOutputDevice = _loopbackAudioOutput->start();
|
||||
|
||||
if (!_loopbackOutputDevice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// do we need to setup a resampler?
|
||||
|
@ -671,26 +679,31 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
}
|
||||
}
|
||||
|
||||
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
||||
static QByteArray loopBackByteArray;
|
||||
loopBackByteArray.resize(numDestinationSamplesRequired(_inputFormat, _outputFormat,
|
||||
inputByteArray.size() / sizeof(int16_t)) * sizeof(int16_t));
|
||||
|
||||
int numInputSamples = inputByteArray.size() / sizeof(int16_t);
|
||||
int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples);
|
||||
|
||||
reverbAlone.resize(numInputSamples * sizeof(int16_t));
|
||||
loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t));
|
||||
|
||||
int16_t* inputSamples = reinterpret_cast<int16_t*>(inputByteArray.data());
|
||||
int16_t* reverbAloneSamples = reinterpret_cast<int16_t*>(reverbAlone.data());
|
||||
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
|
||||
|
||||
if (hasReverb) {
|
||||
updateGverbOptions();
|
||||
addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples,
|
||||
_inputFormat, !_shouldEchoLocally);
|
||||
}
|
||||
|
||||
possibleResampling(_loopbackResampler,
|
||||
reinterpret_cast<int16_t*>(inputByteArray.data()),
|
||||
reinterpret_cast<int16_t*>(loopBackByteArray.data()),
|
||||
inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t),
|
||||
(_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples,
|
||||
numInputSamples, numLoopbackSamples,
|
||||
_inputFormat, _outputFormat);
|
||||
|
||||
if (hasLocalReverb) {
|
||||
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
|
||||
int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
|
||||
updateGverbOptions();
|
||||
addReverb(_gverbLocal, loopbackSamples, numLoopbackSamples, _outputFormat, !_shouldEchoLocally);
|
||||
}
|
||||
|
||||
if (_loopbackOutputDevice) {
|
||||
_loopbackOutputDevice->write(loopBackByteArray);
|
||||
}
|
||||
_loopbackOutputDevice->write(loopBackByteArray);
|
||||
}
|
||||
|
||||
void AudioClient::handleAudioInput() {
|
||||
|
@ -884,11 +897,6 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
reinterpret_cast<int16_t*>(outputBuffer.data()),
|
||||
numNetworkOutputSamples, numDeviceOutputSamples,
|
||||
_desiredOutputFormat, _outputFormat);
|
||||
|
||||
if(_reverb || _receivedAudioStream.hasReverb()) {
|
||||
updateGverbOptions();
|
||||
addReverb(_gverb, (int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::sendMuteEnvironmentPacket() {
|
||||
|
|
|
@ -166,7 +166,7 @@ public slots:
|
|||
|
||||
float getInputVolume() const { return (_audioInput) ? _audioInput->volume() : 0.0f; }
|
||||
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
|
||||
void setReverb(bool reverb) { _reverb = reverb; }
|
||||
void setReverb(bool reverb);
|
||||
void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
void outputNotify();
|
||||
|
@ -241,7 +241,6 @@ private:
|
|||
AudioEffectOptions _scriptReverbOptions;
|
||||
AudioEffectOptions _zoneReverbOptions;
|
||||
AudioEffectOptions* _reverbOptions;
|
||||
ty_gverb* _gverbLocal;
|
||||
ty_gverb* _gverb;
|
||||
|
||||
// possible soxr streams needed for resample
|
||||
|
@ -250,9 +249,10 @@ private:
|
|||
soxr* _loopbackResampler;
|
||||
|
||||
// Adds Reverb
|
||||
void initGverb();
|
||||
ty_gverb* createGverbFilter();
|
||||
void configureGverbFilter(ty_gverb* filter);
|
||||
void updateGverbOptions();
|
||||
void addReverb(ty_gverb* gverb, int16_t* samples, int numSamples, QAudioFormat& format, bool noEcho = false);
|
||||
void addReverb(ty_gverb* gverb, int16_t* samples, int16_t* reverbAlone, int numSamples, QAudioFormat& format, bool noEcho = false);
|
||||
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
|
|
Loading…
Reference in a new issue