diff --git a/Makefile b/Makefile
index b5d8d920..590290c2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
RACK_DIR ?= .
VERSION = 1.dev.$(shell git rev-parse --short HEAD)
-FLAGS += -DAPP_VERSION=$(VERSION)
+FLAGS += -DVERSION=$(VERSION)
FLAGS += -Iinclude
FLAGS += -Idep/include -Idep/lib/libzip/include
diff --git a/include/app/common.hpp b/include/app/common.hpp
index 2d31941b..9d6193dd 100644
--- a/include/app/common.hpp
+++ b/include/app/common.hpp
@@ -8,8 +8,8 @@ namespace rack {
namespace app {
-static const char NAME[] = "VCV Rack";
-static const char VERSION[] = TOSTRING(APP_VERSION);
+static const char APP_NAME[] = "VCV Rack";
+static const char APP_VERSION[] = TOSTRING(VERSION);
static const char API_URL[] = "https://api.vcvrack.com";
static const float SVG_DPI = 75.0;
diff --git a/include/dsp/common.hpp b/include/dsp/common.hpp
index c54adb4f..5d69d450 100644
--- a/include/dsp/common.hpp
+++ b/include/dsp/common.hpp
@@ -33,6 +33,7 @@ inline float hann(float p) {
return 0.5f * (1.f - std::cos(2*M_PI * p));
}
+/** Applies the Hann window to a signal `x` */
inline void hannWindow(float *x, int len) {
for (int i = 0; i < len; i++) {
x[i] *= hann((float) i / (len - 1));
diff --git a/include/dsp/filter.hpp b/include/dsp/filter.hpp
index c0c21699..4e0ea628 100644
--- a/include/dsp/filter.hpp
+++ b/include/dsp/filter.hpp
@@ -84,14 +84,14 @@ struct ExponentialSlewLimiter {
/** Applies exponential smoothing to a signal with the ODE
-dy/dt = x * lambda
+\f$ \frac{dy}{dt} = x \lambda \f$.
*/
struct ExponentialFilter {
float out = 0.f;
- float lambda = 1.f;
+ float lambda = 0.f;
- float process(float in) {
- float y = out + (in - out) * lambda;
+ float process(float deltaTime, float in) {
+ float y = out + (in - out) * lambda * deltaTime;
// If no change was detected, assume float granularity is too small and snap output to input
if (out == y)
out = in;
@@ -99,6 +99,8 @@ struct ExponentialFilter {
out = y;
return out;
}
+
+ DEPRECATED float process(float in) {return process(1.f, in);}
};
diff --git a/res/Core/MIDI-CV.svg b/res/Core/MIDI-CV.svg
index 711bb11f..f80ea423 100644
--- a/res/Core/MIDI-CV.svg
+++ b/res/Core/MIDI-CV.svg
@@ -9,15 +9,423 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="40.639721mm"
+ width="40.639725mm"
height="128.50069mm"
- viewBox="0 0 40.639721 128.50069"
+ viewBox="0 0 40.639725 128.50069"
version="1.1"
- id="svg21243"
- inkscape:version="0.92.2 2405546, 2018-03-11"
+ id="svg22336"
+ inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="MIDI-CV.svg">
+ id="defs22330">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id="metadata22333">
@@ -56,396 +464,736 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
- transform="translate(-311.42837,33.900484)">
+ transform="translate(-77.197996,-46.785373)">
+ d="M 77.291704,46.879082 H 117.74401 V 175.19235 H 77.291704 Z m 0,0"
+ id="path11739" />
+ d="M 117.83772,46.785373 H 77.197996 V 175.28606 H 117.83772 Z M 117.65031,175.09865 H 77.384033 V 46.972787 h 40.266277 z m 0,0"
+ id="path11741" />
+ d="m 94.765228,171.1423 c -0.117133,0 -0.224621,-0.0661 -0.275608,-0.1695 l -1.003212,-2.00505 c -0.07579,-0.15296 -0.01379,-0.33761 0.137802,-0.41479 0.151585,-0.0758 0.337619,-0.0138 0.413413,0.13919 l 0.727605,1.45245 0.726224,-1.45245 c 0.07579,-0.15297 0.261828,-0.21498 0.413414,-0.13919 0.15296,0.0772 0.214972,0.26183 0.137802,0.41479 l -1.001833,2.00505 c -0.05237,0.10336 -0.159854,0.1695 -0.275607,0.1695"
+ id="path11803" />
+ d="m 100.27049,171.1423 c -0.11714,0 -0.22324,-0.0661 -0.275607,-0.1695 l -1.003212,-2.00505 c -0.07579,-0.15296 -0.01379,-0.33761 0.139181,-0.41479 0.151582,-0.0758 0.337619,-0.0138 0.41341,0.13919 l 0.726228,1.45245 0.72623,-1.45245 c 0.0758,-0.15297 0.26182,-0.21498 0.41341,-0.13919 0.15296,0.0772 0.21497,0.26183 0.13918,0.41479 l -1.00321,2.00505 c -0.0524,0.10336 -0.15848,0.1695 -0.27561,0.1695"
+ id="path11805" />
+ d="m 97.667375,171.1423 c -0.72347,0 -1.311892,-0.58705 -1.311892,-1.31051 0,-0.72209 0.588422,-1.31052 1.311892,-1.31052 0.286632,0 0.558105,0.091 0.78686,0.26183 0.135047,0.10197 0.16261,0.29628 0.06063,0.4327 -0.101974,0.13505 -0.294897,0.16261 -0.431324,0.0606 -0.121267,-0.091 -0.264583,-0.13919 -0.416168,-0.13919 -0.383096,0 -0.694531,0.31144 -0.694531,0.69454 0,0.38309 0.311435,0.69452 0.694531,0.69452 0.151585,0 0.294901,-0.0482 0.416168,-0.13918 0.136427,-0.10197 0.32935,-0.0744 0.431324,0.062 0.101978,0.13504 0.07442,0.32935 -0.06063,0.43132 -0.228755,0.17088 -0.500228,0.26183 -0.78686,0.26183"
+ id="path11807" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ d="m 98.087678,169.81662 c 0,0.22187 -0.179148,0.40102 -0.40101,0.40102 -0.220486,0 -0.399633,-0.17915 -0.399633,-0.40102 0,-0.22186 0.179147,-0.401 0.399633,-0.401 0.221862,0 0.40101,0.17914 0.40101,0.401"
+ id="path11809" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ d="m 113.95854,119.20541 c 0,-0.54983 -0.44924,-1.00045 -1.00045,-1.00045 h -7.68119 c -0.55121,0 -1.00046,0.45062 -1.00046,1.00045 v 12.17222 c 0,0.54983 0.44925,1.00045 1.00046,1.00045 h 7.68119 c 0.55121,0 1.00045,-0.45062 1.00045,-1.00045 z m 0,0"
+ id="path11875" />
+ d="m 102.35822,119.20541 c 0,-0.54983 -0.44924,-1.00045 -0.99908,-1.00045 h -7.682564 c -0.549835,0 -1.000456,0.45062 -1.000456,1.00045 v 12.17222 c 0,0.54983 0.450621,1.00045 1.000456,1.00045 h 7.682564 c 0.54984,0 0.99908,-0.45062 0.99908,-1.00045 z m 0,0"
+ id="path11877" />
-
-
-
-
-
-
-
+ d="m 90.75927,119.20541 c 0,-0.54983 -0.450621,-1.00045 -1.000456,-1.00045 h -7.682561 c -0.54984,0 -0.999078,0.45062 -0.999078,1.00045 v 12.17222 c 0,0.54983 0.449238,1.00045 0.999078,1.00045 h 7.682561 c 0.549835,0 1.000456,-0.45062 1.000456,-1.00045 z m 0,0"
+ id="path11879" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ d="m 113.95854,103.20501 c 0,-0.54983 -0.44924,-1.00045 -1.00045,-1.00045 h -7.68119 c -0.55121,0 -1.00046,0.45062 -1.00046,1.00045 v 12.17222 c 0,0.55121 0.44925,1.00045 1.00046,1.00045 h 7.68119 c 0.55121,0 1.00045,-0.44924 1.00045,-1.00045 z m 0,0"
+ id="path11903" />
+ d="m 102.35822,103.20501 c 0,-0.54983 -0.44924,-1.00045 -0.99908,-1.00045 h -7.682564 c -0.549835,0 -1.000456,0.45062 -1.000456,1.00045 v 12.17222 c 0,0.55121 0.450621,1.00045 1.000456,1.00045 h 7.682564 c 0.54984,0 0.99908,-0.44924 0.99908,-1.00045 z m 0,0"
+ id="path11905" />
-
-
-
-
-
-
-
-
-
-
-
-
+ d="m 90.75927,103.20501 c 0,-0.54983 -0.450621,-1.00045 -1.000456,-1.00045 h -7.682561 c -0.54984,0 -0.999078,0.45062 -0.999078,1.00045 v 12.17222 c 0,0.55121 0.449238,1.00045 0.999078,1.00045 h 7.682561 c 0.549835,0 1.000456,-0.44924 1.000456,-1.00045 z m 0,0"
+ id="path11907" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ d="m 113.95854,135.20444 c 0,-0.54984 -0.44924,-0.99908 -1.00045,-0.99908 h -7.68119 c -0.55121,0 -1.00046,0.44924 -1.00046,0.99908 v 12.17359 c 0,0.54984 0.44925,1.00045 1.00046,1.00045 h 7.68119 c 0.55121,0 1.00045,-0.45061 1.00045,-1.00045 z m 0,0"
+ id="path12157" />
+ d="m 102.35822,135.20444 c 0,-0.54984 -0.44924,-0.99908 -0.99908,-0.99908 h -7.682564 c -0.549835,0 -1.000456,0.44924 -1.000456,0.99908 v 12.17359 c 0,0.54984 0.450621,1.00045 1.000456,1.00045 h 7.682564 c 0.54984,0 0.99908,-0.45061 0.99908,-1.00045 z m 0,0"
+ id="path12159" />
-
-
-
-
-
-
-
-
-
-
-
-
+ d="m 90.75927,135.20444 c 0,-0.54984 -0.450621,-0.99908 -1.000456,-0.99908 h -7.682561 c -0.54984,0 -0.999078,0.44924 -0.999078,0.99908 v 12.17359 c 0,0.54984 0.449238,1.00045 0.999078,1.00045 h 7.682561 c 0.549835,0 1.000456,-0.45061 1.000456,-1.00045 z m 0,0"
+ id="path12161" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ d="m 113.95854,151.20484 c 0,-0.54983 -0.44924,-1.00045 -1.00045,-1.00045 h -7.68119 c -0.55121,0 -1.00046,0.45062 -1.00046,1.00045 v 12.17359 c 0,0.54846 0.44925,0.99908 1.00046,0.99908 h 7.68119 c 0.55121,0 1.00045,-0.45062 1.00045,-0.99908 z m 0,0"
+ id="path12209" />
+ d="m 102.35822,151.20484 c 0,-0.54983 -0.44924,-1.00045 -0.99908,-1.00045 h -7.682564 c -0.549835,0 -1.000456,0.45062 -1.000456,1.00045 v 12.17359 c 0,0.54846 0.450621,0.99908 1.000456,0.99908 h 7.682564 c 0.54984,0 0.99908,-0.45062 0.99908,-0.99908 z m 0,0"
+ id="path12211" />
-
-
-
-
-
-
-
-
-
-
-
-
+ d="m 90.75927,151.20484 c 0,-0.54983 -0.450621,-1.00045 -1.000456,-1.00045 h -7.682561 c -0.54984,0 -0.999078,0.45062 -0.999078,1.00045 v 12.17359 c 0,0.54846 0.449238,0.99908 0.999078,0.99908 h 7.682561 c 0.549835,0 1.000456,-0.45062 1.000456,-0.99908 z m 0,0"
+ id="path12213" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Core/MIDI_CC.cpp b/src/Core/MIDI_CC.cpp
index 184c9032..ffb6d0f3 100644
--- a/src/Core/MIDI_CC.cpp
+++ b/src/Core/MIDI_CC.cpp
@@ -25,6 +25,9 @@ struct MIDI_CC : Module {
MIDI_CC() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+ for (int i = 0; i < 16; i++) {
+ valueFilters[i].lambda = 1 / 0.01f;
+ }
onReset();
}
@@ -45,7 +48,8 @@ struct MIDI_CC : Module {
processMessage(msg);
}
- float lambda = APP->engine->getSampleTime() * 100.f;
+ float deltaTime = APP->engine->getSampleTime();
+
for (int i = 0; i < 16; i++) {
if (!outputs[CC_OUTPUT + i].isConnected())
continue;
@@ -53,7 +57,6 @@ struct MIDI_CC : Module {
int cc = learnedCcs[i];
float value = rescale(values[cc], 0, 127, 0.f, 10.f);
- valueFilters[i].lambda = lambda;
// Detect behavior from MIDI buttons.
if ((lastValues[i] == 0 && values[cc] == 127) || (lastValues[i] == 127 && values[cc] == 0)) {
@@ -62,7 +65,7 @@ struct MIDI_CC : Module {
}
else {
// Smooth value with filter
- valueFilters[i].process(value);
+ valueFilters[i].process(deltaTime, value);
}
lastValues[i] = values[cc];
outputs[CC_OUTPUT + i].setVoltage(valueFilters[i].out);
diff --git a/src/Core/MIDI_CV.cpp b/src/Core/MIDI_CV.cpp
index f3ff420b..b1c12494 100644
--- a/src/Core/MIDI_CV.cpp
+++ b/src/Core/MIDI_CV.cpp
@@ -18,8 +18,8 @@ struct MIDI_CV : Module {
PITCH_OUTPUT,
MOD_OUTPUT,
RETRIGGER_OUTPUT,
- CLOCK_1_OUTPUT,
- CLOCK_2_OUTPUT,
+ CLOCK_OUTPUT,
+ CLOCK_DIV_OUTPUT,
START_OUTPUT,
STOP_OUTPUT,
CONTINUE_OUTPUT,
@@ -31,43 +31,57 @@ struct MIDI_CV : Module {
midi::InputQueue midiInput;
- uint8_t mod = 0;
- dsp::ExponentialFilter modFilter;
+ // std::vector heldNotes;
+
+ int channels;
+ enum PolyMode {
+ ROTATE_MODE,
+ REUSE_MODE,
+ RESET_MODE,
+ REASSIGN_MODE,
+ NUM_POLY_MODES
+ };
+ PolyMode polyMode;
+
+ uint32_t clock = 0;
+ int clockDivision;
+
+ bool pedal = false;
+ uint8_t notes[16] = {60};
+ bool gates[128] = {};
+ uint8_t velocities[128] = {};
+ uint8_t aftertouches[128] = {};
+
uint16_t pitch = 8192;
+ uint8_t mod = 0;
+
dsp::ExponentialFilter pitchFilter;
- dsp::PulseGenerator retriggerPulse;
- dsp::PulseGenerator clockPulses[2];
+ dsp::ExponentialFilter modFilter;
+ dsp::PulseGenerator clockPulse;
+ dsp::PulseGenerator clockDividerPulse;
+ dsp::PulseGenerator retriggerPulses[16];
dsp::PulseGenerator startPulse;
dsp::PulseGenerator stopPulse;
dsp::PulseGenerator continuePulse;
- int clock = 0;
- int divisions[2];
-
- struct NoteData {
- uint8_t velocity = 0;
- uint8_t aftertouch = 0;
- };
- NoteData noteData[128];
- std::vector heldNotes;
- uint8_t lastNote;
- bool pedal;
- bool gate;
MIDI_CV() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
- heldNotes.resize(128, 0);
+ // heldNotes.resize(128, 0);
+ pitchFilter.lambda = 1 / 0.01f;
+ modFilter.lambda = 1 / 0.01f;
onReset();
}
void onReset() override {
- heldNotes.clear();
- lastNote = 60;
- pedal = false;
- gate = false;
- clock = 0;
- divisions[0] = 24;
- divisions[1] = 6;
+ // heldNotes.clear();
+ // lastNote = 60;
+ // pedal = false;
+ // gate = false;
+ // clock = 0;
+ channels = 1;
+ polyMode = RESET_MODE;
+ clockDivision = 24;
midiInput.reset();
}
@@ -78,20 +92,26 @@ struct MIDI_CV : Module {
}
float deltaTime = APP->engine->getSampleTime();
- outputs[CV_OUTPUT].setVoltage((lastNote - 60) / 12.f);
- outputs[GATE_OUTPUT].setVoltage(gate ? 10.f : 0.f);
- outputs[VELOCITY_OUTPUT].setVoltage(rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f));
-
- outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f));
- pitchFilter.lambda = 100.f * deltaTime;
- outputs[PITCH_OUTPUT].setVoltage(pitchFilter.process(rescale(pitch, 0, 16384, -5.f, 5.f)));
- modFilter.lambda = 100.f * deltaTime;
- outputs[MOD_OUTPUT].setVoltage(modFilter.process(rescale(mod, 0, 127, 0.f, 10.f)));
+ outputs[CV_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++) {
+ uint8_t note = notes[c];
+ outputs[CV_OUTPUT].setVoltage((note - 60.f) / 12.f, c);
+ outputs[GATE_OUTPUT].setVoltage(gates[note] ? 10.f : 0.f, c);
+ outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[note], 0, 127, 0.f, 10.f), c);
+ outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[note], 0, 127, 0.f, 10.f), c);
+ outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(deltaTime) ? 10.f : 0.f, c);
+ }
- outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulse.process(deltaTime) ? 10.f : 0.f);
- outputs[CLOCK_1_OUTPUT].setVoltage(clockPulses[0].process(deltaTime) ? 10.f : 0.f);
- outputs[CLOCK_2_OUTPUT].setVoltage(clockPulses[1].process(deltaTime) ? 10.f : 0.f);
+ uint16_t pitchAdjusted = (pitch == 16383) ? 16384 : pitch;
+ outputs[PITCH_OUTPUT].setVoltage(pitchFilter.process(deltaTime, rescale(pitchAdjusted, 0, 1<<14, -5.f, 5.f)));
+ outputs[MOD_OUTPUT].setVoltage(modFilter.process(deltaTime, rescale(mod, 0, 127, 0.f, 10.f)));
+ outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(deltaTime) ? 10.f : 0.f);
+ outputs[CLOCK_DIV_OUTPUT].setVoltage(clockDividerPulse.process(deltaTime) ? 10.f : 0.f);
outputs[START_OUTPUT].setVoltage(startPulse.process(deltaTime) ? 10.f : 0.f);
outputs[STOP_OUTPUT].setVoltage(stopPulse.process(deltaTime) ? 10.f : 0.f);
outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(deltaTime) ? 10.f : 0.f);
@@ -108,7 +128,7 @@ struct MIDI_CV : Module {
// note on
case 0x9: {
if (msg.getValue() > 0) {
- noteData[msg.getNote()].velocity = msg.getValue();
+ velocities[msg.getNote()] = msg.getValue();
pressNote(msg.getNote());
}
else {
@@ -118,8 +138,7 @@ struct MIDI_CV : Module {
} break;
// channel aftertouch
case 0xa: {
- uint8_t note = msg.getNote();
- noteData[note].aftertouch = msg.getValue();
+ aftertouches[msg.getNote()] = msg.getValue();
} break;
// cc
case 0xb: {
@@ -127,7 +146,7 @@ struct MIDI_CV : Module {
} break;
// pitch wheel
case 0xe: {
- pitch = msg.getValue() * 128 + msg.getNote();
+ pitch = ((uint16_t) msg.getValue() << 7) | msg.getNote();
} break;
case 0xf: {
processSystem(msg);
@@ -157,16 +176,11 @@ struct MIDI_CV : Module {
switch (msg.getChannel()) {
// Timing
case 0x8: {
- if (clock % divisions[0] == 0) {
- clockPulses[0].trigger(1e-3);
- }
- if (clock % divisions[1] == 0) {
- clockPulses[1].trigger(1e-3);
- }
- if (++clock >= (24*16*16)) {
- // Avoid overflowing the integer
- clock = 0;
+ clockPulse.trigger(1e-3);
+ if (clock % clockDivision == 0) {
+ clockDividerPulse.trigger(1e-3);
}
+ clock++;
} break;
// Start
case 0xa: {
@@ -180,7 +194,6 @@ struct MIDI_CV : Module {
// Stop
case 0xc: {
stopPulse.trigger(1e-3);
- // Reset timing
clock = 0;
} break;
default: break;
@@ -188,33 +201,37 @@ struct MIDI_CV : Module {
}
void pressNote(uint8_t note) {
- // Remove existing similar note
- auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
- if (it != heldNotes.end())
- heldNotes.erase(it);
- // Push note
- heldNotes.push_back(note);
- lastNote = note;
- gate = true;
- retriggerPulse.trigger(1e-3);
+ int c = 0;
+ notes[c] = note;
+ gates[note] = true;
+ // // Remove existing similar note
+ // auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
+ // if (it != heldNotes.end())
+ // heldNotes.erase(it);
+ // // Push note
+ // heldNotes.push_back(note);
+ // lastNote = note;
+ // gate = true;
+ retriggerPulses[c].trigger(1e-3);
}
void releaseNote(uint8_t note) {
- // Remove the note
- auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
- if (it != heldNotes.end())
- heldNotes.erase(it);
- // Hold note if pedal is pressed
- if (pedal)
- return;
- // Set last note
- if (!heldNotes.empty()) {
- lastNote = heldNotes[heldNotes.size() - 1];
- gate = true;
- }
- else {
- gate = false;
- }
+ gates[note] = false;
+ // // Remove the note
+ // auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
+ // if (it != heldNotes.end())
+ // heldNotes.erase(it);
+ // // Hold note if pedal is pressed
+ // if (pedal)
+ // return;
+ // // Set last note
+ // if (!heldNotes.empty()) {
+ // lastNote = heldNotes[heldNotes.size() - 1];
+ // gate = true;
+ // }
+ // else {
+ // gate = false;
+ // }
}
void pressPedal() {
@@ -223,32 +240,30 @@ struct MIDI_CV : Module {
void releasePedal() {
pedal = false;
- releaseNote(255);
+ // releaseNote(255);
}
json_t *dataToJson() override {
json_t *rootJ = json_object();
-
- json_t *divisionsJ = json_array();
- for (int i = 0; i < 2; i++) {
- json_t *divisionJ = json_integer(divisions[i]);
- json_array_append_new(divisionsJ, divisionJ);
- }
- json_object_set_new(rootJ, "divisions", divisionsJ);
-
+ json_object_set_new(rootJ, "channels", json_integer(channels));
+ json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
+ json_object_set_new(rootJ, "clockDivision", json_integer(clockDivision));
json_object_set_new(rootJ, "midi", midiInput.toJson());
return rootJ;
}
void dataFromJson(json_t *rootJ) override {
- json_t *divisionsJ = json_object_get(rootJ, "divisions");
- if (divisionsJ) {
- for (int i = 0; i < 2; i++) {
- json_t *divisionJ = json_array_get(divisionsJ, i);
- if (divisionJ)
- divisions[i] = json_integer_value(divisionJ);
- }
- }
+ json_t *channelsJ = json_object_get(rootJ, "channels");
+ if (channelsJ)
+ channels = json_integer_value(channelsJ);
+
+ json_t *polyModeJ = json_object_get(rootJ, "polyMode");
+ if (polyModeJ)
+ polyMode = (PolyMode) json_integer_value(polyModeJ);
+
+ json_t *clockDivisionJ = json_object_get(rootJ, "clockDivision");
+ if (clockDivisionJ)
+ clockDivision = json_integer_value(clockDivisionJ);
json_t *midiJ = json_object_get(rootJ, "midi");
if (midiJ)
@@ -257,6 +272,96 @@ struct MIDI_CV : Module {
};
+struct ClockDivisionValueItem : MenuItem {
+ MIDI_CV *module;
+ int clockDivision;
+ void onAction(const event::Action &e) override {
+ module->clockDivision = clockDivision;
+ }
+};
+
+
+struct ClockDivisionItem : MenuItem {
+ MIDI_CV *module;
+ Menu *createChildMenu() override {
+ Menu *menu = new Menu;
+ std::vector divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1};
+ std::vector divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
+ for (size_t i = 0; i < divisions.size(); i++) {
+ ClockDivisionValueItem *item = new ClockDivisionValueItem;
+ item->text = divisionNames[i];
+ item->rightText = CHECKMARK(module->clockDivision == divisions[i]);
+ item->module = module;
+ item->clockDivision = divisions[i];
+ menu->addChild(item);
+ }
+ return menu;
+ }
+};
+
+
+struct ChannelValueItem : MenuItem {
+ MIDI_CV *module;
+ int channels;
+ void onAction(const event::Action &e) override {
+ module->channels = channels;
+ }
+};
+
+
+struct ChannelItem : MenuItem {
+ MIDI_CV *module;
+ Menu *createChildMenu() override {
+ Menu *menu = new Menu;
+ for (int channels = 1; channels <= 16; channels++) {
+ ChannelValueItem *item = new ChannelValueItem;
+ if (channels == 1)
+ item->text = "Monophonic";
+ else
+ item->text = string::f("%d", channels);
+ item->rightText = CHECKMARK(module->channels == channels);
+ item->module = module;
+ item->channels = channels;
+ menu->addChild(item);
+ }
+ return menu;
+ }
+};
+
+
+struct PolyModeValueItem : MenuItem {
+ MIDI_CV *module;
+ MIDI_CV::PolyMode polyMode;
+ void onAction(const event::Action &e) override {
+ module->polyMode = polyMode;
+ }
+};
+
+
+struct PolyModeItem : MenuItem {
+ MIDI_CV *module;
+ Menu *createChildMenu() override {
+ Menu *menu = new Menu;
+ std::vector polyModeNames = {
+ "Rotate",
+ "Reuse",
+ "Reset",
+ "Reassign",
+ };
+ for (int i = 0; i < MIDI_CV::NUM_POLY_MODES; i++) {
+ MIDI_CV::PolyMode polyMode = (MIDI_CV::PolyMode) i;
+ PolyModeValueItem *item = new PolyModeValueItem;
+ item->text = polyModeNames[i];
+ item->rightText = CHECKMARK(module->polyMode == polyMode);
+ item->module = module;
+ item->polyMode = polyMode;
+ menu->addChild(item);
+ }
+ return menu;
+ }
+};
+
+
struct MIDI_CVWidget : ModuleWidget {
MIDI_CVWidget(MIDI_CV *module) {
setModule(module);
@@ -273,9 +378,9 @@ struct MIDI_CVWidget : ModuleWidget {
addOutput(createOutput(mm2px(Vec(4.61505, 76.1449)), module, MIDI_CV::AFTERTOUCH_OUTPUT));
addOutput(createOutput(mm2px(Vec(16.214, 76.1449)), module, MIDI_CV::PITCH_OUTPUT));
addOutput(createOutput(mm2px(Vec(27.8143, 76.1449)), module, MIDI_CV::MOD_OUTPUT));
- addOutput(createOutput(mm2px(Vec(4.61505, 92.1439)), module, MIDI_CV::RETRIGGER_OUTPUT));
- addOutput(createOutput(mm2px(Vec(16.214, 92.1439)), module, MIDI_CV::CLOCK_1_OUTPUT));
- addOutput(createOutput(mm2px(Vec(27.8143, 92.1439)), module, MIDI_CV::CLOCK_2_OUTPUT));
+ addOutput(createOutput(mm2px(Vec(4.61505, 92.1439)), module, MIDI_CV::CLOCK_OUTPUT));
+ addOutput(createOutput(mm2px(Vec(16.214, 92.1439)), module, MIDI_CV::CLOCK_DIV_OUTPUT));
+ addOutput(createOutput(mm2px(Vec(27.8143, 92.1439)), module, MIDI_CV::RETRIGGER_OUTPUT));
addOutput(createOutput(mm2px(Vec(4.61505, 108.144)), module, MIDI_CV::START_OUTPUT));
addOutput(createOutput(mm2px(Vec(16.214, 108.144)), module, MIDI_CV::STOP_OUTPUT));
addOutput(createOutput(mm2px(Vec(27.8143, 108.144)), module, MIDI_CV::CONTINUE_OUTPUT));
@@ -290,40 +395,20 @@ struct MIDI_CVWidget : ModuleWidget {
void appendContextMenu(Menu *menu) override {
MIDI_CV *module = dynamic_cast(this->module);
- struct ClockDivisionItem : MenuItem {
- MIDI_CV *module;
- int index;
- int division;
- void onAction(const event::Action &e) override {
- module->divisions[index] = division;
- }
- };
+ menu->addChild(new MenuEntry);
- struct ClockItem : MenuItem {
- MIDI_CV *module;
- int index;
- Menu *createChildMenu() override {
- Menu *menu = new Menu;
- std::vector divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1};
- std::vector divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
- for (size_t i = 0; i < divisions.size(); i++) {
- ClockDivisionItem *item = createMenuItem(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i]));
- item->module = module;
- item->index = index;
- item->division = divisions[i];
- menu->addChild(item);
- }
- return menu;
- }
- };
+ ClockDivisionItem *clockDivisionItem = new ClockDivisionItem;
+ clockDivisionItem->text = "CLK/N divider";
+ clockDivisionItem->module = module;
+ menu->addChild(clockDivisionItem);
- menu->addChild(construct());
- for (int i = 0; i < 2; i++) {
- ClockItem *item = createMenuItem(string::f("CLK %d rate", i + 1));
- item->module = module;
- item->index = i;
- menu->addChild(item);
- }
+ ChannelItem *channelItem = createMenuItem("Polyphony channels");
+ channelItem->module = module;
+ menu->addChild(channelItem);
+
+ PolyModeItem *polyModeItem = createMenuItem("Polyphony mode");
+ polyModeItem->module = module;
+ menu->addChild(polyModeItem);
}
};
diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp
index 6677a0b3..7e3df0a2 100644
--- a/src/app/ModuleWidget.cpp
+++ b/src/app/ModuleWidget.cpp
@@ -399,7 +399,7 @@ void ModuleWidget::fromJson(json_t *rootJ) {
if (versionJ) {
std::string version = json_string_value(versionJ);
if (version != model->plugin->version) {
- INFO("Patch created with %s version %s, using version %s.", pluginSlug.c_str(), version.c_str(), model->plugin->version.c_str());
+ INFO("Patch created with %s v%s, currently using v%s.", pluginSlug.c_str(), version.c_str(), model->plugin->version.c_str());
}
}
@@ -677,7 +677,7 @@ void ModuleWidget::createContextMenu() {
assert(model);
ui::MenuLabel *menuLabel = new ui::MenuLabel;
- menuLabel->text = model->plugin->name + " " + model->name + " " + model->plugin->version;
+ menuLabel->text = model->plugin->name + " " + model->name + " v" + model->plugin->version;
menu->addChild(menuLabel);
ModuleResetItem *resetItem = new ModuleResetItem;
diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp
index 6cc84b66..90e64db2 100644
--- a/src/app/Scene.cpp
+++ b/src/app/Scene.cpp
@@ -58,12 +58,13 @@ void Scene::step() {
// Autosave every 15 seconds
int frame = APP->window->frame;
if (frame > 0 && frame % (60 * 15) == 0) {
+ // DEBUG("frame %d", frame);
APP->patch->save(asset::user("autosave.vcv"));
settings.save(asset::user("settings.json"));
}
// Set zoom every few frames
- if (APP->window->frame % 10 == 0)
+ if (frame % 10 == 0)
zoomWidget->setZoom(std::round(settings.zoom * 100) / 100);
// Request latest version from server
@@ -75,7 +76,7 @@ void Scene::step() {
// Version popup message
if (!latestVersion.empty()) {
- std::string versionMessage = string::f("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", latestVersion.c_str(), app::VERSION);
+ std::string versionMessage = string::f("Rack v%s is available.\n\nYou have Rack v%s.\n\nClose Rack and download new version on the website?", latestVersion.c_str(), app::APP_VERSION);
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) {
std::thread t(system::openBrowser, "https://vcvrack.com/");
t.detach();
@@ -172,7 +173,7 @@ void Scene::runCheckVersion() {
json_t *versionJ = json_object_get(versionResJ, "version");
if (versionJ) {
std::string version = json_string_value(versionJ);
- if (version != app::VERSION) {
+ if (version != app::APP_VERSION) {
latestVersion = version;
}
}
diff --git a/src/main.cpp b/src/main.cpp
index bc0620e3..6e8e48fb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -65,7 +65,7 @@ int main(int argc, char *argv[]) {
logger::init(devMode);
// Log environment
- INFO("%s %s", app::NAME, app::VERSION);
+ INFO("%s v%s", app::APP_NAME, app::APP_VERSION);
if (devMode)
INFO("Development mode");
INFO("System directory: %s", asset::systemDir.c_str());
diff --git a/src/patch.cpp b/src/patch.cpp
index d1076d6c..2f929469 100644
--- a/src/patch.cpp
+++ b/src/patch.cpp
@@ -223,7 +223,7 @@ json_t *PatchManager::toJson() {
json_t *rootJ = json_object();
// version
- json_t *versionJ = json_string(app::VERSION);
+ json_t *versionJ = json_string(app::APP_VERSION);
json_object_set_new(rootJ, "version", versionJ);
// Merge with RackWidget JSON
@@ -243,8 +243,8 @@ void PatchManager::fromJson(json_t *rootJ) {
json_t *versionJ = json_object_get(rootJ, "version");
if (versionJ)
version = json_string_value(versionJ);
- if (version != app::VERSION) {
- INFO("Patch made with Rack version %s, current Rack version is %s", version.c_str(), app::VERSION);
+ if (version != app::APP_VERSION) {
+ INFO("Patch was made with Rack v%s, current Rack version is v%s", version.c_str(), app::APP_VERSION);
}
// Detect old patches with ModuleWidget::params/inputs/outputs indices.
diff --git a/src/plugin.cpp b/src/plugin.cpp
index e073f390..52fc7b7c 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -132,7 +132,7 @@ static bool loadPlugin(std::string path) {
// Add plugin to list
plugins.push_back(plugin);
- INFO("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str());
+ INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str());
return true;
}
diff --git a/src/window.cpp b/src/window.cpp
index 66fb4d67..84cfa0d7 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -332,9 +332,9 @@ void Window::run() {
// Set window title
std::string windowTitle;
- windowTitle = app::NAME;
- windowTitle += " ";
- windowTitle += app::VERSION;
+ windowTitle = app::APP_NAME;
+ windowTitle += " v";
+ windowTitle += app::APP_VERSION;
if (!APP->patch->path.empty()) {
windowTitle += " - ";
windowTitle += string::filename(APP->patch->path);