#if 0 #include #include #include "core.hpp" #include "MidiIO.hpp" struct CCValue { int val = 0; // Controller value TransitionSmoother tSmooth; int num = 0; // Controller number bool numInited = false; // Num inited by config file bool numSelected = false; // Text field selected for midi learn bool changed = false; // Value has been changed by midi message (only if it is in sync!) int sync = 0; // Output value sync (implies diff) bool syncFirst = true; // First value after sync was reset void resetSync() { sync = 0; syncFirst = true; } }; struct MIDICCToCVInterface : MidiIO, Module { enum ParamIds { NUM_PARAMS }; enum InputIds { NUM_INPUTS }; enum OutputIds { NUM_OUTPUTS = 16 }; enum LightIds { NUM_LIGHTS = 16 }; CCValue cc[NUM_OUTPUTS]; MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { for (int i = 0; i < NUM_OUTPUTS; i++) { cc[i].num = i; } } ~MIDICCToCVInterface() {} void step() override; void processMidi(std::vector msg); void resetMidi() override; json_t *toJson() override { json_t *rootJ = json_object(); addBaseJson(rootJ); for (int i = 0; i < NUM_OUTPUTS; i++) { json_object_set_new(rootJ, ("ccNum" + std::to_string(i)).c_str(), json_integer(cc[i].num)); if (outputs[i].active) { json_object_set_new(rootJ, ("ccVal" + std::to_string(i)).c_str(), json_integer(cc[i].val)); } } return rootJ; } void fromJson(json_t *rootJ) override { baseFromJson(rootJ); for (int i = 0; i < NUM_OUTPUTS; i++) { json_t *ccNumJ = json_object_get(rootJ, ("ccNum" + std::to_string(i)).c_str()); if (ccNumJ) { cc[i].num = json_integer_value(ccNumJ); cc[i].numInited = true; } json_t *ccValJ = json_object_get(rootJ, ("ccVal" + std::to_string(i)).c_str()); if (ccValJ) { cc[i].val = json_integer_value(ccValJ); cc[i].tSmooth.set((cc[i].val / 127.0 * 10.0), (cc[i].val / 127.0 * 10.0)); cc[i].resetSync(); } } } void onReset() override { resetMidi(); } }; void MIDICCToCVInterface::step() { if (isPortOpen()) { std::vector message; // midiIn->getMessage returns empty vector if there are no messages in the queue getMessage(&message); if (message.size() > 0) { processMidi(message); } } for (int i = 0; i < NUM_OUTPUTS; i++) { lights[i].setBrightness(cc[i].sync / 127.0); if (cc[i].changed) { cc[i].tSmooth.set(outputs[i].value, (cc[i].val / 127.0 * 10.0), int(engineGetSampleRate() / 32)); cc[i].changed = false; } outputs[i].value = cc[i].tSmooth.next(); } } void MIDICCToCVInterface::resetMidi() { for (int i = 0; i < NUM_OUTPUTS; i++) { cc[i].val = 0; cc[i].resetSync(); cc[i].tSmooth.set(0, 0); } }; void MIDICCToCVInterface::processMidi(std::vector msg) { int channel = msg[0] & 0xf; int status = (msg[0] >> 4) & 0xf; int data1 = msg[1]; int data2 = msg[2]; //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2); // Filter channels if (this->channel >= 0 && this->channel != channel) return; if (status == 0xb) { for (int i = 0; i < NUM_OUTPUTS; i++) { if (cc[i].numSelected) { cc[i].resetSync(); cc[i].num = data1; } if (data1 == cc[i].num) { /* If the first value we received after sync was reset is +/- 1 of * the output value the values are in sync*/ if (cc[i].syncFirst) { cc[i].syncFirst = false; if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { cc[i].sync = 0; } else { cc[i].sync = absi(data2 - cc[i].val); } return; } if (cc[i].sync == 0) { cc[i].val = data2; cc[i].changed = true; } else { cc[i].sync = absi(data2 - cc[i].val); } } } } } struct CCTextField : TextField { void onTextChange() override; void draw(NVGcontext *vg) override; void onMouseDown(EventMouseDown &e) override; void onMouseUp(EventMouseUp &e) override; void onMouseLeave(EventMouseLeave &e) override; int outNum; MIDICCToCVInterface *module; }; void CCTextField::draw(NVGcontext *vg) { /* This is necessary, since the save * file is loaded after constructing the widget*/ if (module->cc[outNum].numInited) { module->cc[outNum].numInited = false; text = std::to_string(module->cc[outNum].num); } /* If number is selected for midi learn*/ if (module->cc[outNum].numSelected) { text = std::to_string(module->cc[outNum].num); } TextField::draw(vg); } void CCTextField::onMouseUp(EventMouseUp &e) { if (e.button == 1) { module->cc[outNum].numSelected = false; e.consumed = true; } TextField::onMouseUp(e); } void CCTextField::onMouseDown(EventMouseDown &e) { if (e.button == 1) { module->cc[outNum].numSelected = true; e.consumed = true; } TextField::onMouseDown(e); } void CCTextField::onMouseLeave(EventMouseLeave &e) { module->cc[outNum].numSelected = false; e.consumed = true; } void CCTextField::onTextChange() { if (text.size() > 0) { try { int num = std::stoi(text); // Only allow valid cc numbers if (num < 0 || num > 127 || text.size() > 3) { text = ""; begin = end = 0; module->cc[outNum].num = -1; } else { module->cc[outNum].num = num; module->cc[outNum].resetSync(); } } catch (...) { text = ""; begin = end = 0; module->cc[outNum].num = -1; } }; } MIDICCToCVWidget::MIDICCToCVWidget() { MIDICCToCVInterface *module = new MIDICCToCVInterface(); setModule(module); box.size = Vec(16 * 15, 380); { Panel *panel = new LightPanel(); panel->box.size = box.size; addChild(panel); } float margin = 5; float labelHeight = 15; float yPos = margin; addChild(createScrew(Vec(15, 0))); addChild(createScrew(Vec(box.size.x - 30, 0))); addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x - 30, 365))); { Label *label = new Label(); label->box.pos = Vec(box.size.x - margin - 11 * 15, margin); label->text = "MIDI CC to CV"; addChild(label); yPos = labelHeight * 2; } { Label *label = new Label(); label->box.pos = Vec(margin, yPos); label->text = "MIDI Interface"; addChild(label); MidiChoice *midiChoice = new MidiChoice(); midiChoice->midiModule = dynamic_cast(module); midiChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos); midiChoice->box.size.x = (box.size.x / 2.0) - margin; addChild(midiChoice); yPos += midiChoice->box.size.y + margin; } { Label *label = new Label(); label->box.pos = Vec(margin, yPos); label->text = "Channel"; addChild(label); ChannelChoice *channelChoice = new ChannelChoice(); channelChoice->midiModule = dynamic_cast(module); channelChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos); channelChoice->box.size.x = (box.size.x / 2.0) - margin; addChild(channelChoice); yPos += channelChoice->box.size.y + margin * 3; } for (int i = 0; i < MIDICCToCVInterface::NUM_OUTPUTS; i++) { CCTextField *ccNumChoice = new CCTextField(); ccNumChoice->module = module; ccNumChoice->outNum = i; ccNumChoice->text = std::to_string(module->cc[i].num); ccNumChoice->box.pos = Vec(11 + (i % 4) * (63), yPos); ccNumChoice->box.size.x = 29; addChild(ccNumChoice); yPos += labelHeight + margin; addOutput(createOutput(Vec((i % 4) * (63) + 10, yPos + 5), module, i)); addChild(createLight>(Vec((i % 4) * (63) + 32, yPos + 5), module, i)); if ((i + 1) % 4 == 0) { yPos += 47 + margin; } else { yPos -= labelHeight + margin; } } } void MIDICCToCVWidget::step() { ModuleWidget::step(); } #endif