Remove sleep_until from processing loop

Fix DSP parameters control in MainWindow
Move QAudioInput and QAudioOutput to different threads
This commit is contained in:
Kirill Kirilenko 2020-11-12 21:18:47 +03:00
parent 45e6daf262
commit 3fd40888aa
5 changed files with 70 additions and 26 deletions

View file

@ -79,7 +79,7 @@ void AudioProcessor::process()
{ {
while (doWork_) while (doWork_)
{ {
auto waitUntil = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(5); std::unique_lock<std::mutex> processLock(processMutex_);
const std::size_t bytesToRead = const std::size_t bytesToRead =
bufferSize_ * format_.sampleSize() / 8 * format_.channelCount(); bufferSize_ * format_.sampleSize() / 8 * format_.channelCount();
@ -119,13 +119,9 @@ void AudioProcessor::process()
{ {
std::unique_lock<std::mutex> lock(outputMutex_); std::unique_lock<std::mutex> lock(outputMutex_);
outputBuffer_.append(buf.constData<char>(), buf.byteCount()); outputBuffer_.append(buf.constData<char>(), buf.byteCount());
}
emit readyRead(); emit readyRead();
} }
}
std::this_thread::sleep_until(waitUntil);
} }
} }
@ -144,6 +140,22 @@ void AudioProcessor::processBuffer(QAudioBuffer& inputBuffer, const QAudioBuffer
emit outputLevelsChanged(outputLevels); emit outputLevelsChanged(outputLevels);
} }
void AudioProcessor::clearBuffers()
{
{
std::unique_lock<std::mutex> lock(inputMutex_);
inputBuffer_.clear();
}
{
std::unique_lock<std::mutex> lock(outputMutex_);
outputBuffer_.clear();
}
{
std::unique_lock<std::mutex> lock(monitorMutex_);
monitorBuffer_.clear();
}
}
bool AudioProcessor::isSequential() const bool AudioProcessor::isSequential() const
{ {
return true; return true;
@ -162,13 +174,8 @@ qint64 AudioProcessor::bytesAvailable() const
bool AudioProcessor::open(QIODevice::OpenMode mode) bool AudioProcessor::open(QIODevice::OpenMode mode)
{ {
std::unique_lock<std::mutex> lock1(inputMutex_); std::unique_lock<std::mutex> lock(processMutex_);
std::unique_lock<std::mutex> lock2(outputMutex_); clearBuffers();
std::unique_lock<std::mutex> lock3(monitorMutex_);
inputBuffer_.clear();
outputBuffer_.clear();
monitorBuffer_.clear();
sourceEncoder_.reset(new WavFileWriter("source.wav", format_)); sourceEncoder_.reset(new WavFileWriter("source.wav", format_));
sourceEncoder_->open(); sourceEncoder_->open();
@ -204,13 +211,8 @@ Backend AudioProcessor::getCurrentBackend() const
void AudioProcessor::switchBackend(Backend backend) void AudioProcessor::switchBackend(Backend backend)
{ {
std::unique_lock<std::mutex> lock1(inputMutex_); std::unique_lock<std::mutex> lock(processMutex_);
std::unique_lock<std::mutex> lock2(outputMutex_); clearBuffers();
std::unique_lock<std::mutex> lock3(monitorMutex_);
inputBuffer_.clear();
outputBuffer_.clear();
monitorBuffer_.clear();
if (backend == Backend::Speex) if (backend == Backend::Speex)
dsp_.reset(new SpeexDSP(format_, monitorFormat_)); dsp_.reset(new SpeexDSP(format_, monitorFormat_));

View file

@ -57,11 +57,14 @@ protected:
private: private:
void process(); void process();
void processBuffer(QAudioBuffer& inputBuffer, const QAudioBuffer& monitorBuffer); void processBuffer(QAudioBuffer& inputBuffer, const QAudioBuffer& monitorBuffer);
void clearBuffers();
mutable std::mutex inputMutex_; mutable std::mutex inputMutex_;
mutable std::mutex outputMutex_; mutable std::mutex outputMutex_;
mutable std::mutex monitorMutex_; mutable std::mutex monitorMutex_;
std::mutex processMutex_;
std::size_t bufferSize_; std::size_t bufferSize_;
const QAudioFormat format_; const QAudioFormat format_;
const QAudioFormat monitorFormat_; const QAudioFormat monitorFormat_;

View file

@ -44,6 +44,9 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow)
{ {
ui->setupUi(this); ui->setupUi(this);
audioInputThread_.start();
audioOutputThread_.start();
qDebug(Gui) << "Enumerating audio devices..."; qDebug(Gui) << "Enumerating audio devices...";
for (auto& deviceInfo : QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) for (auto& deviceInfo : QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
{ {
@ -83,6 +86,8 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow)
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
stopRecording(); stopRecording();
audioInputThread_.exit(0);
audioOutputThread_.exit(0);
} }
void fixFormatForDevice(QAudioFormat& format, const QAudioDeviceInfo& info) void fixFormatForDevice(QAudioFormat& format, const QAudioDeviceInfo& info)
@ -114,6 +119,10 @@ void MainWindow::initializeAudio(const QAudioDeviceInfo& inputDeviceInfo,
audioOutput_.reset(new QAudioOutput(outputDeviceInfo, outputFormat)); audioOutput_.reset(new QAudioOutput(outputDeviceInfo, outputFormat));
monitorInput_.reset(new QAudioInput(monitorDeviceInfo, monitorFormat)); monitorInput_.reset(new QAudioInput(monitorDeviceInfo, monitorFormat));
audioInput_->moveToThread(&audioInputThread_);
audioOutput_->moveToThread(&audioOutputThread_);
monitorInput_->moveToThread(&audioInputThread_);
processor_.reset(new AudioProcessor(captureFormat, monitorFormat, monitorBuffer_)); processor_.reset(new AudioProcessor(captureFormat, monitorFormat, monitorBuffer_));
connect(processor_.get(), &AudioProcessor::voiceActivityChanged, this, connect(processor_.get(), &AudioProcessor::voiceActivityChanged, this,
&MainWindow::updateVoiceActivity); &MainWindow::updateVoiceActivity);
@ -132,6 +141,9 @@ void MainWindow::startRecording()
audioInput_->start(processor_.get()); audioInput_->start(processor_.get());
audioOutput_->start(processor_.get()); audioOutput_->start(processor_.get());
monitorInput_->start(&monitorBuffer_); monitorInput_->start(&monitorBuffer_);
qInfo(Gui) << "input buffer size:" << audioInput_->bufferSize();
qInfo(Gui) << "output buffer size:" << audioOutput_->bufferSize();
} }
void MainWindow::stopRecording() void MainWindow::stopRecording()
@ -191,11 +203,21 @@ QString levelFromCode(int value)
void MainWindow::setupDials(Backend backend) void MainWindow::setupDials(Backend backend)
{ {
ui->noiseGroupBox->setChecked(false); ui->noiseGroupBox->setChecked(false);
ui->agcGroupBox->setChecked(false);
ui->aecGroupBox->setChecked(false);
ui->noiseSuppressionDial->setValue(0); ui->noiseSuppressionDial->setValue(0);
ui->agcGroupBox->setChecked(false);
ui->agcLevelDial->setValue(0); ui->agcLevelDial->setValue(0);
ui->agcLevelValue->setText("0 dBFS"); ui->agcLevelValue->setText("0 dBFS");
ui->agcMaxGainDial->setValue(0);
ui->agcMaxGainValue->setText("0 dB");
ui->agcMaxIncrementDial->setValue(0);
ui->agcMaxIncrementValue->setText("0 dB/sec");
ui->agcMaxDecrementDial->setValue(0);
ui->agcMaxDecrementValue->setText("0 dB/sec");
ui->aecGroupBox->setChecked(false);
ui->aecSuppressionDial->setValue(0);
updateVoiceActivity(false); updateVoiceActivity(false);
if (backend == Backend::Speex) if (backend == Backend::Speex)
@ -234,15 +256,18 @@ void MainWindow::setupDials(Backend backend)
void MainWindow::changeNoiseReductionSettings() void MainWindow::changeNoiseReductionSettings()
{ {
processor_->setEffectParam("noise_reduction_enabled", ui->noiseGroupBox->isChecked());
if (currentBackend() == Backend::Speex) if (currentBackend() == Backend::Speex)
{ {
std::int32_t enabled = ui->noiseGroupBox->isChecked() ? 1 : 0;
processor_->setEffectParam("noise_reduction_enabled", enabled);
std::int32_t maxAttenuation = -ui->noiseSuppressionDial->value(); std::int32_t maxAttenuation = -ui->noiseSuppressionDial->value();
ui->noiseSuppressionValue->setText(QString("%1 dB").arg(-maxAttenuation)); ui->noiseSuppressionValue->setText(QString("%1 dB").arg(-maxAttenuation));
processor_->setEffectParam("noise_reduction_max_attenuation", maxAttenuation); processor_->setEffectParam("noise_reduction_max_attenuation", maxAttenuation);
} }
else else
{ {
processor_->setEffectParam("noise_reduction_enabled", ui->noiseGroupBox->isChecked());
int suppressionLevel = ui->noiseSuppressionDial->value(); int suppressionLevel = ui->noiseSuppressionDial->value();
ui->noiseSuppressionValue->setText(levelFromCode(suppressionLevel)); ui->noiseSuppressionValue->setText(levelFromCode(suppressionLevel));
processor_->setEffectParam("noise_reduction_suppression_level", suppressionLevel); processor_->setEffectParam("noise_reduction_suppression_level", suppressionLevel);
@ -251,10 +276,11 @@ void MainWindow::changeNoiseReductionSettings()
void MainWindow::changeAGCSettings() void MainWindow::changeAGCSettings()
{ {
processor_->setEffectParam("gain_control_enabled", ui->agcGroupBox->isChecked());
if (currentBackend() == Backend::Speex) if (currentBackend() == Backend::Speex)
{ {
std::int32_t enabled = ui->agcGroupBox->isChecked() ? 1 : 0;
processor_->setEffectParam("gain_control_enabled", enabled);
std::int32_t level = std::int32_t level =
QAudio::convertVolume(-ui->agcLevelDial->value(), QAudio::DecibelVolumeScale, QAudio::convertVolume(-ui->agcLevelDial->value(), QAudio::DecibelVolumeScale,
QAudio::LinearVolumeScale) * QAudio::LinearVolumeScale) *
@ -276,6 +302,8 @@ void MainWindow::changeAGCSettings()
} }
else else
{ {
processor_->setEffectParam("gain_control_enabled", ui->agcGroupBox->isChecked());
int level = ui->agcLevelDial->value(); int level = ui->agcLevelDial->value();
ui->agcLevelValue->setText(QString("%1 dBFS").arg(-ui->agcLevelDial->value())); ui->agcLevelValue->setText(QString("%1 dBFS").arg(-ui->agcLevelDial->value()));
processor_->setEffectParam("gain_control_target_level", level); processor_->setEffectParam("gain_control_target_level", level);
@ -288,9 +316,11 @@ void MainWindow::changeAGCSettings()
void MainWindow::changeAECSettings() void MainWindow::changeAECSettings()
{ {
processor_->setEffectParam("echo_cancellation_enabled", ui->aecGroupBox->isChecked());
if (currentBackend() == Backend::Speex) if (currentBackend() == Backend::Speex)
{ {
std::int32_t enabled = ui->aecGroupBox->isChecked() ? 1 : 0;
processor_->setEffectParam("echo_cancellation_enabled", enabled);
std::int32_t maxAttenuation = -ui->aecSuppressionDial->value(); std::int32_t maxAttenuation = -ui->aecSuppressionDial->value();
ui->aecSuppressionValue->setText(QString("%1 dB").arg(-maxAttenuation)); ui->aecSuppressionValue->setText(QString("%1 dB").arg(-maxAttenuation));
@ -298,6 +328,8 @@ void MainWindow::changeAECSettings()
} }
else else
{ {
processor_->setEffectParam("echo_cancellation_enabled", ui->aecGroupBox->isChecked());
int suppressionLevel = ui->aecSuppressionDial->value(); int suppressionLevel = ui->aecSuppressionDial->value();
ui->aecSuppressionValue->setText(levelFromCode(suppressionLevel)); ui->aecSuppressionValue->setText(levelFromCode(suppressionLevel));

View file

@ -7,6 +7,7 @@
#include <QAudioInput> #include <QAudioInput>
#include <QAudioOutput> #include <QAudioOutput>
#include <QMainWindow> #include <QMainWindow>
#include <QThread>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { namespace Ui {
@ -51,6 +52,9 @@ private:
Ui::MainWindow* ui = nullptr; Ui::MainWindow* ui = nullptr;
QThread audioInputThread_;
QThread audioOutputThread_;
QScopedPointer<QAudioInput> audioInput_; QScopedPointer<QAudioInput> audioInput_;
QScopedPointer<QAudioInput> monitorInput_; QScopedPointer<QAudioInput> monitorInput_;
QScopedPointer<QAudioOutput> audioOutput_; QScopedPointer<QAudioOutput> audioOutput_;

View file

@ -166,7 +166,10 @@ void WebRTCDSP::setParameter(const QString& param, QVariant value)
config.noise_suppression.level = config.noise_suppression.level =
static_cast<NoiseSuppressionLevel>(NoiseSuppressionLevel::kLow + value.toUInt()); static_cast<NoiseSuppressionLevel>(NoiseSuppressionLevel::kLow + value.toUInt());
else if (param == "echo_cancellation_enabled") else if (param == "echo_cancellation_enabled")
{
config.echo_canceller.enabled = value.toBool(); config.echo_canceller.enabled = value.toBool();
config.residual_echo_detector.enabled = value.toBool();
}
else if (param == "echo_cancellation_suppression_level") else if (param == "echo_cancellation_suppression_level")
return; // TODO ??? return; // TODO ???
else if (param == "gain_control_enabled") else if (param == "gain_control_enabled")