BogaudioModules

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

addressable_sequence.cpp (5012B)


      1 
      2 #include "addressable_sequence.hpp"
      3 
      4 #define POLY_INPUT "poly_input"
      5 #define SELECT_ON_CLOCK "select_on_clock"
      6 #define TRIGGERED_SELECT "triggered_select"
      7 #define REVERSE_ON_NEGATIVE_CLOCK "reverse_on_negative_clock"
      8 #define WRAP_SELECT_AT_STEPS "wrap_select_at_steps"
      9 
     10 void AddressableSequenceModule::setInputIDs(int clockInputID, int selectInputID) {
     11 	_polyInputID = clockInputID;
     12 	_clockInputID = clockInputID;
     13 	_selectInputID = selectInputID;
     14 }
     15 
     16 void AddressableSequenceModule::reset() {
     17 	for (int i = 0; i < maxChannels; ++i) {
     18 		_step[i] = 0;
     19 		_select[i] = 0;
     20 		_clock[i].reset();
     21 		_negativeClock[i].reset();
     22 		_reset[i].reset();
     23 		_selectTrigger[i].reset();
     24 	}
     25 }
     26 
     27 void AddressableSequenceModule::sampleRateChange() {
     28 	float sr = APP->engine->getSampleRate();
     29 	for (int i = 0; i < maxChannels; ++i) {
     30 		_timer[i].setParams(sr, 0.001f);
     31 	}
     32 }
     33 
     34 json_t* AddressableSequenceModule::saveToJson(json_t* root) {
     35 	json_object_set_new(root, POLY_INPUT, json_integer(_polyInputID));
     36 	json_object_set_new(root, SELECT_ON_CLOCK, json_boolean(_selectOnClock));
     37 	json_object_set_new(root, TRIGGERED_SELECT, json_boolean(_triggeredSelect));
     38 	json_object_set_new(root, REVERSE_ON_NEGATIVE_CLOCK, json_boolean(_reverseOnNegativeClock));
     39 	json_object_set_new(root, WRAP_SELECT_AT_STEPS, json_boolean(_wrapSelectAtSteps));
     40 	return root;
     41 }
     42 
     43 void AddressableSequenceModule::loadFromJson(json_t* root) {
     44 	json_t* p = json_object_get(root, POLY_INPUT);
     45 	if (p) {
     46 		_polyInputID = json_integer_value(p);
     47 	}
     48 
     49 	json_t* s = json_object_get(root, SELECT_ON_CLOCK);
     50 	if (s) {
     51 		_selectOnClock = json_is_true(s);
     52 	}
     53 
     54 	json_t* t = json_object_get(root, TRIGGERED_SELECT);
     55 	if (t) {
     56 		_triggeredSelect = json_is_true(t);
     57 	}
     58 
     59 	json_t* r = json_object_get(root, REVERSE_ON_NEGATIVE_CLOCK);
     60 	if (r) {
     61 		_reverseOnNegativeClock = json_is_true(r);
     62 	}
     63 
     64 	json_t* w = json_object_get(root, WRAP_SELECT_AT_STEPS);
     65 	if (w) {
     66 		_wrapSelectAtSteps = json_is_true(w);
     67 	}
     68 }
     69 
     70 int AddressableSequenceModule::channels() {
     71 	assert(_polyInputID >= 0);
     72 	assert(_clockInputID >= 0);
     73 	assert(_selectInputID >= 0);
     74 	return _polyInputID == _selectInputID ? inputs[_selectInputID].getChannels() : inputs[_clockInputID].getChannels();
     75 }
     76 
     77 int AddressableSequenceModule::nextStep(
     78 	int c,
     79 	Input* resetInput,
     80 	Input& clockInput,
     81 	Param* stepsParam,
     82 	Param& directionParam,
     83 	Param* selectParam,
     84 	Input& selectInput,
     85 	int n
     86 ) {
     87 	bool reset = false;
     88 	if (resetInput) {
     89 		reset = _reset[c].process(resetInput->getPolyVoltage(c));
     90 		if (reset) {
     91 			_timer[c].reset();
     92 		}
     93 	}
     94 	bool timer = _timer[c].next();
     95 	float clockVoltage = clockInput.getPolyVoltage(c);
     96 	bool clock = _clock[c].process(clockVoltage) && !timer;
     97 	bool negativeClock = _negativeClock[c].process(clockVoltage) && _reverseOnNegativeClock && !timer && !clock;
     98 
     99 	int steps = n;
    100 	if (stepsParam) {
    101 		float s = clamp(stepsParam->getValue(), 1.0f, 8.0f);
    102 		s -= 1.0f;
    103 		s /= 7.0f;
    104 		s *= n - 1;
    105 		s += 1.0f;
    106 		steps = s;
    107 	}
    108 	int reverse = 1 - 2 * (directionParam.getValue() == 0.0f);
    109 	_step[c] = (_step[c] + reverse * clock + -reverse * negativeClock) % steps;
    110 	_step[c] += (_step[c] < 0) * steps;
    111 	_step[c] -= _step[c] * reset;
    112 
    113 	float select = n;
    114 	if (selectParam) {
    115 		float s = clamp(selectParam->getValue(), 0.0f, 7.0f) / 7.0f;
    116 		select = s * (n - 1);
    117 	}
    118 	if (_triggeredSelect) {
    119 		if (_selectTrigger[c].process(selectInput.getPolyVoltage(c))) {
    120 			_select[c] = (1 + (int)_select[c]) % ((int)select + 1);
    121 		}
    122 		_select[c] -= _select[c] * reset;
    123 	}
    124 	else {
    125 		select += (clamp(selectInput.getPolyVoltage(c), -9.99f, 9.99f) / 10.f) * (float)n;
    126 		if (!_selectOnClock || clock) {
    127 			_select[c] = select;
    128 		}
    129 	}
    130 
    131 	int s = (_step[c] + (int)_select[c]) % (_wrapSelectAtSteps ? steps : n);
    132 	if (s < 0) {
    133 		return n + s;
    134 	}
    135 	return s;
    136 }
    137 
    138 int AddressableSequenceModule::setStep(int c, int i, int n) {
    139 	return _step[c] = i % n;
    140 }
    141 
    142 
    143 void AddressableSequenceBaseModuleWidget::contextMenu(Menu* menu) {
    144 	auto m = dynamic_cast<AddressableSequenceModule*>(module);
    145 	assert(m);
    146 
    147 	OptionsMenuItem* p = new OptionsMenuItem("Polyphony channels from");
    148 	p->addItem(OptionMenuItem("CLOCK input", [m]() { return m->_polyInputID == m->_clockInputID; }, [m]() { m->_polyInputID = m->_clockInputID; }));
    149 	p->addItem(OptionMenuItem("SELECT input", [m]() { return m->_polyInputID == m->_selectInputID; }, [m]() { m->_polyInputID = m->_selectInputID; }));
    150 	OptionsMenuItem::addToMenu(p, menu);
    151 
    152 	menu->addChild(new BoolOptionMenuItem("Reverse step on negative clock", [m]() { return &m->_reverseOnNegativeClock; }));
    153 	menu->addChild(new BoolOptionMenuItem("Triggered select mode", [m]() { return &m->_triggeredSelect; }));
    154 	menu->addChild(new BoolOptionMenuItem("Wrap select at steps", [m]() { return &m->_wrapSelectAtSteps; }));
    155 }
    156 
    157 
    158 void AddressableSequenceModuleWidget::contextMenu(Menu* menu) {
    159 	AddressableSequenceBaseModuleWidget::contextMenu(menu);
    160 
    161 	auto m = dynamic_cast<AddressableSequenceModule*>(module);
    162 	assert(m);
    163 	menu->addChild(new BoolOptionMenuItem("Select on clock mode", [m]() { return &m->_selectOnClock; }));
    164 }