1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-03-19 22:49:38 -04:00

RNnoise feature: basic implementation of audio

This commit is contained in:
f4exb 2026-01-07 13:39:58 +01:00
parent ed297a824e
commit 4e0eecc595
7 changed files with 202 additions and 49 deletions

View File

@ -86,7 +86,11 @@ Denoiser::~Denoiser()
&Denoiser::networkManagerFinished
);
delete m_networkManager;
stop();
if (m_running)
{
stop();
}
}
void Denoiser::start()
@ -284,7 +288,6 @@ void Denoiser::applySettings(const DenoiserSettings& settings, const QList<QStri
m_worker->getInputMessageQueue()->push(msg);
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||

View File

@ -22,6 +22,9 @@
#include "gui/basicfeaturesettingsdialog.h"
#include "gui/dialpopup.h"
#include "gui/dialogpositioner.h"
#include "gui/crightclickenabler.h"
#include "gui/audioselectdialog.h"
#include "dsp/dspengine.h"
#include "util/db.h"
#include "maincore.h"
@ -157,6 +160,9 @@ DenoiserGUI::DenoiserGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Featu
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect(const QPoint &)));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
@ -341,6 +347,23 @@ void DenoiserGUI::on_showFileDialog_clicked(bool checked)
}
}
void DenoiserGUI::audioSelect(const QPoint& p)
{
qDebug("DenoiserGUI::audioSelect");
AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName);
audioSelect.move(p);
new DialogPositioner(&audioSelect, false);
audioSelect.exec();
if (audioSelect.m_selected)
{
m_settings.m_audioDeviceName = audioSelect.m_audioDeviceName;
m_settingsKeys.append("audioDeviceName");
applySettings();
}
}
void DenoiserGUI::tick()
{
m_channelPowerAvg(m_denoiser->getMagSqAvg());

View File

@ -91,6 +91,7 @@ private slots:
void on_channelApply_clicked();
void on_record_toggled(bool checked);
void on_showFileDialog_clicked(bool checked);
void audioSelect(const QPoint& p);
void updateStatus();
void tick();
};

View File

