Nsgt.cpp (8080B)
1 2 #include "Nsgt.hpp" 3 4 #define ATTACK_MS "attack_ms" 5 #define RELEASE_MS "release_ms" 6 #define THRESHOLD_RANGE "threshold_range" 7 8 void Nsgt::Engine::sampleRateChange() { 9 detector.setSampleRate(APP->engine->getSampleRate()); 10 } 11 12 float Nsgt::ThresholdParamQuantity::getDisplayValue() { 13 float v = getValue(); 14 if (!module) { 15 return v; 16 } 17 18 v *= 30.0f; 19 v -= 24.0f; 20 v *= dynamic_cast<Nsgt*>(module)->_thresholdRange; 21 return v; 22 } 23 24 void Nsgt::ThresholdParamQuantity::setDisplayValue(float v) { 25 if (!module) { 26 return; 27 } 28 Nsgt* m = dynamic_cast<Nsgt*>(module); 29 v /= m->_thresholdRange; 30 v = clamp(v, -24.0f, 6.0f); 31 v += 24.0f; 32 v /= 30.0f; 33 setValue(v); 34 } 35 36 void Nsgt::sampleRateChange() { 37 for (int c = 0; c < _channels; ++c) { 38 _engines[c]->sampleRateChange(); 39 } 40 } 41 42 json_t* Nsgt::saveToJson(json_t* root) { 43 json_object_set_new(root, ATTACK_MS, json_real(_attackMs)); 44 json_object_set_new(root, RELEASE_MS, json_real(_releaseMs)); 45 json_object_set_new(root, THRESHOLD_RANGE, json_real(_thresholdRange)); 46 return root; 47 } 48 49 void Nsgt::loadFromJson(json_t* root) { 50 json_t* a = json_object_get(root, ATTACK_MS); 51 if (a) { 52 _attackMs = std::max(0.0f, (float)json_real_value(a)); 53 } 54 55 json_t* r = json_object_get(root, RELEASE_MS); 56 if (r) { 57 _releaseMs = std::max(0.0f, (float)json_real_value(r)); 58 } 59 60 json_t* tr = json_object_get(root, THRESHOLD_RANGE); 61 if (tr) { 62 _thresholdRange = std::max(0.0f, (float)json_real_value(tr)); 63 } 64 } 65 66 bool Nsgt::active() { 67 return outputs[LEFT_OUTPUT].isConnected() || outputs[RIGHT_OUTPUT].isConnected(); 68 } 69 70 int Nsgt::channels() { 71 return inputs[LEFT_INPUT].getChannels(); 72 } 73 74 void Nsgt::addChannel(int c) { 75 _engines[c] = new Engine(); 76 _engines[c]->sampleRateChange(); 77 } 78 79 void Nsgt::removeChannel(int c) { 80 delete _engines[c]; 81 _engines[c] = NULL; 82 } 83 84 void Nsgt::modulate() { 85 _softKnee = params[KNEE_PARAM].getValue() > 0.5f; 86 } 87 88 void Nsgt::modulateChannel(int c) { 89 Engine& e = *_engines[c]; 90 91 e.thresholdDb = params[THRESHOLD_PARAM].getValue(); 92 if (inputs[THRESHOLD_INPUT].isConnected()) { 93 e.thresholdDb *= clamp(inputs[THRESHOLD_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); 94 } 95 e.thresholdDb *= 30.0f; 96 e.thresholdDb -= 24.0f; 97 e.thresholdDb *= _thresholdRange; 98 99 float ratio = params[RATIO_PARAM].getValue(); 100 if (inputs[RATIO_INPUT].isConnected()) { 101 ratio *= clamp(inputs[RATIO_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); 102 } 103 if (e.ratioKnob != ratio) { 104 e.ratioKnob = ratio; 105 ratio = powf(ratio, 1.5f); 106 ratio = 1.0f - ratio; 107 ratio *= M_PI; 108 ratio *= 0.25f; 109 ratio = tanf(ratio); 110 ratio = 1.0f / ratio; 111 e.ratio = ratio; 112 } 113 114 float sr = APP->engine->getSampleRate(); 115 e.attackSL.setParams(sr, _attackMs); 116 e.releaseSL.setParams(sr, _releaseMs); 117 } 118 119 void Nsgt::processChannel(const ProcessArgs& args, int c) { 120 Engine& e = *_engines[c]; 121 122 float leftInput = inputs[LEFT_INPUT].getPolyVoltage(c); 123 float rightInput = inputs[RIGHT_INPUT].getPolyVoltage(c); 124 float env = e.detector.next(leftInput + rightInput); 125 if (env > e.lastEnv) { 126 env = e.attackSL.next(env, e.lastEnv); 127 } 128 else { 129 env = e.releaseSL.next(env, e.lastEnv); 130 } 131 e.lastEnv = env; 132 133 float detectorDb = amplitudeToDecibels(env / 5.0f); 134 float compressionDb = e.noiseGate.compressionDb(detectorDb, e.thresholdDb, e.ratio, _softKnee); 135 e.amplifier.setLevel(-compressionDb); 136 if (outputs[LEFT_OUTPUT].isConnected()) { 137 outputs[LEFT_OUTPUT].setChannels(_channels); 138 outputs[LEFT_OUTPUT].setVoltage(e.saturator.next(e.amplifier.next(leftInput)), c); 139 } 140 if (outputs[RIGHT_OUTPUT].isConnected()) { 141 outputs[RIGHT_OUTPUT].setChannels(_channels); 142 outputs[RIGHT_OUTPUT].setVoltage(e.saturator.next(e.amplifier.next(rightInput)), c); 143 } 144 } 145 146 struct ARQuantity : Quantity { 147 typedef std::function<float&(Nsgt* m)> ValueRefFN; 148 149 Nsgt* _module; 150 std::string _label; 151 float _maxMs; 152 float _defaultMs; 153 ValueRefFN _moduleValueRef; 154 155 ARQuantity( 156 Nsgt* m, 157 const char* label, 158 float maxMs, 159 float defaultMs, 160 ValueRefFN moduleValueRef 161 ) 162 : _module(m) 163 , _label(label) 164 , _maxMs(maxMs) 165 , _defaultMs(defaultMs) 166 , _moduleValueRef(moduleValueRef) 167 {} 168 169 void setValue(float value) override { 170 value = clamp(value, getMinValue(), getMaxValue()); 171 if (_module) { 172 _moduleValueRef(_module) = valueToMs(value); 173 } 174 } 175 176 float getValue() override { 177 if (_module) { 178 return msToValue(_moduleValueRef(_module)); 179 } 180 return getDefaultValue(); 181 } 182 183 float getMinValue() override { return 0.0f; } 184 float getMaxValue() override { return msToValue(_maxMs); } 185 float getDefaultValue() override { return msToValue(_defaultMs); } 186 float getDisplayValue() override { return roundf(valueToMs(getValue())); } 187 void setDisplayValue(float displayValue) override { setValue(msToValue(displayValue)); } 188 std::string getLabel() override { return _label; } 189 std::string getUnit() override { return "ms"; } 190 float valueToMs(float v) { return v * v * _maxMs; } 191 float msToValue(float ms) { return sqrtf(ms / _maxMs); }; 192 }; 193 194 struct ARSlider : ui::Slider { 195 ARSlider(ARQuantity* q) { 196 quantity = q; // q now owned. 197 box.size.x = 200.0f; 198 } 199 virtual ~ARSlider() { 200 delete quantity; 201 } 202 }; 203 204 struct AttackMenuItem : MenuItem { 205 Nsgt* _module; 206 207 AttackMenuItem(Nsgt* m) : _module(m) { 208 this->text = "Attack time"; 209 this->rightText = "▸"; 210 } 211 212 Menu* createChildMenu() override { 213 Menu* menu = new Menu; 214 menu->addChild(new ARSlider(new ARQuantity( 215 _module, 216 "Attack", 217 Nsgt::maxAttackMs, 218 Nsgt::defaultAttackMs, 219 [](Nsgt* m) -> float& { return m->_attackMs; } 220 ))); 221 return menu; 222 } 223 }; 224 225 struct ReleaseMenuItem : MenuItem { 226 Nsgt* _module; 227 228 ReleaseMenuItem(Nsgt* m) : _module(m) { 229 this->text = "Release time"; 230 this->rightText = "▸"; 231 } 232 233 Menu* createChildMenu() override { 234 Menu* menu = new Menu; 235 menu->addChild(new ARSlider(new ARQuantity( 236 _module, 237 "Release", 238 Nsgt::maxReleaseMs, 239 Nsgt::defaultReleaseMs, 240 [](Nsgt* m) -> float& { return m->_releaseMs; } 241 ))); 242 return menu; 243 } 244 }; 245 246 struct NsgtWidget : BGModuleWidget { 247 static constexpr int hp = 6; 248 249 NsgtWidget(Nsgt* module) { 250 setModule(module); 251 box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); 252 setPanel(box.size, "Nsgt"); 253 createScrews(); 254 255 // generated by svg_widgets.rb 256 auto thresholdParamPosition = Vec(26.0, 52.0); 257 auto ratioParamPosition = Vec(26.0, 135.0); 258 auto kneeParamPosition = Vec(39.5, 199.5); 259 260 auto leftInputPosition = Vec(16.0, 244.0); 261 auto rightInputPosition = Vec(50.0, 244.0); 262 auto thresholdInputPosition = Vec(16.0, 280.0); 263 auto ratioInputPosition = Vec(50.0, 280.0); 264 265 auto leftOutputPosition = Vec(16.0, 320.0); 266 auto rightOutputPosition = Vec(50.0, 320.0); 267 // end generated by svg_widgets.rb 268 269 addParam(createParam<Knob38>(thresholdParamPosition, module, Nsgt::THRESHOLD_PARAM)); 270 addParam(createParam<Knob38>(ratioParamPosition, module, Nsgt::RATIO_PARAM)); 271 addParam(createParam<SliderSwitch2State14>(kneeParamPosition, module, Nsgt::KNEE_PARAM)); 272 273 addInput(createInput<Port24>(leftInputPosition, module, Nsgt::LEFT_INPUT)); 274 addInput(createInput<Port24>(rightInputPosition, module, Nsgt::RIGHT_INPUT)); 275 addInput(createInput<Port24>(thresholdInputPosition, module, Nsgt::THRESHOLD_INPUT)); 276 addInput(createInput<Port24>(ratioInputPosition, module, Nsgt::RATIO_INPUT)); 277 278 addOutput(createOutput<Port24>(leftOutputPosition, module, Nsgt::LEFT_OUTPUT)); 279 addOutput(createOutput<Port24>(rightOutputPosition, module, Nsgt::RIGHT_OUTPUT)); 280 } 281 282 void contextMenu(Menu* menu) override { 283 auto m = dynamic_cast<Nsgt*>(module); 284 assert(m); 285 286 menu->addChild(new AttackMenuItem(m)); 287 menu->addChild(new ReleaseMenuItem(m)); 288 289 OptionsMenuItem* tr = new OptionsMenuItem("Threshold range"); 290 tr->addItem(OptionMenuItem("1x (-24dB to 6dB)", [m]() { return m->_thresholdRange == 1.0f; }, [m]() { m->_thresholdRange = 1.0f; })); 291 tr->addItem(OptionMenuItem("2x (-48dB to 12dB)", [m]() { return m->_thresholdRange == 2.0f; }, [m]() { m->_thresholdRange = 2.0f; })); 292 OptionsMenuItem::addToMenu(tr, menu); 293 } 294 }; 295 296 Model* modelNsgt = bogaudio::createModel<Nsgt, NsgtWidget>("Bogaudio-Nsgt", "NSGT", "Noise gate", "Dynamics", "Polyphonic");