BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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");