Skip to content

Commit

Permalink
Make sync one way (VCV -> hardware)
Browse files Browse the repository at this point in the history
* some cleanup
  • Loading branch information
hemmer committed Mar 24, 2024
1 parent 8095c80 commit 3d4830f
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 38 deletions.
2 changes: 1 addition & 1 deletion docs/MIDIThingV2.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The VCV counterpart is designed to allow users to quickly get up and running wit

## Setup

To use, first ensure the MIDI Thing v2 is plugged into your computer, and visible as a MIDI device. Then select it, either from the top of the module, or the right click context menu. Then click "SYNC" - this puts the MIDI Thing into a preset designed to work with VCV Rack, and syncronises settings/voltage ranges etc.
To use, first ensure the MIDI Thing v2 is plugged into your computer, and visible as a MIDI device. Then select it, either from the top of the module, or the right click context menu. Then click "SYNC" - this puts the MIDI Thing into a preset designed to work with VCV Rack, and syncronises settings/voltage ranges etc. Note that for now, sync is one-way (VCV to hardware).

![MIDI Thing Config](img/MidiThingV2.png "MIDI Thing v2 Setup")

Expand Down
122 changes: 85 additions & 37 deletions src/MidiThing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct MidiThing : Module {
LIGHTS_LEN
};
/// Port mode
enum PORTMODE_t : uint8_t {
enum PORTMODE_t {
NOPORTMODE = 0,
MODE10V,
MODEPN5V,
Expand All @@ -123,7 +123,8 @@ struct MidiThing : Module {

const std::vector<float> updateRates = {200., 1000., 4000., 16000.};
const std::vector<std::string> updateRateNames = {"200 Hz (fewest active channels, slowest, lowest-cpu)", "1 kHz", "4 kHz",
"16 kHz (most active channels, fast, highest-cpu)"};
"16 kHz (most active channels, fast, highest-cpu)"
};
int updateRateIdx = 1;

// use Pre-def 4 for bridge mode
Expand All @@ -147,10 +148,11 @@ struct MidiThing : Module {

}

void refreshConfig() {
void requestAllChannelsParamsOverSysex() {
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 3; ++col) {
requestParamOverSysex(row, col, 2);
const int PORT_CONFIG = 2;
requestParamOverSysex(row, col, PORT_CONFIG);
}
}
}
Expand All @@ -168,22 +170,43 @@ struct MidiThing : Module {
// DEBUG("Predef %d msg request sent: %s", predef, msg.toString().c_str());
}

uint8_t port = 0;
void setVoltageMode(uint8_t row, uint8_t col, uint8_t outputMode_) {
port = 3 * row + col;
// +1 because enum starts at 1
portModes[port] = (PORTMODE_t)(outputMode_ + 1);
void setMidiMergeViaSysEx(bool mergeOn) {
midi::Message msg;
msg.bytes.resize(8);

msg.bytes = {0xF0, 0x7D, 0x19, 0x00, 0x05, 0x02, 0x00, (uint8_t) mergeOn, 0xF7};
midiOut.setChannel(0);
midiOut.sendMessage(msg);
// DEBUG("Predef %d msg request sent: %s", mergeOn, msg.toString().c_str());
}


void setVoltageModeOnHardware(uint8_t row, uint8_t col, PORTMODE_t outputMode_) {
uint8_t port = 3 * row + col;
portModes[port] = outputMode_;

midi::Message msg;
msg.bytes.resize(8);
// F0 7D 17 2n 02 02 00 0m F7
// Where n = 0 based port number
// and m is the volt output mode to select from:
msg.bytes = {0xF0, 0x7D, 0x17, static_cast<unsigned char>(32 + port), 0x02, 0x02, 0x00, portModes[port], 0xF7};
msg.bytes = {0xF0, 0x7D, 0x17, static_cast<unsigned char>(32 + port), 0x02, 0x02, 0x00, (uint8_t) portModes[port], 0xF7};
midiOut.sendMessage(msg);
// DEBUG("Voltage mode msg sent: port %d (%d), mode %d", port, static_cast<unsigned char>(32 + port), portModes[port]);
}

void setVoltageModeOnHardware(uint8_t row, uint8_t col) {
setVoltageModeOnHardware(row, col, portModes[3 * row + col]);
}

void syncVcvStateToHardware() {
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 3; ++col) {
setVoltageModeOnHardware(row, col);
}
}
}


midi::InputQueue inputQueue;
void requestParamOverSysex(uint8_t row, uint8_t col, uint8_t mode) {
Expand Down Expand Up @@ -238,42 +261,54 @@ struct MidiThing : Module {
}
}

// one way sync (VCV -> hardware) for now
void doSync() {
// switch to VCV template (predef 4)
setPredef(4);

// disable MIDI merge (otherwise large sample rates will not work)
setMidiMergeViaSysEx(false);

// send full VCV config
syncVcvStateToHardware();

// disabled for now, but this would request what state the hardware is in
if (parseSysExMessagesFromHardware) {
requestAllChannelsParamsOverSysex();
}
}

