Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for Mixer, Erwin & Werner #26

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 77 additions & 77 deletions src/erwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@
#include "osdialog.h"
#include <math.h>

#define NUM_CHANNELS 4
#define NUM_SCALES 16
static constexpr const int NUM_CHANNELS = 4;
static constexpr const int NUM_SCALES = 16;
static constexpr const int NUM_NOTES = 12;

struct Erwin : Module {
enum ParamIds {
CHANNEL_TRANSPOSE_PARAM,
NOTE_PARAM = CHANNEL_TRANSPOSE_PARAM + NUM_CHANNELS,
SELECT_PARAM = NOTE_PARAM + 12,
SELECT_PARAM = NOTE_PARAM + NUM_NOTES,
NUM_PARAMS
};
enum InputIds {
TRANSPOSE_INPUT,
SEMI_INPUT,
IN_INPUT,
SELECT_INPUT = IN_INPUT + 4,
SELECT_INPUT = IN_INPUT + NUM_CHANNELS,
NUM_INPUTS
};
enum OutputIds {
OUT_OUTPUT,
NUM_OUTPUTS = OUT_OUTPUT + 4
NUM_OUTPUTS = OUT_OUTPUT + NUM_CHANNELS
};
enum LightIds {
NOTE_LIGHT,
NUM_LIGHTS = NOTE_LIGHT + 12
NUM_LIGHTS = NOTE_LIGHT + NUM_NOTES
};

enum QModes {
Expand All @@ -41,17 +42,16 @@ struct Erwin : Module {
configParam(Erwin::CHANNEL_TRANSPOSE_PARAM + 2, -4, 4, 0, "octave");
configParam(Erwin::CHANNEL_TRANSPOSE_PARAM + 3, -4, 4, 0, "octave");
configParam(Erwin::SELECT_PARAM, 0, 15, 0, "scene", "", 0, 1, 1);
for (int i = 0; i < 12; i++) {
for (int i = 0; i < NUM_NOTES; i++) {
configParam(Erwin::NOTE_PARAM + i, 0.0, 1.0, 0.0, "enable/disable note");
}
for (int i = 0; i < 4; i++) {
for (int i = 0; i < NUM_CHANNELS; i++) {
configInput(IN_INPUT + i, string::f("channel %i", i + 1));
configOutput(OUT_OUTPUT + i, string::f("channel %i", i + 1));
}
configInput(SELECT_INPUT, "scene selection");
configInput(TRANSPOSE_INPUT, "transposition");
configInput(SEMI_INPUT, "semi");
onReset();
}

void process(const ProcessArgs &args) override;
Expand All @@ -60,21 +60,20 @@ struct Erwin : Module {
void onReset() override;

int mode = 0;
bool noteState[12 * NUM_SCALES] = {};
bool noteState[NUM_NOTES * NUM_SCALES] = {};
int octave = 0;
int transposeOctave = 0;
int transposeSemi = 0;
float freq = 0.0f;

dsp::SchmittTrigger noteTriggers[12];
dsp::SchmittTrigger noteTriggers[NUM_NOTES];
};

json_t* Erwin::dataToJson() {
json_t *rootJ = json_object();

// Note values
json_t *gatesJ = json_array();
for (int i = 0; i < 12 * NUM_SCALES; i++) {
for (int i = 0; i < NUM_NOTES * NUM_SCALES; i++) {
json_t *gateJ = json_boolean(noteState[i]);
json_array_append_new(gatesJ, gateJ);
}
Expand Down Expand Up @@ -104,83 +103,85 @@ void Erwin::dataFromJson(json_t *rootJ) {
}
}

void Erwin::onReset() {
for (int i = 0; i < 12 * NUM_SCALES; i++) noteState[i] = 0;
void Erwin::onReset() {
for (int i = 0; i < NUM_NOTES * NUM_SCALES; i++) noteState[i] = false;
}

void Erwin::process(const ProcessArgs &args) {

// Scale selection
int scaleOffset = clamp((int)(params[SELECT_PARAM].getValue()
+ inputs[SELECT_INPUT].getVoltage() * NUM_SCALES /10),0,15) * 12;
+ inputs[SELECT_INPUT].getVoltage() * NUM_SCALES / 10), 0, 15) * 12;
bool* currentScale = noteState + scaleOffset;

// limit to 1 octave
transposeSemi = (int)round(inputs[SEMI_INPUT].getVoltage() * 1.2);

for(unsigned int y=0; y<NUM_CHANNELS; y++) {
for(unsigned int y = 0; y < NUM_CHANNELS; y++) {
// normalize to first channel
if(!inputs[IN_INPUT + y].isConnected()) {
inputs[IN_INPUT + y].setVoltage(inputs[IN_INPUT].getVoltage(), 0);
}

octave = trunc(inputs[IN_INPUT+y].getVoltage());
freq = inputs[IN_INPUT+y].getVoltage() - octave;
// limit to 4 octaves
transposeOctave = clamp((int)round(inputs[TRANSPOSE_INPUT].getVoltage() / 2.5)
+ (int)round(params[CHANNEL_TRANSPOSE_PARAM + y].getValue()),-4, 4);

// index of the quantized note
int index = 0;

int semiUp = ceilN(freq * 12);
int semiDown = (int)trunc(freq * 12);
uint8_t stepsUp = 0;
uint8_t stepsDown = 0;

while(!currentScale[modN(semiUp + stepsUp,12)] && stepsUp < 12)
stepsUp++;
while(!currentScale[modN(semiDown - stepsDown, 12)] && stepsDown < 12)
stepsDown++;

// Reset for empty scales to avoid transposing by 1 octave
stepsUp %= 12;
stepsDown %= 12;

switch(mode) {
case QModes::UP:
index = semiUp + stepsUp;
break;
case QModes::DOWN:
index = semiDown - stepsDown;
break;
case QModes::NEAREST:
if (stepsUp < stepsDown)
index = semiUp + stepsUp;
else
index = semiDown - stepsDown;
break;
}

if(transposeSemi)
index += transposeSemi;
int currentPolyphony = std::max(1, inputs[IN_INPUT + y].getChannels());
outputs[OUT_OUTPUT + y].setChannels(currentPolyphony);

for (int c = 0; c < currentPolyphony; c++) {
octave = trunc(inputs[IN_INPUT+y].getPolyVoltage(c));
freq = inputs[IN_INPUT+y].getPolyVoltage(c) - octave;
// limit to 4 octaves
transposeOctave = clamp((int)round(inputs[TRANSPOSE_INPUT].getPolyVoltage(c) / 2.5)
+ (int)round(params[CHANNEL_TRANSPOSE_PARAM + y].getValue()), -4, 4);

// index of the quantized note
int index = 0;

int semiUp = ceilN(freq * 12);
int semiDown = (int)trunc(freq * 12);
uint8_t stepsUp = 0;
uint8_t stepsDown = 0;

while(!currentScale[modN(semiUp + stepsUp, 12)] && stepsUp < 12)
stepsUp++;
while(!currentScale[modN(semiDown - stepsDown, 12)] && stepsDown < 12)
stepsDown++;

// Reset for empty scales to avoid transposing by 1 octave
stepsUp %= 12;
stepsDown %= 12;

switch(mode) {
case QModes::UP:
index = semiUp + stepsUp;
break;
case QModes::DOWN:
index = semiDown - stepsDown;
break;
case QModes::NEAREST:
if (stepsUp < stepsDown)
index = semiUp + stepsUp;
else
index = semiDown - stepsDown;
break;
}

outputs[OUT_OUTPUT + y].setVoltage(octave + index * 1/12.0 + transposeOctave);
if(transposeSemi) index += transposeSemi;

outputs[OUT_OUTPUT + y].setVoltage(octave + index * 1/12.0 + transposeOctave, c);
}
}

// Note buttons
for (int i = 0; i < 12; i++) {
for (int i = 0; i < NUM_NOTES; i++) {
if (noteTriggers[i].process(params[NOTE_PARAM + i].getValue())) {
currentScale[i] = !currentScale[i];
}
lights[NOTE_LIGHT + i].value = (currentScale[i] >= 1.0) ? 0.7 : 0;
}

}

struct ErwinWidget : ModuleWidget {
ErwinWidget(Erwin *module) {
explicit ErwinWidget(Erwin *module) {
setModule(module);
box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
setPanel(createPanel(asset::plugin(pluginInstance, "res/reface/rewin_bg.svg")));
Expand All @@ -193,9 +194,9 @@ struct ErwinWidget : ModuleWidget {
addInput(createInput<ReIOPort>(Vec(85.75, 108.75), module, Erwin::TRANSPOSE_INPUT));
addInput(createInput<ReIOPort>(Vec(48.25, 108.75), module, Erwin::SEMI_INPUT));

for(int i=0;i<NUM_CHANNELS;i++) {
addOutput(createOutput<ReIOPort>(Vec(92.75, 198.75 + i*42), module, Erwin::OUT_OUTPUT + i));
addInput(createInput<ReIOPort>(Vec(62.75, 198.75 + i*42), module, Erwin::IN_INPUT + i));
for(int i = 0; i < NUM_CHANNELS; i++) {
addOutput(createOutput<ReIOPort>(Vec(92.75, 198.75 + i * 42), module, Erwin::OUT_OUTPUT + i));
addInput(createInput<ReIOPort>(Vec(62.75, 198.75 + i * 42), module, Erwin::IN_INPUT + i));
}

addParam(createParam<ReSnapKnobSRed>(Vec(80, 181), module, Erwin::CHANNEL_TRANSPOSE_PARAM));
Expand All @@ -204,19 +205,18 @@ struct ErwinWidget : ModuleWidget {
addParam(createParam<ReSnapKnobSBlue>(Vec(80, 308), module, Erwin::CHANNEL_TRANSPOSE_PARAM + 3));

/* note buttons */
int white=0;
int black = 0;
for(int i=0; i<12; i++) {
int white = 0, black = 0;
for(int i = 0; i < NUM_NOTES; i++) {
if (i == 1 || i == 3 || i == 6 || i == 8 || i == 10 ) {
addParam(createParam<ReButtonM>(Vec(8, 312 - black*28), module, Erwin::NOTE_PARAM + i));
addChild(createLight<ReLightM<ReBlueLight>>(Vec(10, 314 - black*28), module, Erwin::NOTE_LIGHT + i));
addParam(createParam<ReButtonM>(Vec(8, 312 - black * 28), module, Erwin::NOTE_PARAM + i));
addChild(createLight<ReLightM<ReBlueLight>>(Vec(10, 314 - black * 28), module, Erwin::NOTE_LIGHT + i));
black++;
}
else {
if(i == 4)
black++;
addParam(createParam<ReButtonM>(Vec(33, 326 - white*28), module, Erwin::NOTE_PARAM + i));
addChild(createLight<ReLightM<ReBlueLight>>(Vec(35, 328 - white*28), module, Erwin::NOTE_LIGHT + i));
addParam(createParam<ReButtonM>(Vec(33, 326 - white * 28), module, Erwin::NOTE_PARAM + i));
addChild(createLight<ReLightM<ReBlueLight>>(Vec(35, 328 - white * 28), module, Erwin::NOTE_LIGHT + i));
white++;
}
}
Expand Down Expand Up @@ -259,7 +259,7 @@ struct ErwinWidget : ModuleWidget {
async_dialog_filebrowser(true, nullptr, "Save scales", [rootJ](char* path) {
pathSelected(rootJ, path);
});
#else
#else
char* path = osdialog_file(OSDIALOG_SAVE, NULL, "rewin.json", NULL);
pathSelected(rootJ, path);
#endif
Expand All @@ -285,7 +285,7 @@ struct ErwinWidget : ModuleWidget {
async_dialog_filebrowser(false, nullptr, "Load scales", [module](char* path) {
pathSelected(module, path);
});
#else
#else
char* path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, NULL);
pathSelected(module, path);
#endif
Expand All @@ -301,19 +301,19 @@ struct ErwinWidget : ModuleWidget {
if(!gatesJ || json_array_size(gatesJ) != 12 * NUM_SCALES) {
#ifdef USING_CARDINAL_NOT_RACK
async_dialog_message("rewin: invalid input file");
#else
#else
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "rewin: invalid input file");
#endif
#endif
return;
}
module->dataFromJson(rootJ);
}
else {
#ifdef USING_CARDINAL_NOT_RACK
async_dialog_message("rewin: can't load file - see logfile for details");
#else
#else
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "rewin: can't load file - see logfile for details");
#endif
#endif
DEBUG("Error: Can't import file %s", path);
DEBUG("Text: %s", error.text);
DEBUG("Source: %s", error.source);
Expand Down
6 changes: 3 additions & 3 deletions src/mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void Mixer::process(const ProcessArgs &args) {
float aux2LIn = inputs[AUX2_L_INPUT].getNormalVoltage(0.0f);
float aux2RIn = inputs[AUX2_R_INPUT].getNormalVoltage(0.0f);

for(int i=0;i<NUM_CHANNELS;i++) {
for(int i = 0; i < NUM_CHANNELS; i++) {
// sum polyphonic cables, as per vcv voltage standards, #5
float in = inputs[CH1_INPUT + i].getVoltageSum();

Expand All @@ -148,7 +148,7 @@ void Mixer::process(const ProcessArgs &args) {
lights[MUTE_LIGHT + i].value = (channels[i].mute) ? 1.0f : 0.0f;
}

if(!channels[i].mute) {
if(!channels[i].mute && inputs[CH1_INPUT + i].isConnected()) {
float gain = pow(10, params[GAIN_PARAM + i].getValue()/20.0f);
gain *= inputs[CH1_GAIN_INPUT + i].getNormalVoltage(10.0f) / 10.0f;

Expand Down Expand Up @@ -234,7 +234,7 @@ void Mixer::process(const ProcessArgs &args) {
}

struct MixerWidget : ModuleWidget {
MixerWidget(Mixer *module) {
explicit MixerWidget(Mixer *module) {
setModule(module);
box.size = Vec(28 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
setPanel(createPanel(asset::plugin(pluginInstance, "res/reface/rexmix_bg.svg")));
Expand Down
36 changes: 21 additions & 15 deletions src/werner.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "repelzen.hpp"

#define NUM_CHANNELS 4
#define MAX_POLY 16

struct Werner : Module {
enum ParamIds {
Expand Down Expand Up @@ -32,8 +33,8 @@ struct Werner : Module {

void process(const ProcessArgs &args) override;

dsp::PulseGenerator gatePulse[NUM_CHANNELS];
float lastValue[NUM_CHANNELS];
dsp::PulseGenerator gatePulse[NUM_CHANNELS][MAX_POLY];
float lastValue[NUM_CHANNELS][MAX_POLY] {};
int res = 16;
float minDelta = 0;
int frame = 0;
Expand All @@ -45,26 +46,31 @@ void Werner::process(const ProcessArgs &args) {
res = (int)clamp(params[TIME_PARAM].getValue() * 4400.0f, 16.0f, 4400.0f);
minDelta = params[DELTA_PARAM].getValue() * 2.0f;

if(++frame > res) {
for(int i=0; i<NUM_CHANNELS;i++) {
float value = inputs[CV_INPUT + i].getVoltage();

if(abs(value - lastValue[i]) > minDelta) {
gatePulse[i].trigger(0.01);
}

lastValue[i] = value;
}
frame = 0;
if(++frame > res) {
for(int i=0; i<NUM_CHANNELS;i++) {
int currentPolyphony = std::max(1, inputs[CV_INPUT + i].getChannels());
outputs[GATE_OUTPUT + i].setChannels(currentPolyphony);
for (int c = 0; c < currentPolyphony; c++) {
float value = inputs[CV_INPUT + i].getPolyVoltage(c);
if(abs(value - lastValue[i][c]) > minDelta) {
gatePulse[i][c].trigger(0.01);
}
lastValue[i][c] = value;
}
}
frame = 0;
}

for(int i=0; i<NUM_CHANNELS;i++) {
outputs[GATE_OUTPUT + i].setVoltage(gatePulse[i].process(1.0 / args.sampleRate) ? 10.0 : 0.0);
int currentPolyphony = std::max(1, inputs[CV_INPUT + i].getChannels());
for (int c = 0; c < currentPolyphony; c++) {
outputs[GATE_OUTPUT + i].setVoltage(gatePulse[i][c].process(1.0 / args.sampleRate) ? 10.0 : 0.0, c);
}
}
}

struct WernerWidget : ModuleWidget {
WernerWidget(Werner *module) {
explicit WernerWidget(Werner *module) {
setModule(module);
box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
setPanel(createPanel(asset::plugin(pluginInstance, "res/reface/retrig_bg.svg")));
Expand Down