Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX(client): Positional audio fixes #6234

Merged
merged 3 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/mumble/AudioConfigDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,22 +866,22 @@ void AudioOutputDialog::on_qsbMinimumDistance_valueChanged(double value) {
qsMinDistance->setValue(value * 10);

// Ensure that max distance is always a least 1m larger than min distance
qsMaxDistance->setValue(std::max(qsMaxDistance->value(), static_cast< int >(value * 10) + 1));
qsMaxDistance->setValue(std::max(qsMaxDistance->value(), static_cast< int >(value * 10) + 10));
}

void AudioOutputDialog::on_qsMaxDistance_valueChanged(int value) {
QSignalBlocker blocker(qsbMaximumDistance);
qsbMaximumDistance->setValue(value / 10.0f);

// Ensure that max distance is always a least 1m larger than min distance
// Ensure that min distance is always a least 1m less than max distance
qsbMinimumDistance->setValue(std::min(qsbMinimumDistance->value(), (value / 10.0) - 1));
}
void AudioOutputDialog::on_qsbMaximumDistance_valueChanged(double value) {
QSignalBlocker blocker(qsMaxDistance);
qsMaxDistance->setValue(value * 10);

// Ensure that max distance is always a least 1m larger than min distance
qsMinDistance->setValue(std::min(qsMinDistance->value(), static_cast< int >(value * 10) - 1));
// Ensure that min distance is always a least 1m less than max distance
qsMinDistance->setValue(std::min(qsMinDistance->value(), static_cast< int >(value * 10) - 10));
}