// debug only
bool setFrame = true;
bool parseSysExMessagesFromHardware = false;
int numActiveChannels = 0;
dsp::BooleanTrigger buttonTrigger;
dsp::Timer rateLimiterTimer;
PORTMODE_t portModes[NUM_INPUTS] = {};
void process(const ProcessArgs& args) override {

if (buttonTrigger.process(params[REFRESH_PARAM].getValue())) {

// currently this sets the predef to 4, which will reset ranges etc
// TODO: figure this out!
setPredef(4);
refreshConfig();
doSync();
}

//DEBUG("inputDriver id: %d, outMidi id: %d", inputQueue.getDriverId(), midiOut.getDriverId());
//DEBUG("inputDevice id: %d, outMidi id: %d", inputQueue.getDeviceId(), midiOut.getDeviceId());
//DEBUG("inputChannel: %d, outChannel: %d", inputQueue.getChannel(), midiOut.getChannel());

midi::Message msg;
uint8_t outData[32] = {};
while (inputQueue.tryPop(&msg, args.frame)) {
DEBUG("msg (size: %d): %s", msg.getSize(), msg.toString().c_str());
// disabled for now, but this is how VCV would read SysEx coming from the hardware (if requested above)
if (parseSysExMessagesFromHardware) {
midi::Message msg;
uint8_t outData[32] = {};
while (inputQueue.tryPop(&msg, args.frame)) {
// DEBUG("msg (size: %d): %s", msg.getSize(), msg.toString().c_str());

uint8_t outLen = decodeSysEx(&msg.bytes[0], outData, msg.bytes.size(), false);
if (outLen > 3) {
uint8_t outLen = decodeSysEx(&msg.bytes[0], outData, msg.bytes.size(), false);
if (outLen > 3) {

int channel = (outData[2] & 0x0f) >> 0;
int channel = (outData[2] & 0x0f) >> 0;

if (channel >= 0 && channel < NUM_INPUTS) {
if (outData[outLen - 1] < LASTPORTMODE) {
portModes[channel] = (PORTMODE_t) outData[outLen - 1];
DEBUG("Channel %d, %d: mode %d (%s)", outData[2], channel, portModes[channel], cfgPortModeNames[portModes[channel]]);
if (channel >= 0 && channel < NUM_INPUTS) {
if (outData[outLen - 1] < LASTPORTMODE) {
portModes[channel] = (PORTMODE_t) outData[outLen - 1];
// DEBUG("Channel %d, %d: mode %d (%s)", outData[2], channel, portModes[channel], cfgPortModeNames[portModes[channel]]);
}
}

}
}
}
Expand Down Expand Up @@ -333,6 +368,10 @@ struct MidiThing : Module {
json_object_set_new(rootJ, "setFrame", json_boolean(setFrame));
json_object_set_new(rootJ, "updateRateIdx", json_integer(updateRateIdx));

for (int c = 0; c < NUM_INPUTS; ++c) {
json_object_set_new(rootJ, string::f("portMode%d", c).c_str(), json_integer(portModes[c]));
}

return rootJ;
}

Expand All @@ -357,7 +396,15 @@ struct MidiThing : Module {
updateRateIdx = json_integer_value(updateRateIdxJ);
}

refreshConfig();
for (int c = 0; c < NUM_INPUTS; ++c) {
json_t* portModeJ = json_object_get(rootJ, string::f("portMode%d", c).c_str());
if (portModeJ) {
portModes[c] = (PORTMODE_t)json_integer_value(portModeJ);
}
}

// requestAllChannelsParamsOverSysex();
syncVcvStateToHardware();
}
};

Expand All @@ -375,8 +422,9 @@ struct MidiThingPort : PJ301MPort {
[ = ]() {
return module->getVoltageMode(row, col);
},
[ = ](int mode) {
module->setVoltageMode(row, col, mode);
[ = ](int modeIdx) {
MidiThing::PORTMODE_t mode = (MidiThing::PORTMODE_t)(modeIdx + 1);
module->setVoltageModeOnHardware(row, col, mode);
}
));

Expand Down Expand Up @@ -486,7 +534,8 @@ struct LEDDisplay : LightWidget {
return module->getVoltageMode(row, col) == i;
},
[ = ]() {
module->setVoltageMode(row, col, i);;
MidiThing::PORTMODE_t mode = (MidiThing::PORTMODE_t)(i + 1);
module->setVoltageModeOnHardware(row, col, mode);
}
));
}
Expand Down Expand Up @@ -743,7 +792,7 @@ struct MidiThingWidget : ModuleWidget {
module->inputQueue.setDeviceId(deviceId);
module->inputQueue.setChannel(0); // TODO update

module->refreshConfig();
module->doSync();

// DEBUG("Updating Output MIDI settings - driver: %s, device: %s",
// driver->getName().c_str(), driver->getOutputDeviceName(deviceId).c_str());
Expand All @@ -761,7 +810,6 @@ struct MidiThingWidget : ModuleWidget {
},
[ = ](int mode) {
module->setPredef(mode + 1);
module->refreshConfig();
}));


Expand Down

0 comments on commit 3d4830f

Please sign in to comment.