@ -17,6 +17,7 @@
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "audio/audiodevicemanager.h"
#include "denoisersettings.h"
@ -43,6 +44,7 @@ void DenoiserSettings::resetToDefaults()
m_denoiserType = DenoiserType::DenoiserType_RNnoise;
m_title = "Denoiser";
m_audioMute = false;
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_rgbColor = 0xffd700; // gold
m_useReverseAPI = false;
m_reverseAPIAddress = "localhost";
@ -61,6 +63,7 @@ QByteArray DenoiserSettings::serialize() const
s.writeS32(1, static_cast<qint32>(m_denoiserType));
s.writeBool(14, m_audioMute);
s.writeString(15, m_audioDeviceName);
s.writeString(2, m_title);
s.writeU32(3, m_rgbColor);
s.writeBool(4, m_useReverseAPI);
@ -99,6 +102,7 @@ bool DenoiserSettings::deserialize(const QByteArray& data)
d.readS32(1, &itmp, 1);
m_denoiserType = static_cast<DenoiserType>(itmp);
d.readBool(14, &m_audioMute, false);
d.readString(15, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readString(2, &m_title, "Denoiser");
d.readU32(3, &m_rgbColor, 0xffd700); // gold
d.readBool(4, &m_useReverseAPI, false);
@ -143,6 +147,9 @@ void DenoiserSettings::applySettings(const QStringList& settingsKeys, const Deno
if (settingsKeys.contains("audioMute")) {
m_audioMute = settings.m_audioMute;
}
if (settingsKeys.contains("audioDeviceName")) {
m_audioDeviceName = settings.m_audioDeviceName;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
@ -185,6 +192,9 @@ QString DenoiserSettings::getDebugString(const QStringList& settingsKeys, bool f
if (settingsKeys.contains("audioMute") || force) {
debugString += QString("Audio Mute: %1 ").arg(m_audioMute ? "true" : "false");
}
if (settingsKeys.contains("audioDeviceName") || force) {
debugString += QString("Audio Device Name: %1 ").arg(m_audioDeviceName);
}
if (settingsKeys.contains("title") || force) {
debugString += QString("Title: %1 ").arg(m_title);
}

View File

@ -33,6 +33,7 @@ struct DenoiserSettings
DenoiserType m_denoiserType;
bool m_audioMute;
QString m_audioDeviceName;
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;

View File

@ -16,6 +16,8 @@
///////////////////////////////////////////////////////////////////////////////////
#include "dsp/wavfilerecord.h"
#include "audio/audiodevicemanager.h"
#include "dsp/dspengine.h"
#include "denoiserworker.h"
@ -31,13 +33,20 @@ DenoiserWorker::DenoiserWorker(QObject *parent) :
m_sampleBufferSize(0),
m_channelPowerAvg(),
m_wavFileRecord(nullptr),
m_recordSilenceNbSamples(0),
m_recordSilenceCount(0),
m_nbBytes(0)
{
m_audioBuffer.resize(4800);
m_audioBufferFill = 0;
m_audioFifo.setSize(4800 * 4);
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(getAudioFifo(), getInputMessageQueue());
}
DenoiserWorker::~DenoiserWorker()
{
m_inputMessageQueue.clear();
DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(getAudioFifo());
}
void DenoiserWorker::reset()
@ -106,20 +115,7 @@ void DenoiserWorker::feedPart(
for (int is = 0; is < countSamples; is++)
{
const Sample& sample = m_sampleBuffer[is];
if ((sample.m_real == 0) && (sample.m_imag == 0))
{
if (m_wavFileRecord->isRecording()) {
m_wavFileRecord->stopRecording();
}
}
else
{
if (!m_wavFileRecord->isRecording()) {
m_wavFileRecord->startRecording();
}
writeSampleToFile(sample);
}
writeSampleToFile(sample);
}
}
}
@ -204,16 +200,87 @@ bool DenoiserWorker::handleMessage(const Message& cmd)
void DenoiserWorker::applySettings(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force)
{
QMutexLocker mutexLocker(&m_mutex);
Q_UNUSED(settingsKeys)
Q_UNUSED(force)
m_settings = settings;
if (settingsKeys.contains("fileRecordName") || force)
{
if (m_wavFileRecord)
{
QStringList dotBreakout = settings.m_fileRecordName.split(QLatin1Char('.'));
if (dotBreakout.size() > 1)
{
QString extension = dotBreakout.last();
if (extension != "wav") {
dotBreakout.last() = "wav";
}
}
else
{
dotBreakout.append("wav");
}
QString newFileRecordName = dotBreakout.join(QLatin1Char('.'));
QString fileBase;
FileRecordInterface::guessTypeFromFileName(newFileRecordName, fileBase);
qDebug("DemodAnalyzerWorker::applySettings: newFileRecordName: %s fileBase: %s", qPrintable(newFileRecordName), qPrintable(fileBase));
m_wavFileRecord->setFileName(fileBase);
}
}
if (settingsKeys.contains("recordToFile") || force)
{
if (m_wavFileRecord)
{
if (settings.m_recordToFile)
{
if (!m_wavFileRecord->isRecording()) {
m_wavFileRecord->startRecording();
}
}
else
{
if (m_wavFileRecord->isRecording()) {
m_wavFileRecord->stopRecording();
}
}
m_recordSilenceCount = 0;
}
}
if ((settingsKeys.contains("audioDeviceName") && (settings.m_audioDeviceName != m_settings.m_audioDeviceName)) || force)
{
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName);
audioDeviceManager->removeAudioSink(getAudioFifo());
audioDeviceManager->addAudioSink(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex);
unsigned int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
qDebug() << "DenoiserWorker::applySettings: audio device name:" << settings.m_audioDeviceName
<< " index:" << audioDeviceIndex << " sample rate:" << audioSampleRate;
// TODO: handle sample rate change
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
void DenoiserWorker::applySampleRate(int sampleRate)
{
QMutexLocker mutexLocker(&m_mutex);
m_sinkSampleRate = sampleRate;
if (m_wavFileRecord)
{
if (m_wavFileRecord->isRecording()) {
m_wavFileRecord->stopRecording();
}
m_wavFileRecord->setSampleRate(m_sinkSampleRate);
}
}
void DenoiserWorker::handleData()
@ -243,3 +310,69 @@ void DenoiserWorker::handleData()
m_dataFifo->readCommit((unsigned int) count);
}
}
void DenoiserWorker::processSample(
DataFifo::DataType dataType,
const QByteArray::const_iterator& begin,
int i
)
{
switch(dataType)
{
case DataFifo::DataTypeI16: {
int16_t *s = (int16_t*) begin;
double re = s[i] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(0);
m_audioBuffer[m_audioBufferFill].l = s[i];
m_audioBuffer[m_audioBufferFill].r = s[i];
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
std::size_t res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("DenoiserWorker::processSample: %lu/%lu audio samples written", res, m_audioBufferFill);
m_audioFifo.clear();
}
m_audioBufferFill = 0;
}
}
break;
case DataFifo::DataTypeCI16: {
int16_t *s = (int16_t*) begin;
double re = s[2*i] / (double) std::numeric_limits<int16_t>::max();
double im = s[2*i+1] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re + im*im;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(im * SDR_RX_SCALEF);
m_audioBuffer[m_audioBufferFill].l = s[2*i];
m_audioBuffer[m_audioBufferFill].r = s[2*i+1];
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
std::size_t res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("DenoiserWorker::processSample: %lu/%lu audio samples written", res, m_audioBufferFill);
m_audioFifo.clear();
}
m_audioBufferFill = 0;
}
}
break;
}
}

