#include "global_pre.hpp" #include "Erratic.hpp" #include "midi.hpp" #include "MPEBaseWidget.hpp" #include "global_ui.hpp" struct MidiValue { int val = 0; // Controller value // TransitionSmoother tSmooth; bool changed = false; // Value has been changed by midi message (only if it is in sync!) }; struct MPEPlusValue { uint16_t val = 0; // Controller value int MSB = 0 ; int LSB = 0; bool changed = false; // Value has been changed by midi message (only if it is in sync!) }; struct MidiPedalValue { int val = 0; // Controller value int cc ; // need to set it bool changed = false; // Value has been changed by midi message (only if it is in sync!) }; struct MPEToCV : Module { enum ParamIds { RESET_PARAM, NUM_PARAMS, BEND_RANGE_PARAM }; enum InputIds { NUM_INPUTS }; enum OutputIds { PITCH_OUTPUT = 0, GATE_OUTPUT, VELOCITY_OUTPUT, PRESSURE_OUTPUT, Y_OUTPUT, PEDAL_OUTPUT, NUM_OUTPUTS }; enum LightIds { RESET_LIGHT, VELOCITY_LIGHT, PRESSURE_LIGHT, Y_AXIS_LIGHT, PEDAL_LIGHT, NUM_LIGHTS }; MidiInputQueue midiInput; int bendRange = 48; // our default is 48 (common for ROLI), Continuum has 96. int channel = 2; // Our default channel is 2. ROLI users will want to set this is 2 int globalChannel = 16; // Our default channel is 16. ROLI users will want to set this is 1 bool MPEPlus = false ; // This is specially useful for Haken Continuum int YaxisCC = 74 ; bool newNote = false; std::list notes; bool pedal = false; int note = 60; // C4, most modules should use 261.626 Hz int vel = 0; MidiValue mod; MidiValue afterTouch; MidiValue pitchWheel; MidiValue Yaxis ; MidiPedalValue midiPedalOne ; // Used for MPE+ MPEPlusValue MPEPlusyAxis, MPEPluszAxis ; bool gate = false; // Reset note parameters when you release, ie, receive note off bool noteOffReset = true; bool resetNoteNow = false; // SchmittTrigger resetTrigger; MPEToCV(); // ~MPEToCV() { // }; void step() override; void pressNote(int note); void releaseNote(int note); void processMessage(MidiMessage msg); json_t *toJson() override { json_t *rootJ = json_object(); json_object_set_new(rootJ, "midi", midiInput.toJson()); json_object_set_new(rootJ, "bendRange", json_integer(bendRange)); json_object_set_new(rootJ, "midiChannel", json_integer(channel)); json_object_set_new(rootJ, "globalMidiChannel", json_integer(globalChannel)); json_object_set_new(rootJ, "MPEMode", json_integer(MPEPlus)); json_object_set_new(rootJ, "noteOffReset", json_boolean(noteOffReset)); return rootJ; } void fromJson(json_t *rootJ) override { json_t *midiJ = json_object_get(rootJ, "midi"); midiInput.fromJson(midiJ); json_t *bendRangeJ = json_object_get(rootJ, "bendRange"); if (bendRangeJ) { bendRange = json_integer_value(bendRangeJ); } json_t *midiChannelJ = json_object_get(rootJ, "midiChannel"); if (midiChannelJ) { channel = json_integer_value(midiChannelJ); } json_t *globalMidiChannelJ = json_object_get(rootJ, "globalMidiChannel"); if (globalMidiChannelJ) { globalChannel = json_integer_value(globalMidiChannelJ); } json_t *MPEModeJ = json_object_get(rootJ, "MPEMode"); if (MPEModeJ) { MPEPlus = json_integer_value(MPEModeJ); } json_t *noteOffResetJ = json_object_get(rootJ, "noteOffReset"); if (noteOffResetJ) { noteOffReset = json_boolean_value(noteOffResetJ); } } }; // MPEMidiWidget stuff struct BendRangeItem : MenuItem { MPEToCV *mpetocv; int bendRange ; void onAction(EventAction &e) override { mpetocv->bendRange = bendRange; } }; struct BendRangeChoice : LedDisplayChoice { // MPEToCVWidget *mpetocvwidget; MPEToCV *mpetocv; int bendRange ; void onAction(EventAction &e) override { #ifdef USE_VST2 Menu *menu = rack::global_ui->ui.gScene->createMenu(); #else Menu *menu = gScene->createMenu(); #endif // USE_VST2 menu->addChild(construct(&MenuLabel::text, "Bend Range")); std::vector bendRanges = {1,2,3,4,12,24,48,96}; // The bend range we use for (auto const& bendRangeValue: bendRanges) { BendRangeItem *item = new BendRangeItem(); item->mpetocv = mpetocv; item->text = std::to_string(bendRangeValue); item->bendRange = bendRangeValue; menu->addChild(item); } // mpetocv->bendRange = bendRange; } void step() override { color = nvgRGB(0xff, 0x00, 0x00); color.a = 0.8f; text = stringf("%d", mpetocv->bendRange); // text = stringf("Range: %d semitones", mpetocv->bendRange); // rightText = (mpetocv->bendRange==bendRange) ? "✔" : ""; } }; struct MidiChannelItem : MenuItem { MPEToCV *mpetocv; int channel ; void onAction(EventAction &e) override { mpetocv->channel = channel; } }; struct MidiChannelChoice : LedDisplayChoice { // MPEToCVWidget *mpetocvwidget; MPEToCV *mpetocv; int channel ; void onAction(EventAction &e) override { #ifdef USE_VST2 Menu *menu = rack::global_ui->ui.gScene->createMenu(); #else Menu *menu = gScene->createMenu(); #endif // USE_VST2 menu->addChild(construct(&MenuLabel::text, "Midi channel")); std::vector bendRanges = {1,2,3,4,12,24,48,96}; // The bend range we use for (int c=1; c <= 16 ; c++) { MidiChannelItem *item = new MidiChannelItem(); item->mpetocv = mpetocv; item->text = std::to_string(c); item->channel = c; menu->addChild(item); } } void step() override { color = nvgRGB(0xff, 0x00, 0x00); color.a = 0.8f; text = std::to_string(mpetocv->channel); } }; struct GlobalMidiChannelItem : MenuItem { MPEToCV *mpetocv; int channel ; void onAction(EventAction &e) override { mpetocv->globalChannel = channel; } }; struct GlobalMidiChannelChoice : LedDisplayChoice { // MPEToCVWidget *mpetocvwidget; MPEToCV *mpetocv; int channel ; void onAction(EventAction &e) override { #ifdef USE_VST2 Menu *menu = rack::global_ui->ui.gScene->createMenu(); #else Menu *menu = gScene->createMenu(); #endif // USE_VST2 menu->addChild(construct(&MenuLabel::text, "Global Midi channel")); for (int c=1; c <= 16 ; c++) { GlobalMidiChannelItem *item = new GlobalMidiChannelItem(); item->mpetocv = mpetocv; item->text = std::to_string(c); item->channel = c; menu->addChild(item); } } void step() override { color = nvgRGB(0xff, 0x00, 0x00); color.a = 0.8f; text = std::to_string(mpetocv->globalChannel); } }; struct MPEModeItem : MenuItem { MPEToCV *mpetocv; bool MPEPlus ; void onAction(EventAction &e) override { mpetocv->MPEPlus = MPEPlus; } }; struct MPEModeChoice : LedDisplayChoice { // MPEToCVWidget *mpetocvwidget; MPEToCV *mpetocv; bool MPEPlus ; void onAction(EventAction &e) override { #ifdef USE_VST2 Menu *menu = rack::global_ui->ui.gScene->createMenu(); #else Menu *menu = gScene->createMenu(); #endif // USE_VST2 menu->addChild(construct(&MenuLabel::text, "MPE mode")); // MPE MPEModeItem *MPE = new MPEModeItem(); MPE->mpetocv = mpetocv; MPE->text = "MPE - Standard (ROLI, etc)"; MPE->MPEPlus = false; menu->addChild(MPE); // MPE Plus MPEModeItem *MPEPlus = new MPEModeItem(); MPEPlus->mpetocv = mpetocv; MPEPlus->text = "MPE+ - High Res for Haken Continuum"; MPEPlus->MPEPlus = true; menu->addChild(MPEPlus); } void step() override { // color = nvgRGB(0xff, 0x00, 0x00); // color.a = 0.8f; if (mpetocv->MPEPlus) { text = "MPE+"; } else { text = "MPE"; } } }; // We extend the midi to follow similar design struct MPEMidiWidget : MPEBaseWidget { LedDisplaySeparator *hSeparators[2]; LedDisplaySeparator *vSeparators[3]; // LedDisplayChoice *ccChoices[4][4]; MPEToCV *mpetocv ; BendRangeChoice *bendRangeChoice ; MidiChannelChoice *midiChannelChoice ; GlobalMidiChannelChoice *globalMidiChannelChoice ; MPEModeChoice *mpeModeChoice ; MPEMidiWidget() { } void initialize(MPEToCV *mpetocv) { this->mpetocv = mpetocv; Vec pos = deviceChoice->box.getBottomLeft(); for (int y = 0; y < 2; y++) { hSeparators[y] = Widget::create(pos); addChild(hSeparators[y]); } midiChannelChoice = Widget::create(pos); midiChannelChoice->mpetocv = mpetocv ; addChild(midiChannelChoice); globalMidiChannelChoice = Widget::create(pos); globalMidiChannelChoice->mpetocv = mpetocv ; addChild(globalMidiChannelChoice); bendRangeChoice = Widget::create(pos); bendRangeChoice->mpetocv = mpetocv ; addChild(bendRangeChoice); mpeModeChoice = Widget::create(pos); mpeModeChoice->mpetocv = mpetocv ; addChild(mpeModeChoice); for (int x = 0; x < 3; x++) { vSeparators[x] = Widget::create(pos); vSeparators[x]->box.size.y = midiChannelChoice->box.size.y; addChild(vSeparators[x]); } // for (int x = 0; x < 3; x++) { // } } void step() override { MPEBaseWidget::step(); midiChannelChoice->box.size.x = box.size.x/4; midiChannelChoice->box.pos.x = 0; globalMidiChannelChoice->box.size.x = box.size.x/4; globalMidiChannelChoice->box.pos.x = box.size.x/4; bendRangeChoice->box.size.x = box.size.x/4; bendRangeChoice->box.pos.x = box.size.x/4 * 2 ; mpeModeChoice->box.size.x = box.size.x/4; mpeModeChoice->box.pos.x = box.size.x/4 * 3 - 5 ; for (int y = 0; y < 2; y++) { hSeparators[y]->box.size.x = box.size.x; } for (int x = 0; x < 3; x++) { vSeparators[x]->box.pos.x = box.size.x / 4 * (x+1); } } };