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 @@
+
+
+
+
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 @@
+
+
+
+
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;
}