ComputerscareBlankExpander.cpp (8183B)
1 #include "Computerscare.hpp" 2 #include "CustomBlankFunctions.hpp" 3 4 struct ComputerscareBlankExpander; 5 6 const std::string clockModeDescriptions[3] = {"Sync\nAnimation will synchronize to a steady clock signal", "Scan\nAnimation will linearly follow a 0-10v CV. 0v → frame 1, 10v → last frame", "Frame Advance\nClock signal will advance the animation by 1 frame" }; 7 8 9 struct FrameOffsetParam : ParamQuantity { 10 int numFrames = -1; 11 void setNumFrames(int num) { numFrames = num; } 12 std::string getDisplayValueString() override { 13 float val = getValue(); 14 return string::f("%i", 1 + mapBlankFrameOffset(val, numFrames)); 15 } 16 }; 17 18 19 //template <const std::string& options> 20 struct ClockModeParamQuantity : ParamQuantity { 21 std::string getDisplayValueString() override { 22 int val = getValue(); 23 return clockModeDescriptions[val]; 24 } 25 }; 26 27 28 29 struct ComputerscareBlankExpander : Module { 30 float rightMessages[2][11] = {}; 31 bool motherConnected = false; 32 float lastFrame = -1; 33 int numFrames = 1; 34 bool scrubbing = false; 35 int lastTick = -1; 36 37 38 enum ParamIds { 39 CLOCK_MODE, 40 MANUAL_RESET_BUTTON, 41 ZERO_OFFSET, 42 MANUAL_NEXT_FILE_BUTTON, 43 NUM_PARAMS 44 }; 45 enum InputIds { 46 SYNC_INPUT, 47 RESET_INPUT, 48 NEXT_FILE_INPUT, 49 NUM_INPUTS 50 }; 51 enum OutputIds { 52 EOC_OUTPUT, 53 EACH_FRAME_OUTPUT, 54 NUM_OUTPUTS 55 }; 56 enum LightIds { 57 NUM_LIGHTS 58 }; 59 60 dsp::SchmittTrigger eocMessageReadTrigger; 61 dsp::SchmittTrigger eachFrameReadTrigger; 62 63 dsp::SchmittTrigger syncTrigger; 64 65 dsp::PulseGenerator eocPulse; 66 dsp::PulseGenerator eachFramePulse; 67 68 dsp::Timer syncTimer; 69 70 FrameOffsetParam* frameOffsetQuantity; 71 72 ComputerscareBlankExpander() { 73 74 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 75 configParam<ClockModeParamQuantity>(CLOCK_MODE, 0.f, 2.f, 0.f, "Clock Mode"); 76 configParam(MANUAL_RESET_BUTTON, 0.f, 1.f, 0.f, "Manual Reset"); 77 configParam<FrameOffsetParam>(ZERO_OFFSET, 0.f, 0.999f, 0.f, "EOC / Reset Frame #"); 78 configParam(MANUAL_NEXT_FILE_BUTTON, 0.f, 1.f, 0.f, "Next File (see right click menu of mother for options)"); 79 80 configInput(SYNC_INPUT, "Sync"); 81 configInput(RESET_INPUT, "Reset"); 82 configInput(NEXT_FILE_INPUT, "Next Slideshow File"); 83 configOutput(EOC_OUTPUT, "End of Animation"); 84 configOutput(EACH_FRAME_OUTPUT, "Frame Change"); 85 86 frameOffsetQuantity = dynamic_cast<FrameOffsetParam*>(paramQuantities[ZERO_OFFSET]); 87 88 rightExpander.producerMessage = rightMessages[0]; 89 rightExpander.consumerMessage = rightMessages[1]; 90 } 91 void process(const ProcessArgs &args) override { 92 if (rightExpander.module && rightExpander.module->model == modelComputerscareBlank) { 93 // Get consumer message 94 float *messageFromMother = (float*) rightExpander.consumerMessage; 95 motherConnected = true; 96 97 float *messageToSendToMother = (float*) rightExpander.module->leftExpander.producerMessage; 98 99 100 float currentFrame = messageFromMother[0]; 101 int newNumFrames = messageFromMother[1]; 102 int mappedFrame = messageFromMother[2]; 103 int scrubFrame = messageFromMother[3]; 104 int tick = messageFromMother[4]; 105 106 107 if (newNumFrames != numFrames) { 108 numFrames = newNumFrames; 109 frameOffsetQuantity->setNumFrames(numFrames); 110 } 111 112 113 114 if (eocMessageReadTrigger.process(currentFrame == 0 ? 10.f : 0.f)) { 115 eocPulse.trigger(1e-3f); 116 } 117 if (eachFrameReadTrigger.process(lastTick != tick ? 10.f : 0.f)) { 118 eachFramePulse.trigger(1e-3f); 119 } 120 121 122 messageToSendToMother[0] = params[CLOCK_MODE].getValue(); 123 124 messageToSendToMother[1] = inputs[SYNC_INPUT].isConnected(); 125 messageToSendToMother[2] = inputs[SYNC_INPUT].getVoltage(); 126 127 messageToSendToMother[3] = inputs[RESET_INPUT].isConnected(); 128 messageToSendToMother[4] = inputs[RESET_INPUT].getVoltage(); 129 130 messageToSendToMother[5] = inputs[NEXT_FILE_INPUT].isConnected(); 131 messageToSendToMother[6] = inputs[NEXT_FILE_INPUT].getVoltage();; 132 133 messageToSendToMother[7] = params[ZERO_OFFSET].getValue(); 134 135 messageToSendToMother[8] = scrubbing; 136 137 messageToSendToMother[9] = params[MANUAL_RESET_BUTTON].getValue() * 10; 138 139 messageToSendToMother[10] = params[MANUAL_NEXT_FILE_BUTTON].getValue() * 10; 140 141 142 outputs[EOC_OUTPUT].setVoltage(eocPulse.process(args.sampleTime) ? 10.f : 0.f); 143 outputs[EACH_FRAME_OUTPUT].setVoltage(eachFramePulse.process(args.sampleTime) ? 10.f : 0.f); 144 145 rightExpander.module->leftExpander.messageFlipRequested = true; 146 lastFrame = currentFrame; 147 lastTick = tick; 148 } 149 else { 150 motherConnected = false; 151 // No mother module is connected. 152 } 153 } 154 void setScrubbing(bool scrub) { 155 scrubbing = scrub; 156 } 157 }; 158 struct FrameScrubKnob : SmallKnob { 159 ComputerscareBlankExpander* module; 160 void onDragStart(const event::DragStart& e) override { 161 module->setScrubbing(true); 162 SmallKnob::onDragStart(e); 163 } 164 void onDragEnd(const event::DragEnd& e) override { 165 module->setScrubbing(false); 166 SmallKnob::onDragEnd(e); 167 } 168 void onDragMove(const event::DragMove& e) override { 169 SmallKnob::onDragMove(e); 170 }; 171 }; 172 struct ClockModeButton : app::SvgSwitch { 173 ClockModeButton() { 174 shadow->opacity = 0.f; 175 //momentary = true; 176 addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/blank-clock-mode-sync.svg"))); 177 addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/blank-clock-mode-scan.svg"))); 178 addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/blank-clock-mode-frame.svg"))); 179 } 180 }; 181 struct LogoWidget : SvgWidget { 182 ComputerscareBlankExpander *module; 183 int motherConnected = -1; 184 LogoWidget() { 185 setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/computerscare-logo-normal.svg"))); 186 SvgWidget(); 187 } 188 void step() override { 189 if (module) { 190 if (module->motherConnected != motherConnected) { 191 if (module->motherConnected) { 192 setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/computerscare-logo-normal.svg"))); 193 } else { 194 setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/computerscare-logo-sad.svg"))); 195 } 196 } 197 motherConnected = module->motherConnected; 198 } 199 } 200 }; 201 struct ComputerscareBlankExpanderWidget : ModuleWidget { 202 ComputerscareBlankExpanderWidget(ComputerscareBlankExpander *module) { 203 setModule(module); 204 box.size = Vec(30, 380); 205 { 206 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 207 panel->box.size = box.size; 208 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareCustomBlankExpanderPanel.svg"))); 209 addChild(panel); 210 } 211 212 LogoWidget *logo = new LogoWidget(); 213 logo->module = module; 214 addChild(logo); 215 216 float inStartY = 20; 217 float dY = 40; 218 219 float outStartY = 250; 220 221 addParam(createParam<ClockModeButton>(Vec(0.5, inStartY + .25 * dY), module, ComputerscareBlankExpander::CLOCK_MODE)); 222 addInput(createInput<InPort>(Vec(2, inStartY + 0.75 * dY), module, ComputerscareBlankExpander::SYNC_INPUT)); 223 224 addParam(createParam<ComputerscareResetButton>(Vec(0, inStartY + 1.75 * dY), module, ComputerscareBlankExpander::MANUAL_RESET_BUTTON)); 225 addInput(createInput<InPort>(Vec(2, inStartY + 2.25 * dY), module, ComputerscareBlankExpander::RESET_INPUT)); 226 227 addParam(createParam<ComputerscareNextButton>(Vec(0, inStartY + 3.25 * dY), module, ComputerscareBlankExpander::MANUAL_NEXT_FILE_BUTTON)); 228 addInput(createInput<InPort>(Vec(2, inStartY + 3.75 * dY), module, ComputerscareBlankExpander::NEXT_FILE_INPUT)); 229 230 addOutput(createOutput<PointingUpPentagonPort>(Vec(2, 236), module, ComputerscareBlankExpander::EACH_FRAME_OUTPUT)); 231 232 frameOffsetKnob = createParam<FrameScrubKnob>(Vec(6, 294), module, ComputerscareBlankExpander::ZERO_OFFSET); 233 frameOffsetKnob->module = module; 234 235 addParam(frameOffsetKnob); 236 addOutput(createOutput<PointingUpPentagonPort>(Vec(2, 320), module, ComputerscareBlankExpander::EOC_OUTPUT)); 237 } 238 void step() { 239 240 ModuleWidget::step(); 241 } 242 FrameScrubKnob* frameOffsetKnob; 243 }; 244 Model *modelComputerscareBlankExpander = createModel<ComputerscareBlankExpander, ComputerscareBlankExpanderWidget>("computerscare-blank-expander");