diff --git a/eurorack b/eurorack index 8eb95d9..eb6c8e9 160000 --- a/eurorack +++ b/eurorack @@ -1 +1 @@ -Subproject commit 8eb95d9783bb5953a95c37647bd7270b11d6d09e +Subproject commit eb6c8e9a5728a109ebe559aa78c4023be0b710e8 diff --git a/res/CKSS_rot_0.svg b/res/CKSS_rot_0.svg new file mode 100644 index 0000000..eb0f38c --- /dev/null +++ b/res/CKSS_rot_0.svg @@ -0,0 +1,114 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/CKSS_rot_1.svg b/res/CKSS_rot_1.svg new file mode 100644 index 0000000..bbeffaa --- /dev/null +++ b/res/CKSS_rot_1.svg @@ -0,0 +1,120 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AudibleInstruments.cpp b/src/AudibleInstruments.cpp index c31d275..1bd04f7 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/Mixer"); + createModel(plugin, "Frames", "Keyframer/Mixer"); } diff --git a/src/Frames.cpp b/src/Frames.cpp index 54f2044..e124f13 100644 --- a/src/Frames.cpp +++ b/src/Frames.cpp @@ -1,6 +1,7 @@ #include "AudibleInstruments.hpp" #include #include "frames/keyframer.h" +#include "frames/poly_lfo.h" struct Frames : Module { @@ -32,12 +33,16 @@ struct Frames : Module { OUT3_OUTPUT, OUT4_OUTPUT, FRAME_STEP_OUTPUT, - NUM_OUTPUTS + GAIN1_LIGHT, + EDIT_LIGHT = GAIN1_LIGHT + 4, + FRAME_LIGHT, + NUM_OUTPUTS = FRAME_LIGHT + 3 }; - float lights[6] = {}; - float color[3] = {}; frames::Keyframer keyframer; + frames::PolyLfo poly_lfo; + bool poly_lfo_mode = true; + SchmittTrigger addTrigger; SchmittTrigger delTrigger; bool clearKeyframes = false; @@ -50,46 +55,69 @@ struct Frames : Module { Frames::Frames() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { memset(&keyframer, 0, sizeof(keyframer)); keyframer.Init(); + memset(&poly_lfo, 0, sizeof(poly_lfo)); + poly_lfo.Init(); } void Frames::step() { + // Handle buttons if (clearKeyframes) { keyframer.Clear(); clearKeyframes = false; } - for (int i = 0; i < 4; i++) { - keyframer.set_immediate(i, params[GAIN1_PARAM + i].value * 65535.0); + // Set gain knobs + if (poly_lfo_mode) { + poly_lfo.set_shape(params[GAIN1_PARAM].value * 65535.0); + poly_lfo.set_shape_spread(params[GAIN2_PARAM].value * 65535.0); + poly_lfo.set_spread(params[GAIN3_PARAM].value * 65535.0); + poly_lfo.set_coupling(params[GAIN4_PARAM].value * 65535.0); + } + else { + 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); + int32_t frame = clampf(params[FRAME_PARAM].value + params[MODULATION_PARAM].value * inputs[FRAME_INPUT].value / 10.0, 0.0, 1.0) * 65535.0; - if (addTrigger.process(params[ADD_PARAM].value)) { - uint16_t f[4] = {65000, 30000, 20000, 10000}; - keyframer.AddKeyframe(frame, f); + // Render, handle buttons + if (poly_lfo_mode) { + poly_lfo.Render(frame); } - if (delTrigger.process(params[DEL_PARAM].value)) { - keyframer.RemoveKeyframe(frame); + else { + int16_t nearestFrame = keyframer.FindNearestKeyframe(frame, 2048); + + 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); } - keyframer.Evaluate(frame); + // Get gains float gains[4]; for (int i = 0; i < 4; i++) { - float lin = keyframer.level(i) / 65535.0; - gains[i] = lin; + if (poly_lfo_mode) { + // gains[i] = poly_lfo.level(i) / 255.0; + gains[i] = poly_lfo.level16(i) / 65535.0; + } + else { + float lin = keyframer.level(i) / 65535.0; + gains[i] = lin; + } } - printf("%f %f %f %f\n", gains[0], gains[1], gains[2], gains[3]); + // 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]; @@ -106,23 +134,55 @@ void Frames::step() { outputs[MIX_OUTPUT].value = clampf(mix / 2.0, -10.0, 10.0); + // Set lights for (int i = 0; i < 4; i++) - lights[i] = gains[i]; + outputs[GAIN1_LIGHT + i].value = gains[i]; + + if (poly_lfo_mode) { + outputs[EDIT_LIGHT].value = (poly_lfo.level(0) > 128 ? 1.0 : 0.0); + } + else { + outputs[EDIT_LIGHT].value = 1.0; + } + // Set frame light colors + const uint8_t *colors; + if (poly_lfo_mode) + colors = poly_lfo.color(); + else + colors = keyframer.color(); for (int c = 0; c < 3; c++) - color[c] = keyframer.color(c) / 255.0; + outputs[FRAME_LIGHT + c].value = colors[c] / 255.0; } -struct FramesLight : ValueLight { +struct FramesLight : Light { + float *colors[3]; FramesLight() { box.size = Vec(67, 67); } void step() { - 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)); + const NVGcolor red = COLOR_RED; + const NVGcolor green = COLOR_GREEN; + const NVGcolor blue = COLOR_BLUE; + float r = *colors[0]; + float g = *colors[1]; + float b = *colors[2]; + color.r = red.r * r + green.r * g + blue.r * b; + color.g = red.g * r + green.g * g + blue.g * b; + color.b = red.b * r + green.b * g + blue.b * b; + color.a = 1.0; + // Lighten + color = nvgLerpRGBA(color, COLOR_WHITE, 0.5); + } +}; + +struct CKSSRot : SVGSwitch, ToggleSwitch { + CKSSRot() { + addFrame(SVG::load(assetPlugin(plugin, "res/CKSS_rot_0.svg"))); + addFrame(SVG::load(assetPlugin(plugin, "res/CKSS_rot_1.svg"))); + sw->wrap(); + box.size = sw->box.size; } }; @@ -148,11 +208,11 @@ 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(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(91, 117), module, Frames::FRAME_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(208, 141), module, Frames::MODULATION_PARAM, -1.0, 1.0, 0.0)); 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)); + addParam(createParam(Vec(18, 239), 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)); @@ -168,12 +228,19 @@ FramesWidget::FramesWidget() { 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)); + addChild(createValueLight>(Vec(30, 101), &module->outputs[Frames::GAIN1_LIGHT + 0].value)); + addChild(createValueLight>(Vec(97, 101), &module->outputs[Frames::GAIN1_LIGHT + 1].value)); + addChild(createValueLight>(Vec(165, 101), &module->outputs[Frames::GAIN1_LIGHT + 2].value)); + addChild(createValueLight>(Vec(232, 101), &module->outputs[Frames::GAIN1_LIGHT + 3].value)); + addChild(createValueLight>(Vec(61, 155), &module->outputs[Frames::EDIT_LIGHT].value)); + { + FramesLight *framesLight = new FramesLight(); + framesLight->box.pos = Vec(102, 128); + framesLight->colors[0] = &module->outputs[Frames::FRAME_LIGHT + 0].value; + framesLight->colors[1] = &module->outputs[Frames::FRAME_LIGHT + 1].value; + framesLight->colors[2] = &module->outputs[Frames::FRAME_LIGHT + 2].value; + addChild(framesLight); + } } @@ -207,6 +274,17 @@ struct FramesClearItem : MenuItem { } }; +struct FramesModeItem : MenuItem { + Frames *frames; + bool poly_lfo_mode; + void onAction() { + frames->poly_lfo_mode = poly_lfo_mode; + } + void step() { + rightText = (frames->poly_lfo_mode == poly_lfo_mode) ? "✔" : ""; + } +}; + Menu *FramesWidget::createContextMenu() { Menu *menu = ModuleWidget::createContextMenu(); @@ -221,5 +299,10 @@ Menu *FramesWidget::createContextMenu() { } menu->pushChild(construct(&MenuItem::text, "Clear Keyframes", &FramesClearItem::frames, frames)); + menu->pushChild(construct()); + menu->pushChild(construct(&MenuEntry::text, "Mode")); + menu->pushChild(construct(&MenuItem::text, "Keyframer", &FramesModeItem::frames, frames, &FramesModeItem::poly_lfo_mode, false)); + menu->pushChild(construct(&MenuItem::text, "Poly LFO", &FramesModeItem::frames, frames, &FramesModeItem::poly_lfo_mode, true)); + return menu; }