Browse Source

Tuning of Morphader

* more efficient crossfade function
* correcting crossfade CV logic
* added "differences from hardware section"
tags/v2.1.0
hemmer 4 years ago
parent
commit
34933da038
4 changed files with 47 additions and 39 deletions
  1. +14
    -1
      README.md
  2. +0
    -7
      src/ABC.cpp
  3. +13
    -16
      src/Morphader.cpp
  4. +20
    -15
      src/plugin.hpp

+ 14
- 1
README.md View File

@@ -2,4 +2,17 @@

Based on [Befaco](http://www.befaco.org/) Eurorack modules.

[VCV Library page](https://library.vcvrack.com/Befaco)
[VCV Library page](https://library.vcvrack.com/Befaco)


## Differences with hardware

We have tried to make the VCV implementations as authentic as possible, however there are some minor changes that have been made (either for usuability or to bring modules in line with VCV rack conventions and standards).

* Sampling Modulator removes the DC offset of "Clock Out" and "Trigg Out" to allow these to be easily used as oscillators - the legacy behaviour (0 - 10V pulses) can be selected via the context menu.

* The hardware version of Morphader accepts 0-8V CV for the crossfade control, here we widen this to accept 0-10V.

* Chopping Kinky hardward is DC coupled, but we add the option (default disabled) to remove this offset.

* The hardware Muxlicer assigns multiple functions to the "Speed Div/Mult" dial, that cannot be reproduced with a single mouse click. Some of these have been moved to the context menu, specifically: quadratic gates, the "All In" normalled voltage, and the output clock division/mult.

+ 0
- 7
src/ABC.cpp View File

@@ -11,13 +11,6 @@ static T clip(T x) {
/ (1.0f + 1.54167f * simd::pow(x, 12) + 0.642361f * simd::pow(x, 24) + 0.0579909f * simd::pow(x, 36));
}


static float exponentialBipolar80Pade_5_4(float x) {
return (0.109568 * x + 0.281588 * std::pow(x, 3) + 0.133841 * std::pow(x, 5))
/ (1. - 0.630374 * std::pow(x, 2) + 0.166271 * std::pow(x, 4));
}


struct ABC : Module {
enum ParamIds {
B1_LEVEL_PARAM,


+ 13
- 16
src/Morphader.cpp View File

@@ -10,8 +10,8 @@ inline T equalSumCrossfade(T a, T b, const float p) {
// equal power crossfade, -1 <= p <= 1
template <typename T>
inline T equalPowerCrossfade(T a, T b, const float p) {
// TODO: investigate more efficient representation (avoid exp)
return std::min(std::exp(4.f * p), 1.f) * b + std::min(std::exp(4.f * -p), 1.f) * a;
//return std::min(std::exp(4.f * p), 1.f) * b + std::min(std::exp(4.f * -p), 1.f) * a;
return std::min(exponentialBipolar80Pade_5_4(p + 1), 1.f) * b + std::min(exponentialBipolar80Pade_5_4(1 - p), 1.f) * a;
}

// TExponentialSlewLimiter doesn't appear to work correctly (tried for -1 -> +1, and 0 -> 1)
@@ -95,13 +95,6 @@ struct Morphader : Module {
}
};

struct FaderLagParam : ParamQuantity {
std::string getDisplayValueString() override {
const float slewTime = 2.f / (slewMax * std::pow(slewMin / slewMax, getValue()));
return string::f("%.3gs", slewTime);
}
};

Morphader() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

@@ -117,7 +110,7 @@ struct Morphader : Module {
configParam<AudioCVModeParam>(MODE + i, AUDIO_MODE, CV_MODE, AUDIO_MODE, "Mode " + std::to_string(i + 1));
}

configParam<FaderLagParam>(FADER_LAG_PARAM, 0.f, 1.f, 0.f, "Fader lag");
configParam(FADER_LAG_PARAM, 2.0f / slewMax, 2.0f / slewMin, 2.0f / slewMax, "Fader lag", "s");
configParam(FADER_PARAM, -1.f, 1.f, 0.f, "Fader");
}

@@ -126,14 +119,14 @@ struct Morphader : Module {

simd::float_4 channelCrossfades = {0.f};

slewLimiter.setSlew(slewMax * std::pow(slewMin / slewMax, params[FADER_LAG_PARAM].getValue()));
slewLimiter.setSlew(2.0f / params[FADER_LAG_PARAM].getValue());
const float masterCrossfadeValue = slewLimiter.process(deltaTime, params[FADER_PARAM].getValue());

for (int i = 0; i < NUM_MIXER_CHANNELS; i++) {

if (i == 0) {
// CV will be added to master for channel 1, and if not connected, the normalled value of 5.0V will correspond to the midpoint
const float crossfadeCV = clamp(inputs[CV_INPUT + i].getNormalVoltage(5.f), 0.f, 10.f);
const float crossfadeCV = clamp(inputs[CV_INPUT + i].getVoltage(), 0.f, 10.f);
channelCrossfades[i] = params[CV_PARAM].getValue() * rescale(crossfadeCV, 0.f, 10.f, 0.f, +2.f) + masterCrossfadeValue;
}
else {
@@ -142,16 +135,18 @@ struct Morphader : Module {
const float crossfadeCV = clamp(inputs[CV_INPUT + i].getVoltage(), 0.f, 10.f);
channelCrossfades[i] = rescale(crossfadeCV, 0.f, 10.f, -1.f, +1.f);
}
// if channel 1 is plugged in, use that
// if channel 1 is plugged in, but this channel isn't, channel 1 is normalled - in
// this scenario, however the CV is summed with the crossfader
else if (inputs[CV_INPUT + 0].isConnected()) {
// TODO: is this right, or is is channelCrossfades[i] (i.e. with master fader)?
const float crossfadeCV = clamp(inputs[CV_INPUT + 0].getVoltage(), 0.f, 10.f);
channelCrossfades[i] = params[CV_PARAM].getValue() * rescale(crossfadeCV, 0.f, 10.f, -1.f, +1.f);
channelCrossfades[i] = params[CV_PARAM].getValue() * rescale(crossfadeCV, 0.f, 10.f, 0.f, +2.f) + masterCrossfadeValue;
}
else {
channelCrossfades[i] = masterCrossfadeValue;
}
}

channelCrossfades[i] = clamp(channelCrossfades[i], -1.f, +1.f);
}

return channelCrossfades;
@@ -183,7 +178,9 @@ struct Morphader : Module {
break;
}
case AUDIO_MODE: {
out[c / 4] = equalPowerCrossfade(inA, inB, channelCrossfades[i]);
// in audio mode, close to the centre point it is possible to get large voltages
// (e.g. if A and B are both 10V const), so clip
out[c / 4] = clamp(equalPowerCrossfade(inA, inB, channelCrossfades[i]), -12.f, +12.f);
break;
}
default: assert(false);


+ 20
- 15
src/plugin.hpp View File

@@ -4,21 +4,21 @@
using namespace rack;


extern Plugin *pluginInstance;
extern Model *modelEvenVCO;
extern Model *modelRampage;
extern Model *modelABC;
extern Model *modelSpringReverb;
extern Model *modelMixer;
extern Model *modelSlewLimiter;
extern Model *modelDualAtenuverter;
extern Model *modelPercall;
extern Model *modelHexmixVCA;
extern Model *modelChoppingKinky;
extern Model *modelKickall;
extern Model *modelSamplingModulator;
extern Model *modelMorphader;
extern Plugin* pluginInstance;
extern Model* modelEvenVCO;
extern Model* modelRampage;
extern Model* modelABC;
extern Model* modelSpringReverb;
extern Model* modelMixer;
extern Model* modelSlewLimiter;
extern Model* modelDualAtenuverter;
extern Model* modelPercall;
extern Model* modelHexmixVCA;
extern Model* modelChoppingKinky;
extern Model* modelKickall;
extern Model* modelSamplingModulator;
extern Model* modelMorphader;


struct Knurlie : SvgScrew {
@@ -121,6 +121,11 @@ T tanh_pade(T x) {
return 12.f * x * q / (36.f * x2 + q * q);
}

template <typename T>
T exponentialBipolar80Pade_5_4(T x) {
return (T(0.109568) * x + T(0.281588) * simd::pow(x, 3) + T(0.133841) * simd::pow(x, 5))
/ (T(1.) - T(0.630374) * simd::pow(x, 2) + T(0.166271) * simd::pow(x, 4));
}

struct ADEnvelope {
enum Stage {


Loading…
Cancel
Save