commit a3153aa0e6920c4197fe84510405d49a4d31bb8a
parent 5ff69894cbe8d5d43987613b5a2dd4b7c03fe372
Author: Adam M <[email protected]>
Date: Tue, 29 Dec 2020 22:43:26 -0600
Add blank expander for cv control, fix gif animation jitter
Diffstat:
6 files changed, 261 insertions(+), 15 deletions(-)
diff --git a/plugin.json b/plugin.json
@@ -76,6 +76,11 @@
"description":"Customizable, resizable, lovable blank panel. \nLoad your own PNG, JPEG, BMP, or GIF.",
"tags":["Blank","Visual"]
},
+ {"slug":"computerscare-blank-expander",
+ "name":"Custom Blank Expander",
+ "description":"Allows CV Control of Computerscare Custom Blank GIF Animation",
+ "tags":["Blank","Visual"]
+ },
{"slug":"computerscare-stoly-fick-pigure",
"name":"Stoly Fick Pigure",
"description":"Draw a stick figure",
diff --git a/res/ComputerscareCustomBlankExpanderPanel.svg b/res/ComputerscareCustomBlankExpanderPanel.svg
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="30"
+ height="380"
+ viewBox="0 0 7.9374995 100.54167"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.0.1 (c497b03c, 2020-09-10)"
+ sodipodi:docname="ComputerscareCustomBlankExpanderPanel.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="18.625644"
+ inkscape:cy="329.86693"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="false"
+ inkscape:window-width="1252"
+ inkscape:window-height="855"
+ inkscape:window-x="0"
+ inkscape:window-y="23"
+ inkscape:window-maximized="0"
+ units="px" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ id="rect2605"
+ style="fill:#e7e7e7;fill-opacity:1;stroke:#171717;stroke-width:0.19605;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 0.11351532,0.11283527 H 7.999953 L 7.952706,100.43718 0.11351532,99.96175 Z"
+ sodipodi:nodetypes="ccccc" />
+ <g
+ id="g9547"
+ transform="matrix(0.34537853,-0.08593646,0,0.33853324,-3.4926401,0.35544723)"
+ style="display:inline;enable-background:new">
+ <g
+ style="display:inline"
+ inkscape:label="Layer 1"
+ id="layer4" />
+ <path
+ id="rect5872"
+ d="m 11.987289,9.3234482 h 7.991524 v 3.9957608 h -7.991524 z"
+ style="opacity:1;fill:#0f0f00;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5874"
+ d="m 14.667374,10.662096 h 3.995763 v 1.331921 h -3.995763 z"
+ style="opacity:1;fill:#ffffff;fill-opacity:0.985714;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5878"
+ d="m -29.323654,9.3234482 h 7.989551 v 3.9957608 h -7.989551 z"
+ style="opacity:1;fill:#0c0c00;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ transform="scale(-1,1)"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5880"
+ d="m 26.64423,10.662096 -3.171232,0.209056 v 1.331921 l 3.171232,-0.209056 z"
+ style="opacity:1;fill:#ffffff;fill-opacity:0.985714;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5882"
+ d="m 19.995058,14.667374 h 1.331921 v 3.995762 h -1.331921 z"
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5884"
+ d="m 20.888507,17.21991 2.579094,0.32072 v 1.33192 l -2.579094,-0.32072 z"
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5886"
+ d="m 11.954803,20.011301 h 2.696328 v 2.663842 h -2.696328 z"
+ style="opacity:1;fill:#000200;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5888"
+ d="m 27.970341,20.011301 h 2.696327 v 2.663842 h -2.696327 z"
+ style="opacity:1;fill:#000200;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect5890"
+ d="m 14.651131,20.011301 h 13.351695 v 1.33192 H 14.651131 Z"
+ style="opacity:1;fill:#000200;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/src/Computerscare.cpp b/src/Computerscare.cpp
@@ -20,6 +20,7 @@ void init(Plugin *p) {
p->addModel(modelComputerscareFolyPace);
p->addModel(modelComputerscareBlank);
+ p->addModel(modelComputerscareBlankExpander);
p->addModel(modelComputerscareStolyFickPigure);
p->addModel(modelComputerscareGolyPenerator);
diff --git a/src/Computerscare.hpp b/src/Computerscare.hpp
@@ -39,6 +39,7 @@ extern Model *modelComputerscareSolyPequencer;
extern Model *modelComputerscareFolyPace;
extern Model *modelComputerscareStolyFickPigure;
extern Model *modelComputerscareBlank;
+extern Model *modelComputerscareBlankExpander;
extern Model *modelComputerscareGolyPenerator;
extern Model *modelComputerscareMolyPatrix;
diff --git a/src/ComputerscareBlank.cpp b/src/ComputerscareBlank.cpp
@@ -31,6 +31,8 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
int numFrames = 0;
int stepCounter = 0;
float frameDelay = .5;
+ std::vector<float> frameDelays;
+
int samplesDelay = 10000;
int speed = 100000;
int imageStatus = 0;
@@ -48,9 +50,7 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
NUM_PARAMS
};
enum InputIds {
- NUM_INPUTS,
- CLOCK_INPUT,
- RESET_INPUT
+ NUM_INPUTS
};
enum OutputIds {
NUM_OUTPUTS
@@ -62,7 +62,7 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
ComputerscareBlank() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
- configMenuParam(ANIMATION_SPEED, 0.05f, 20.f, 1.0, "Animation Speed", 2,"x");
+ configMenuParam(ANIMATION_SPEED, 0.05f, 20.f, 1.0, "Animation Speed", 2, "x");
configParam(ANIMATION_ENABLED, 0.f, 1.f, 1.f, "Animation Enabled");
configParam(CONSTANT_FRAME_DELAY, 0.f, 1.f, 0.f, "Constant Frame Delay");
configMenuParam(END_BEHAVIOR, 0.f, 5.f, 0.f, "Animation End Behavior", 2);
@@ -79,8 +79,8 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
endBehaviorDescriptions.push_back("Load Next");
endBehaviorDescriptions.push_back("Load Previous");
-
- configMenuParam(ANIMATION_MODE, 0.f, "Animation Mode",animationModeDescriptions);
+
+ configMenuParam(ANIMATION_MODE, 0.f, "Animation Mode", animationModeDescriptions);
paths.push_back("empty");
@@ -98,6 +98,21 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
}
}
}
+ if (rightExpander.module && rightExpander.module->model == modelComputerscareBlankExpander) {
+ // Get message from right expander
+ float *message = (float*) rightExpander.module->leftExpander.producerMessage;
+ // Write message
+ /*for (int i = 0; i < 8; i++) {
+ message[i] = inputs[i].getVoltage() / 10.f;
+ }*/
+ if (stepCounter == 90) {
+
+
+ }
+ message[0] = (currentFrame == 0) ? 10.f : 0.f;
+ // Flip messages at the end of the timestep
+ rightExpander.module->leftExpander.messageFlipRequested = true;
+ }
}
void onReset() override {
zoomX = 1;
@@ -182,6 +197,9 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
}
}
}
+ void setFrameDelays(std::vector<float> frameDelaysSeconds) {
+ frameDelays = frameDelaysSeconds;
+ }
std::string getPath() {
//return numFrames > 0 ? paths[currentFrame] : "";
return paths[0];
@@ -193,12 +211,13 @@ struct ComputerscareBlank : ComputerscareMenuParamModule {
else {
prevFrame();
}
- if(currentFrame == 0) {
+ if (currentFrame == 0) {
int eb = params[END_BEHAVIOR].getValue();
- if(eb == 3 ) {
+ if (eb == 3 ) {
loadRandomGif();
}
}
+ setFrameDelay(frameDelays[currentFrame]);
}
void nextFrame() {
currentFrame++;
@@ -317,7 +336,7 @@ struct KeyboardControlChildMenu : MenuItem {
Menu *menu = new Menu;
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "A,S,D,F: Translate image position"));
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Z,X: Zoom in/out"));
- menu->addChild(construct<MenuLabel>(&MenuLabel::text, "J,L: Previous / next frame"));
+ menu->addChild(construct<MenuLabel>(&MenuLabel::text, "J,L: Previous / next frame"));
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "K: Go to first frame"));
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "I: Go to random frame"));
@@ -391,6 +410,7 @@ struct PNGDisplay : TransparentWidget {
img = gifBuddy.getHandle();
blankModule->setFrameCount(gifBuddy.getFrameCount());
+ blankModule->setFrameDelays(gifBuddy.getAllFrameDelaysSeconds());
blankModule->setFrameDelay(gifBuddy.getSecondsDelay(0));
blankModule->setImageStatus(gifBuddy.getImageStatus());
@@ -421,12 +441,20 @@ struct PNGDisplay : TransparentWidget {
nvgFill(args.vg);
nvgClosePath(args.vg);
}
+ //if (blankModule->currentFrame != currentFrame) {
+ gifBuddy.displayGifFrame(args.vg, currentFrame);
+ //}
+ }
+ }
+ void step() override {
+ if (blankModule && blankModule->loadedJSON) {
if (blankModule->currentFrame != currentFrame) {
currentFrame = blankModule->currentFrame;
- blankModule->setFrameDelay(gifBuddy.getSecondsDelay(currentFrame));
- gifBuddy.displayGifFrame(args.vg, currentFrame);
+ //blankModule->setFrameDelay(gifBuddy.getSecondsDelay(currentFrame));
+
}
}
+ TransparentWidget::step();
}
};
@@ -481,7 +509,7 @@ struct ComputerscareBlankWidget : MenuParamModuleWidget {
menu->addChild(new MenuEntry);
//menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Keyboard Controls:"));
-
+
KeyboardControlChildMenu *kbMenu = new KeyboardControlChildMenu();
kbMenu->text = "Keyboard Controls";
kbMenu->rightText = RIGHT_ARROW;
@@ -663,4 +691,89 @@ struct ComputerscareBlankWidget : MenuParamModuleWidget {
SmallLetterDisplay* smallLetterDisplay;
};
-Model *modelComputerscareBlank = createModel<ComputerscareBlank, ComputerscareBlankWidget>("computerscare-blank");
-\ No newline at end of file
+struct ComputerscareBlankExpander : Module {
+ float leftMessages[2][8] = {};
+ bool isConnected = false;
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ CLOCK_INPUT,
+ RESET_INPUT,
+ SCAN_INPUT,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ EOC_OUTPUT,
+ NUM_OUTPUTS
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ dsp::SchmittTrigger eocMessageReadTrigger;
+ dsp::PulseGenerator eocPulse;
+ dsp::PulseGenerator eachFramePulse;
+
+
+ ComputerscareBlankExpander() {
+
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+
+ leftExpander.producerMessage = leftMessages[0];
+ leftExpander.consumerMessage = leftMessages[1];
+ }
+
+ void process(const ProcessArgs &args) override {
+ if (leftExpander.module && leftExpander.module->model == modelComputerscareBlank) {
+ // Get consumer message
+ float *message = (float*) leftExpander.consumerMessage;
+ isConnected = true;
+
+ //eocMessageReadTrigger.process(message[0]);
+ if (eocMessageReadTrigger.process(message[0])) {
+ eocPulse.trigger(1e-3);
+ }
+ outputs[EOC_OUTPUT].setVoltage(eocPulse.process(args.sampleTime) ? 10.f : 0.f);
+ //DEBUG("HANG ON TO HIM MURPH!");
+ /*for (int i = 0; i < 8; i++) {
+ lights[i].setBrightness(message[i]);
+ }*/
+ }
+ else {
+ isConnected = false;
+ // No mother module is connected.
+ // TODO Clear the lights.
+ }
+ }
+};
+struct ComputerscareBlankExpanderWidget : ModuleWidget {
+ ComputerscareBlankExpanderWidget(ComputerscareBlankExpander *module) {
+ setModule(module);
+ box.size = Vec(30, 380);
+ {
+ ComputerscareSVGPanel *panel = new ComputerscareSVGPanel();
+ panel->box.size = box.size;
+ panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareCustomBlankExpanderPanel.svg")));
+ addChild(panel);
+ }
+
+ DEBUG("clock_input:%i", ComputerscareBlankExpander::CLOCK_INPUT);
+
+ float inStartY = 30;
+ float dY = 40;
+
+ float outStartY = 250;
+
+ addInput(createInput<InPort>(Vec(2, inStartY), module, ComputerscareBlankExpander::CLOCK_INPUT));
+ addInput(createInput<InPort>(Vec(2, inStartY + dY), module, ComputerscareBlankExpander::SCAN_INPUT));
+ addInput(createInput<InPort>(Vec(2, inStartY + 2 * dY), module, ComputerscareBlankExpander::RESET_INPUT));
+
+ addOutput(createOutput<PointingUpPentagonPort>(Vec(2, outStartY), module, ComputerscareBlankExpander::EOC_OUTPUT));
+
+
+ }
+};
+
+Model *modelComputerscareBlank = createModel<ComputerscareBlank, ComputerscareBlankWidget>("computerscare-blank");
+Model *modelComputerscareBlankExpander = createModel<ComputerscareBlankExpander, ComputerscareBlankExpanderWidget>("computerscare-blank-expander");
diff --git a/src/animatedGif.hpp b/src/animatedGif.hpp
@@ -146,6 +146,7 @@ STBIDEF unsigned char *stbi_xload(char const *filename, int *x, int *y, int *fra
struct AnimatedGifBuddy {
std::vector<unsigned char*> framePointers;
std::vector<int> frameDelays;
+ std::vector<float> frameDelaysSeconds;
int imageHandle;
bool initialized = false;
int numFrames = -1;
@@ -183,13 +184,14 @@ struct AnimatedGifBuddy {
printf("image status:%i\n", imageStatus);
return 0;
}
+ updateFrameDelaysSeconds();
image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
initialized = true;
return image;
}
void displayGifFrame(NVGcontext* ctx, int frameNumber) {
- if (initialized && frameNumber < numFrames && (imageStatus == 1 && numFrames > 0)) {
+ if (initialized && (frameNumber < numFrames) && (imageStatus == 1 && numFrames > 0)) {
const unsigned char* dataAtFrame = framePointers[frameNumber];
nvgUpdateImage(ctx, imageHandle, dataAtFrame);
}
@@ -207,4 +209,13 @@ struct AnimatedGifBuddy {
}
return secondsDelay;
}
+ void updateFrameDelaysSeconds() {
+ frameDelaysSeconds.resize(0);
+ for (unsigned int i = 0; i < frameDelays.size(); i++) {
+ frameDelaysSeconds.push_back( ((float) frameDelays[i]) / 100);
+ }
+ }
+ std::vector<float> getAllFrameDelaysSeconds() {
+ return frameDelaysSeconds;
+ }
};
\ No newline at end of file