Skip to content

Commit

Permalink
REFAC(audio): Move echo cancellation options into an enum.
Browse files Browse the repository at this point in the history
  • Loading branch information
TerryGeng authored and Krzmbrzl committed Feb 11, 2021
1 parent db17c25 commit dafbce2
Show file tree
Hide file tree
Showing 18 changed files with 191 additions and 170 deletions.
4 changes: 2 additions & 2 deletions src/mumble/ALSAAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ALSAAudioInputRegistrar : public AudioInputRegistrar {
virtual AudioInput *create();
virtual const QList< audioDevice > getDeviceChoices();
virtual void setDeviceChoice(const QVariant &, Settings &);
virtual bool canEcho(int ,const QString &) const;
virtual bool canEcho(EchoCancelOptionID echoCancelID, const QString &outputSystem) const;
virtual bool isMicrophoneAccessDeniedByOS() { return false; };
};

Expand Down Expand Up @@ -113,7 +113,7 @@ void ALSAAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &
s.qsALSAInput = choice.toString();
}

bool ALSAAudioInputRegistrar::canEcho(int, const QString &) const {
bool ALSAAudioInputRegistrar::canEcho(EchoCancelOptionID, const QString &) const {
return false;
}

Expand Down
12 changes: 6 additions & 6 deletions src/mumble/ASIOInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ class ASIOAudioInputRegistrar : public AudioInputRegistrar {
virtual AudioInput *create();
virtual const QList< audioDevice > getDeviceChoices();
virtual void setDeviceChoice(const QVariant &, Settings &);
virtual bool canEcho(int, const QString &) const;
virtual bool canEcho(EchoCancelOptionID echoCancelID, const QString &outputSystem) const;
virtual bool isMicrophoneAccessDeniedByOS() { return false; };
};

ASIOAudioInputRegistrar::ASIOAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("ASIO")) {
useSpeexEchoCancellation();
echoOptions.push_back(EchoCancelOptionID::SPEEX_MIXED);
echoOptions.push_back(EchoCancelOptionID::SPEEX_MULTICHANNEL);
}

