BogaudioModules

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

Mix4.cpp (10346B)


      1 
      2 #include "Mix4.hpp"
      3 
      4 #define POLY_OFFSET "poly_channel_offset"
      5 
      6 void Mix4::onRandomize(const RandomizeEvent& e) {
      7 	Module::onRandomize(e);
      8 	for (int i = 0; i < 4; ++i) {
      9 		getParamQuantity(MUTE1_PARAM + 3*i)->setValue(random::uniform() > 0.5f);
     10 	}
     11 }
     12 
     13 json_t* Mix4::saveToJson(json_t* root) {
     14 	root = DimmableMixerModule::saveToJson(root);
     15 	json_object_set_new(root, POLY_OFFSET, json_integer(_polyChannelOffset));
     16 	return root;
     17 }
     18 
     19 void Mix4::loadFromJson(json_t* root) {
     20 	DimmableMixerModule::loadFromJson(root);
     21 	json_t* o = json_object_get(root, POLY_OFFSET);
     22 	if (o) {
     23 		_polyChannelOffset = json_integer_value(o);
     24 	}
     25 }
     26 
     27 void Mix4::sampleRateChange() {
     28 	float sr = APP->engine->getSampleRate();
     29 	for (int i = 0; i < 4; ++i) {
     30 		_channels[i]->setSampleRate(sr);
     31 		_panSLs[i].setParams(sr, MIXER_PAN_SLEW_MS, 2.0f);
     32 	}
     33 	_slewLimiter.setParams(sr, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels);
     34 	_levelCVSL.setParams(sr, MixerChannel::levelSlewTimeMS, 1.0f);
     35 	_rms.setSampleRate(sr);
     36 }
     37 
     38 void Mix4::processAll(const ProcessArgs& args) {
     39 	Mix4ExpanderMessage* toExp = &_dummyExpanderMessage;
     40 	Mix4ExpanderMessage* fromExp = &_dummyExpanderMessage;
     41 	if (expanderConnected()) {
     42 		toExp = toExpander();
     43 		fromExp = fromExpander();
     44 	}
     45 
     46 	if (!(
     47 		inputs[IN1_INPUT].isConnected() ||
     48 		inputs[IN2_INPUT].isConnected() ||
     49 		inputs[IN3_INPUT].isConnected() ||
     50 		inputs[IN4_INPUT].isConnected()
     51 	)) {
     52 		if (_wasActive > 0) {
     53 			--_wasActive;
     54 			for (int i = 0; i < 4; ++i) {
     55 				_channels[i]->reset();
     56 				toExp->active[i] = false;
     57 			}
     58 			_rmsLevel = 0.0f;
     59 			outputs[L_OUTPUT].setVoltage(0.0f);
     60 			outputs[R_OUTPUT].setVoltage(0.0f);
     61 		}
     62 		return;
     63 	}
     64 	_wasActive = 2;
     65 
     66 	bool solo =
     67 		params[MUTE1_PARAM].getValue() > 1.5f ||
     68 		params[MUTE2_PARAM].getValue() > 1.5f ||
     69 		params[MUTE3_PARAM].getValue() > 1.5f ||
     70 		params[MUTE4_PARAM].getValue() > 1.5f;
     71 
     72 	{
     73 		float sample = 0.0f;
     74 		if (_polyChannelOffset >= 0) {
     75 			sample = inputs[IN1_INPUT].getPolyVoltage(_polyChannelOffset);
     76 		} else {
     77 			sample = inputs[IN1_INPUT].getVoltageSum();
     78 		}
     79 		_channels[0]->next(sample, solo, 0, _linearCV);
     80 		toExp->preFader[0] = sample;
     81 		toExp->active[0] = inputs[IN1_INPUT].isConnected();
     82 
     83 		for (int i = 1; i < 4; ++i) {
     84 			float sample = 0.0f;
     85 			if (inputs[IN1_INPUT + 3 * i].isConnected()) {
     86 				sample = inputs[IN1_INPUT + 3 * i].getVoltageSum();
     87 				_channels[i]->next(sample, solo, 0, _linearCV);
     88 				_channelActive[i] = true;
     89 			}
     90 			else if (_polyChannelOffset >= 0) {
     91 				sample = inputs[IN1_INPUT].getPolyVoltage(_polyChannelOffset + i);
     92 				_channels[i]->next(sample, solo, 0, _linearCV);
     93 				_channelActive[i] = true;
     94 			}
     95 			else if (_channelActive[i]) {
     96 				_channels[i]->reset();
     97 				_channelActive[i] = false;
     98 			}
     99 			toExp->preFader[i] = sample;
    100 			toExp->active[i] = _channelActive[i];
    101 		}
    102 	}
    103 
    104 	float levelCV = 1.0f;
    105 	if (inputs[MIX_CV_INPUT].isConnected()) {
    106 		levelCV = clamp(inputs[MIX_CV_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
    107 	}
    108 	float level = Amplifier::minDecibels;
    109 	if (params[MIX_MUTE_PARAM].getValue() < 0.5f) {
    110 		level = params[MIX_PARAM].getValue();
    111 		if (!_linearCV) {
    112 			level *= levelCV;
    113 		}
    114 		level *= MixerChannel::maxDecibels - MixerChannel::minDecibels;
    115 		level += MixerChannel::minDecibels;
    116 		if (params[MIX_DIM_PARAM].getValue() > 0.5f) {
    117 			level = std::max(Amplifier::minDecibels, level - _dimDb);
    118 		}
    119 	}
    120 	_amplifier.setLevel(_slewLimiter.next(level));
    121 	levelCV = _levelCVSL.next(levelCV);
    122 
    123 	float outs[4];
    124 	for (int i = 0; i < 4; ++i) {
    125 		toExp->postFader[i] = outs[i] = _channels[i]->out;
    126 	}
    127 
    128 	float mono = 0.0f;
    129 	float left = 0.0f;
    130 	float right = 0.0f;
    131 	if (expanderConnected()) {
    132 		mono += fromExp->returnA[0] + fromExp->returnB[0];
    133 		left += fromExp->returnA[0] + fromExp->returnB[0];
    134 		right += fromExp->returnA[1] + fromExp->returnB[1];
    135 		std::copy(fromExp->postEQ, fromExp->postEQ + 4, outs);
    136 	}
    137 
    138 	for (int i = 0; i < 4; ++i) {
    139 		mono += outs[i];
    140 	}
    141 	mono = _amplifier.next(mono);
    142 	if (_linearCV) {
    143 		mono *= levelCV;
    144 	}
    145 	mono = _saturator.next(mono);
    146 	_rmsLevel = _rms.next(mono) / 5.0f;
    147 
    148 	if (outputs[L_OUTPUT].isConnected() && outputs[R_OUTPUT].isConnected()) {
    149 		for (int i = 0; i < 4; ++i) {
    150 			float pan = clamp(params[PAN1_PARAM + 3 * i].getValue(), -1.0f, 1.0f);
    151 			if (inputs[PAN1_INPUT + 3 * i].isConnected()) {
    152 				pan *= clamp(inputs[PAN1_INPUT + 3 * i].getVoltage() / 5.0f, -1.0f, 1.0f);
    153 			}
    154 			_panners[i].setPan(_panSLs[i].next(pan));
    155 			float l, r;
    156 			_panners[i].next(outs[i], l, r);
    157 			left += l;
    158 			right += r;
    159 		}
    160 
    161 		left = _amplifier.next(left);
    162 		if (_linearCV) {
    163 			left *= levelCV;
    164 		}
    165 		left = _saturator.next(left);
    166 		outputs[L_OUTPUT].setVoltage(left);
    167 
    168 		right = _amplifier.next(right);
    169 		if (_linearCV) {
    170 			right *= levelCV;
    171 		}
    172 		right = _saturator.next(right);
    173 		outputs[R_OUTPUT].setVoltage(right);
    174 	}
    175 	else {
    176 		outputs[L_OUTPUT].setVoltage(mono);
    177 		outputs[R_OUTPUT].setVoltage(mono);
    178 	}
    179 }
    180 
    181 struct Mix4Widget : DimmableMixerWidget {
    182 	static constexpr int hp = 15;
    183 
    184 	Mix4Widget(Mix4* module) {
    185 		setModule(module);
    186 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    187 		setPanel(box.size, "Mix4");
    188 		createScrews();
    189 
    190 		// generated by svg_widgets.rb
    191 		auto level1ParamPosition = Vec(17.5, 32.0);
    192 		auto mute1ParamPosition = Vec(17.5, 185.0);
    193 		auto pan1ParamPosition = Vec(18.5, 223.0);
    194 		auto level2ParamPosition = Vec(60.5, 32.0);
    195 		auto mute2ParamPosition = Vec(60.5, 185.0);
    196 		auto pan2ParamPosition = Vec(61.5, 223.0);
    197 		auto level3ParamPosition = Vec(103.5, 32.0);
    198 		auto mute3ParamPosition = Vec(103.5, 185.0);
    199 		auto pan3ParamPosition = Vec(104.5, 223.0);
    200 		auto level4ParamPosition = Vec(146.5, 32.0);
    201 		auto mute4ParamPosition = Vec(146.5, 185.0);
    202 		auto pan4ParamPosition = Vec(147.5, 223.0);
    203 		auto mixParamPosition = Vec(189.5, 32.0);
    204 		auto mixMuteParamPosition = Vec(189.5, 185.0);
    205 		auto mixDimParamPosition = Vec(189.5, 218.0);
    206 
    207 		auto cv1InputPosition = Vec(14.5, 255.0);
    208 		auto pan1InputPosition = Vec(14.5, 290.0);
    209 		auto in1InputPosition = Vec(14.5, 325.0);
    210 		auto cv2InputPosition = Vec(57.5, 255.0);
    211 		auto pan2InputPosition = Vec(57.5, 290.0);
    212 		auto in2InputPosition = Vec(57.5, 325.0);
    213 		auto cv3InputPosition = Vec(100.5, 255.0);
    214 		auto pan3InputPosition = Vec(100.5, 290.0);
    215 		auto in3InputPosition = Vec(100.5, 325.0);
    216 		auto cv4InputPosition = Vec(143.5, 255.0);
    217 		auto pan4InputPosition = Vec(143.5, 290.0);
    218 		auto in4InputPosition = Vec(143.5, 325.0);
    219 		auto mixCvInputPosition = Vec(186.5, 252.0);
    220 
    221 		auto lOutputPosition = Vec(186.5, 290.0);
    222 		auto rOutputPosition = Vec(186.5, 325.0);
    223 		// end generated by svg_widgets.rb
    224 
    225 		addSlider(level1ParamPosition, module, Mix4::LEVEL1_PARAM, module ? &module->_channels[0]->rms : NULL);
    226 		addParam(createParam<Knob16>(pan1ParamPosition, module, Mix4::PAN1_PARAM));
    227 		addParam(createParam<SoloMuteButton>(mute1ParamPosition, module, Mix4::MUTE1_PARAM));
    228 		addSlider(level2ParamPosition, module, Mix4::LEVEL2_PARAM, module ? &module->_channels[1]->rms : NULL);
    229 		addParam(createParam<Knob16>(pan2ParamPosition, module, Mix4::PAN2_PARAM));
    230 		addParam(createParam<SoloMuteButton>(mute2ParamPosition, module, Mix4::MUTE2_PARAM));
    231 		addSlider(level3ParamPosition, module, Mix4::LEVEL3_PARAM, module ? &module->_channels[2]->rms : NULL);
    232 		addParam(createParam<Knob16>(pan3ParamPosition, module, Mix4::PAN3_PARAM));
    233 		addParam(createParam<SoloMuteButton>(mute3ParamPosition, module, Mix4::MUTE3_PARAM));
    234 		addSlider(level4ParamPosition, module, Mix4::LEVEL4_PARAM, module ? &module->_channels[3]->rms : NULL);
    235 		addParam(createParam<Knob16>(pan4ParamPosition, module, Mix4::PAN4_PARAM));
    236 		addParam(createParam<SoloMuteButton>(mute4ParamPosition, module, Mix4::MUTE4_PARAM));
    237 		addSlider(mixParamPosition, module, Mix4::MIX_PARAM, module ? &module->_rmsLevel : NULL);
    238 		addParam(createParam<MuteButton>(mixMuteParamPosition, module, Mix4::MIX_MUTE_PARAM));
    239 		addParam(createParam<MuteButton>(mixDimParamPosition, module, Mix4::MIX_DIM_PARAM));
    240 
    241 		addInput(createInput<Port24>(cv1InputPosition, module, Mix4::CV1_INPUT));
    242 		addInput(createInput<Port24>(pan1InputPosition, module, Mix4::PAN1_INPUT));
    243 		addInput(createInput<Port24>(in1InputPosition, module, Mix4::IN1_INPUT));
    244 		addInput(createInput<Port24>(cv2InputPosition, module, Mix4::CV2_INPUT));
    245 		addInput(createInput<Port24>(pan2InputPosition, module, Mix4::PAN2_INPUT));
    246 		addInput(createInput<Port24>(in2InputPosition, module, Mix4::IN2_INPUT));
    247 		addInput(createInput<Port24>(cv3InputPosition, module, Mix4::CV3_INPUT));
    248 		addInput(createInput<Port24>(pan3InputPosition, module, Mix4::PAN3_INPUT));
    249 		addInput(createInput<Port24>(in3InputPosition, module, Mix4::IN3_INPUT));
    250 		addInput(createInput<Port24>(cv4InputPosition, module, Mix4::CV4_INPUT));
    251 		addInput(createInput<Port24>(pan4InputPosition, module, Mix4::PAN4_INPUT));
    252 		addInput(createInput<Port24>(in4InputPosition, module, Mix4::IN4_INPUT));
    253 		addInput(createInput<Port24>(mixCvInputPosition, module, Mix4::MIX_CV_INPUT));
    254 
    255 		addOutput(createOutput<Port24>(lOutputPosition, module, Mix4::L_OUTPUT));
    256 		addOutput(createOutput<Port24>(rOutputPosition, module, Mix4::R_OUTPUT));
    257 	}
    258 
    259 	void addSlider(Vec position, Mix4* module, int id, float* rms) {
    260 		auto slider = createParam<VUSlider151>(position, module, id);
    261 		if (rms) {
    262 			dynamic_cast<VUSlider*>(slider)->setVULevel(rms);
    263 		}
    264 		addParam(slider);
    265 	}
    266 
    267 	void contextMenu(Menu* menu) override {
    268 		DimmableMixerWidget::contextMenu(menu);
    269 		auto m = dynamic_cast<Mix4*>(module);
    270 		assert(m);
    271 		OptionsMenuItem* mi = new OptionsMenuItem("Input 1 poly spread");
    272 		mi->addItem(OptionMenuItem("None", [m]() { return m->_polyChannelOffset == -1; }, [m]() { m->_polyChannelOffset = -1; }));
    273 		mi->addItem(OptionMenuItem("Channels 1-4", [m]() { return m->_polyChannelOffset == 0; }, [m]() { m->_polyChannelOffset = 0; }));
    274 		mi->addItem(OptionMenuItem("Channels 5-8", [m]() { return m->_polyChannelOffset == 4; }, [m]() { m->_polyChannelOffset = 4; }));
    275 		mi->addItem(OptionMenuItem("Channels 9-12", [m]() { return m->_polyChannelOffset == 8; }, [m]() { m->_polyChannelOffset = 8; }));
    276 		mi->addItem(OptionMenuItem("Channels 13-16", [m]() { return m->_polyChannelOffset == 12; }, [m]() { m->_polyChannelOffset = 12; }));
    277 		OptionsMenuItem::addToMenu(mi, menu);
    278 	}
    279 };
    280 
    281 Model* modelMix4 = bogaudio::createModel<Mix4, Mix4Widget>("Bogaudio-Mix4", "MIX4", "4-channel mixer and panner", "Mixer", "Panning");