Prototype noise blanker

pull/801/head
AlexandreRouma 2022-06-26 03:42:22 +02:00
rodzic c3ddffb3a9
commit 3f6687659e
12 zmienionych plików z 143 dodań i 52 usunięć

Wyświetl plik

@ -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;
};
}

Wyświetl plik

@ -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;
};
}

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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; }

Wyświetl plik

@ -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;

Wyświetl plik

@ -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 {