BogaudioModules

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

Arp.cpp (11920B)


      1 
      2 #include "Arp.hpp"
      3 #include <unordered_set>
      4 
      5 #define NOTES_IMMEDIATE_MODE "notes_immediate"
      6 #define FIXED_GATE_MODE "fixed_gate"
      7 
      8 void Arp::NoteSet::Note::reset() {
      9 	pitch = 0.0f;
     10 	channel = -1;
     11 }
     12 
     13 bool Arp::NoteSet::nextPitch(Mode mode, float& pitchOut) {
     14 	if (_syncNext) {
     15 		_syncNext = false;
     16 		sync();
     17 	}
     18 	if (_noteCount <= 0) {
     19 		return false;
     20 	}
     21 
     22 	switch (mode) {
     23 		case UP_MODE: {
     24 			_playIndex = (_playIndex + 1) % _noteCount;
     25 			_syncNext = _syncTo && _playIndex == _noteCount - 1;
     26 			pitchOut = _notesByPitch[_playIndex].pitch;
     27 			return true;
     28 		}
     29 
     30 		case DOWN_MODE: {
     31 			--_playIndex;
     32 			if (_playIndex < 0) {
     33 				_playIndex = _noteCount - 1;
     34 			}
     35 			_syncNext = _syncTo && _playIndex == 0;
     36 			pitchOut = _notesByPitch[_playIndex].pitch;
     37 			return true;
     38 		}
     39 
     40 		case UP_DOWN_MODE: {
     41 			if (_up) {
     42 				++_playIndex;
     43 				if (_playIndex >= _noteCount) {
     44 					_playIndex = std::max(0, _noteCount - 2);
     45 					_up = false;
     46 				}
     47 			}
     48 			else {
     49 				--_playIndex;
     50 				if (_playIndex < 0) {
     51 					_playIndex = 1 % _noteCount;
     52 					_up = true;
     53 				}
     54 				_syncNext = _syncTo && (_playIndex == 0 || _playIndex == 1);
     55 			}
     56 			pitchOut = _notesByPitch[_playIndex].pitch;
     57 			return true;
     58 		}
     59 
     60 		case UP_DOWN_REPEAT_MODE: {
     61 			if (_up) {
     62 				++_playIndex;
     63 				if (_playIndex >= _noteCount) {
     64 					_playIndex = _noteCount - 1;
     65 					_up = false;
     66 				}
     67 			}
     68 			else {
     69 				--_playIndex;
     70 				if (_playIndex < 0) {
     71 					_playIndex = 0;
     72 					_up = true;
     73 				}
     74 				_syncNext = _syncTo && _playIndex == 0;
     75 			}
     76 			pitchOut = _notesByPitch[_playIndex].pitch;
     77 			return true;
     78 		}
     79 
     80 		case IN_ORDER_MODE: {
     81 			_playIndex = (_playIndex + 1) % _noteCount;
     82 			_syncNext = _syncTo && _playIndex == _noteCount - 1;
     83 			pitchOut = _notesAsPlayed[_playIndex].pitch;
     84 			return true;
     85 		}
     86 
     87 		case RANDOM_MODE: {
     88 			_playIndex = (_playIndex + 1) % _noteCount;
     89 			_syncNext = _syncTo && _playIndex == _noteCount - 1;
     90 			pitchOut = _notesAsPlayed[random::u32() % _noteCount].pitch;
     91 			return true;
     92 		}
     93 
     94 		case SHUFFLE_MODE: {
     95 			_playIndex = (_playIndex + 1) % _noteCount;
     96 			if (_playIndex == 0) {
     97 				std::fill(_shuffleMask, _shuffleMask + maxChannels, false);
     98 			}
     99 			_syncNext = _syncTo && _playIndex == _noteCount - 1;
    100 
    101 			int n = random::u32() % (_noteCount - _playIndex);
    102 			int i = 0;
    103 			for (; i < _noteCount; ++i) {
    104 				if (!_shuffleMask[i]) {
    105 					if (n < 1) {
    106 						_shuffleMask[i] = true;
    107 						break;
    108 					}
    109 					--n;
    110 				}
    111 			}
    112 			pitchOut = _notesAsPlayed[i].pitch;
    113 			return true;
    114 		}
    115 	}
    116 
    117 	assert(false);
    118 	return false;
    119 }
    120 
    121 void Arp::NoteSet::reset() {
    122 	resetSequence();
    123 	_notesDirty = false;
    124 	_noteCount = 0;
    125 	for (int c = 0; c < maxChannels; ++c) {
    126 		_noteOn[c] = false;
    127 		_notesAsPlayed[c].reset();
    128 		_notesByPitch[c].reset();
    129 	}
    130 }
    131 
    132 void Arp::NoteSet::resetSequence() {
    133 	_playIndex = -1;
    134 	_up = true;
    135 }
    136 
    137 void Arp::NoteSet::addNote(int c, float pitch) {
    138 	for (int i = 0; i < _noteCount; ++i) {
    139 		if (_notesByPitch[i].pitch == pitch) {
    140 			return;
    141 		}
    142 	}
    143 
    144 	dropNote(c);
    145 	_noteOn[c] = true;
    146 	_notesDirty = true;
    147 
    148 	Note n;
    149 	n.pitch = pitch;
    150 	n.channel = c;
    151 	int i = 0;
    152 	while (n.pitch >= _notesByPitch[i].pitch && i < _noteCount) {
    153 		++i;
    154 	}
    155 	assert(i <= maxChannels);
    156 
    157 	if (i >= maxChannels) {
    158 		i = maxChannels - 1;
    159 	}
    160 	else {
    161 		shuffleUp(_notesByPitch, i);
    162 	}
    163 	_notesByPitch[i] = n;
    164 
    165 	_notesAsPlayed[_noteCount] = n;
    166 	++_noteCount;
    167 	assert(_noteCount <= maxChannels);
    168 	assert(uniqueChannelsCount(_notesAsPlayed) == _noteCount);
    169 	assert(uniqueChannelsCount(_notesByPitch) == _noteCount);
    170 }
    171 
    172 void Arp::NoteSet::dropNote(int c) {
    173 	if (!_noteOn[c]) {
    174 		return;
    175 	}
    176 	_noteOn[c] = false;
    177 	_notesDirty = true;
    178 
    179 	assert(_noteCount > 0);
    180 	int i = 0;
    181 	while (_notesAsPlayed[i].channel != c && i < _noteCount) {
    182 		++i;
    183 	}
    184 	assert(i < _noteCount);
    185 	shuffleDown(_notesAsPlayed, i);
    186 	_notesAsPlayed[_noteCount - 1].reset();
    187 
    188 	i = 0;
    189 	while (_notesByPitch[i].channel != c && i < _noteCount) {
    190 		++i;
    191 	}
    192 	assert(i < _noteCount);
    193 	shuffleDown(_notesByPitch, i);
    194 	_notesByPitch[_noteCount - 1].reset();
    195 
    196 	--_noteCount;
    197 	assert(_noteCount >= 0);
    198 	assert(uniqueChannelsCount(_notesAsPlayed) == _noteCount);
    199 	assert(uniqueChannelsCount(_notesByPitch) == _noteCount);
    200 }
    201 
    202 void Arp::NoteSet::shuffleUp(Note* notes, int index) {
    203 	for (int i = _noteCount; i > index; --i) {
    204 		notes[i] = notes[i - 1];
    205 	}
    206 }
    207 
    208 void Arp::NoteSet::shuffleDown(Note* notes, int index) {
    209 	for (int n = _noteCount - 1; index < n; ++index) {
    210 		notes[index] = notes[index + 1];
    211 	}
    212 }
    213 
    214 void Arp::NoteSet::sync() {
    215 	if (!_syncTo || !_syncTo->_notesDirty) {
    216 		return;
    217 	}
    218 
    219 	_noteCount = _syncTo->_noteCount;
    220 	_playIndex = -1;
    221 	std::copy(_syncTo->_noteOn, _syncTo->_noteOn + maxChannels, _noteOn);
    222 	std::copy(_syncTo->_notesAsPlayed, _syncTo->_notesAsPlayed + _noteCount, _notesAsPlayed);
    223 	std::copy(_syncTo->_notesByPitch, _syncTo->_notesByPitch + _noteCount, _notesByPitch);
    224 	_syncTo->_notesDirty = false;
    225 }
    226 
    227 int Arp::NoteSet::uniqueChannelsCount(Note* notes) {
    228 	std::unordered_set<int> channels;
    229 	for (int i = 0; i < maxChannels; ++i) {
    230 		if (notes[i].channel >= 0) {
    231 			channels.insert(notes[i].channel);
    232 		}
    233 	}
    234 	return channels.size();
    235 }
    236 
    237 void Arp::reset() {
    238 	_clockTrigger.reset();
    239 	_resetTrigger.reset();
    240 	_anyHigh = false;
    241 	_secondsSinceLastClock = -1.0f;
    242 	_clockSeconds = 0.1f;
    243 	_gateGenerator.process(1000.0f);
    244 	for (int c = 0; c < maxChannels; ++c) {
    245 		_gateTrigger[c].reset();
    246 		_gateHigh[c] = false;
    247 	}
    248 	_currentNotes->reset();
    249 	_playbackNotes->reset();
    250 }
    251 
    252 void Arp::sampleRateChange() {
    253 	_sampleTime = APP->engine->getSampleTime();
    254 }
    255 
    256 json_t* Arp::saveToJson(json_t* root) {
    257 	json_object_set_new(root, NOTES_IMMEDIATE_MODE, json_boolean(_notesImmediate));
    258 	json_object_set_new(root, FIXED_GATE_MODE, json_boolean(_fixedGate));
    259 	return root;
    260 }
    261 
    262 void Arp::loadFromJson(json_t* root) {
    263 	json_t* ni = json_object_get(root, NOTES_IMMEDIATE_MODE);
    264 	if (ni) {
    265 		_notesImmediate = json_is_true(ni);
    266 	}
    267 
    268 	json_t* fg = json_object_get(root, FIXED_GATE_MODE);
    269 	if (fg) {
    270 		_fixedGate = json_is_true(fg);
    271 	}
    272 }
    273 
    274 int Arp::channels() {
    275 	return inputs[PITCH_INPUT].getChannels();
    276 }
    277 
    278 void Arp::addChannel(int c) {
    279 	_gateTrigger[c].reset();
    280 }
    281 
    282 void Arp::removeChannel(int c) {
    283 	_currentNotes->dropNote(c);
    284 }
    285 
    286 void Arp::modulate() {
    287 	_mode = (Mode)clamp(params[MODE_PARAM].getValue(), 0.0f, 6.0f);
    288 	_gateLength = clamp(params[GATE_LENGTH_PARAM].getValue(), 0.0f, 1.0f);
    289 
    290 	bool hold = params[HOLD_PARAM].getValue() > 0.5f;
    291 	if (_hold && !hold) {
    292 		dropAllNotes();
    293 	}
    294 	_hold = hold;
    295 }
    296 
    297 void Arp::processAll(const ProcessArgs& args) {
    298 	lights[UP_LIGHT].value = _mode == UP_MODE;
    299 	lights[DOWN_LIGHT].value = _mode == DOWN_MODE;
    300 	lights[UP_DOWN_LIGHT].value = _mode == UP_DOWN_MODE;
    301 	lights[UP_DOWN_REPEAT_LIGHT].value = _mode == UP_DOWN_REPEAT_MODE;
    302 	lights[IN_ORDER_LIGHT].value = _mode == IN_ORDER_MODE;
    303 	lights[RANDOM_LIGHT].value = _mode == RANDOM_MODE;
    304 	lights[SHUFFLE_LIGHT].value = _mode == SHUFFLE_MODE;
    305 
    306 	if (_resetTrigger.process(inputs[RESET_INPUT].getVoltage())) {
    307 		_currentNotes->resetSequence();
    308 		_playbackNotes->resetSequence();
    309 	}
    310 
    311 	bool wasAnyHigh = _anyHigh;
    312 	_anyHigh = false;
    313 	bool firstAdd = true;
    314 	for (int c = 0; c < _channels; ++c) {
    315 		if (_gateTrigger[c].process(inputs[GATE_INPUT].getPolyVoltage(c))) {
    316 			if (_hold && !wasAnyHigh && firstAdd) {
    317 				dropAllNotes();
    318 			}
    319 			firstAdd = false;
    320 			_anyHigh = true;
    321 			_gateHigh[c] = true;
    322 			_currentNotes->addNote(c, inputs[PITCH_INPUT].getPolyVoltage(c));
    323 			if (_currentNotes->noteCount() == 1) {
    324 				_playbackNotes->sync();
    325 			}
    326 		}
    327 		else if (_gateHigh[c]) {
    328 			if (!_gateTrigger[c].isHigh()) {
    329 				_gateHigh[c] = false;
    330 				if (!_hold) {
    331 					_currentNotes->dropNote(c);
    332 				}
    333 			}
    334 			else {
    335 				_anyHigh = true;
    336 			}
    337 		}
    338 	}
    339 
    340 	bool clock = false;
    341 	if (inputs[CLOCK_INPUT].isConnected()) {
    342 		clock = _clockTrigger.process(inputs[CLOCK_INPUT].getVoltage());
    343 		if (clock) {
    344 			if (_secondsSinceLastClock > 0.0f) {
    345 				_clockSeconds = _secondsSinceLastClock;
    346 			}
    347 			_secondsSinceLastClock = 0.0f;
    348 		}
    349 		_secondsSinceLastClock += _sampleTime;
    350 	}
    351 
    352 	NoteSet* notes = _notesImmediate ? _currentNotes : _playbackNotes;
    353 	if (clock) {
    354 		if (notes->nextPitch(_mode, _pitchOut)) {
    355 			_gateGenerator.reset();
    356 			float gl = _gateLength;
    357 			if (_fixedGate) {
    358 				gl *= 0.5f;
    359 			}
    360 			else {
    361 				gl *= _clockSeconds;
    362 			}
    363 			_gateGenerator.trigger(std::max(0.001f, gl));
    364 		}
    365 	}
    366 	outputs[PITCH_OUTPUT].setVoltage(_pitchOut);
    367 	outputs[GATE_OUTPUT].setVoltage(_gateGenerator.process(_sampleTime) * 5.0f);
    368 }
    369 
    370 void Arp::dropAllNotes() {
    371 	for (int c = 0; c < _channels; ++c) {
    372 		if (!_gateHigh[c]) {
    373 			_currentNotes->dropNote(c);
    374 		}
    375 	}
    376 }
    377 
    378 struct ArpWidget : BGModuleWidget {
    379 	static constexpr int hp = 3;
    380 
    381 	ArpWidget(Arp* module) {
    382 		setModule(module);
    383 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    384 		setPanel(box.size, "Arp");
    385 		createScrews();
    386 
    387 		// generated by svg_widgets.rb
    388 		auto modeParamPosition = Vec(24.0, 57.0);
    389 		auto gateLengthParamPosition = Vec(14.5, 87.5);
    390 		auto holdParamPosition = Vec(29.0, 114.0);
    391 
    392 		auto clockInputPosition = Vec(10.5, 132.0);
    393 		auto resetInputPosition = Vec(10.5, 167.0);
    394 		auto pitchInputPosition = Vec(10.5, 202.0);
    395 		auto gateInputPosition = Vec(10.5, 237.0);
    396 
    397 		auto pitchOutputPosition = Vec(10.5, 275.0);
    398 		auto gateOutputPosition = Vec(10.5, 310.0);
    399 
    400 		auto upLightPosition = Vec(3.0, 28.0);
    401 		auto downLightPosition = Vec(24.0, 28.0);
    402 		auto upDownLightPosition = Vec(3.0, 38.0);
    403 		auto upDownRepeatLightPosition = Vec(24.0, 38.0);
    404 		auto inOrderLightPosition = Vec(3.0, 48.0);
    405 		auto randomLightPosition = Vec(24.0, 48.0);
    406 		auto shuffleLightPosition = Vec(3.0, 58.0);
    407 		// end generated by svg_widgets.rb
    408 
    409 		addParam(createParam<StatefulButton9>(modeParamPosition, module, Arp::MODE_PARAM));
    410 		addParam(createParam<Knob16>(gateLengthParamPosition, module, Arp::GATE_LENGTH_PARAM));
    411 		addParam(createParam<IndicatorButtonGreen9>(holdParamPosition, module, Arp::HOLD_PARAM));
    412 
    413 		addInput(createInput<Port24>(clockInputPosition, module, Arp::CLOCK_INPUT));
    414 		addInput(createInput<Port24>(resetInputPosition, module, Arp::RESET_INPUT));
    415 		addInput(createInput<Port24>(pitchInputPosition, module, Arp::PITCH_INPUT));
    416 		addInput(createInput<Port24>(gateInputPosition, module, Arp::GATE_INPUT));
    417 
    418 		addOutput(createOutput<Port24>(pitchOutputPosition, module, Arp::PITCH_OUTPUT));
    419 		addOutput(createOutput<Port24>(gateOutputPosition, module, Arp::GATE_OUTPUT));
    420 
    421 		addChild(createLight<BGSmallLight<GreenLight>>(upLightPosition, module, Arp::UP_LIGHT));
    422 		addChild(createLight<BGSmallLight<GreenLight>>(downLightPosition, module, Arp::DOWN_LIGHT));
    423 		addChild(createLight<BGSmallLight<GreenLight>>(upDownLightPosition, module, Arp::UP_DOWN_LIGHT));
    424 		addChild(createLight<BGSmallLight<GreenLight>>(upDownRepeatLightPosition, module, Arp::UP_DOWN_REPEAT_LIGHT));
    425 		addChild(createLight<BGSmallLight<GreenLight>>(inOrderLightPosition, module, Arp::IN_ORDER_LIGHT));
    426 		addChild(createLight<BGSmallLight<GreenLight>>(randomLightPosition, module, Arp::RANDOM_LIGHT));
    427 		addChild(createLight<BGSmallLight<GreenLight>>(shuffleLightPosition, module, Arp::SHUFFLE_LIGHT));
    428 	}
    429 
    430 	void contextMenu(Menu* menu) override {
    431 		auto m = dynamic_cast<Arp*>(module);
    432 		assert(m);
    433 
    434 		OptionsMenuItem* ni = new OptionsMenuItem("Use new notes");
    435 		ni->addItem(OptionMenuItem("On arpeggio restart", [m]() { return !m->_notesImmediate; }, [m]() { m->_notesImmediate = false; }));
    436 		ni->addItem(OptionMenuItem("Immediately", [m]() { return m->_notesImmediate; }, [m]() { m->_notesImmediate = true; }));
    437 		OptionsMenuItem::addToMenu(ni, menu);
    438 
    439 		OptionsMenuItem* fg = new OptionsMenuItem("Max gate length");
    440 		fg->addItem(OptionMenuItem("Clock interval", [m]() { return !m->_fixedGate; }, [m]() { m->_fixedGate = false; }));
    441 		fg->addItem(OptionMenuItem("Fixed (500ms)", [m]() { return m->_fixedGate; }, [m]() { m->_fixedGate = true; }));
    442 		OptionsMenuItem::addToMenu(fg, menu);
    443 	}
    444 };
    445 
    446 Model* modelArp = createModel<Arp, ArpWidget>("Bogaudio-Arp", "ARP", "Polyphonic-input arpeggiator", "Arpeggiator", "Polyphonic");