diff --git a/src/ADSR.cpp b/src/ADSR.cpp index 5113070..c4824e7 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -221,28 +221,10 @@ struct ADSR : Module { static constexpr float maxStageTime = 10.f; // in seconds // given a value from the slider and/or cv (rescaled to range 0 to 1), transform into the appropriate time in seconds - static float convertCVToTimeInSeconds(float cv) { - float cv2 = cv * cv; - // according to hardware, slider appears to respond roughly as a quartic - return minStageTime + (maxStageTime - minStageTime) * cv2 * cv2; + static float convertCVToTimeInSeconds(float cv) { + return minStageTime * std::pow(maxStageTime / minStageTime, cv); } - // given a time in seconds, transform into the appropriate CV/slider value (in range 0, 1) - static float convertTimeInSecondsToCV(float timeInSecs) { - // according to hardware, slider appears to respond roughly as a quartic - return std::pow((timeInSecs - minStageTime) / (maxStageTime - minStageTime), 0.25f); - } - - struct StageTimeParam : ParamQuantity { - std::string getDisplayValueString() override { - return string::f("%.3f", convertCVToTimeInSeconds(getValue())); - } - - void setDisplayValue(float v) override { - ParamQuantity::setDisplayValue(convertTimeInSecondsToCV(v)); - } - }; - struct TriggerGateParamQuantity : ParamQuantity { std::string getDisplayValueString() override { switch ((EnvelopeMode) getValue()) { @@ -259,10 +241,10 @@ struct ADSR : Module { configParam(MANUAL_TRIGGER_PARAM, 0.f, 1.f, 0.f, "Trigger envelope"); configParam(SHAPE_PARAM, 0.f, 1.f, 0.f, "Envelope shape"); - configParam(ATTACK_PARAM, 0.f, 1.f, 0.f, "Attack time", "s"); - configParam(DECAY_PARAM, 0.f, 1.f, 0.f, "Decay time", "s"); + configParam(ATTACK_PARAM, 0.f, 1.f, 0.f, "Attack time", "s", maxStageTime / minStageTime, minStageTime); + configParam(DECAY_PARAM, 0.f, 1.f, 0.f, "Decay time", "s", maxStageTime / minStageTime, minStageTime); configParam(SUSTAIN_PARAM, 0.f, 1.f, 0.f, "Sustain level", "%", 0.f, 100.f); - configParam(RELEASE_PARAM, 0.f, 1.f, 0.f, "Release time", "s"); + configParam(RELEASE_PARAM, 0.f, 1.f, 0.f, "Release time", "s", maxStageTime / minStageTime, minStageTime); cvDivider.setDivision(16); } diff --git a/src/Mex.cpp b/src/Mex.cpp index b84a5f4..f69b253 100644 --- a/src/Mex.cpp +++ b/src/Mex.cpp @@ -28,10 +28,6 @@ struct Mex : Module { dsp::SchmittTrigger gateInTrigger; - // this expander communicates with the mother module (Muxlicer) purely - // through this pointer (it cannot modify Muxlicer, read-only) - Muxlicer const* mother = nullptr; - struct GateSwitchParamQuantity : ParamQuantity { std::string getDisplayValueString() override { @@ -52,72 +48,59 @@ struct Mex : Module { } } + Muxlicer* findHostModulePtr(Module* module) { + if (module) { + if (module->leftExpander.module) { + // if it's Muxlicer, we're done + if (module->leftExpander.module->model == modelMuxlicer) { + return reinterpret_cast(module->leftExpander.module); + } + // if it's Mex, keep recursing + else if (module->leftExpander.module->model == modelMex) { + return findHostModulePtr(module->leftExpander.module); + } + } + } + + return nullptr; + } + void process(const ProcessArgs& args) override { for (int i = 0; i < 8; i++) { lights[i].setBrightness(0.f); } - if (leftExpander.module) { - // this expander is active if: - // * muxlicer is to the left or - if (leftExpander.module->model == modelMuxlicer) { - mother = reinterpret_cast(leftExpander.module); - } - // * an active Mex is to the left - else if (leftExpander.module->model == modelMex) { - Mex* moduleMex = reinterpret_cast(leftExpander.module); - if (moduleMex) { - mother = moduleMex->mother; - } - } - else { - mother = nullptr; - } + Muxlicer const* mother = findHostModulePtr(this); - if (mother) { + if (mother) { - float gate = 0.f; + float gate = 0.f; - if (mother->playState != Muxlicer::STATE_STOPPED) { - const int currentStep = clamp(mother->addressIndex, 0, 7); - StepState state = (StepState) params[STEP_PARAM + currentStep].getValue(); - if (state == MUXLICER_MODE) { - gate = mother->isAllGatesOutHigh; + if (mother->playState != Muxlicer::STATE_STOPPED) { + const int currentStep = clamp(mother->addressIndex, 0, 7); + StepState state = (StepState) params[STEP_PARAM + currentStep].getValue(); + if (state == MUXLICER_MODE) { + gate = mother->isAllGatesOutHigh; + } + else if (state == GATE_IN_MODE) { + // gate in will convert non-gate signals to gates (via schmitt trigger) + // if input is present + if (inputs[GATE_IN_INPUT].isConnected()) { + gateInTrigger.process(inputs[GATE_IN_INPUT].getVoltage()); + gate = gateInTrigger.isHigh(); } - else if (state == GATE_IN_MODE) { - // gate in will convert non-gate signals to gates (via schmitt trigger) - // if input is present - if (inputs[GATE_IN_INPUT].isConnected()) { - gateInTrigger.process(inputs[GATE_IN_INPUT].getVoltage()); - gate = gateInTrigger.isHigh(); - } - // otherwise the main Muxlicer output clock (including divisions/multiplications) - // is normalled in - else { - gate = mother->isOutputClockHigh; - } + // otherwise the main Muxlicer output clock (including divisions/multiplications) + // is normalled in + else { + gate = mother->isOutputClockHigh; } - - lights[currentStep].setBrightness(gate); } - outputs[OUT_OUTPUT].setVoltage(gate * 10.f); - - // if there's another Mex to the right, update it to also point at the message we just received, - // i.e. just forward on the message - if (rightExpander.module && rightExpander.module->model == modelMex) { - Mex* moduleMexRight = reinterpret_cast(rightExpander.module); - - // assign current message pointer to the right expander - moduleMexRight->mother = mother; - } + lights[currentStep].setBrightness(gate); } - } - // if we've become disconnected, i.e. no module to the left, then break the connection - // which will propagate to all expanders to the right - else { - mother = nullptr; + + outputs[OUT_OUTPUT].setVoltage(gate * 10.f); } } }; diff --git a/src/Morphader.cpp b/src/Morphader.cpp index 041dcb2..145f122 100644 --- a/src/Morphader.cpp +++ b/src/Morphader.cpp @@ -1,5 +1,6 @@ #include "plugin.hpp" +using simd::float_4; // equal sum crossfade, -1 <= p <= 1 template @@ -16,43 +17,8 @@ inline T equalPowerCrossfade(T a, T b, const float p) { // TExponentialSlewLimiter doesn't appear to work as is required for this application. // I think it is due to the absence of the logic that stops the output rising / falling too quickly, -// i.e. faster than the original signal? I think the following modification would yield the -// expected behaviour (see 2 lines in process). -/* -template -struct TExponentialSlewLimiter { - T out = 0.f; - T riseLambda = 0.f; - T fallLambda = 0.f; - - void reset() { - out = 0.f; - } - - void setRiseFall(T riseLambda, T fallLambda) { - this->riseLambda = riseLambda; - this->fallLambda = fallLambda; - } - T process(T deltaTime, T in) { - // MODIFICATION: - T rising = in > out; - T lambda = simd::ifelse(rising, riseLambda, fallLambda); - T y = out + (in - out) * lambda * deltaTime; - // If the change from the old out to the new out is too small for floats, set `in` directly. - out = simd::ifelse(out == y, in, y); - - // MODIFICATION: - out = simd::ifelse(rising, simd::ifelse(out > in, in, y), simd::ifelse(out < in, in, y)); - return out; - } - DEPRECATED T process(T in) { - return process(1.f, in); - } -}; -*/ - -// For now, I provide this implementation (essentialy the same as SlewLimiter.cpp), but ideally I -// would replace with updated library function +// i.e. faster than the original signal? For now, we use this implementation (essentialy the same as +// SlewLimiter.cpp) struct ExpLogSlewLimiter { float out = 0.f; @@ -114,7 +80,7 @@ struct Morphader : Module { }; static const int NUM_MIXER_CHANNELS = 4; - const simd::float_4 normal10VSimd = {10.f}; + const float_4 normal10VSimd = {10.f}; ExpLogSlewLimiter slewLimiter; // minimum and maximum slopes in volts per second, they specify the time to get @@ -152,9 +118,9 @@ struct Morphader : Module { } // determine the cross-fade between -1 (A) and +1 (B) for each of the 4 channels - simd::float_4 determineChannelCrossfades(const float deltaTime) { + float_4 determineChannelCrossfades(const float deltaTime) { - simd::float_4 channelCrossfades = {}; + float_4 channelCrossfades = {}; const float slewLambda = 2.0f / params[FADER_LAG_PARAM].getValue(); slewLimiter.setSlew(slewLambda); const float masterCrossfadeValue = slewLimiter.process(deltaTime, params[FADER_PARAM].getValue()); @@ -192,8 +158,8 @@ struct Morphader : Module { void process(const ProcessArgs& args) override { int maxChannels = 1; - simd::float_4 mix[4] = {0.f}; - const simd::float_4 channelCrossfades = determineChannelCrossfades(args.sampleTime); + float_4 mix[4] = {}; + const float_4 channelCrossfades = determineChannelCrossfades(args.sampleTime); for (int i = 0; i < NUM_MIXER_CHANNELS; i++) { @@ -204,10 +170,10 @@ struct Morphader : Module { maxChannels = std::max(maxChannels, channels); } - simd::float_4 out[4] = {0.f}; + float_4 out[4] = {}; for (int c = 0; c < channels; c += 4) { - simd::float_4 inA = inputs[A_INPUT + i].getNormalVoltageSimd(normal10VSimd, c) * params[A_LEVEL + i].getValue(); - simd::float_4 inB = inputs[B_INPUT + i].getNormalVoltageSimd(normal10VSimd, c) * params[B_LEVEL + i].getValue(); + float_4 inA = inputs[A_INPUT + i].getNormalVoltageSimd(normal10VSimd, c) * params[A_LEVEL + i].getValue(); + float_4 inB = inputs[B_INPUT + i].getNormalVoltageSimd(normal10VSimd, c) * params[B_LEVEL + i].getValue(); switch (static_cast(params[MODE + i].getValue())) { case CV_MODE: { @@ -221,7 +187,9 @@ struct Morphader : Module { out[c / 4] = equalPowerCrossfade(inA, inB, channelCrossfades[i]); break; } - default: assert(false); + default: { + out[c / 4] = 0.f; + } } } @@ -257,7 +225,11 @@ struct Morphader : Module { lights[B_LED + i].setBrightness(equalSumCrossfade(0.f, 1.f, channelCrossfades[i])); break; } - default: assert(false); + default: { + lights[A_LED + i].setBrightness(0.f); + lights[B_LED + i].setBrightness(0.f); + break; + } } } // end loop over mixer channels } diff --git a/src/Muxlicer.hpp b/src/Muxlicer.hpp index 920d155..ef38195 100644 --- a/src/Muxlicer.hpp +++ b/src/Muxlicer.hpp @@ -641,25 +641,39 @@ struct Muxlicer : Module { void dataFromJson(json_t* rootJ) override { json_t* modeJ = json_object_get(rootJ, "modeCOMIO"); - modeCOMIO = (Muxlicer::ModeCOMIO) json_integer_value(modeJ); + if (modeJ) { + modeCOMIO = (Muxlicer::ModeCOMIO) json_integer_value(modeJ); + } json_t* quadraticJ = json_object_get(rootJ, "quadraticGatesOnly"); - quadraticGatesOnly = json_boolean_value(quadraticJ); + if (quadraticJ) { + quadraticGatesOnly = json_boolean_value(quadraticJ); + } json_t* allInNormalVoltageJ = json_object_get(rootJ, "allInNormalVoltage"); - allInNormalVoltage = json_integer_value(allInNormalVoltageJ); + if (allInNormalVoltageJ) { + allInNormalVoltage = json_integer_value(allInNormalVoltageJ); + } json_t* mainClockMultDivJ = json_object_get(rootJ, "mainClockMultDiv"); - mainClockMultDiv.multDiv = json_integer_value(mainClockMultDivJ); + if (mainClockMultDivJ) { + mainClockMultDiv.multDiv = json_integer_value(mainClockMultDivJ); + } json_t* outputClockMultDivJ = json_object_get(rootJ, "outputClockMultDiv"); - outputClockMultDiv.multDiv = json_integer_value(outputClockMultDivJ); + if (outputClockMultDivJ) { + outputClockMultDiv.multDiv = json_integer_value(outputClockMultDivJ); + } json_t* playStateJ = json_object_get(rootJ, "playState"); - playState = (PlayState) json_integer_value(playStateJ); + if (playStateJ) { + playState = (PlayState) json_integer_value(playStateJ); + } json_t* outputClockFollowsPlayModeJ = json_object_get(rootJ, "outputClockFollowsPlayMode"); - outputClockFollowsPlayMode = json_boolean_value(outputClockFollowsPlayModeJ); + if (outputClockFollowsPlayModeJ) { + outputClockFollowsPlayMode = json_boolean_value(outputClockFollowsPlayModeJ); + } updateParamFromMainClockMultDiv(); }