AudioInput *ASIOAudioInputRegistrar::create() {
Expand All @@ -43,10 +44,9 @@ const QList< audioDevice > ASIOAudioInputRegistrar::getDeviceChoices() {
return qlReturn;
}

bool ASIOAudioInputRegistrar::canEcho(int echoOption, const QString &) const {
return (echoOption == ECHO_CANCEL_DISABLED
|| echoOption == ECHO_CANCEL_SPEEX_MIXED
|| echoOption == ECHO_CANCEL_SPEEX_MULTICHANNEL);
bool ASIOAudioInputRegistrar::canEcho(EchoCancelOptionID echoOption, const QString &) const {
return (echoOption == EchoCancelOptionID::SPEEX_MIXED
|| echoOption == EchoCancelOptionID::SPEEX_MULTICHANNEL);
}

void ASIOAudioInputRegistrar::setDeviceChoice(const QVariant &, Settings &) {
Expand Down
25 changes: 13 additions & 12 deletions src/mumble/AudioConfigDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,12 @@ void AudioInputDialog::verifyMicrophonePermission() {
if (air->name == QLatin1String("CoreAudio")) {
qlInputHelp->setVisible(true);
qlInputHelp->setText(tr("Access to the microphone was denied. Please allow Mumble to use the microphone "
"by changing the settings in System Preferences -> Security & Privacy -> Privacy -> "
"Microphone."));
"by changing the settings in System Preferences -> Security & Privacy -> Privacy -> "
"Microphone."));
} else if (air->name == QLatin1String("WASAPI")) {
qlInputHelp->setVisible(true);
qlInputHelp->setText( tr("Access to the microphone was denied. Please check that your operating system's "
"microphone settings allow Mumble to use the microphone."));
"microphone settings allow Mumble to use the microphone."));
}
} else {
qcbDevice->setEnabled(true);
Expand Down Expand Up @@ -297,8 +297,7 @@ void AudioInputDialog::save() const {
s.qsTxAudioCueOff = qlePushClickPathOff->text();

s.qsAudioInput = qcbSystem->currentText();
s.iEchoOption = qcbEcho->currentData().toInt();
s.bEcho = s.iEchoOption != ECHO_CANCEL_DISABLED;
s.echoOption = static_cast<EchoCancelOptionID>(qcbEcho->currentData().toInt());
s.bExclusiveInput = qcbExclusive->isChecked();

if (AudioInputRegistrar::qmNew) {
Expand Down Expand Up @@ -516,14 +515,16 @@ void AudioInputDialog::updateEchoEnableState() {
qcbEcho->insertItem(0, tr("Disabled"), "disabled");
qcbEcho->setItemData(0, tr("Disable echo cancellation."), Qt::ToolTipRole);

for (int i=0; i<air->echoOptions.count(); ++i) {
EchoCancellationOption eco = air->echoOptions[i];
if (air->canEcho(eco.id, outputInterface)) {
int i = 0;
for (EchoCancelOptionID ecoid : air->echoOptions) {
if (air->canEcho(ecoid, outputInterface)) {
++i;
hasUsableEchoOption = true;
qcbEcho->insertItem(i+1, eco.description, eco.id);
qcbEcho->setItemData(i+1, eco.explanation, Qt::ToolTipRole);
if (s.iEchoOption == eco.id) {
qcbEcho->setCurrentIndex(i+1);
const EchoCancelOption &echoOption = echoCancelOptions[static_cast<int>(ecoid)];
qcbEcho->insertItem(i, echoOption.description, static_cast<int>(ecoid));
qcbEcho->setItemData(i, echoOption.explanation, Qt::ToolTipRole);
if (s.echoOption == ecoid) {
qcbEcho->setCurrentIndex(i);
}
}
}
Expand Down
15 changes: 1 addition & 14 deletions src/mumble/AudioInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,6 @@ AudioInputRegistrar::~AudioInputRegistrar() {
qmNew->remove(name);
}

void AudioInputRegistrar::useSpeexEchoCancellation() {
echoOptions.append(EchoCancellationOption(ECHO_CANCEL_SPEEX_MIXED,
tr("Mixed echo cancellation (speex)"),
tr("Mixed has low CPU impact, but only works well if your "
"speakers are equally loud and equidistant from the microphone.")));
echoOptions.append(EchoCancellationOption(ECHO_CANCEL_SPEEX_MULTICHANNEL,
tr("Multichannel echo cancellation (speex)"),
tr("Multichannel echo cancellation provides much better echo "
"cancellation, but at a higher CPU cost. "
"Multichannel echo cancellation requires more CPU, so you should try mixed first.")));

}

AudioInputPtr AudioInputRegistrar::newFromChoice(QString choice) {
if (!qmNew)
return AudioInputPtr();
Expand Down Expand Up @@ -527,7 +514,7 @@ void AudioInput::initializeMixer() {
pfMicInput = new float[iMicLength];

if (iEchoChannels > 0) {
bEchoMulti = (g.s.iEchoOption == ECHO_CANCEL_SPEEX_MULTICHANNEL);
bEchoMulti = (g.s.echoOption == EchoCancelOptionID::SPEEX_MULTICHANNEL);
if (iEchoFreq != iSampleRate)
srsEcho = speex_resampler_init(bEchoMulti ? iEchoChannels : 1, iEchoFreq, iSampleRate, 3, &err);
iEchoLength = (iFrameSize * iEchoFreq) / iSampleRate;
Expand Down
30 changes: 6 additions & 24 deletions src/mumble/AudioInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <vector>

#include "Audio.h"
#include "EchoCancelOption.h"
#include "Message.h"
#include "Settings.h"
#include "Timer.h"
Expand Down Expand Up @@ -119,18 +120,8 @@ class Resynchronizer {
enum { S0, S1a, S1b, S2, S3, S4a, S4b, S5 } state = S0; ///< Queue fill control statemachine
};

struct EchoCancellationOption {
EchoCancellationOption(int id, QString description, QString explanation) : id(id),
description(description),
explanation(explanation) {};
int id;
QString description;
QString explanation;
};

class AudioInputRegistrar : public QObject {
class AudioInputRegistrar {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioInputRegistrar)
public:
static QMap< QString, AudioInputRegistrar * > *qmNew;
Expand All @@ -140,23 +131,19 @@ class AudioInputRegistrar : public QObject {
const QString name;
int priority;

// A list of echo cancellation options available for this backend.
QList<EchoCancellationOption> echoOptions;
/// A list of echo cancellation options available for this backend.
std::vector< EchoCancelOptionID > echoOptions;

AudioInputRegistrar(const QString &n, int priority = 0);
virtual ~AudioInputRegistrar();
virtual AudioInput *create() = 0;
virtual const QList< audioDevice > getDeviceChoices() = 0;
virtual void setDeviceChoice(const QVariant &, Settings &) = 0;

// Check that is the given echoOption and outputSystem combination is available for echo cancellation
virtual bool canEcho(int echoOptionId, const QString &outputSystem) const = 0;
/// Check that given combination of echoOption and outputSystem combination is suitable for echo cancellation
virtual bool canEcho(EchoCancelOptionID echoOptionId, const QString &outputSystem) const = 0;
virtual bool canExclusive() const;

// add speex echo cancellation as into the echoOptions if this backend supports
// tapping the system's audio output.
void useSpeexEchoCancellation();

/**
* Check if Mumble's microphone access has been denied by the OS.
* Both Windows and macOS have builtin privacy safeguards that display a message asking for users'
Expand Down Expand Up @@ -300,9 +287,4 @@ class AudioInput : public QThread {
bool isTransmitting() const;
};

#define ECHO_CANCEL_DISABLED 0
#define ECHO_CANCEL_DEFAULT 1
#define ECHO_CANCEL_SPEEX_MIXED 10
#define ECHO_CANCEL_SPEEX_MULTICHANNEL 11

#endif
35 changes: 20 additions & 15 deletions src/mumble/AudioWizard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ AudioWizard::AudioWizard(QWidget *p) : QWizard(p) {
qcbInput->addItem(air->name);
if (air->name == AudioInputRegistrar::current) {
qcbInput->setCurrentIndex(qcbInput->count() - 1);
qcbEcho->setEnabled(hasUsableEchoCancellation(air, qcbOutput->currentText()));
EchoCancelOptionID echoCancelOptionId = firstUsableEchoCancellation(air, qcbOutput->currentText());
if (echoCancelOptionId != EchoCancelOptionID::DISABLED) {
qcbEcho->setEnabled(true);
qcbEcho->setChecked(g.s.echoOption != EchoCancelOptionID::DISABLED);
}
}
QList< audioDevice > ql = air->getDeviceChoices();
}
Expand All @@ -58,8 +62,6 @@ AudioWizard::AudioWizard(QWidget *p) : QWizard(p) {
qcbInput->setEnabled(false);
}

qcbEcho->setChecked(g.s.bEcho);

if (AudioOutputRegistrar::qmNew) {
foreach (AudioOutputRegistrar *aor, *AudioOutputRegistrar::qmNew) {
qcbOutput->addItem(aor->name);
Expand Down Expand Up @@ -239,7 +241,8 @@ void AudioWizard::on_qcbInputDevice_activated(int) {
air->setDeviceChoice(qcbInputDevice->itemData(idx), g.s);
}

qcbEcho->setEnabled(hasUsableEchoCancellation(air, qcbOutput->currentText()));
EchoCancelOptionID echoCancelOptionId = firstUsableEchoCancellation(air, qcbOutput->currentText());
qcbEcho->setEnabled(echoCancelOptionId != EchoCancelOptionID::DISABLED);

g.ai = AudioInputPtr(air->create());
g.ai->start(QThread::HighestPriority);
Expand Down Expand Up @@ -279,8 +282,9 @@ void AudioWizard::on_qcbOutputDevice_activated(int) {
bDelay = aor->usesOutputDelay();
}

AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText());
qcbEcho->setEnabled(hasUsableEchoCancellation(air, qcbOutput->currentText()));
AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText());
EchoCancelOptionID echoCancelOptionId = firstUsableEchoCancellation(air, qcbOutput->currentText());
qcbEcho->setEnabled(echoCancelOptionId != EchoCancelOptionID::DISABLED);

g.ao = AudioOutputPtr(aor->create());
g.ao->start(QThread::HighPriority);
Expand Down Expand Up @@ -651,9 +655,11 @@ void AudioWizard::on_qpbPTT_clicked() {
}

void AudioWizard::on_qcbEcho_clicked(bool on) {
g.s.bEcho = on;
if (g.s.bEcho) {
g.s.iEchoOption = 1; // ECHO_CANCEL_DEFAULT = 1
if (on) {
AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText());
g.s.echoOption = firstUsableEchoCancellation(air, qcbOutput->currentText());
} else {
g.s.echoOption = EchoCancelOptionID::DISABLED;
}
restartAudio();
}
Expand Down Expand Up @@ -732,13 +738,12 @@ void AudioWizard::on_qrbQualityCustom_clicked() {
restartAudio();
}

bool AudioWizard::hasUsableEchoCancellation(AudioInputRegistrar *air, const QString outputSys) {
for (int i=0; i<air->echoOptions.count(); ++i) {
EchoCancellationOption eco = air->echoOptions[i];
if (air->canEcho(eco.id, outputSys)) {
return true;
EchoCancelOptionID AudioWizard::firstUsableEchoCancellation(AudioInputRegistrar *air, const QString outputSys) {
for (EchoCancelOptionID ecoid : air->echoOptions) {
if (air->canEcho(ecoid, outputSys)) {
return ecoid;
}
}

return false;
return EchoCancelOptionID::DISABLED;
}
8 changes: 6 additions & 2 deletions src/mumble/AudioWizard.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

#include "ui_AudioWizard.h"

#include "AudioOutput.h"
#include "AudioInput.h"
#include "AudioOutput.h"
#include "AudioStats.h"
#include "Settings.h"

Expand All @@ -18,7 +18,11 @@ class AudioWizard : public QWizard, public Ui::AudioWizard {
Q_OBJECT
Q_DISABLE_COPY(AudioWizard)

bool hasUsableEchoCancellation(AudioInputRegistrar *air, QString outputSys);
/// Which echo cancellation is usable depends on the audio backend and the device combination.
/// This function will iterate through the list of available echo cancellation in the audio backend and check with
/// the backend whether this echo cancellation option works for current device combination.
EchoCancelOptionID firstUsableEchoCancellation(AudioInputRegistrar *air, QString outputSys);

protected:
QList< QVariant > pttButtons;
bool bTransmitChanged;
Expand Down
1 change: 1 addition & 0 deletions src/mumble/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ set(MUMBLE_SOURCES
"Database.h"
"DeveloperConsole.cpp"
"DeveloperConsole.h"
"EchoCancelOption.h"
"Global.cpp"
"Global.h"
"GlobalShortcut.cpp"
Expand Down
39 changes: 18 additions & 21 deletions src/mumble/CoreAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
#ifndef MUMBLE_MUMBLE_COREAUDIO_H_
# define MUMBLE_MUMBLE_COREAUDIO_H_

# include <AudioToolbox/AudioToolbox.h>

# include "AudioInput.h"
# include "AudioOutput.h"
# include <AudioToolbox/AudioToolbox.h>

enum AUElement { OUTPUT = 0, INPUT = 1 };
enum AUDirection { OUTPUT = 0, INPUT = 1 };

class CoreAudioSystem : public QObject {
private:
Expand All @@ -26,28 +25,28 @@ class CoreAudioInput : public AudioInput {
private:
Q_OBJECT
Q_DISABLE_COPY(CoreAudioInput)
// Open HAL AU as input and pass back the output stream description.
/// Open HAL AU as input and pass back the output stream description.
bool openAUHAL(AudioStreamBasicDescription &streamDescription);

// Open VoiceProcessingIO AU as input, utilizing macOS's builtin echo cancellation,
// and pass back the output stream description.
/// Open VoiceProcessingIO AU as input, utilizing macOS's builtin echo cancellation,
/// and pass back the output stream description.
bool openAUVoip(AudioStreamBasicDescription &streamDescription);

// Initialize input AU with preferred parameters of Mumble
/// Initialize input AU with preferred parameters of Mumble
bool initializeInputAU(AudioUnit au, AudioStreamBasicDescription &streamDescription, int &actualBufferLength);

protected:
// Hardware Abstraction Layer's AudioUnit, directly interacts with the hardware
AudioUnit auHAL {};
// VoiceProcessingIO AU, provides audio input and echo cancellation
AudioUnit auVoip {};
AudioDeviceID inputDevId {};
AudioDeviceID echoOutputDevId {};
AudioBufferList buflist {};
/// Hardware Abstraction Layer's AudioUnit, directly interacts with the hardware
AudioUnit auHAL{};
/// VoiceProcessingIO AU, provides audio input and echo cancellation
AudioUnit auVoip{};
AudioDeviceID inputDevId{};
AudioDeviceID echoOutputDevId{};
AudioBufferList buflist{};
static void propertyChange(void *udata, AudioUnit au, AudioUnitPropertyID prop, AudioUnitScope scope,
AudioUnitElement element);
AudioUnitElement element);
static OSStatus inputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
UInt32 busnum, UInt32 npackets, AudioBufferList *buflist);
UInt32 busnum, UInt32 npackets, AudioBufferList *buflist);

public:
CoreAudioInput();
Expand All @@ -61,8 +60,8 @@ class CoreAudioOutput : public AudioOutput {
Q_OBJECT
Q_DISABLE_COPY(CoreAudioOutput)
protected:
// Hardware Abstraction Layer's AudioOutputUnit, directly interacts with the hardware
AudioUnit auHAL {};
/// Hardware Abstraction Layer's AudioOutputUnit, directly interacts with the hardware
AudioUnit auHAL{};
static void propertyChange(void *udata, AudioUnit au, AudioUnitPropertyID prop, AudioUnitScope scope,
AudioUnitElement element);
static OSStatus outputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
Expand All @@ -81,7 +80,7 @@ class CoreAudioInputRegistrar : public AudioInputRegistrar {
virtual AudioInput *create();
virtual const QList< audioDevice > getDeviceChoices();
virtual void setDeviceChoice(const QVariant &, Settings &);
virtual bool canEcho(int, const QString &) const;
virtual bool canEcho(EchoCancelOptionID echoCancelID, const QString &outputSystem) const;
virtual bool isMicrophoneAccessDeniedByOS();
};

Expand All @@ -94,8 +93,6 @@ class CoreAudioOutputRegistrar : public AudioOutputRegistrar {
bool canMuteOthers() const;
};

#define ECHO_CANCEL_APPLE 100

#else
class CoreAudioSystem;
class CoreAudioInput;
Expand Down
Loading

0 comments on commit dafbce2

Please sign in to comment.