mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-06 10:47:34 +01:00
Prototype noise blanker
This commit is contained in:
parent
c3ddffb3a9
commit
3f6687659e
78
core/src/dsp/noise_reduction/noise_blanker.h
Normal file
78
core/src/dsp/noise_reduction/noise_blanker.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "../processor.h"
|
||||
|
||||
namespace dsp::noise_reduction {
|
||||
class NoiseBlanker : public Processor<complex_t, complex_t> {
|
||||
using base_type = Processor<complex_t, complex_t>;
|
||||
public:
|
||||
NoiseBlanker() {}
|
||||
|
||||
NoiseBlanker(stream<complex_t>* in, double rate, double level) { init(in, rate, level); }
|
||||
|
||||
void init(stream<complex_t>* in, double rate, double level) {
|
||||
_rate = rate;
|
||||
_invRate = 1.0f - _rate;
|
||||
_level = level;
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
void setRate(double rate) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_rate = rate;
|
||||
_invRate = 1.0f - _rate;
|
||||
}
|
||||
|
||||
void setLevel(double level) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_level = level;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
amp = 1.0f;
|
||||
}
|
||||
|
||||
inline int process(int count, complex_t* in, complex_t* out) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Get signal amplitude
|
||||
float inAmp = in[i].amplitude();
|
||||
|
||||
// Update average amplitude
|
||||
float gain = 1.0f;
|
||||
if (inAmp != 0.0f) {
|
||||
amp = (amp * _invRate) + (inAmp * _rate);
|
||||
float excess = inAmp / amp;
|
||||
if (excess > _level) {
|
||||
gain = 1.0f / excess;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale output by gain
|
||||
out[i] = in[i] * gain;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (!base_type::out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
protected:
|
||||
float _rate;
|
||||
float _invRate;
|
||||
float _level;
|
||||
|
||||
float amp = 1.0;
|
||||
|
||||
};
|
||||
}
|
@ -38,17 +38,13 @@ namespace demod {
|
||||
virtual double getMinBandwidth() = 0;
|
||||
virtual double getMaxBandwidth() = 0;
|
||||
virtual bool getBandwidthLocked() = 0;
|
||||
virtual double getMaxAFBandwidth() = 0;
|
||||
virtual double getDefaultSnapInterval() = 0;
|
||||
virtual int getVFOReference() = 0;
|
||||
virtual bool getDeempAllowed() = 0;
|
||||
virtual bool getPostProcEnabled() = 0;
|
||||
virtual int getDefaultDeemphasisMode() = 0;
|
||||
virtual double getAFBandwidth(double bandwidth) = 0;
|
||||
virtual bool getFMIFNRAllowed() = 0;
|
||||
virtual bool getNBAllowed() = 0;
|
||||
|
||||
virtual bool getDynamicAFBandwidth() = 0;
|
||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||
};
|
||||
}
|
||||
|
@ -79,14 +79,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 1000.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate(); }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 1000.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -85,14 +85,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 50.0; }
|
||||
double getMaxBandwidth() { return 500.0; }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 10.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + (float)tone; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -72,14 +72,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 1000.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 100.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -72,14 +72,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 500.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 100.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_UPPER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -50,14 +50,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 1000.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate(); }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 2500.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return true; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return true; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -52,14 +52,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return audioSampleRate; }
|
||||
double getMaxBandwidth() { return audioSampleRate; }
|
||||
bool getBandwidthLocked() { return true; }
|
||||
double getMaxAFBandwidth() { return audioSampleRate; }
|
||||
double getDefaultSnapInterval() { return 2500.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return false; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return false; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
||||
|
@ -73,14 +73,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 500.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return getIFSampleRate() / 2.0; }
|
||||
double getDefaultSnapInterval() { return 100.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_LOWER; }
|
||||
bool getDeempAllowed() { return false; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -76,14 +76,11 @@ namespace demod {
|
||||
double getMinBandwidth() { return 50000.0; }
|
||||
double getMaxBandwidth() { return getIFSampleRate(); }
|
||||
bool getBandwidthLocked() { return false; }
|
||||
double getMaxAFBandwidth() { return 16000.0; }
|
||||
double getDefaultSnapInterval() { return 100000.0; }
|
||||
int getVFOReference() { return ImGui::WaterfallVFO::REF_CENTER; }
|
||||
bool getDeempAllowed() { return true; }
|
||||
bool getPostProcEnabled() { return true; }
|
||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
|
||||
double getAFBandwidth(double bandwidth) { return 16000.0; }
|
||||
bool getDynamicAFBandwidth() { return false; }
|
||||
bool getFMIFNRAllowed() { return true; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <config.h>
|
||||
#include <dsp/chain.h>
|
||||
#include <dsp/noise_reduction/noise_blanker.h>
|
||||
#include <dsp/noise_reduction/fm_if.h>
|
||||
#include <dsp/noise_reduction/squelch.h>
|
||||
#include <dsp/multirate/rational_resampler.h>
|
||||
@ -68,9 +69,11 @@ public:
|
||||
ifChainOutputChanged.handler = ifChainOutputChangeHandler;
|
||||
ifChain.init(vfo->output);
|
||||
|
||||
nb.init(NULL, 500.0 / 24000.0, 10.0);
|
||||
fmnr.init(NULL, 32);
|
||||
squelch.init(NULL, MIN_SQUELCH);
|
||||
|
||||
ifChain.addBlock(&nb, false);
|
||||
ifChain.addBlock(&squelch, false);
|
||||
ifChain.addBlock(&fmnr, false);
|
||||
|
||||
@ -228,6 +231,18 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Noise blanker
|
||||
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
||||
_this->setNBEnabled(_this->nbEnabled);
|
||||
}
|
||||
if (!_this->nbEnabled && _this->enabled) { style::beginDisabled(); }
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderFloat(("##_radio_nb_lvl_" + _this->name).c_str(), &_this->nbLevel, _this->MIN_NB, _this->MAX_NB, "%.3fdB")) {
|
||||
_this->setNBLevel(_this->nbLevel);
|
||||
}
|
||||
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
||||
|
||||
// Squelch
|
||||
if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) {
|
||||
_this->setSquelchEnabled(_this->squelchEnabled);
|
||||
@ -351,6 +366,7 @@ private:
|
||||
nbAllowed = selectedDemod->getNBAllowed();
|
||||
nbEnabled = false;
|
||||
nbLevel = 0.0f;
|
||||
double ifSamplerate = selectedDemod->getIFSampleRate();
|
||||
config.acquire();
|
||||
if (config.conf[name][selectedDemod->getName()].contains("bandwidth")) {
|
||||
bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"];
|
||||
@ -387,9 +403,6 @@ private:
|
||||
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerEnabled")) {
|
||||
nbEnabled = config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"];
|
||||
}
|
||||
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerEnabled")) {
|
||||
nbEnabled = config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"];
|
||||
}
|
||||
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerLevel")) {
|
||||
nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"];
|
||||
}
|
||||
@ -400,18 +413,23 @@ private:
|
||||
vfo->setBandwidthLimits(minBandwidth, maxBandwidth, selectedDemod->getBandwidthLocked());
|
||||
vfo->setReference(selectedDemod->getVFOReference());
|
||||
vfo->setSnapInterval(snapInterval);
|
||||
vfo->setSampleRate(selectedDemod->getIFSampleRate(), bandwidth);
|
||||
vfo->setSampleRate(ifSamplerate, bandwidth);
|
||||
}
|
||||
|
||||
// Configure bandwidth
|
||||
setBandwidth(bandwidth);
|
||||
|
||||
// Configure noise blanker
|
||||
nb.setRate(500.0 / ifSamplerate);
|
||||
setNBLevel(nbLevel);
|
||||
setNBEnabled(nbEnabled);
|
||||
|
||||
// Configure FM IF Noise Reduction
|
||||
setIFNRPreset((selectedDemodID == RADIO_DEMOD_NFM) ? ifnrPresets[fmIFPresetId] : IFNR_PRESET_BROADCAST);
|
||||
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
|
||||
|
||||
// Configure squelch
|
||||
squelch.setLevel(squelchLevel);
|
||||
setSquelchLevel(squelchLevel);
|
||||
setSquelchEnabled(squelchEnabled);
|
||||
|
||||
// Configure AF chain
|
||||
@ -439,18 +457,9 @@ private:
|
||||
bw = std::clamp<double>(bw, minBandwidth, maxBandwidth);
|
||||
bandwidth = bw;
|
||||
if (!selectedDemod) { return; }
|
||||
float audioBW = std::min<float>(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth));
|
||||
audioBW = std::min<float>(audioBW, audioSampleRate / 2.0);
|
||||
vfo->setBandwidth(bandwidth);
|
||||
selectedDemod->setBandwidth(bandwidth);
|
||||
|
||||
// // Only bother with setting the resampling setting if we're actually post processing and dynamic bw is enabled
|
||||
// if (selectedDemod->getDynamicAFBandwidth() && postProcEnabled) {
|
||||
// win.setCutoff(audioBW);
|
||||
// win.setTransWidth(audioBW);
|
||||
// resamp.block.updateWindow(&win);
|
||||
// }
|
||||
|
||||
config.acquire();
|
||||
config.conf[name][selectedDemod->getName()]["bandwidth"] = bandwidth;
|
||||
config.release(true);
|
||||
@ -469,17 +478,10 @@ private:
|
||||
vfo->setSampleRate(selectedDemod->getIFSampleRate(), bandwidth);
|
||||
return;
|
||||
}
|
||||
float audioBW = std::min<float>(selectedDemod->getMaxAFBandwidth(), selectedDemod->getAFBandwidth(bandwidth));
|
||||
audioBW = std::min<float>(audioBW, audioSampleRate / 2.0);
|
||||
|
||||
afChain.stop();
|
||||
|
||||
// // Configure resampler
|
||||
// resamp.block.setOutSampleRate(audioSampleRate);
|
||||
// win.setSampleRate(selectedDemod->getAFSampleRate() * resamp.block.getInterpolation());
|
||||
// win.setCutoff(audioBW);
|
||||
// win.setTransWidth(audioBW);
|
||||
// resamp.block.updateWindow(&win);
|
||||
// Configure resampler
|
||||
resamp.setOutSamplerate(audioSampleRate);
|
||||
|
||||
// Configure deemphasis sample rate
|
||||
@ -501,6 +503,27 @@ private:
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
void setNBEnabled(bool enable) {
|
||||
nbEnabled = enable;
|
||||
if (!selectedDemod) { return; }
|
||||
ifChain.setBlockEnabled(&nb, nbEnabled, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
||||
|
||||
// Save config
|
||||
config.acquire();
|
||||
config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"] = nbEnabled;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
void setNBLevel(float level) {
|
||||
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
|
||||
nb.setLevel(nbLevel);
|
||||
|
||||
// Save config
|
||||
config.acquire();
|
||||
config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"] = nbLevel;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
void setSquelchEnabled(bool enable) {
|
||||
squelchEnabled = enable;
|
||||
if (!selectedDemod) { return; }
|
||||
@ -623,6 +646,7 @@ private:
|
||||
|
||||
// IF chain
|
||||
dsp::chain<dsp::complex_t> ifChain;
|
||||
dsp::noise_reduction::NoiseBlanker nb;
|
||||
dsp::noise_reduction::FMIF fmnr;
|
||||
dsp::noise_reduction::Squelch squelch;
|
||||
|
||||
@ -663,9 +687,11 @@ private:
|
||||
float notchWidth = 500;
|
||||
|
||||
bool nbAllowed;
|
||||
bool nbEnabled;
|
||||
float nbLevel = -100.0f;
|
||||
bool nbEnabled = false;
|
||||
float nbLevel = 10.0f;
|
||||
|
||||
const double MIN_NB = 1.0;
|
||||
const double MAX_NB = 10.0;
|
||||
const double MIN_SQUELCH = -100.0;
|
||||
const double MAX_SQUELCH = 0.0;
|
||||
|
||||
|
@ -67,11 +67,15 @@ public:
|
||||
if (!config.conf[name].contains("audioVolume")) {
|
||||
config.conf[name]["audioVolume"] = 1.0;
|
||||
}
|
||||
if (!config.conf[name].contains("ignoreSilence")) {
|
||||
config.conf[name]["ignoreSilence"] = false;
|
||||
}
|
||||
|
||||
recMode = config.conf[name]["mode"];
|
||||
folderSelect.setPath(config.conf[name]["recPath"]);
|
||||
selectedStreamName = config.conf[name]["audioStream"];
|
||||
audioVolume = config.conf[name]["audioVolume"];
|
||||
ignoreSilence = config.conf[name]["ignoreSilence"];
|
||||
config.release(created);
|
||||
|
||||
// Init audio path
|
||||
@ -291,6 +295,12 @@ private:
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ing_silence_", name), &ignoreSilence)) {
|
||||
config.acquire();
|
||||
config.conf[name]["ignoreSilence"] = ignoreSilence;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (!folderSelect.pathIsValid() || selectedStreamName == "") { style::beginDisabled(); }
|
||||
if (!recording) {
|
||||
if (ImGui::Button(CONCAT("Record##_recorder_rec_", name), ImVec2(menuColumnWidth, 0))) {
|
||||
@ -314,6 +324,9 @@ private:
|
||||
|
||||
static void _audioHandler(dsp::stereo_t* data, int count, void* ctx) {
|
||||
RecorderModule* _this = (RecorderModule*)ctx;
|
||||
if (_this->ignoreSilence && data[0].l == 0.0f && data[0].r == 0.0f) {
|
||||
return;
|
||||
}
|
||||
volk_32f_s32f_convert_16i(_this->wavSampleBuf, (float*)data, 32767.0f, count * 2);
|
||||
_this->audioWriter->writeSamples(_this->wavSampleBuf, count * 2 * sizeof(int16_t));
|
||||
_this->samplesWritten += count;
|
||||
@ -514,6 +527,8 @@ private:
|
||||
EventHandler<std::string> streamRegisteredHandler;
|
||||
EventHandler<std::string> streamUnregisterHandler;
|
||||
EventHandler<std::string> streamUnregisteredHandler;
|
||||
|
||||
bool ignoreSilence = false;
|
||||
};
|
||||
|
||||
struct RecorderContext_t {
|
||||
|
Loading…
Reference in New Issue
Block a user