#include "Southpole.hpp" #include "dsp/digital.hpp" namespace rack_plugin_Southpole { #define NSNAKEBUSS 16 #define NSNAKEPORTS 10 struct Snake : Module { enum ParamIds { PLUS_PARAM, MINUS_PARAM, NUM_PARAMS }; enum InputIds { IN_INPUT, NUM_INPUTS = IN_INPUT + NSNAKEPORTS + 1 }; enum OutputIds { OUT_OUTPUT, NUM_OUTPUTS = OUT_OUTPUT + NSNAKEPORTS + 1 }; enum LightIds { LOCK_LIGHT, NUM_LIGHTS = LOCK_LIGHT + 2*NSNAKEPORTS + 2 }; static int nsnakes; static float cable[NSNAKEBUSS][NSNAKEPORTS]; static int lockid[NSNAKEBUSS][NSNAKEPORTS]; int buss = 0; int id; SchmittTrigger plusTrigger; SchmittTrigger minusTrigger; Snake() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { // first Snake module instantiation if (nsnakes == 0) { //printf("initialize Snake system\n"); for (int b=0; b< NSNAKEBUSS; b++) { for (int i=0; i< NSNAKEPORTS; i++) { cable[b][i] = 0.; lockid[b][i] = 0; } } } nsnakes++; buss = 0; id = nsnakes; //dump("constructor"); } ~Snake() { // clean up for (int i=0; i < NSNAKEPORTS; i++) { if ( lockid[buss][i] == id ) { lockid[buss][i] = 0; cable[buss][i] = 0; } } nsnakes--; //dump("destructor"); } void step() override; json_t *toJson() override { json_t *rootJ = json_object(); json_object_set_new(rootJ, "buss", json_integer(buss)); return rootJ; } void fromJson(json_t *rootJ) override { json_t *bussJ = json_object_get(rootJ, "buss"); if (bussJ) { buss = json_integer_value(bussJ); } //dump("fromJson"); } void dump( const char * where="" ) { printf( "%p [%s] (%d) buss %d: id %d, lockid: [ ", this, where, nsnakes, buss, id ); for (int i=0; i< NSNAKEPORTS; i++) { printf("%d, ", lockid[buss][i]); } printf(" ] %f %f\n", params[PLUS_PARAM].value, params[MINUS_PARAM].value); } }; int Snake::nsnakes = 0; float Snake::cable[NSNAKEBUSS][NSNAKEPORTS]; int Snake::lockid[NSNAKEBUSS][NSNAKEPORTS]; void Snake::step() { // change buss on trigger if (plusTrigger.process(params[PLUS_PARAM].value)) { if (buss < NSNAKEBUSS-1) { // free and clean up current buss for (int i=0; i < NSNAKEPORTS; i++) { if ( lockid[buss][i] == id ) { lockid[buss][i] = 0; cable[buss][i] = 0; } } buss++; //dump("plus"); } } if (minusTrigger.process(params[MINUS_PARAM].value)) { if (buss > 0) { // free and clean up current buss for (int i=0; i < NSNAKEPORTS; i++) { if ( lockid[buss][i] == id ) { lockid[buss][i] = 0; cable[buss][i] = 0; } } buss--; //dump("minus"); } } for (int i=0; i < NSNAKEPORTS; i++) { // if active try to lock input if ( inputs[IN_INPUT+i].active ) { if ( lockid[buss][i] == 0 ) { lockid[buss][i] = id; //dump("lock"); } if ( lockid[buss][i] == id ) { cable[buss][i] = inputs[IN_INPUT+i].value; } } else if ( lockid[buss][i] == id ) { lockid[buss][i] = 0; cable[buss][i] = 0; //dump("release"); } // operate lights if ( lockid[buss][i] == 0 ) { lights[LOCK_LIGHT+2*i ].setBrightness(0); lights[LOCK_LIGHT+2*i+1].setBrightness(0); } else if ( lockid[buss][i] == id ) { lights[LOCK_LIGHT+2*i ].setBrightness(1.0); lights[LOCK_LIGHT+2*i+1].setBrightness(0.); } else { lights[LOCK_LIGHT+2*i ].setBrightness(0.); lights[LOCK_LIGHT+2*i+1].setBrightness(1.); } // set output outputs[OUT_OUTPUT+i].value = cable[buss][i]; } } struct SnakeDisplay : TransparentWidget { Snake *module; std::shared_ptr font; SnakeDisplay() { font = Font::load(assetPlugin(plugin, "res/hdad-segment14-1.002/Segment14.ttf")); } void draw(NVGcontext *vg) override { // Background NVGcolor backgroundColor = nvgRGB(0x30, 0x10, 0x10); NVGcolor borderColor = nvgRGB(0xd0, 0xd0, 0xd0); nvgBeginPath(vg); nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0); nvgFillColor(vg, backgroundColor); nvgFill(vg); nvgStrokeWidth(vg, 1.5); nvgStrokeColor(vg, borderColor); nvgStroke(vg); nvgFontSize(vg, 20.); nvgFontFaceId(vg, font->handle); nvgTextLetterSpacing(vg, 2.); Vec textPos = Vec(5, 28); NVGcolor textColor = nvgRGB(0xff, 0x00, 0x00); nvgFillColor(vg, nvgTransRGBA(textColor, 16)); nvgText(vg, textPos.x, textPos.y, "~~~~", NULL); nvgFillColor(vg, textColor); char strbuss[4]; sprintf(strbuss,"%1x",module->buss); nvgText(vg, textPos.x, textPos.y, strbuss, NULL); } }; struct SnakeWidget : ModuleWidget { SnakeWidget(Snake *module) : ModuleWidget(module) { box.size = Vec(15*4, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/Snake.svg"))); addChild(panel); } { SnakeDisplay *display = new SnakeDisplay(); display->box.pos = Vec(5., 30.); display->box.size = Vec(25., 34.); display->module = module; addChild(display); } addParam(ParamWidget::create(Vec( 40, 30 ), module, Snake::PLUS_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec( 40, 50 ), module, Snake::MINUS_PARAM, 0.0, 1.0, 0.0)); float y1 = 85; float yh = 26; for (int i=0; i< NSNAKEPORTS; i++) { float y = y1+i*yh + floor(i/5)*yh*.4; addInput(Port::create( Vec( 5, y), Port::INPUT, module, Snake::IN_INPUT + i)); addOutput(Port::create(Vec(34, y), Port::OUTPUT, module, Snake::OUT_OUTPUT + i)); addChild(ModuleLightWidget::create>(Vec(26, y), module, Snake::LOCK_LIGHT + 2*i)); } } }; } // namespace rack_plugin_Southpole using namespace rack_plugin_Southpole; RACK_PLUGIN_MODEL_INIT(Southpole, Snake) { Model *modelSnake = Model::create( "Southpole", "Snake", "Snake - multicore", UTILITY_TAG); return modelSnake; }