From 6953cbf1cd341b616bc336dd9083d1b734b10e9f Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 11 Oct 2017 14:05:55 -0400 Subject: [PATCH] Implement normal Frames mode --- src/Frames.cpp | 154 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 43 deletions(-) diff --git a/src/Frames.cpp b/src/Frames.cpp index e124f13..e5657c9 100644 --- a/src/Frames.cpp +++ b/src/Frames.cpp @@ -42,6 +42,7 @@ struct Frames : Module { frames::Keyframer keyframer; frames::PolyLfo poly_lfo; bool poly_lfo_mode = true; + uint16_t lastControls[4] = {}; SchmittTrigger addTrigger; SchmittTrigger delTrigger; @@ -49,6 +50,18 @@ struct Frames : Module { Frames(); void step(); + + json_t *toJson() { + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "polyLfo", json_boolean(poly_lfo_mode)); + return rootJ; + } + + void fromJson(json_t *rootJ) { + json_t *polyLfoJ = json_object_get(rootJ, "polyLfo"); + if (polyLfoJ) + poly_lfo_mode = json_boolean_value(polyLfoJ); + } }; @@ -57,46 +70,64 @@ Frames::Frames() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { keyframer.Init(); memset(&poly_lfo, 0, sizeof(poly_lfo)); poly_lfo.Init(); + + for (int i = 0; i < 4; i++) { + keyframer.mutable_settings(i)->easing_curve = frames::EASING_CURVE_LINEAR; + } } void Frames::step() { - // Handle buttons - if (clearKeyframes) { - keyframer.Clear(); - clearKeyframes = false; + // Set gain and timestamp knobs + uint16_t controls[4]; + for (int i = 0; i < 4; i++) { + controls[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 timestamp = clampf(params[FRAME_PARAM].value + params[MODULATION_PARAM].value * inputs[FRAME_INPUT].value / 10.0, 0.0, 1.0) * 65535.0; + int16_t nearestIndex = -1; + if (!poly_lfo_mode) { + nearestIndex = keyframer.FindNearestKeyframe(timestamp, 2048); } - int32_t frame = clampf(params[FRAME_PARAM].value + params[MODULATION_PARAM].value * inputs[FRAME_INPUT].value / 10.0, 0.0, 1.0) * 65535.0; - // Render, handle buttons if (poly_lfo_mode) { - poly_lfo.Render(frame); + if (controls[0] != lastControls[0]) + poly_lfo.set_shape(controls[0]); + if (controls[1] != lastControls[1]) + poly_lfo.set_shape_spread(controls[1]); + if (controls[2] != lastControls[2]) + poly_lfo.set_spread(controls[2]); + if (controls[3] != lastControls[3]) + poly_lfo.set_coupling(controls[3]); + poly_lfo.Render(timestamp); } else { - int16_t nearestFrame = keyframer.FindNearestKeyframe(frame, 2048); + for (int i = 0; i < 4; i++) { + if (controls[i] != lastControls[i]) { + // Update recently moved control + if (keyframer.num_keyframes() == 0) { + keyframer.set_immediate(i, controls[i]); + } + if (nearestIndex >= 0) { + frames::Keyframe *nearestKeyframe = keyframer.mutable_keyframe(nearestIndex); + nearestKeyframe->values[i] = controls[i]; + } + } + } if (addTrigger.process(params[ADD_PARAM].value)) { - uint16_t f[4] = {65000, 30000, 20000, 10000}; - keyframer.AddKeyframe(frame, f); + if (nearestIndex < 0) { + keyframer.AddKeyframe(timestamp, controls); + } } if (delTrigger.process(params[DEL_PARAM].value)) { - keyframer.RemoveKeyframe(frame); + if (nearestIndex >= 0) { + int32_t nearestTimestamp = keyframer.keyframe(nearestIndex).timestamp; + keyframer.RemoveKeyframe(nearestTimestamp); + } } - keyframer.Evaluate(frame); + keyframer.Evaluate(timestamp); } // Get gains @@ -111,48 +142,61 @@ void Frames::step() { gains[i] = lin; } } - // printf("%f %f %f %f\n", gains[0], gains[1], gains[2], gains[3]); + + // Update last controls + for (int i = 0; i < 4; i++) { + lastControls[i] = controls[i]; + } // Get inputs float all = ((int)params[OFFSET_PARAM].value == 1) ? 10.0 : 0.0; - if (inputs[ALL_INPUT].active) + if (inputs[ALL_INPUT].active) { all = inputs[ALL_INPUT].value; + } float ins[4]; - for (int i = 0; i < 4; i++) + 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) + if (outputs[OUT1_OUTPUT + i].active) { outputs[OUT1_OUTPUT + i].value = ins[i]; - else + } + else { mix += ins[i]; + } } outputs[MIX_OUTPUT].value = clampf(mix / 2.0, -10.0, 10.0); // Set lights - for (int i = 0; i < 4; i++) + for (int i = 0; i < 4; 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; + // TODO + outputs[EDIT_LIGHT].value = (nearestIndex >= 0 ? 1.0 : 0.0); } // Set frame light colors const uint8_t *colors; - if (poly_lfo_mode) + if (poly_lfo_mode) { colors = poly_lfo.color(); - else + } + else { colors = keyframer.color(); - for (int c = 0; c < 3; c++) + } + for (int c = 0; c < 3; c++) { outputs[FRAME_LIGHT + c].value = colors[c] / 255.0; + } } @@ -244,24 +288,48 @@ FramesWidget::FramesWidget() { } +struct FramesCurveItem : MenuItem { + Frames *frames; + uint8_t channel; + frames::EasingCurve curve; + void onAction() { + frames->keyframer.mutable_settings(channel)->easing_curve = curve; + } + void step() { + rightText = (frames->keyframer.mutable_settings(channel)->easing_curve == curve) ? "✔" : ""; + } +}; + +struct FramesResponseItem : MenuItem { + Frames *frames; + uint8_t channel; + uint8_t response; + void onAction() { + frames->keyframer.mutable_settings(channel)->response = response; + } + void step() { + rightText = (frames->keyframer.mutable_settings(channel)->response = response) ? "✔" : ""; + } +}; + struct FramesChannelSettingsItem : MenuItem { Frames *frames; - int channel; + uint8_t 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(&MenuEntry::text, "Step", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_STEP)); + menu->pushChild(construct(&MenuEntry::text, "Linear", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_LINEAR)); + menu->pushChild(construct(&MenuEntry::text, "Accelerating", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_IN_QUARTIC)); + menu->pushChild(construct(&MenuEntry::text, "Decelerating", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_OUT_QUARTIC)); + menu->pushChild(construct(&MenuEntry::text, "Smooth Departure/Arrival", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_SINE)); + menu->pushChild(construct(&MenuEntry::text, "Bouncing", &FramesCurveItem::frames, frames, &FramesCurveItem::channel, channel, &FramesCurveItem::curve, frames::EASING_CURVE_BOUNCE)); menu->pushChild(construct()); menu->pushChild(construct(&MenuEntry::text, "Response Curve")); - menu->pushChild(construct(&MenuEntry::text, "Linear")); - menu->pushChild(construct(&MenuEntry::text, "Exponential")); + menu->pushChild(construct(&MenuEntry::text, "Linear", &FramesResponseItem::frames, frames, &FramesResponseItem::channel, channel, &FramesResponseItem::response, 0)); + menu->pushChild(construct(&MenuEntry::text, "Exponential", &FramesResponseItem::frames, frames, &FramesResponseItem::channel, channel, &FramesResponseItem::response, 1)); return menu; } @@ -270,7 +338,7 @@ struct FramesChannelSettingsItem : MenuItem { struct FramesClearItem : MenuItem { Frames *frames; void onAction() { - frames->clearKeyframes = true; + frames->keyframer.Clear(); } };