MIDI-Map: fix param/filter value initialization. MIDI-CV: refactor.tags/v1.1.4
@@ -68,6 +68,10 @@ struct TExponentialFilter { | |||||
this->lambda = lambda; | this->lambda = lambda; | ||||
} | } | ||||
void setTau(T tau) { | |||||
this->lambda = 1 / tau; | |||||
} | |||||
T process(T deltaTime, T in) { | T process(T deltaTime, T in) { | ||||
T y = out + (in - out) * lambda * deltaTime; | T y = out + (in - out) * lambda * deltaTime; | ||||
// If no change was made between the old and new output, assume T granularity is too small and snap output to input | // If no change was made between the old and new output, assume T granularity is too small and snap output to input | ||||
@@ -98,6 +102,10 @@ struct TPeakFilter { | |||||
this->lambda = lambda; | this->lambda = lambda; | ||||
} | } | ||||
void setTau(T tau) { | |||||
this->lambda = 1 / tau; | |||||
} | |||||
T process(T deltaTime, T in) { | T process(T deltaTime, T in) { | ||||
T y = out + (in - out) * lambda * deltaTime; | T y = out + (in - out) * lambda * deltaTime; | ||||
out = simd::fmax(y, in); | out = simd::fmax(y, in); | ||||
@@ -25,12 +25,11 @@ struct MIDI_CC : Module { | |||||
int learningId; | int learningId; | ||||
int learnedCcs[16]; | int learnedCcs[16]; | ||||
dsp::ExponentialFilter valueFilters[16]; | dsp::ExponentialFilter valueFilters[16]; | ||||
int8_t lastValues[16] = {}; | |||||
MIDI_CC() { | MIDI_CC() { | ||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
valueFilters[i].lambda = 1 / 0.01f; | |||||
valueFilters[i].setTau(1 / 30.f); | |||||
} | } | ||||
onReset(); | onReset(); | ||||
} | } | ||||
@@ -57,11 +56,10 @@ struct MIDI_CC : Module { | |||||
continue; | continue; | ||||
int cc = learnedCcs[i]; | int cc = learnedCcs[i]; | ||||
float value = rescale(values[cc], 0, 127, 0.f, 10.f); | |||||
float value = values[cc] / 127.f; | |||||
// Detect behavior from MIDI buttons. | // Detect behavior from MIDI buttons. | ||||
if ((lastValues[i] == 0 && values[cc] == 127) || (lastValues[i] == 127 && values[cc] == 0)) { | |||||
if (std::fabs(valueFilters[i].out - value) >= 1.f) { | |||||
// Jump value | // Jump value | ||||
valueFilters[i].out = value; | valueFilters[i].out = value; | ||||
} | } | ||||
@@ -69,8 +67,7 @@ struct MIDI_CC : Module { | |||||
// Smooth value with filter | // Smooth value with filter | ||||
valueFilters[i].process(args.sampleTime, value); | valueFilters[i].process(args.sampleTime, value); | ||||
} | } | ||||
lastValues[i] = values[cc]; | |||||
outputs[CC_OUTPUT + i].setVoltage(valueFilters[i].out); | |||||
outputs[CC_OUTPUT + i].setVoltage(valueFilters[i].out * 10.f); | |||||
} | } | ||||
} | } | ||||
@@ -74,8 +74,8 @@ struct MIDI_CV : Module { | |||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
heldNotes.reserve(128); | heldNotes.reserve(128); | ||||
for (int c = 0; c < 16; c++) { | for (int c = 0; c < 16; c++) { | ||||
pitchFilters[c].lambda = 1 / 0.01f; | |||||
modFilters[c].lambda = 1 / 0.01f; | |||||
pitchFilters[c].setTau(1 / 30.f); | |||||
modFilters[c].setTau(1 / 30.f); | |||||
} | } | ||||
onReset(); | onReset(); | ||||
} | } | ||||
@@ -42,6 +42,8 @@ struct MIDI_Map : Module { | |||||
int8_t values[128]; | int8_t values[128]; | ||||
/** The smoothing processor (normalized between 0 and 1) of each channel */ | /** The smoothing processor (normalized between 0 and 1) of each channel */ | ||||
dsp::ExponentialFilter valueFilters[MAX_CHANNELS]; | dsp::ExponentialFilter valueFilters[MAX_CHANNELS]; | ||||
bool filterInitialized[MAX_CHANNELS] = {}; | |||||
dsp::ClockDivider divider; | |||||
MIDI_Map() { | MIDI_Map() { | ||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
@@ -49,6 +51,10 @@ struct MIDI_Map : Module { | |||||
paramHandles[id].color = nvgRGB(0xff, 0xff, 0x40); | paramHandles[id].color = nvgRGB(0xff, 0xff, 0x40); | ||||
APP->engine->addParamHandle(¶mHandles[id]); | APP->engine->addParamHandle(¶mHandles[id]); | ||||
} | } | ||||
for (int i = 0; i < MAX_CHANNELS; i++) { | |||||
valueFilters[i].setTau(1 / 30.f); | |||||
} | |||||
divider.setDivision(32); | |||||
onReset(); | onReset(); | ||||
} | } | ||||
@@ -71,38 +77,46 @@ struct MIDI_Map : Module { | |||||
} | } | ||||
void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
midi::Message msg; | |||||
while (midiInput.shift(&msg)) { | |||||
processMessage(msg); | |||||
} | |||||
if (divider.process()) { | |||||
midi::Message msg; | |||||
while (midiInput.shift(&msg)) { | |||||
processMessage(msg); | |||||
} | |||||
// Step channels | |||||
for (int id = 0; id < mapLen; id++) { | |||||
int cc = ccs[id]; | |||||
if (cc < 0) | |||||
continue; | |||||
// Check if CC value has been set | |||||
if (values[cc] < 0) | |||||
continue; | |||||
// Get Module | |||||
Module* module = paramHandles[id].module; | |||||
if (!module) | |||||
continue; | |||||
// Get ParamQuantity | |||||
int paramId = paramHandles[id].paramId; | |||||
ParamQuantity* paramQuantity = module->paramQuantities[paramId]; | |||||
if (!paramQuantity) | |||||
continue; | |||||
if (!paramQuantity->isBounded()) | |||||
continue; | |||||
// Set ParamQuantity | |||||
float v = rescale(values[cc], 0, 127, 0.f, 1.f); | |||||
v = valueFilters[id].process(args.sampleTime, v); | |||||
paramQuantity->setScaledValue(v); | |||||
// Step channels | |||||
for (int id = 0; id < mapLen; id++) { | |||||
int cc = ccs[id]; | |||||
if (cc < 0) | |||||
continue; | |||||
// Get Module | |||||
Module* module = paramHandles[id].module; | |||||
if (!module) | |||||
continue; | |||||
// Get ParamQuantity | |||||
int paramId = paramHandles[id].paramId; | |||||
ParamQuantity* paramQuantity = module->paramQuantities[paramId]; | |||||
if (!paramQuantity) | |||||
continue; | |||||
if (!paramQuantity->isBounded()) | |||||
continue; | |||||
// Set filter from param value if filter is uninitialized | |||||
if (!filterInitialized[id]) { | |||||
valueFilters[id].out = paramQuantity->getScaledValue(); | |||||
filterInitialized[id] = true; | |||||
} | |||||
// Set param if value has been initialized | |||||
if (values[cc] >= 0) { | |||||
float v = values[cc] / 127.f; | |||||
v = valueFilters[id].process(args.sampleTime * divider.getDivision(), v); | |||||
paramQuantity->setScaledValue(v); | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
void processMessage(midi::Message msg) { | void processMessage(midi::Message msg) { | ||||
// DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.getChannel(), msg.getNote(), msg.getValue()); | |||||
switch (msg.getStatus()) { | switch (msg.getStatus()) { | ||||
// cc | // cc | ||||
case 0xb: { | case 0xb: { | ||||