diff --git a/.gitmodules b/.gitmodules index bd30e37..65909e9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "eurorack"] path = eurorack - url = https://github.com/pichenettes/eurorack.git + url = https://github.com/AndrewBelt/eurorack.git diff --git a/Makefile b/Makefile index 2c2ca1b..2c75b28 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,10 @@ SOURCES = $(wildcard src/*.cpp) \ eurorack/warps/dsp/oscillator.cc \ eurorack/warps/dsp/vocoder.cc \ eurorack/warps/dsp/filter_bank.cc \ - eurorack/warps/resources.cc + eurorack/warps/resources.cc \ + eurorack/frames/keyframer.cc \ + eurorack/frames/resources.cc \ + eurorack/frames/poly_lfo.cc include ../../plugin.mk diff --git a/eurorack b/eurorack index d19d52a..8eb95d9 160000 --- a/eurorack +++ b/eurorack @@ -1 +1 @@ -Subproject commit d19d52a2db8d8cf53e1d789d19c75197efbe365a +Subproject commit 8eb95d9783bb5953a95c37647bd7270b11d6d09e diff --git a/res/Frames.png b/res/Frames.png index c0f52e8..0e0e5b7 100644 Binary files a/res/Frames.png and b/res/Frames.png differ diff --git a/src/AudibleInstruments.cpp b/src/AudibleInstruments.cpp index ad05ea2..c31d275 100644 --- a/src/AudibleInstruments.cpp +++ b/src/AudibleInstruments.cpp @@ -22,5 +22,5 @@ void init(rack::Plugin *p) { createModel(plugin, "Branches", "Bernoulli Gate"); createModel(plugin, "Blinds", "Quad VC-polarizer"); createModel(plugin, "Veils", "Quad VCA"); - // createModel(plugin, "Frames", "Keyframer"); + // createModel(plugin, "Frames", "Keyframer/Mixer"); } diff --git a/src/AudibleInstruments.hpp b/src/AudibleInstruments.hpp index 8de182c..d7e1aff 100644 --- a/src/AudibleInstruments.hpp +++ b/src/AudibleInstruments.hpp @@ -70,4 +70,5 @@ struct VeilsWidget : ModuleWidget { struct FramesWidget : ModuleWidget { FramesWidget(); + Menu *createContextMenu(); }; diff --git a/src/Braids.cpp b/src/Braids.cpp index 88ebc10..0884ebf 100644 --- a/src/Braids.cpp +++ b/src/Braids.cpp @@ -304,32 +304,14 @@ struct BraidsSettingItem : MenuItem { Menu *BraidsWidget::createContextMenu() { Menu *menu = ModuleWidget::createContextMenu(); - MenuLabel *spacerLabel = new MenuLabel(); - menu->pushChild(spacerLabel); - - MenuLabel *optionsLabel = new MenuLabel(); - optionsLabel->text = "Options"; - menu->pushChild(optionsLabel); - Braids *braids = dynamic_cast(module); assert(braids); - BraidsSettingItem *metaItem = new BraidsSettingItem(); - metaItem->text = "META"; - metaItem->setting = &braids->settings.meta_modulation; - menu->pushChild(metaItem); - - BraidsSettingItem *drftItem = new BraidsSettingItem(); - drftItem->text = "DRFT"; - drftItem->setting = &braids->settings.vco_drift; - drftItem->onValue = 4; - menu->pushChild(drftItem); - - BraidsSettingItem *signItem = new BraidsSettingItem(); - signItem->text = "SIGN"; - signItem->setting = &braids->settings.signature; - signItem->onValue = 4; - menu->pushChild(signItem); + menu->pushChild(construct()); + menu->pushChild(construct(&MenuEntry::text, "Options")); + menu->pushChild(construct(&MenuEntry::text, "META", &BraidsSettingItem::setting, &braids->settings.meta_modulation)); + menu->pushChild(construct(&MenuEntry::text, "DRFT", &BraidsSettingItem::setting, &braids->settings.vco_drift, &BraidsSettingItem::onValue, 4)); + menu->pushChild(construct(&MenuEntry::text, "SIGN", &BraidsSettingItem::setting, &braids->settings.signature, &BraidsSettingItem::onValue, 4)); return menu; } diff --git a/src/Elements.cpp b/src/Elements.cpp index 54bb011..7cf1b78 100644 --- a/src/Elements.cpp +++ b/src/Elements.cpp @@ -297,33 +297,14 @@ struct ElementsModalItem : MenuItem { Menu *ElementsWidget::createContextMenu() { Menu *menu = ModuleWidget::createContextMenu(); - MenuLabel *spacerLabel = new MenuLabel(); - menu->pushChild(spacerLabel); - Elements *elements = dynamic_cast(module); assert(elements); - MenuLabel *modeLabel = new MenuLabel(); - modeLabel->text = "Alternative Models"; - menu->pushChild(modeLabel); - - ElementsModalItem *originalItem = new ElementsModalItem(); - originalItem->text = "Original"; - originalItem->elements = elements; - originalItem->model = 0; - menu->pushChild(originalItem); - - ElementsModalItem *stringItem = new ElementsModalItem(); - stringItem->text = "Non-linear string"; - stringItem->elements = elements; - stringItem->model = 1; - menu->pushChild(stringItem); - - ElementsModalItem *chordsItem = new ElementsModalItem(); - chordsItem->text = "Chords"; - chordsItem->elements = elements; - chordsItem->model = 2; - menu->pushChild(chordsItem); + menu->pushChild(construct()); + menu->pushChild(construct(&MenuEntry::text, "Alternative Models")); + menu->pushChild(construct(&MenuEntry::text, "Original", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 0)); + menu->pushChild(construct(&MenuEntry::text, "Non-Linear String", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 1)); + menu->pushChild(construct(&MenuEntry::text, "Chords", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 2)); return menu; } diff --git a/src/Frames.cpp b/src/Frames.cpp index fd4267e..54f2044 100644 --- a/src/Frames.cpp +++ b/src/Frames.cpp @@ -1,5 +1,6 @@ #include "AudibleInstruments.hpp" #include +#include "frames/keyframer.h" struct Frames : Module { @@ -34,14 +35,82 @@ struct Frames : Module { NUM_OUTPUTS }; - float lights[1] = {}; + float lights[6] = {}; + float color[3] = {}; + frames::Keyframer keyframer; + SchmittTrigger addTrigger; + SchmittTrigger delTrigger; + bool clearKeyframes = false; - Frames() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} + Frames(); void step(); }; +Frames::Frames() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { + memset(&keyframer, 0, sizeof(keyframer)); + keyframer.Init(); +} + + void Frames::step() { + if (clearKeyframes) { + keyframer.Clear(); + clearKeyframes = false; + } + + for (int i = 0; i < 4; i++) { + keyframer.set_immediate(i, params[GAIN1_PARAM + i].value * 65535.0); + } + + int32_t frame = (params[FRAME_PARAM].value + inputs[FRAME_INPUT].value) * 65535.0; + frame = clampi(frame, 0, 65535); + int16_t nearestFrame = keyframer.FindNearestKeyframe(frame, 2048); + printf("%d\n", nearestFrame); + + if (addTrigger.process(params[ADD_PARAM].value)) { + uint16_t f[4] = {65000, 30000, 20000, 10000}; + keyframer.AddKeyframe(frame, f); + } + if (delTrigger.process(params[DEL_PARAM].value)) { + keyframer.RemoveKeyframe(frame); + } + keyframer.Evaluate(frame); + + float gains[4]; + for (int i = 0; i < 4; i++) { + float lin = keyframer.level(i) / 65535.0; + gains[i] = lin; + } + printf("%f %f %f %f\n", gains[0], gains[1], gains[2], gains[3]); + + // Get inputs + float all = ((int)params[OFFSET_PARAM].value == 1) ? 10.0 : 0.0; + if (inputs[ALL_INPUT].active) + all = inputs[ALL_INPUT].value; + + // TODO multiply by correct gains + float ins[4]; + for (int i = 0; i < 4; i++) + ins[i] = inputs[IN1_INPUT + i].normalize(all) * gains[i]; + + // Set outputs + float mix = 0.0; + + for (int i = 0; i < 4; i++) { + if (outputs[OUT1_OUTPUT + i].active) + outputs[OUT1_OUTPUT + i].value = ins[i]; + else + mix += ins[i]; + } + + outputs[MIX_OUTPUT].value = clampf(mix / 2.0, -10.0, 10.0); + + for (int i = 0; i < 4; i++) + lights[i] = gains[i]; + + for (int c = 0; c < 3; c++) + color[c] = keyframer.color(c) / 255.0; } @@ -49,25 +118,11 @@ struct FramesLight : ValueLight { FramesLight() { box.size = Vec(67, 67); } - void step() { - // TODO Set these to Frames' actual colors - static NVGcolor colors[9] = { - nvgHSL(0.5, 0.3, 0.85), - nvgHSL(0.6, 0.3, 0.85), - nvgHSL(0.7, 0.3, 0.85), - nvgHSL(0.8, 0.3, 0.85), - nvgHSL(0.9, 0.3, 0.85), - nvgHSL(0.0, 0.3, 0.85), - nvgHSL(0.1, 0.3, 0.85), - nvgHSL(0.2, 0.3, 0.85), - nvgHSL(0.3, 0.3, 0.85), - }; - int i = clampi((int) *value, 0, 7); - NVGcolor color0 = colors[i]; - NVGcolor color1 = colors[i + 1]; - float p = fmodf(*value, 1.0); - color = nvgLerpRGBA(color0, color1, p); + color = nvgRGBf( + clampf(value[0] * 4, 0.0, 1.0), + clampf(value[1] * 4, 0.0, 1.0), + clampf(value[2] * 4, 0.0, 1.0)); } }; @@ -93,22 +148,78 @@ FramesWidget::FramesWidget() { addParam(createParam(Vec(81, 52), module, Frames::GAIN2_PARAM, 0.0, 1.0, 0.5)); addParam(createParam(Vec(149, 52), module, Frames::GAIN3_PARAM, 0.0, 1.0, 0.5)); addParam(createParam(Vec(216, 52), module, Frames::GAIN4_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(90, 117), module, Frames::FRAME_PARAM, 0.0, 63.0, 0.0)); + addParam(createParam(Vec(91, 117), module, Frames::FRAME_PARAM, 0.0, 63.0, 0.0)); addParam(createParam(Vec(208, 141), module, Frames::MODULATION_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(19, 123), module, Frames::ADD_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(19, 172), module, Frames::DEL_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(21, 235), module, Frames::OFFSET_PARAM, 0.0, 1.0, 0.0)); + + addInput(createInput(Vec(12, 271), module, Frames::ALL_INPUT)); + addInput(createInput(Vec(55, 271), module, Frames::IN1_INPUT)); + addInput(createInput(Vec(98, 271), module, Frames::IN2_INPUT)); + addInput(createInput(Vec(141, 271), module, Frames::IN3_INPUT)); + addInput(createInput(Vec(184, 271), module, Frames::IN4_INPUT)); + addInput(createInput(Vec(227, 271), module, Frames::FRAME_INPUT)); + + addOutput(createOutput(Vec(12, 313), module, Frames::MIX_OUTPUT)); + addOutput(createOutput(Vec(55, 313), module, Frames::OUT1_OUTPUT)); + addOutput(createOutput(Vec(98, 313), module, Frames::OUT2_OUTPUT)); + addOutput(createOutput(Vec(141, 313), module, Frames::OUT3_OUTPUT)); + addOutput(createOutput(Vec(184, 313), module, Frames::OUT4_OUTPUT)); + addOutput(createOutput(Vec(227, 313), module, Frames::FRAME_STEP_OUTPUT)); + + addChild(createValueLight>(Vec(30, 101), &module->lights[0])); + addChild(createValueLight>(Vec(97, 101), &module->lights[1])); + addChild(createValueLight>(Vec(165, 101), &module->lights[2])); + addChild(createValueLight>(Vec(232, 101), &module->lights[3])); + addChild(createValueLight>(Vec(61, 155), &module->lights[4])); + addChild(createValueLight(Vec(102, 128), module->color)); +} + + +struct FramesChannelSettingsItem : MenuItem { + Frames *frames; + int channel; + Menu *createChildMenu() { + Menu *menu = new Menu(); + + // TODO + menu->pushChild(construct(&MenuEntry::text, "Interpolation Curve")); + menu->pushChild(construct(&MenuEntry::text, "Step")); + menu->pushChild(construct(&MenuEntry::text, "Linear")); + menu->pushChild(construct(&MenuEntry::text, "Accelerating")); + menu->pushChild(construct(&MenuEntry::text, "Decelerating")); + menu->pushChild(construct(&MenuEntry::text, "Smooth Departure/Arrival")); + menu->pushChild(construct(&MenuEntry::text, "Bouncing")); + menu->pushChild(construct()); + menu->pushChild(construct(&MenuEntry::text, "Response Curve")); + menu->pushChild(construct(&MenuEntry::text, "Linear")); + menu->pushChild(construct(&MenuEntry::text, "Exponential")); + + return menu; + } +}; + +struct FramesClearItem : MenuItem { + Frames *frames; + void onAction() { + frames->clearKeyframes = true; + } +}; + + +Menu *FramesWidget::createContextMenu() { + Menu *menu = ModuleWidget::createContextMenu(); + + Frames *frames = dynamic_cast(module); + assert(frames); + + menu->pushChild(construct()); + menu->pushChild(construct(&MenuEntry::text, "Channel Settings")); + for (int i = 0; i < 4; i++) { + menu->pushChild(construct(&MenuItem::text, stringf("Channel %d", i + 1), &FramesChannelSettingsItem::frames, frames, &FramesChannelSettingsItem::channel, i)); + } + menu->pushChild(construct(&MenuItem::text, "Clear Keyframes", &FramesClearItem::frames, frames)); - addInput(createInput(Vec(12, 269), module, Frames::ALL_INPUT)); - addInput(createInput(Vec(55, 269), module, Frames::IN1_INPUT)); - addInput(createInput(Vec(98, 269), module, Frames::IN2_INPUT)); - addInput(createInput(Vec(141, 269), module, Frames::IN3_INPUT)); - addInput(createInput(Vec(184, 269), module, Frames::IN4_INPUT)); - addInput(createInput(Vec(227, 269), module, Frames::FRAME_INPUT)); - - addOutput(createOutput(Vec(12, 312), module, Frames::MIX_OUTPUT)); - addOutput(createOutput(Vec(55, 312), module, Frames::OUT1_OUTPUT)); - addOutput(createOutput(Vec(98, 312), module, Frames::OUT2_OUTPUT)); - addOutput(createOutput(Vec(141, 312), module, Frames::OUT3_OUTPUT)); - addOutput(createOutput(Vec(184, 312), module, Frames::OUT4_OUTPUT)); - addOutput(createOutput(Vec(227, 312), module, Frames::FRAME_STEP_OUTPUT)); - - addChild(createValueLight(Vec(101, 128), &module->lights[0])); + return menu; } diff --git a/src/Tides.cpp b/src/Tides.cpp index 5d58718..2ad477f 100644 --- a/src/Tides.cpp +++ b/src/Tides.cpp @@ -1,7 +1,5 @@ #include "AudibleInstruments.hpp" #include -// Disable encapsulation. Might not be ABI compatible, but if it works then it works. -#define private public #include "tides/generator.h" @@ -133,24 +131,7 @@ void Tides::step() { generator.set_sync(inputs[CLOCK_INPUT].active); // Generator - // Stolen code from the private `generator.Process()` method - while (generator.render_block_ != generator.playback_block_) { - uint8_t* in = generator.input_samples_[generator.render_block_]; - tides::GeneratorSample* out = generator.output_samples_[generator.render_block_]; - if (!wavetableHack) { - if (generator.range_ == tides::GENERATOR_RANGE_HIGH) { - generator.ProcessAudioRate(in, out, tides::kBlockSize); - } - else { - generator.ProcessControlRate(in, out, tides::kBlockSize); - } - generator.ProcessFilterWavefolder(out, tides::kBlockSize); - } - else { - generator.ProcessWavetable(in, out, tides::kBlockSize); - } - generator.render_block_ = (generator.render_block_ + 1) % tides::kNumBlocks; - } + generator.Process(wavetableHack); } // Level