void AudioOutputDialog::on_qsMinimumVolume_valueChanged(int value) {
Expand Down
68 changes: 45 additions & 23 deletions src/mumble/AudioOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,40 @@ AudioOutput::~AudioOutput() {
float AudioOutput::calcGain(float dotproduct, float distance) {
// dotproduct is in the range [-1, 1], thus we renormalize it to the range [0, 1]
float dotfactor = (dotproduct + 1.0f) / 2.0f;
float att;

// Volume on the ear opposite to the sound should never reach 0 in the real world.
// Therefore, we define the minimum volume as 1/4th of the theoretical maximum (ignoring
// the sound direction but taking distance into account) for _any_ ear.
const float offset = (1.0f - dotfactor) * 0.25f;
dotfactor += offset;

float att;

// No distance attenuation
if (Global::get().s.fAudioMaxDistVolume > 0.99f) {
att = qMin(1.0f, dotfactor + Global::get().s.fAudioBloom);
if (distance < 0.01f) {
// Listener is "inside" source -> no attenuation
// Without this extra check, we would have a dotfactor of 0.5
// despite being numerically inside the source leading to a loss
// of volume.
att = 1.0f;
} else if (Global::get().s.fAudioMaxDistVolume > 0.99f) {
// User selected no distance attenuation
att = std::min(1.0f, dotfactor + Global::get().s.fAudioBloom);
} else if (distance < Global::get().s.fAudioMinDistance) {
// Fade in blooming as soon as the sound source enters fAudioMinDistance and increase it to its full
// capability when the audio source is at the same position as the local player
float bloomfac = Global::get().s.fAudioBloom * (1.0f - distance / Global::get().s.fAudioMinDistance);

att = qMin(1.0f, bloomfac + dotfactor);
att = std::min(1.0f, bloomfac + dotfactor);
} else {
float datt;

if (distance >= Global::get().s.fAudioMaxDistance) {
datt = Global::get().s.fAudioMaxDistVolume;
} else {
float mvol = Global::get().s.fAudioMaxDistVolume;
if (mvol < 0.01f)
mvol = 0.01f;
if (mvol < 0.005f) {
mvol = 0.005f;
}

float drel = (distance - Global::get().s.fAudioMinDistance)
/ (Global::get().s.fAudioMaxDistance - Global::get().s.fAudioMinDistance);
Expand Down Expand Up @@ -660,21 +673,30 @@ bool AudioOutput::mix(void *outbuff, unsigned int frameCount) {
}
}

const bool isAudible =
(Global::get().s.fAudioMaxDistVolume > 0) || (len < Global::get().s.fAudioMaxDistance);

for (unsigned int s = 0; s < nchan; ++s) {
const float dot = bSpeakerPositional[s]
? connectionVec.x * speaker[s * 3 + 0] + connectionVec.y * speaker[s * 3 + 1]
+ connectionVec.z * speaker[s * 3 + 2]
: 1.0f;
// Volume on the ear opposite to the sound should never reach 0 in the real world.
// The gain is multiplied by 19/20 and 1/20 is added. This will have the effect
// of bringing the lowest value up to 1/20, while keeping the highest value at 1.
// E.g. calcGain() = 1; 1 * 19/20 + 1/20 = 0.95 + 0.05 = 1
// calcGain() = 0; 0 * 19/20 + 1/20 = 0 + 0.05 = 0.05
const float str = svol[s] * (1 / 20.0 + (19 / 20.0) * calcGain(dot, len)) * volumeAdjustment;
float channelVol;
if (isAudible) {
// In the current contex, we know that sound reaches at least one ear.
channelVol = svol[s] * calcGain(dot, len) * volumeAdjustment;
} else {
// The user has set the minimum positional volume to 0 and this sound source
// is exceeding the positional volume range. This means that the sound is completely
// inaudible at the current position. We therefore set the volume the to 0,
// making sure the user really can not hear any audio from that source.
channelVol = 0;
}

float *RESTRICT o = output + s;
const float old = (buffer->pfVolume[s] >= 0.0f) ? buffer->pfVolume[s] : str;
const float inc = (str - old) / static_cast< float >(frameCount);
buffer->pfVolume[s] = str;
const float old = (buffer->pfVolume[s] >= 0.0f) ? buffer->pfVolume[s] : channelVol;
const float inc = (channelVol - old) / static_cast< float >(frameCount);
buffer->pfVolume[s] = channelVol;

// Calculates the ITD offset of the audio data this frame.
// Interaural Time Delay (ITD) is a small time delay between your ears
Expand All @@ -692,10 +714,10 @@ bool AudioOutput::mix(void *outbuff, unsigned int frameCount) {
const float incOffset = (offset - oldOffset) / static_cast< float >(frameCount);
buffer->piOffset[s] = offset;
/*
qWarning("%d: Pos %f %f %f : Dot %f Len %f Str %f", s, speaker[s*3+0],
speaker[s*3+1], speaker[s*3+2], dot, len, str);
qWarning("%d: Pos %f %f %f : Dot %f Len %f ChannelVol %f", s, speaker[s*3+0],
speaker[s*3+1], speaker[s*3+2], dot, len, channelVol);
*/
if ((old >= 0.00000001f) || (str >= 0.00000001f)) {
if ((old >= 0.00000001f) || (channelVol >= 0.00000001f)) {
for (unsigned int i = 0; i < frameCount; ++i) {
unsigned int currentOffset = oldOffset + incOffset * i;
if (speech && speech->bStereo) {
Expand All @@ -714,19 +736,19 @@ bool AudioOutput::mix(void *outbuff, unsigned int frameCount) {
// Mix the current audio source into the output by adding it to the elements of the output buffer after
// having applied a volume adjustment
for (unsigned int s = 0; s < nchan; ++s) {
const float str = svol[s] * volumeAdjustment;
float *RESTRICT o = output + s;
const float channelVol = svol[s] * volumeAdjustment;
float *RESTRICT o = output + s;
if (buffer->bStereo) {
// Linear-panning stereo stream according to the projection of fSpeaker vector on left-right
// direction.
// frame: for a stereo stream, the [LR] pair inside ...[LR]LRLRLR.... is a frame
for (unsigned int i = 0; i < frameCount; ++i)
o[i * nchan] += (pfBuffer[2 * i] * fStereoPanningFactor[2 * s + 0]
+ pfBuffer[2 * i + 1] * fStereoPanningFactor[2 * s + 1])
* str;
* channelVol;
} else {
for (unsigned int i = 0; i < frameCount; ++i)
o[i * nchan] += pfBuffer[i] * str;
o[i * nchan] += pfBuffer[i] * channelVol;
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/mumble/ManualPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static QPointer< Manual > mDlg = nullptr;
static bool bLinkable = false;
static bool bActive = true;

static int iAzimuth = 180;
static int iAzimuth = 0;
static int iElevation = 0;

static const QString defaultContext = QString::fromLatin1("Mumble");
Expand Down Expand Up @@ -52,8 +52,8 @@ Manual::Manual(QWidget *p) : QDialog(p) {
// The center of the indicator's circle will represent the current position
indicator.addEllipse(QRectF(-indicatorDiameter / 2, -indicatorDiameter / 2, indicatorDiameter, indicatorDiameter));
// A line will indicate the indicator's orientation (azimuth)
indicator.moveTo(0, indicatorDiameter / 2);
indicator.lineTo(0, indicatorDiameter);
indicator.moveTo(0, -indicatorDiameter / 2);
indicator.lineTo(0, -indicatorDiameter);

m_qgiPosition = m_qgsScene->addPath(indicator);

Expand Down
Loading