Browse Source

Implement PROB and RND parameters in Random.

tags/v2.0.1
Andrew Belt 3 years ago
parent
commit
4ddef43a00
1 changed files with 100 additions and 95 deletions
  1. +100
    -95
      src/Random.cpp

+ 100
- 95
src/Random.cpp View File

@@ -8,8 +8,8 @@ struct Random : Module {
OFFSET_PARAM, OFFSET_PARAM,
MODE_PARAM, MODE_PARAM,
// new in 2.0 // new in 2.0
PROB_PARAM, // TODO
RAND_PARAM, // TODO
PROB_PARAM,
RAND_PARAM,
RATE_CV_PARAM, RATE_CV_PARAM,
SHAPE_CV_PARAM, SHAPE_CV_PARAM,
PROB_CV_PARAM, PROB_CV_PARAM,
@@ -19,7 +19,7 @@ struct Random : Module {
enum InputIds { enum InputIds {
RATE_INPUT, RATE_INPUT,
SHAPE_INPUT, SHAPE_INPUT,
TRIGGER_INPUT,
TRIG_INPUT,
EXTERNAL_INPUT, EXTERNAL_INPUT,
// new in 2.0 // new in 2.0
PROB_INPUT, PROB_INPUT,
@@ -32,7 +32,7 @@ struct Random : Module {
SMOOTH_OUTPUT, SMOOTH_OUTPUT,
EXPONENTIAL_OUTPUT, EXPONENTIAL_OUTPUT,
// new in 2.0 // new in 2.0
GATE_OUTPUT, // TODO
TRIG_OUTPUT,
NUM_OUTPUTS NUM_OUTPUTS
}; };
enum LightIds { enum LightIds {
@@ -44,101 +44,112 @@ struct Random : Module {
NUM_LIGHTS NUM_LIGHTS
}; };


dsp::SchmittTrigger trigTrigger;
dsp::BooleanTrigger offsetTrigger;
float lastValue = 0.f;
float value = 0.f;
float lastVoltage = 0.f;
float nextVoltage = 0.f;
/** Waits at 1 until reset */
float phase = 0.f;
float clockFreq = 0.f;
/** Internal clock phase */
float clockPhase = 0.f; float clockPhase = 0.f;
int trigFrame = 0;
int lastTrigFrames = INT_MAX;
/** External clock timer */
dsp::Timer clockTimer;
dsp::SchmittTrigger clockTrigger;
dsp::PulseGenerator trigGenerator;


Random() { Random() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(RATE_PARAM, std::log2(0.002f), std::log2(2000.f), std::log2(2.f), "Rate", " Hz", 2);
configParam(RATE_PARAM, std::log2(0.002f), std::log2(2000.f), std::log2(2.f), "Clock rate", " Hz", 2);
configParam(SHAPE_PARAM, 0.f, 1.f, 1.f, "Shape", "%", 0, 100); configParam(SHAPE_PARAM, 0.f, 1.f, 1.f, "Shape", "%", 0, 100);
configParam(PROB_PARAM, 0.f, 1.f, 1.f, "Probability", "%", 0, 100);
configParam(RAND_PARAM, 0.f, 1.f, 1.f, "Randomness", "%", 0, 100);
configParam(PROB_PARAM, 0.f, 1.f, 1.f, "Trigger probability", "%", 0, 100);
configParam(RAND_PARAM, 0.f, 1.f, 1.f, "Random spread", "%", 0, 100);


configParam(RATE_CV_PARAM, -1.f, 1.f, 0.f, "Rate CV", "%", 0, 100);
configParam(RATE_CV_PARAM, -1.f, 1.f, 0.f, "Clock rate CV", "%", 0, 100);
getParamQuantity(RATE_CV_PARAM)->randomizeEnabled = false; getParamQuantity(RATE_CV_PARAM)->randomizeEnabled = false;
configParam(SHAPE_CV_PARAM, -1.f, 1.f, 0.f, "Shape CV", "%", 0, 100); configParam(SHAPE_CV_PARAM, -1.f, 1.f, 0.f, "Shape CV", "%", 0, 100);
getParamQuantity(SHAPE_CV_PARAM)->randomizeEnabled = false; getParamQuantity(SHAPE_CV_PARAM)->randomizeEnabled = false;
configParam(PROB_CV_PARAM, -1.f, 1.f, 0.f, "Probability CV", "%", 0, 100);
configParam(PROB_CV_PARAM, -1.f, 1.f, 0.f, "Trigger probability CV", "%", 0, 100);
getParamQuantity(PROB_CV_PARAM)->randomizeEnabled = false; getParamQuantity(PROB_CV_PARAM)->randomizeEnabled = false;
configParam(RAND_CV_PARAM, -1.f, 1.f, 0.f, "Randomness CV", "%", 0, 100);
configParam(RAND_CV_PARAM, -1.f, 1.f, 0.f, "Random spread CV", "%", 0, 100);
getParamQuantity(RAND_CV_PARAM)->randomizeEnabled = false; getParamQuantity(RAND_CV_PARAM)->randomizeEnabled = false;


configSwitch(OFFSET_PARAM, 0.f, 1.f, 0.f, "Offset", {"Bipolar", "Unipolar"}); configSwitch(OFFSET_PARAM, 0.f, 1.f, 0.f, "Offset", {"Bipolar", "Unipolar"});


configInput(RATE_INPUT, "Rate");
configInput(RATE_INPUT, "Clock rate");
configInput(SHAPE_INPUT, "Shape"); configInput(SHAPE_INPUT, "Shape");
configInput(PROB_INPUT, "Probability");
configInput(RAND_INPUT, "Randomness");
configInput(TRIGGER_INPUT, "Trigger");
configInput(PROB_INPUT, "Trigger probability");
configInput(RAND_INPUT, "Random spread");
configInput(TRIG_INPUT, "Trigger");
configInput(EXTERNAL_INPUT, "External"); configInput(EXTERNAL_INPUT, "External");


configOutput(STEPPED_OUTPUT, "Stepped"); configOutput(STEPPED_OUTPUT, "Stepped");
configOutput(LINEAR_OUTPUT, "Linear"); configOutput(LINEAR_OUTPUT, "Linear");
configOutput(SMOOTH_OUTPUT, "Smooth"); configOutput(SMOOTH_OUTPUT, "Smooth");
configOutput(EXPONENTIAL_OUTPUT, "Exponential"); configOutput(EXPONENTIAL_OUTPUT, "Exponential");
configOutput(GATE_OUTPUT, "Gate");
configOutput(TRIG_OUTPUT, "Trigger");
} }


void trigger() {
lastValue = value;
if (inputs[EXTERNAL_INPUT].isConnected()) {
value = inputs[EXTERNAL_INPUT].getVoltage() / 10.f;
}
else {
// Choose a new random value
bool absolute = params[MODE_PARAM].getValue() > 0.f;
bool uni = params[OFFSET_PARAM].getValue() > 0.f;
if (absolute) {
value = random::uniform();
if (!uni)
value -= 0.5f;
void process(const ProcessArgs& args) override {
// Params
float shape = params[SHAPE_PARAM].getValue();
shape += inputs[SHAPE_INPUT].getVoltage() / 10.f * params[SHAPE_CV_PARAM].getValue();
shape = clamp(shape, 0.f, 1.f);

float rand = params[RAND_PARAM].getValue();
rand += inputs[RAND_INPUT].getVoltage() / 10.f * params[RAND_CV_PARAM].getValue();
rand = clamp(rand, 0.f, 1.f);

bool uni = params[OFFSET_PARAM].getValue() > 0.f;

auto trigger = [&]() {
float prob = params[PROB_PARAM].getValue();
prob += inputs[PROB_INPUT].getVoltage() / 10.f * params[PROB_CV_PARAM].getValue();
prob = clamp(prob, 0.f, 1.f);

lights[RATE_LIGHT].setBrightness(3.f);

// Probabilistic trigger
if (prob < 1.f && random::uniform() > prob)
return;

// Generate next random voltage
lastVoltage = nextVoltage;
if (inputs[EXTERNAL_INPUT].isConnected()) {
nextVoltage = inputs[EXTERNAL_INPUT].getVoltage();
} }
else { else {
// Switch to uni if bi
if (!uni)
value += 0.5f;
float deltaValue = random::normal();
// Bias based on value
deltaValue -= (value - 0.5f) * 2.f;
// Scale delta and accumulate value
const float stdDev = 1 / 10.f;
deltaValue *= stdDev;
value += deltaValue;
value = clamp(value, 0.f, 1.f);
// Switch back to bi
// Crossfade new random voltage with last
float v = 10.f * random::uniform();
if (!uni) if (!uni)
value -= 0.5f;
v -= 5.f;
nextVoltage = crossfade(nextVoltage, v, rand);
} }
}
lights[RATE_LIGHT].setBrightness(3.f);
}


void process(const ProcessArgs& args) override {
if (inputs[TRIGGER_INPUT].isConnected()) {
// Advance clock phase based on tempo estimate
trigFrame++;
float deltaPhase = 1.f / lastTrigFrames;
clockPhase += deltaPhase;
clockPhase = std::min(clockPhase, 1.f);
// Trigger
if (trigTrigger.process(rescale(inputs[TRIGGER_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) {
clockPhase = 0.f;
lastTrigFrames = trigFrame;
trigFrame = 0;
phase = 0.f;

trigGenerator.trigger(1e-3f);
lights[PROB_LIGHT].setBrightness(3.f);
};

// Trigger or internal clock
float deltaPhase;

if (inputs[TRIG_INPUT].isConnected()) {
clockTimer.process(args.sampleTime);

if (clockTrigger.process(inputs[TRIG_INPUT].getVoltage(), 0.1f, 2.f)) {
clockFreq = 1.f / clockTimer.getTime();
clockTimer.reset();
trigger(); trigger();
} }

deltaPhase = std::fmin(clockFreq * args.sampleTime, 0.5f);
} }
else { else {
// Advance clock phase by rate // Advance clock phase by rate
float rate = params[RATE_PARAM].getValue(); float rate = params[RATE_PARAM].getValue();
rate += inputs[RATE_PARAM].getVoltage() * params[RATE_CV_PARAM].getValue(); rate += inputs[RATE_PARAM].getVoltage() * params[RATE_CV_PARAM].getValue();
float clockFreq = std::pow(2.f, rate);
float deltaPhase = std::fmin(clockFreq * args.sampleTime, 0.5f);
clockFreq = std::pow(2.f, rate);
deltaPhase = std::fmin(clockFreq * args.sampleTime, 0.5f);
clockPhase += deltaPhase; clockPhase += deltaPhase;
// Trigger // Trigger
if (clockPhase >= 1.f) { if (clockPhase >= 1.f) {
@@ -147,25 +158,16 @@ struct Random : Module {
} }
} }


// Params
float shape = params[SHAPE_PARAM].getValue();
shape += inputs[SHAPE_INPUT].getVoltage() / 10.f * params[SHAPE_CV_PARAM].getValue();
shape = clamp(shape, 0.f, 1.f);

float prob = params[PROB_PARAM].getValue();
prob += inputs[PROB_INPUT].getVoltage() / 10.f * params[PROB_CV_PARAM].getValue();
prob = clamp(prob, 0.f, 1.f);

float rand = params[RAND_PARAM].getValue();
rand += inputs[RAND_INPUT].getVoltage() / 10.f * params[RAND_CV_PARAM].getValue();
rand = clamp(rand, 0.f, 1.f);
// Advance phase
phase += deltaPhase;
phase = std::min(1.f, phase);


// Stepped // Stepped
if (outputs[STEPPED_OUTPUT].isConnected()) { if (outputs[STEPPED_OUTPUT].isConnected()) {
float steps = std::ceil(std::pow(shape, 2) * 15 + 1); float steps = std::ceil(std::pow(shape, 2) * 15 + 1);
float v = std::ceil(clockPhase * steps) / steps;
v = rescale(v, 0.f, 1.f, lastValue, value);
outputs[STEPPED_OUTPUT].setVoltage(v * 10.f);
float v = std::ceil(phase * steps) / steps;
v = rescale(v, 0.f, 1.f, lastVoltage, nextVoltage);
outputs[STEPPED_OUTPUT].setVoltage(v);
} }


// Linear // Linear
@@ -173,13 +175,13 @@ struct Random : Module {
float slope = 1 / shape; float slope = 1 / shape;
float v; float v;
if (slope < 1e6f) { if (slope < 1e6f) {
v = std::fmin(clockPhase * slope, 1.f);
v = std::fmin(phase * slope, 1.f);
} }
else { else {
v = 1.f; v = 1.f;
} }
v = rescale(v, 0.f, 1.f, lastValue, value);
outputs[LINEAR_OUTPUT].setVoltage(v * 10.f);
v = rescale(v, 0.f, 1.f, lastVoltage, nextVoltage);
outputs[LINEAR_OUTPUT].setVoltage(v);
} }


// Smooth // Smooth
@@ -187,39 +189,42 @@ struct Random : Module {
float p = 1 / shape; float p = 1 / shape;
float v; float v;
if (p < 1e6f) { if (p < 1e6f) {
v = std::fmin(clockPhase * p, 1.f);
v = std::fmin(phase * p, 1.f);
v = std::cos(M_PI * v); v = std::cos(M_PI * v);
} }
else { else {
v = -1.f; v = -1.f;
} }
v = rescale(v, 1.f, -1.f, lastValue, value);
outputs[SMOOTH_OUTPUT].setVoltage(v * 10.f);
v = rescale(v, 1.f, -1.f, lastVoltage, nextVoltage);
outputs[SMOOTH_OUTPUT].setVoltage(v);
} }


// Exp // Exp
if (outputs[EXPONENTIAL_OUTPUT].isConnected()) { if (outputs[EXPONENTIAL_OUTPUT].isConnected()) {
float b = std::pow(shape, 4);
float b = std::pow(shape, 8);
float v; float v;
if (0.999f < b) { if (0.999f < b) {
v = clockPhase;
v = phase;
} }
else if (1e-20f < b) { else if (1e-20f < b) {
v = (std::pow(b, clockPhase) - 1.f) / (b - 1.f);
v = (std::pow(b, phase) - 1.f) / (b - 1.f);
} }
else { else {
v = 1.f; v = 1.f;
} }
v = rescale(v, 0.f, 1.f, lastValue, value);
outputs[EXPONENTIAL_OUTPUT].setVoltage(v * 10.f);
v = rescale(v, 0.f, 1.f, lastVoltage, nextVoltage);
outputs[EXPONENTIAL_OUTPUT].setVoltage(v);
} }


// Trigger output
outputs[TRIG_OUTPUT].setVoltage(trigGenerator.process(args.sampleTime) ? 0.f : 10.f);

// Lights // Lights
lights[RATE_LIGHT].setSmoothBrightness(0.f, args.sampleTime); lights[RATE_LIGHT].setSmoothBrightness(0.f, args.sampleTime);
lights[SHAPE_LIGHT].setBrightness(shape);
lights[PROB_LIGHT].setBrightness(prob);
lights[PROB_LIGHT].setSmoothBrightness(0.f, args.sampleTime);
lights[RAND_LIGHT].setBrightness(rand); lights[RAND_LIGHT].setBrightness(rand);
// lights[OFFSET_LIGHT].setBrightness(uni);
lights[SHAPE_LIGHT].setBrightness(shape);
lights[OFFSET_LIGHT].setBrightness(uni);
} }


void paramsFromJson(json_t* rootJ) override { void paramsFromJson(json_t* rootJ) override {
@@ -257,10 +262,10 @@ struct RandomWidget : ModuleWidget {
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.317, 80.549)), module, Random::PROB_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.317, 80.549)), module, Random::PROB_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.154, 80.553)), module, Random::RAND_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.154, 80.553)), module, Random::RAND_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(38.991, 80.557)), module, Random::SHAPE_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(38.991, 80.557)), module, Random::SHAPE_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.479, 96.859)), module, Random::TRIGGER_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.479, 96.859)), module, Random::TRIG_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.317, 96.859)), module, Random::EXTERNAL_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.317, 96.859)), module, Random::EXTERNAL_INPUT));


addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(38.991, 96.859)), module, Random::GATE_OUTPUT));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(38.991, 96.859)), module, Random::TRIG_OUTPUT));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.479, 113.115)), module, Random::STEPPED_OUTPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.479, 113.115)), module, Random::STEPPED_OUTPUT));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(17.317, 113.115)), module, Random::LINEAR_OUTPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(17.317, 113.115)), module, Random::LINEAR_OUTPUT));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(28.154, 113.115)), module, Random::EXPONENTIAL_OUTPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(28.154, 113.115)), module, Random::EXPONENTIAL_OUTPUT));


Loading…
Cancel
Save