| @@ -11,3 +11,54 @@ extern Model *modelMIDICCToCVInterface; | |||||
| extern Model *modelMIDITriggerToCVInterface; | extern Model *modelMIDITriggerToCVInterface; | ||||
| extern Model *modelBlank; | extern Model *modelBlank; | ||||
| extern Model *modelNotes; | extern Model *modelNotes; | ||||
| struct GridChoice : LedDisplayChoice { | |||||
| virtual void setId(int id) {} | |||||
| }; | |||||
| struct Grid16MidiWidget : MidiWidget { | |||||
| LedDisplaySeparator *hSeparators[4]; | |||||
| LedDisplaySeparator *vSeparators[4]; | |||||
| GridChoice *gridChoices[4][4]; | |||||
| void createGridChoices() { | |||||
| Vec pos = channelChoice->box.getBottomLeft(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(vSeparators[x]); | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(hSeparators[y]); | |||||
| for (int x = 0; x < 4; x++) { | |||||
| GridChoice *gridChoice = createGridChoice(); | |||||
| assert(gridChoice); | |||||
| gridChoice->box.pos = pos; | |||||
| gridChoice->setId(4*y + x); | |||||
| gridChoices[x][y] = gridChoice; | |||||
| addChild(gridChoice); | |||||
| } | |||||
| pos = gridChoices[0][y]->box.getBottomLeft(); | |||||
| } | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.size.y = pos.y - vSeparators[x]->box.pos.y; | |||||
| } | |||||
| } | |||||
| void step() override { | |||||
| MidiWidget::step(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y]->box.size.x = box.size.x; | |||||
| for (int x = 0; x < 4; x++) { | |||||
| gridChoices[x][y]->box.size.x = box.size.x / 4; | |||||
| gridChoices[x][y]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| } | |||||
| } | |||||
| virtual GridChoice *createGridChoice() {return NULL;} | |||||
| }; | |||||
| @@ -3,57 +3,6 @@ | |||||
| #include "dsp/filter.hpp" | #include "dsp/filter.hpp" | ||||
| struct CcChoice : LedDisplayChoice { | |||||
| CcChoice() { | |||||
| box.size.y = mm2px(6.666); | |||||
| textOffset.y -= 4; | |||||
| } | |||||
| }; | |||||
| struct CcMidiWidget : MidiWidget { | |||||
| LedDisplaySeparator *hSeparators[4]; | |||||
| LedDisplaySeparator *vSeparators[4]; | |||||
| LedDisplayChoice *ccChoices[4][4]; | |||||
| CcMidiWidget() { | |||||
| Vec pos = channelChoice->box.getBottomLeft(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(vSeparators[x]); | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(hSeparators[y]); | |||||
| for (int x = 0; x < 4; x++) { | |||||
| CcChoice *ccChoice = Widget::create<CcChoice>(pos); | |||||
| ccChoice->text = stringf("%d", x*4+y); | |||||
| ccChoices[x][y] = ccChoice; | |||||
| addChild(ccChoice); | |||||
| } | |||||
| pos = ccChoices[0][y]->box.getBottomLeft(); | |||||
| } | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.size.y = pos.y - vSeparators[x]->box.pos.y; | |||||
| } | |||||
| } | |||||
| void step() override { | |||||
| MidiWidget::step(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y]->box.size.x = box.size.x; | |||||
| for (int x = 0; x < 4; x++) { | |||||
| ccChoices[x][y]->box.size.x = box.size.x / 4; | |||||
| ccChoices[x][y]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct MIDICCToCVInterface : Module { | struct MIDICCToCVInterface : Module { | ||||
| enum ParamIds { | enum ParamIds { | ||||
| NUM_PARAMS | NUM_PARAMS | ||||
| @@ -70,10 +19,23 @@ struct MIDICCToCVInterface : Module { | |||||
| }; | }; | ||||
| MidiInputQueue midiInput; | MidiInputQueue midiInput; | ||||
| uint8_t cvs[128] = {}; | |||||
| uint8_t cvs[16]; | |||||
| ExponentialFilter ccFilters[16]; | ExponentialFilter ccFilters[16]; | ||||
| MIDICCToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||||
| int learningId = -1; | |||||
| uint8_t learnedCcs[16] = {}; | |||||
| MIDICCToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
| onReset(); | |||||
| } | |||||
| void onReset() override { | |||||
| for (int i = 0; i < 16; i++) { | |||||
| cvs[i] = 0; | |||||
| learnedCcs[i] = i; | |||||
| } | |||||
| learningId = -1; | |||||
| } | |||||
| void step() override { | void step() override { | ||||
| MidiMessage msg; | MidiMessage msg; | ||||
| @@ -90,11 +52,20 @@ struct MIDICCToCVInterface : Module { | |||||
| } | } | ||||
| void processMessage(MidiMessage msg) { | void processMessage(MidiMessage msg) { | ||||
| debug("MIDI: %01x %01x %02x %02x", msg.status(), msg.channel(), msg.data1, msg.data2); | |||||
| switch (msg.status()) { | switch (msg.status()) { | ||||
| // cc | // cc | ||||
| case 0xb: { | case 0xb: { | ||||
| cvs[msg.note()] = msg.value(); | |||||
| uint8_t cc = msg.note(); | |||||
| // Learn | |||||
| if (learningId >= 0) { | |||||
| learnedCcs[learningId] = cc; | |||||
| } | |||||
| // Set CV | |||||
| for (int i = 0; i < 16; i++) { | |||||
| if (learnedCcs[i] == cc) { | |||||
| cvs[i] = msg.value(); | |||||
| } | |||||
| } | |||||
| } break; | } break; | ||||
| default: break; | default: break; | ||||
| } | } | ||||
| @@ -113,6 +84,51 @@ struct MIDICCToCVInterface : Module { | |||||
| }; | }; | ||||
| struct MidiCcChoice : GridChoice { | |||||
| MIDICCToCVInterface *module; | |||||
| int id; | |||||
| MidiCcChoice() { | |||||
| box.size.y = mm2px(6.666); | |||||
| textOffset.y -= 4; | |||||
| } | |||||
| void setId(int id) override { | |||||
| this->id = id; | |||||
| } | |||||
| void step() override { | |||||
| if (module->learningId == id) { | |||||
| text = "LRN"; | |||||
| color.a = 0.5; | |||||
| } | |||||
| else { | |||||
| text = stringf("%d", id); | |||||
| color.a = 1.0; | |||||
| } | |||||
| } | |||||
| void onFocus(EventFocus &e) override { | |||||
| e.consumed = true; | |||||
| module->learningId = id; | |||||
| } | |||||
| void onDefocus(EventDefocus &e) override { | |||||
| module->learningId = -1; | |||||
| } | |||||
| }; | |||||
| struct MidiCcWidget : Grid16MidiWidget { | |||||
| MIDICCToCVInterface *module; | |||||
| GridChoice *createGridChoice() override { | |||||
| MidiCcChoice *gridChoice = new MidiCcChoice(); | |||||
| gridChoice->module = module; | |||||
| return gridChoice; | |||||
| } | |||||
| }; | |||||
| struct MIDICCToCVInterfaceWidget : ModuleWidget { | struct MIDICCToCVInterfaceWidget : ModuleWidget { | ||||
| MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) { | MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) { | ||||
| setPanel(SVG::load(assetGlobal("res/Core/MIDICCToCVInterface.svg"))); | setPanel(SVG::load(assetGlobal("res/Core/MIDICCToCVInterface.svg"))); | ||||
| @@ -139,11 +155,12 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget { | |||||
| addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14)); | addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14)); | ||||
| addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15)); | addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15)); | ||||
| MidiWidget *midiWidget = Widget::create<CcMidiWidget>(mm2px(Vec(3.399621, 14.837339))); | |||||
| MidiCcWidget *midiWidget = Widget::create<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339))); | |||||
| midiWidget->module = module; | |||||
| midiWidget->box.size = mm2px(Vec(44, 54.667)); | midiWidget->box.size = mm2px(Vec(44, 54.667)); | ||||
| midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
| midiWidget->createGridChoices(); | |||||
| addChild(midiWidget); | addChild(midiWidget); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -1,57 +1,5 @@ | |||||
| #include "Core.hpp" | #include "Core.hpp" | ||||
| #include "midi.hpp" | #include "midi.hpp" | ||||
| #include "dsp/filter.hpp" | |||||
| struct CcChoice : LedDisplayChoice { | |||||
| CcChoice() { | |||||
| box.size.y = mm2px(6.666); | |||||
| textOffset.y -= 4; | |||||
| } | |||||
| }; | |||||
| struct CcMidiWidget : MidiWidget { | |||||
| LedDisplaySeparator *hSeparators[4]; | |||||
| LedDisplaySeparator *vSeparators[4]; | |||||
| LedDisplayChoice *ccChoices[4][4]; | |||||
| CcMidiWidget() { | |||||
| Vec pos = channelChoice->box.getBottomLeft(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(vSeparators[x]); | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y] = Widget::create<LedDisplaySeparator>(pos); | |||||
| addChild(hSeparators[y]); | |||||
| for (int x = 0; x < 4; x++) { | |||||
| CcChoice *ccChoice = Widget::create<CcChoice>(pos); | |||||
| ccChoice->text = stringf("%d", x*4+y); | |||||
| ccChoices[x][y] = ccChoice; | |||||
| addChild(ccChoice); | |||||
| } | |||||
| pos = ccChoices[0][y]->box.getBottomLeft(); | |||||
| } | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.size.y = pos.y - vSeparators[x]->box.pos.y; | |||||
| } | |||||
| } | |||||
| void step() override { | |||||
| MidiWidget::step(); | |||||
| for (int x = 1; x < 4; x++) { | |||||
| vSeparators[x]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| for (int y = 0; y < 4; y++) { | |||||
| hSeparators[y]->box.size.x = box.size.x; | |||||
| for (int x = 0; x < 4; x++) { | |||||
| ccChoices[x][y]->box.size.x = box.size.x / 4; | |||||
| ccChoices[x][y]->box.pos.x = box.size.x / 4 * x; | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct MIDITriggerToCVInterface : Module { | struct MIDITriggerToCVInterface : Module { | ||||
| @@ -73,6 +21,8 @@ struct MIDITriggerToCVInterface : Module { | |||||
| bool gates[16]; | bool gates[16]; | ||||
| float gateTimes[16]; | float gateTimes[16]; | ||||
| int learningId = -1; | |||||
| uint8_t learnedNotes[16] = {}; | |||||
| MIDITriggerToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | MIDITriggerToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | ||||
| onReset(); | onReset(); | ||||
| @@ -82,26 +32,32 @@ struct MIDITriggerToCVInterface : Module { | |||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| gates[i] = false; | gates[i] = false; | ||||
| gateTimes[i] = 0.f; | gateTimes[i] = 0.f; | ||||
| learnedNotes[i] = i + 36; | |||||
| } | } | ||||
| learningId = -1; | |||||
| } | } | ||||
| void pressNote(uint8_t note) { | void pressNote(uint8_t note) { | ||||
| // TEMP | |||||
| if (note >= 16) | |||||
| return; | |||||
| int i = note; | |||||
| gates[i] = true; | |||||
| gateTimes[i] = 1e-3f; | |||||
| // Learn | |||||
| if (learningId >= 0) { | |||||
| learnedNotes[learningId] = note; | |||||
| } | |||||
| // Find id | |||||
| for (int i = 0; i < 16; i++) { | |||||
| if (learnedNotes[i] == note) { | |||||
| gates[i] = true; | |||||
| gateTimes[i] = 1e-3f; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void releaseNote(uint8_t note) { | void releaseNote(uint8_t note) { | ||||
| // TEMP | |||||
| if (note >= 16) | |||||
| return; | |||||
| int i = note; | |||||
| gates[i] = false; | |||||
| // Find id | |||||
| for (int i = 0; i < 16; i++) { | |||||
| if (learnedNotes[i] == note) { | |||||
| gates[i] = false; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| @@ -158,6 +114,58 @@ struct MIDITriggerToCVInterface : Module { | |||||
| }; | }; | ||||
| struct MidiTrigChoice : GridChoice { | |||||
| MIDITriggerToCVInterface *module; | |||||
| int id; | |||||
| MidiTrigChoice() { | |||||
| box.size.y = mm2px(6.666); | |||||
| textOffset.y -= 4; | |||||
| textOffset.x -= 4; | |||||
| } | |||||
| void setId(int id) override { | |||||
| this->id = id; | |||||
| } | |||||
| void step() override { | |||||
| if (module->learningId == id) { | |||||
| text = "LRN"; | |||||
| color.a = 0.5; | |||||
| } | |||||
| else { | |||||
| uint8_t note = module->learnedNotes[id]; | |||||
| static const char *noteNames[] = { | |||||
| "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" | |||||
| }; | |||||
| int oct = note / 12 - 1; | |||||
| int semi = note % 12; | |||||
| text = stringf("%s%d", noteNames[semi], oct); | |||||
| color.a = 1.0; | |||||
| } | |||||
| } | |||||
| void onFocus(EventFocus &e) override { | |||||
| e.consumed = true; | |||||
| module->learningId = id; | |||||
| } | |||||
| void onDefocus(EventDefocus &e) override { | |||||
| module->learningId = -1; | |||||
| } | |||||
| }; | |||||
| struct MidiTrigWidget : Grid16MidiWidget { | |||||
| MIDITriggerToCVInterface *module; | |||||
| GridChoice *createGridChoice() override { | |||||
| MidiTrigChoice *gridChoice = new MidiTrigChoice(); | |||||
| gridChoice->module = module; | |||||
| return gridChoice; | |||||
| } | |||||
| }; | |||||
| struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | ||||
| MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) { | MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) { | ||||
| setPanel(SVG::load(assetGlobal("res/Core/MIDITriggerToCVInterface.svg"))); | setPanel(SVG::load(assetGlobal("res/Core/MIDITriggerToCVInterface.svg"))); | ||||
| @@ -184,9 +192,11 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||||
| addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14)); | addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14)); | ||||
| addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15)); | addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15)); | ||||
| MidiWidget *midiWidget = Widget::create<CcMidiWidget>(mm2px(Vec(3.399621, 14.837339))); | |||||
| MidiTrigWidget *midiWidget = Widget::create<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339))); | |||||
| midiWidget->module = module; | |||||
| midiWidget->box.size = mm2px(Vec(44, 54.667)); | midiWidget->box.size = mm2px(Vec(44, 54.667)); | ||||
| midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
| midiWidget->createGridChoices(); | |||||
| addChild(midiWidget); | addChild(midiWidget); | ||||
| } | } | ||||
| @@ -31,7 +31,6 @@ void TextField::draw(NVGcontext *vg) { | |||||
| } | } | ||||
| void TextField::onMouseDown(EventMouseDown &e) { | void TextField::onMouseDown(EventMouseDown &e) { | ||||
| debug("%d", this == gFocusedWidget); | |||||
| if (e.button == 0) { | if (e.button == 0) { | ||||
| cursor = selection = getTextPosition(e.pos); | cursor = selection = getTextPosition(e.pos); | ||||
| } | } | ||||