View File

@ -18,14 +18,16 @@
#define INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_
#include <QObject>
#include <QMutex>
#include <QRecursiveMutex>
#include <QByteArray>
#include <QDebug>
#include "util/movingaverage.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "dsp/dsptypes.h"
#include "dsp/datafifo.h"
#include "audio/audiofifo.h"
#include "denoisersettings.h"
@ -86,7 +88,7 @@ public:
double getMagSq() const { return m_magsq; }
double getMagSqAvg() const { return (double) m_channelPowerAvg; }
private:
private:
DataFifo *m_dataFifo;
int m_sinkSampleRate;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
@ -97,9 +99,15 @@ private:
int m_sampleBufferSize;
MovingAverageUtil<double, double, 480> m_channelPowerAvg;
WavFileRecord* m_wavFileRecord;
int m_recordSilenceNbSamples;
int m_recordSilenceCount;
int m_nbBytes;
AudioVector m_audioBuffer;
AudioFifo m_audioFifo;
std::size_t m_audioBufferFill;
QRecursiveMutex m_mutex;
AudioFifo *getAudioFifo() { return &m_audioFifo; }
void feedPart(
const QByteArray::const_iterator& begin,
const QByteArray::const_iterator& end,
@ -109,37 +117,11 @@ private:
bool handleMessage(const Message& cmd);
void writeSampleToFile(const Sample& sample);
inline void processSample(
void processSample(
DataFifo::DataType dataType,
const QByteArray::const_iterator& begin,
int i
)
{
switch(dataType)
{
case DataFifo::DataTypeI16: {
int16_t *s = (int16_t*) begin;
double re = s[i] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(0);
}
break;
case DataFifo::DataTypeCI16: {
int16_t *s = (int16_t*) begin;
double re = s[2*i] / (double) std::numeric_limits<int16_t>::max();
double im = s[2*i+1] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re + im*im;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(im * SDR_RX_SCALEF);
}
break;
}
}
);
private slots: