| @@ -5,20 +5,21 @@ | |||||
| #include "MidiIO.hpp" | #include "MidiIO.hpp" | ||||
| struct CCValue { | struct CCValue { | ||||
| int val = 0; | |||||
| int val = 0; // Controller value | |||||
| TransitionSmoother tSmooth; | TransitionSmoother tSmooth; | ||||
| int num = 0; // controller number | |||||
| bool inited = false; | |||||
| bool changed = false; | |||||
| 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) | int sync = 0; // Output value sync (implies diff) | ||||
| bool syncFirst = true; | |||||
| bool onFocus = false; // Text field for output focused | |||||
| bool syncFirst = true; // First value after sync was reset | |||||
| void resetSync() { | |||||
| sync = 0; | |||||
| syncFirst = true; | |||||
| } | |||||
| }; | }; | ||||
| /* | |||||
| * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod weel to | |||||
| * CV | |||||
| */ | |||||
| struct MIDICCToCVInterface : MidiIO, Module { | struct MIDICCToCVInterface : MidiIO, Module { | ||||
| enum ParamIds { | enum ParamIds { | ||||
| NUM_PARAMS | NUM_PARAMS | ||||
| @@ -35,7 +36,11 @@ struct MIDICCToCVInterface : MidiIO, Module { | |||||
| CCValue cc[NUM_OUTPUTS]; | CCValue cc[NUM_OUTPUTS]; | ||||
| MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||||
| MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | |||||
| cc[i].num = i; | |||||
| } | |||||
| } | |||||
| ~MIDICCToCVInterface() {} | ~MIDICCToCVInterface() {} | ||||
| @@ -63,14 +68,14 @@ struct MIDICCToCVInterface : MidiIO, Module { | |||||
| json_t *ccNumJ = json_object_get(rootJ, ("ccNum" + std::to_string(i)).c_str()); | json_t *ccNumJ = json_object_get(rootJ, ("ccNum" + std::to_string(i)).c_str()); | ||||
| if (ccNumJ) { | if (ccNumJ) { | ||||
| cc[i].num = json_integer_value(ccNumJ); | cc[i].num = json_integer_value(ccNumJ); | ||||
| cc[i].inited = true; | |||||
| cc[i].numInited = true; | |||||
| } | } | ||||
| json_t *ccValJ = json_object_get(rootJ, ("ccVal" + std::to_string(i)).c_str()); | json_t *ccValJ = json_object_get(rootJ, ("ccVal" + std::to_string(i)).c_str()); | ||||
| if (ccValJ) { | if (ccValJ) { | ||||
| cc[i].val = json_integer_value(ccValJ); | cc[i].val = json_integer_value(ccValJ); | ||||
| outputs[i].value = (cc[i].val/127.0) * 10.0; | |||||
| cc[i].changed = true; | |||||
| cc[i].tSmooth.set((cc[i].val / 127.0 * 10.0), (cc[i].val / 127.0 * 10.0)); | |||||
| cc[i].resetSync(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -99,8 +104,8 @@ void MIDICCToCVInterface::step() { | |||||
| lights[i].setBrightness(cc[i].sync / 127.0); | 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)); | |||||
| 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; | cc[i].changed = false; | ||||
| } | } | ||||
| @@ -111,10 +116,8 @@ void MIDICCToCVInterface::step() { | |||||
| void MIDICCToCVInterface::resetMidi() { | void MIDICCToCVInterface::resetMidi() { | ||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | for (int i = 0; i < NUM_OUTPUTS; i++) { | ||||
| cc[i].val = 0; | cc[i].val = 0; | ||||
| cc[i].sync = 0; | |||||
| cc[i].syncFirst = true; | |||||
| outputs[i].value = 0; | |||||
| cc[i].changed = true; | |||||
| cc[i].resetSync(); | |||||
| cc[i].tSmooth.set(0,0); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -132,21 +135,22 @@ void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||||
| if (status == 0xb) { | if (status == 0xb) { | ||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | for (int i = 0; i < NUM_OUTPUTS; i++) { | ||||
| if (cc[i].onFocus) { | |||||
| cc[i].sync = true; | |||||
| cc[i].syncFirst = true; | |||||
| if (cc[i].numSelected) { | |||||
| cc[i].resetSync(); | |||||
| cc[i].num = data1; | cc[i].num = data1; | ||||
| } | } | ||||
| if (data1 == cc[i].num) { | 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) { | if (cc[i].syncFirst) { | ||||
| cc[i].syncFirst = false; | cc[i].syncFirst = false; | ||||
| if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { | if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) { | ||||
| cc[i].sync = 0; | cc[i].sync = 0; | ||||
| } else { | |||||
| }else { | |||||
| cc[i].sync = absi(data2 - cc[i].val); | cc[i].sync = absi(data2 - cc[i].val); | ||||
| } | } | ||||
| return; | |||||
| } | } | ||||
| if (cc[i].sync == 0) { | if (cc[i].sync == 0) { | ||||
| @@ -179,12 +183,13 @@ struct CCTextField : TextField { | |||||
| void CCTextField::draw(NVGcontext *vg) { | void CCTextField::draw(NVGcontext *vg) { | ||||
| /* This is necessary, since the save | /* This is necessary, since the save | ||||
| * file is loaded after constructing the widget*/ | * file is loaded after constructing the widget*/ | ||||
| if (module->cc[num].inited) { | |||||
| module->cc[num].inited = false; | |||||
| if (module->cc[num].numInited) { | |||||
| module->cc[num].numInited = false; | |||||
| text = std::to_string(module->cc[num].num); | text = std::to_string(module->cc[num].num); | ||||
| } | } | ||||
| if (module->cc[num].onFocus) { | |||||
| /* If number is selected for midi learn*/ | |||||
| if (module->cc[num].numSelected) { | |||||
| text = std::to_string(module->cc[num].num); | text = std::to_string(module->cc[num].num); | ||||
| } | } | ||||
| @@ -193,19 +198,19 @@ void CCTextField::draw(NVGcontext *vg) { | |||||
| void CCTextField::onMouseUpOpaque(int button) { | void CCTextField::onMouseUpOpaque(int button) { | ||||
| if (button == 1) { | if (button == 1) { | ||||
| module->cc[num].onFocus = false; | |||||
| module->cc[num].numSelected = false; | |||||
| } | } | ||||
| } | } | ||||
| void CCTextField::onMouseDownOpaque(int button) { | void CCTextField::onMouseDownOpaque(int button) { | ||||
| if (button == 1) { | if (button == 1) { | ||||
| module->cc[num].onFocus = true; | |||||
| module->cc[num].numSelected = true; | |||||
| } | } | ||||
| } | } | ||||
| void CCTextField::onMouseLeave() { | void CCTextField::onMouseLeave() { | ||||
| module->cc[num].onFocus = false; | |||||
| module->cc[num].numSelected = false; | |||||
| } | } | ||||
| @@ -222,10 +227,7 @@ void CCTextField::onTextChange() { | |||||
| return; | return; | ||||
| } | } | ||||
| if (!module->cc[num].inited) { | |||||
| module->cc[num].sync = 0; | |||||
| module->cc[num].syncFirst = true; | |||||
| } | |||||
| module->cc[num].resetSync(); | |||||
| } catch (...) { | } catch (...) { | ||||
| text = ""; | text = ""; | ||||