@@ -302,179 +302,234 @@ struct Davies1900hLargeRedKnob : Davies1900hKnob { | |||
struct Rogan : app::SvgKnob { | |||
widget::SvgWidget* bg; | |||
widget::SvgWidget* fg; | |||
Rogan() { | |||
minAngle = -0.83 * M_PI; | |||
maxAngle = 0.83 * M_PI; | |||
bg = new widget::SvgWidget; | |||
fb->addChildBelow(bg, tw); | |||
fg = new widget::SvgWidget; | |||
fb->addChildAbove(fg, tw); | |||
} | |||
}; | |||
struct Rogan6PSWhite : Rogan { | |||
Rogan6PSWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan6PSWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan5PSGray : Rogan { | |||
Rogan5PSGray() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan5PSGray.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSBlue : Rogan { | |||
Rogan3PSBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSRed : Rogan { | |||
Rogan3PSRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSGreen : Rogan { | |||
Rogan3PSGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSWhite : Rogan { | |||
Rogan3PSWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite.svg"))); | |||
widget::SvgWidget* bg = new widget::SvgWidget; | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fb->addChildBelow(bg, tw); | |||
widget::SvgWidget* fg = new widget::SvgWidget; | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
fb->addChildAbove(fg, tw); | |||
} | |||
}; | |||
struct Rogan3PBlue : Rogan { | |||
Rogan3PBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PRed : Rogan { | |||
Rogan3PRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PGreen : Rogan { | |||
Rogan3PGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan3PWhite : Rogan { | |||
Rogan3PWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2SGray : Rogan { | |||
Rogan2SGray() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2SGray.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSBlue : Rogan { | |||
Rogan2PSBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PSBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSRed : Rogan { | |||
Rogan2PSRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PSRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSGreen : Rogan { | |||
Rogan2PSGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PSGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSWhite : Rogan { | |||
Rogan2PSWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PSWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PBlue : Rogan { | |||
Rogan2PBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PRed : Rogan { | |||
Rogan2PRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PGreen : Rogan { | |||
Rogan2PGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan2PWhite : Rogan { | |||
Rogan2PWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan2PWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSBlue : Rogan { | |||
Rogan1PSBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PSBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSRed : Rogan { | |||
Rogan1PSRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PSRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSGreen : Rogan { | |||
Rogan1PSGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PSGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSWhite : Rogan { | |||
Rogan1PSWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PSWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PBlue : Rogan { | |||
Rogan1PBlue() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PBlue.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PRed : Rogan { | |||
Rogan1PRed() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PRed.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PGreen : Rogan { | |||
Rogan1PGreen() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PGreen.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
struct Rogan1PWhite : Rogan { | |||
Rogan1PWhite() { | |||
setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan1PWhite.svg"))); | |||
bg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-bg.svg"))); | |||
fg->setSvg(Svg::load(asset::system("res/ComponentLibrary/Rogan3PSWhite-fg.svg"))); | |||
} | |||
}; | |||
@@ -21,6 +21,7 @@ struct MIDI_CC : Module { | |||
}; | |||
midi::InputQueue midiInput; | |||
/** [cc][channel] */ | |||
int8_t ccValues[128][16]; | |||
/** When LSB is enabled for CC 0-31, the MSB is stored here until the LSB is received. | |||
@@ -33,7 +34,7 @@ struct MIDI_CC : Module { | |||
dsp::ExponentialFilter valueFilters[16][16]; | |||
bool smooth; | |||
bool mpeMode; | |||
bool lsbEnabled; | |||
bool lsbMode; | |||
MIDI_CC() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
@@ -66,7 +67,7 @@ struct MIDI_CC : Module { | |||
midiInput.reset(); | |||
smooth = true; | |||
mpeMode = false; | |||
lsbEnabled = false; | |||
lsbMode = false; | |||
} | |||
void process(const ProcessArgs& args) override { | |||
@@ -80,6 +81,7 @@ struct MIDI_CC : Module { | |||
} | |||
int channels = mpeMode ? 16 : 1; | |||
for (int i = 0; i < 16; i++) { | |||
if (!outputs[CC_OUTPUT + i].isConnected()) | |||
continue; | |||
@@ -89,7 +91,7 @@ struct MIDI_CC : Module { | |||
for (int c = 0; c < channels; c++) { | |||
int16_t cellValue = int16_t(ccValues[cc][c]) * 128; | |||
if (lsbEnabled && cc < 32) | |||
if (lsbMode && cc < 32) | |||
cellValue += ccValues[cc + 32][c]; | |||
// Maximum value for 14-bit CC should be MSB=127 LSB=0, not MSB=127 LSB=127, because this is the maximum value that 7-bit controllers can send. | |||
float value = float(cellValue) / (128 * 127); | |||
@@ -135,11 +137,11 @@ struct MIDI_CC : Module { | |||
learningId = -1; | |||
} | |||
if (lsbEnabled && cc < 32) { | |||
if (lsbMode && cc < 32) { | |||
// Don't set MSB yet. Wait for LSB to be received. | |||
msbValues[cc][c] = value; | |||
} | |||
else if (lsbEnabled && 32 <= cc && cc < 64) { | |||
else if (lsbMode && 32 <= cc && cc < 64) { | |||
// Apply MSB when LSB is received | |||
ccValues[cc - 32][c] = msbValues[cc - 32][c]; | |||
ccValues[cc][c] = value; | |||
@@ -170,7 +172,7 @@ struct MIDI_CC : Module { | |||
json_object_set_new(rootJ, "smooth", json_boolean(smooth)); | |||
json_object_set_new(rootJ, "mpeMode", json_boolean(mpeMode)); | |||
json_object_set_new(rootJ, "lsbEnabled", json_boolean(lsbEnabled)); | |||
json_object_set_new(rootJ, "lsbMode", json_boolean(lsbMode)); | |||
return rootJ; | |||
} | |||
@@ -206,9 +208,9 @@ struct MIDI_CC : Module { | |||
if (mpeModeJ) | |||
mpeMode = json_boolean_value(mpeModeJ); | |||
json_t* lsbEnabledJ = json_object_get(rootJ, "lsbEnabled"); | |||
json_t* lsbEnabledJ = json_object_get(rootJ, "lsbMode"); | |||
if (lsbEnabledJ) | |||
lsbEnabled = json_boolean_value(lsbEnabledJ); | |||
lsbMode = json_boolean_value(lsbEnabledJ); | |||
} | |||
}; | |||
@@ -259,7 +261,6 @@ struct MIDI_CCWidget : ModuleWidget { | |||
module->smooth ^= true; | |||
} | |||
}; | |||
SmoothItem* smoothItem = new SmoothItem; | |||
smoothItem->text = "Smooth CC"; | |||
smoothItem->rightText = CHECKMARK(module->smooth); | |||
@@ -272,25 +273,23 @@ struct MIDI_CCWidget : ModuleWidget { | |||
module->mpeMode ^= true; | |||
} | |||
}; | |||
MpeModeItem* mpeModeItem = new MpeModeItem; | |||
mpeModeItem->text = "MPE mode"; | |||
mpeModeItem->rightText = CHECKMARK(module->mpeMode); | |||
mpeModeItem->module = module; | |||
menu->addChild(mpeModeItem); | |||
struct LSBItem : MenuItem { | |||
struct LsbModeItem : MenuItem { | |||
MIDI_CC* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->lsbEnabled ^= true; | |||
module->lsbMode ^= true; | |||
} | |||
}; | |||
LSBItem* highResolutionItem = new LSBItem; | |||
highResolutionItem->text = "CC 0-31 controls are 14-bit"; | |||
highResolutionItem->rightText = CHECKMARK(module->lsbEnabled); | |||
highResolutionItem->module = module; | |||
menu->addChild(highResolutionItem); | |||
LsbModeItem* lsbItem = new LsbModeItem; | |||
lsbItem->text = "CC 0-31 controls are 14-bit"; | |||
lsbItem->rightText = CHECKMARK(module->lsbMode); | |||
lsbItem->module = module; | |||
menu->addChild(lsbItem); | |||
} | |||
}; | |||
@@ -15,11 +15,11 @@ struct MIDI_CV : Module { | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
CV_OUTPUT, | |||
PITCH_OUTPUT, | |||
GATE_OUTPUT, | |||
VELOCITY_OUTPUT, | |||
AFTERTOUCH_OUTPUT, | |||
PITCH_OUTPUT, | |||
PW_OUTPUT, | |||
MOD_OUTPUT, | |||
RETRIGGER_OUTPUT, | |||
CLOCK_OUTPUT, | |||
@@ -59,10 +59,14 @@ struct MIDI_CV : Module { | |||
int rotateIndex; | |||
// 16 channels for MPE. When MPE is disabled, only the first channel is used. | |||
uint16_t pitches[16]; | |||
/** Pitch wheel. | |||
When MPE is disabled, only the first channel is used. | |||
[channel] | |||
*/ | |||
uint16_t pws[16]; | |||
/** [channel] */ | |||
uint8_t mods[16]; | |||
dsp::ExponentialFilter pitchFilters[16]; | |||
dsp::ExponentialFilter pwFilters[16]; | |||
dsp::ExponentialFilter modFilters[16]; | |||
dsp::PulseGenerator clockPulse; | |||
@@ -74,11 +78,11 @@ struct MIDI_CV : Module { | |||
MIDI_CV() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
configOutput(CV_OUTPUT, "V/oct"); | |||
configOutput(PITCH_OUTPUT, "Pitch (1V/oct)"); | |||
configOutput(GATE_OUTPUT, "Gate"); | |||
configOutput(VELOCITY_OUTPUT, "Velocity"); | |||
configOutput(AFTERTOUCH_OUTPUT, "Aftertouch"); | |||
configOutput(PITCH_OUTPUT, "Pitch wheel"); | |||
configOutput(PW_OUTPUT, "Pitch wheel"); | |||
configOutput(MOD_OUTPUT, "Mod wheel"); | |||
configOutput(RETRIGGER_OUTPUT, "Retrigger"); | |||
configOutput(CLOCK_OUTPUT, "Clock"); | |||
@@ -88,7 +92,7 @@ struct MIDI_CV : Module { | |||
configOutput(CONTINUE_OUTPUT, "Continue"); | |||
heldNotes.reserve(128); | |||
for (int c = 0; c < 16; c++) { | |||
pitchFilters[c].setTau(1 / 30.f); | |||
pwFilters[c].setTau(1 / 30.f); | |||
modFilters[c].setTau(1 / 30.f); | |||
} | |||
onReset(); | |||
@@ -110,9 +114,9 @@ struct MIDI_CV : Module { | |||
gates[c] = false; | |||
velocities[c] = 0; | |||
aftertouches[c] = 0; | |||
pitches[c] = 8192; | |||
pws[c] = 8192; | |||
mods[c] = 0; | |||
pitchFilters[c].reset(); | |||
pwFilters[c].reset(); | |||
modFilters[c].reset(); | |||
} | |||
pedal = false; | |||
@@ -130,13 +134,13 @@ struct MIDI_CV : Module { | |||
midiInput.queue.pop(); | |||
} | |||
outputs[CV_OUTPUT].setChannels(channels); | |||
outputs[PITCH_OUTPUT].setChannels(channels); | |||
outputs[GATE_OUTPUT].setChannels(channels); | |||
outputs[VELOCITY_OUTPUT].setChannels(channels); | |||
outputs[AFTERTOUCH_OUTPUT].setChannels(channels); | |||
outputs[RETRIGGER_OUTPUT].setChannels(channels); | |||
for (int c = 0; c < channels; c++) { | |||
outputs[CV_OUTPUT].setVoltage((notes[c] - 60.f) / 12.f, c); | |||
outputs[PITCH_OUTPUT].setVoltage((notes[c] - 60.f) / 12.f, c); | |||
outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c); | |||
outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c); | |||
outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c); | |||
@@ -144,37 +148,25 @@ struct MIDI_CV : Module { | |||
} | |||
// Set pitch and mod wheel | |||
auto updatePitch = [&](int c) { | |||
float pitch = ((int) pitches[c] - 8192) / 8191.f; | |||
pitch = clamp(pitch, -1.f, 1.f); | |||
int wheelChannels = (polyMode == MPE_MODE) ? 16 : 1; | |||
outputs[PW_OUTPUT].setChannels(wheelChannels); | |||
outputs[MOD_OUTPUT].setChannels(wheelChannels); | |||
for (int c = 0; c < wheelChannels; c++) { | |||
float pw = ((int) pws[c] - 8192) / 8191.f; | |||
pw = clamp(pw, -1.f, 1.f); | |||
if (smooth) | |||
pitch = pitchFilters[c].process(args.sampleTime, pitch); | |||
pw = pwFilters[c].process(args.sampleTime, pw); | |||
else | |||
pitchFilters[c].out = pitch; | |||
outputs[PITCH_OUTPUT].setVoltage(pitchFilters[c].out * 5.f); | |||
}; | |||
auto updateMod = [&](int c) { | |||
pwFilters[c].out = pw; | |||
outputs[PW_OUTPUT].setVoltage(pw * 5.f); | |||
float mod = mods[c] / 127.f; | |||
mod = clamp(mod, 0.f, 1.f); | |||
if (smooth) | |||
modFilters[c].process(args.sampleTime, mod); | |||
mod = modFilters[c].process(args.sampleTime, mod); | |||
else | |||
modFilters[c].out = mod; | |||
outputs[MOD_OUTPUT].setVoltage(modFilters[c].out * 10.f); | |||
}; | |||
if (polyMode == MPE_MODE) { | |||
for (int c = 0; c < channels; c++) { | |||
updatePitch(c); | |||
outputs[PITCH_OUTPUT].setChannels(1); | |||
updateMod(c); | |||
outputs[MOD_OUTPUT].setChannels(1); | |||
} | |||
} | |||
else { | |||
updatePitch(0); | |||
outputs[PITCH_OUTPUT].setChannels(1); | |||
updateMod(0); | |||
outputs[MOD_OUTPUT].setChannels(1); | |||
outputs[MOD_OUTPUT].setVoltage(mod * 10.f); | |||
} | |||
outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(args.sampleTime) ? 10.f : 0.f); | |||
@@ -233,7 +225,7 @@ struct MIDI_CV : Module { | |||
// pitch wheel | |||
case 0xe: { | |||
int c = (polyMode == MPE_MODE) ? msg.getChannel() : 0; | |||
pitches[c] = ((uint16_t) msg.getValue() << 7) | msg.getNote(); | |||
pws[c] = ((uint16_t) msg.getValue() << 7) | msg.getNote(); | |||
} break; | |||
case 0xf: { | |||
processSystem(msg); | |||
@@ -440,7 +432,7 @@ struct MIDI_CV : Module { | |||
json_object_set_new(rootJ, "clockDivision", json_integer(clockDivision)); | |||
// Saving/restoring pitch and mod doesn't make much sense for MPE. | |||
if (polyMode != MPE_MODE) { | |||
json_object_set_new(rootJ, "lastPitch", json_integer(pitches[0])); | |||
json_object_set_new(rootJ, "lastPitch", json_integer(pws[0])); | |||
json_object_set_new(rootJ, "lastMod", json_integer(mods[0])); | |||
} | |||
json_object_set_new(rootJ, "midi", midiInput.toJson()); | |||
@@ -466,7 +458,7 @@ struct MIDI_CV : Module { | |||
json_t* lastPitchJ = json_object_get(rootJ, "lastPitch"); | |||
if (lastPitchJ) | |||
pitches[0] = json_integer_value(lastPitchJ); | |||
pws[0] = json_integer_value(lastPitchJ); | |||
json_t* lastModJ = json_object_get(rootJ, "lastMod"); | |||
if (lastModJ) | |||
@@ -489,11 +481,11 @@ struct MIDI_CVWidget : ModuleWidget { | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), module, MIDI_CV::CV_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), module, MIDI_CV::PITCH_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), module, MIDI_CV::GATE_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), module, MIDI_CV::VELOCITY_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), module, MIDI_CV::AFTERTOUCH_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), module, MIDI_CV::PITCH_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), module, MIDI_CV::PW_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), module, MIDI_CV::MOD_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), module, MIDI_CV::CLOCK_OUTPUT)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), module, MIDI_CV::CLOCK_DIV_OUTPUT)); | |||
@@ -519,7 +511,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
module->smooth ^= true; | |||
} | |||
}; | |||
SmoothItem* smoothItem = new SmoothItem; | |||
smoothItem->text = "Smooth pitch/mod wheel"; | |||
smoothItem->rightText = CHECKMARK(module->smooth); | |||
@@ -533,7 +524,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
module->clockDivision = clockDivision; | |||
} | |||
}; | |||
struct ClockDivisionItem : MenuItem { | |||
MIDI_CV* module; | |||
Menu* createChildMenu() override { | |||
@@ -551,7 +541,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
return menu; | |||
} | |||
}; | |||
ClockDivisionItem* clockDivisionItem = new ClockDivisionItem; | |||
clockDivisionItem->text = "CLK/N divider"; | |||
clockDivisionItem->rightText = RIGHT_ARROW; | |||
@@ -565,7 +554,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
module->setChannels(channels); | |||
} | |||
}; | |||
struct ChannelItem : MenuItem { | |||
MIDI_CV* module; | |||
Menu* createChildMenu() override { | |||
@@ -584,7 +572,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
return menu; | |||
} | |||
}; | |||
ChannelItem* channelItem = new ChannelItem; | |||
channelItem->text = "Polyphony channels"; | |||
channelItem->rightText = string::f("%d", module->channels) + " " + RIGHT_ARROW; | |||
@@ -598,7 +585,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
module->setPolyMode(polyMode); | |||
} | |||
}; | |||
struct PolyModeItem : MenuItem { | |||
MIDI_CV* module; | |||
Menu* createChildMenu() override { | |||
@@ -621,7 +607,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
return menu; | |||
} | |||
}; | |||
PolyModeItem* polyModeItem = new PolyModeItem; | |||
polyModeItem->text = "Polyphony mode"; | |||
polyModeItem->rightText = RIGHT_ARROW; | |||
@@ -634,7 +619,6 @@ struct MIDI_CVWidget : ModuleWidget { | |||
module->panic(); | |||
} | |||
}; | |||
PanicItem* panicItem = new PanicItem; | |||
panicItem->text = "Panic"; | |||
panicItem->module = module; | |||
@@ -13,7 +13,7 @@ struct MIDI_Gate : Module { | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
ENUMS(TRIG_OUTPUT, 16), | |||
ENUMS(GATE_OUTPUTS, 16), | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
@@ -22,21 +22,23 @@ struct MIDI_Gate : Module { | |||
midi::InputQueue midiInput; | |||
/** [cell][c] */ | |||
/** [cell][channel] */ | |||
bool gates[16][16]; | |||
/** [cell][c] */ | |||
/** [cell][channel] */ | |||
float gateTimes[16][16]; | |||
/** [cell][c] */ | |||
/** [cell][channel] */ | |||
uint8_t velocities[16][16]; | |||
int learningId = -1; | |||
uint8_t learnedNotes[16] = {}; | |||
bool velocityMode = false; | |||
bool mpeMode = false; | |||
/** Cell ID in learn mode, or -1 if none. */ | |||
int learningId; | |||
/** [cell] */ | |||
uint8_t learnedNotes[16]; | |||
bool velocityMode; | |||
bool mpeMode; | |||
MIDI_Gate() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
for (int i = 0; i < 16; i++) | |||
configOutput(TRIG_OUTPUT + i, string::f("Cell %d", i + 1)); | |||
configOutput(GATE_OUTPUTS + i, string::f("Gate %d", i + 1)); | |||
onReset(); | |||
} | |||
@@ -76,16 +78,16 @@ struct MIDI_Gate : Module { | |||
int channels = mpeMode ? 16 : 1; | |||
for (int i = 0; i < 16; i++) { | |||
outputs[TRIG_OUTPUT + i].setChannels(channels); | |||
outputs[GATE_OUTPUTS + i].setChannels(channels); | |||
for (int c = 0; c < channels; c++) { | |||
// Make sure all pulses last longer than 1ms | |||
if (gates[i][c] || gateTimes[i][c] > 0.f) { | |||
float velocity = velocityMode ? (velocities[i][c] / 127.f) : 1.f; | |||
outputs[TRIG_OUTPUT + i].setVoltage(velocity * 10.f, c); | |||
outputs[GATE_OUTPUTS + i].setVoltage(velocity * 10.f, c); | |||
gateTimes[i][c] -= args.sampleTime; | |||
} | |||
else { | |||
outputs[TRIG_OUTPUT + i].setVoltage(0.f, c); | |||
outputs[GATE_OUTPUTS + i].setVoltage(0.f, c); | |||
} | |||
} | |||
} | |||
@@ -181,30 +183,6 @@ struct MIDI_Gate : Module { | |||
}; | |||
struct MIDI_GateVelocityItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->velocityMode ^= true; | |||
} | |||
}; | |||
struct MIDI_GateMpeModeItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->mpeMode ^= true; | |||
} | |||
}; | |||
struct MIDI_GatePanicItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->panic(); | |||
} | |||
}; | |||
struct MIDI_GateWidget : ModuleWidget { | |||
MIDI_GateWidget(MIDI_Gate* module) { | |||
setModule(module); | |||
@@ -215,22 +193,22 @@ struct MIDI_GateWidget : ModuleWidget { | |||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDI_Gate::TRIG_OUTPUT + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDI_Gate::TRIG_OUTPUT + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDI_Gate::TRIG_OUTPUT + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDI_Gate::TRIG_OUTPUT + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDI_Gate::TRIG_OUTPUT + 4)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDI_Gate::TRIG_OUTPUT + 5)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDI_Gate::TRIG_OUTPUT + 6)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDI_Gate::TRIG_OUTPUT + 7)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDI_Gate::TRIG_OUTPUT + 8)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDI_Gate::TRIG_OUTPUT + 9)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDI_Gate::TRIG_OUTPUT + 10)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDI_Gate::TRIG_OUTPUT + 11)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDI_Gate::TRIG_OUTPUT + 12)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDI_Gate::TRIG_OUTPUT + 13)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDI_Gate::TRIG_OUTPUT + 14)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDI_Gate::TRIG_OUTPUT + 15)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDI_Gate::GATE_OUTPUTS + 0)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDI_Gate::GATE_OUTPUTS + 1)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDI_Gate::GATE_OUTPUTS + 2)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDI_Gate::GATE_OUTPUTS + 3)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDI_Gate::GATE_OUTPUTS + 4)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDI_Gate::GATE_OUTPUTS + 5)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDI_Gate::GATE_OUTPUTS + 6)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDI_Gate::GATE_OUTPUTS + 7)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDI_Gate::GATE_OUTPUTS + 8)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDI_Gate::GATE_OUTPUTS + 9)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDI_Gate::GATE_OUTPUTS + 10)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDI_Gate::GATE_OUTPUTS + 11)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDI_Gate::GATE_OUTPUTS + 12)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDI_Gate::GATE_OUTPUTS + 13)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDI_Gate::GATE_OUTPUTS + 14)); | |||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDI_Gate::GATE_OUTPUTS + 15)); | |||
typedef Grid16MidiWidget<NoteChoice<MIDI_Gate>> TMidiWidget; | |||
TMidiWidget* midiWidget = createWidget<TMidiWidget>(mm2px(Vec(3.399621, 14.837339))); | |||
@@ -243,18 +221,36 @@ struct MIDI_GateWidget : ModuleWidget { | |||
void appendContextMenu(Menu* menu) override { | |||
MIDI_Gate* module = dynamic_cast<MIDI_Gate*>(this->module); | |||
struct VelocityItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->velocityMode ^= true; | |||
} | |||
}; | |||
menu->addChild(new MenuSeparator); | |||
MIDI_GateVelocityItem* velocityItem = createMenuItem<MIDI_GateVelocityItem>("Velocity mode", CHECKMARK(module->velocityMode)); | |||
VelocityItem* velocityItem = createMenuItem<VelocityItem>("Velocity mode", CHECKMARK(module->velocityMode)); | |||
velocityItem->module = module; | |||
menu->addChild(velocityItem); | |||
MIDI_GateMpeModeItem* mpeModeItem = new MIDI_GateMpeModeItem; | |||
struct MpeModeItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->mpeMode ^= true; | |||
} | |||
}; | |||
MpeModeItem* mpeModeItem = new MpeModeItem; | |||
mpeModeItem->text = "MPE mode"; | |||
mpeModeItem->rightText = CHECKMARK(module->mpeMode); | |||
mpeModeItem->module = module; | |||
menu->addChild(mpeModeItem); | |||
MIDI_GatePanicItem* panicItem = new MIDI_GatePanicItem; | |||
struct PanicItem : MenuItem { | |||
MIDI_Gate* module; | |||
void onAction(const ActionEvent& e) override { | |||
module->panic(); | |||
} | |||
}; | |||
PanicItem* panicItem = new PanicItem; | |||
panicItem->text = "Panic"; | |||
panicItem->module = module; | |||
menu->addChild(panicItem); | |||