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:
parent
ed297a824e
commit
4e0eecc595
@ -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) ||
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ struct DenoiserSettings
|
||||
|
||||
DenoiserType m_denoiserType;
|
||||
bool m_audioMute;
|
||||
QString m_audioDeviceName;
|
||||
QString m_title;
|
||||
quint32 m_rgbColor;
|
||||
bool m_useReverseAPI;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user