* fixed bug with Muxlicer outputClockFollowsPlayModetags/v2.1.0
@@ -1,5 +1,8 @@ | |||
# Change Log | |||
## v2.0.0 (unreleased) | |||
* update to Rack 2 API (added tooltips, bypass, removed boilerplate etc) | |||
## v1.2.0 (unreleased) | |||
* Released new modules: Muxlicer, Mex, Morphader, VC ADSR, Sampling Modulator, ST Mix | |||
@@ -47,6 +47,18 @@ struct EvenVCO : Module { | |||
configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5); | |||
configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones"); | |||
configParam(PWM_PARAM, -1.0, 1.0, 0.0, "Pulse width"); | |||
configInput(PITCH1_INPUT, "Pitch 1"); | |||
configInput(PITCH2_INPUT, "Pitch 2"); | |||
configInput(FM_INPUT, "FM"); | |||
configInput(SYNC_INPUT, "Sync (not implemented)"); | |||
configInput(PWM_INPUT, "Pulse Width Modulation"); | |||
configOutput(TRI_OUTPUT, "Triangle"); | |||
configOutput(SINE_OUTPUT, "Sine"); | |||
configOutput(EVEN_OUTPUT, "Even"); | |||
configOutput(SAW_OUTPUT, "Sawtooth"); | |||
configOutput(SQUARE_OUTPUT, "Square"); | |||
} | |||
void process(const ProcessArgs& args) override { | |||
@@ -194,17 +194,7 @@ struct HexmixVCAWidget : ModuleWidget { | |||
assert(module); | |||
menu->addChild(new MenuSeparator()); | |||
struct MixMenuItem : MenuItem { | |||
HexmixVCA* module; | |||
void onAction(const event::Action& e) override { | |||
module->finalRowIsMix ^= true; | |||
} | |||
}; | |||
MixMenuItem* mixItem = createMenuItem<MixMenuItem>("Final row is mix", CHECKMARK(module->finalRowIsMix)); | |||
mixItem->module = module; | |||
menu->addChild(mixItem); | |||
menu->addChild(createBoolPtrMenuItem("Final row is mix", &module->finalRowIsMix)); | |||
} | |||
}; | |||
@@ -49,7 +49,7 @@ struct Kickall : Module { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
// TODO: review this mapping, using displayBase multiplier seems more normal | |||
configParam(TUNE_PARAM, FREQ_A0, FREQ_B2, 0.5 * (FREQ_A0 + FREQ_B2), "Tune", "Hz"); | |||
configParam(TRIGG_BUTTON_PARAM, 0.f, 1.f, 0.f, "Manual trigger"); | |||
configButton(TRIGG_BUTTON_PARAM, "Manual trigger"); | |||
configParam(SHAPE_PARAM, 0.f, 1.f, 0.f, "Wave shape"); | |||
configParam(DECAY_PARAM, 0.f, 1.f, 0.01f, "VCA Envelope decay time"); | |||
configParam(TIME_PARAM, 0.f, 1.0f, 0.f, "Pitch envelope decay time"); | |||
@@ -61,6 +61,15 @@ struct Kickall : Module { | |||
pitch.attackTime = 0.00165; | |||
pitch.decayShape = 3.0; | |||
configInput(TRIGG_INPUT, "Trigger"); | |||
configInput(VOLUME_INPUT, "Gain"); | |||
configInput(TUNE_INPUT, "Tune (V/Oct)"); | |||
configInput(SHAPE_INPUT, "Shape CV"); | |||
configInput(DECAY_INPUT, "Decay CV"); | |||
configOutput(OUT_OUTPUT, "Kick"); | |||
configLight(ENV_LIGHT, "Volume envelope"); | |||
// calculate up/downsampling rates | |||
onSampleRateChange(); | |||
} | |||
@@ -28,23 +28,11 @@ struct Mex : Module { | |||
dsp::SchmittTrigger gateInTrigger; | |||
struct GateSwitchParamQuantity : ParamQuantity { | |||
std::string getDisplayValueString() override { | |||
switch ((StepState) ParamQuantity::getValue()) { | |||
case GATE_IN_MODE: return "Gate in/Clock Out"; | |||
case MUTE_MODE: return "Muted"; | |||
case MUXLICER_MODE: return "All Gates"; | |||
default: return ""; | |||
} | |||
} | |||
}; | |||
Mex() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
for (int i = 0; i < 8; ++i) { | |||
configParam<GateSwitchParamQuantity>(STEP_PARAM + i, 0.f, 2.f, 0.f, string::f("Step %d", i + 1)); | |||
configSwitch(STEP_PARAM + i, 0.f, 2.f, 0.f, string::f("Step %d", i + 1), {"Gate in/Clock Out", "Muted", "All Gates"}); | |||
} | |||
} | |||
@@ -35,6 +35,9 @@ struct Mixer : Module { | |||
configParam(CH2_PARAM, 0.0, 1.0, 0.0, "Ch 2 level", "%", 0, 100); | |||
configParam(CH3_PARAM, 0.0, 1.0, 0.0, "Ch 3 level", "%", 0, 100); | |||
configParam(CH4_PARAM, 0.0, 1.0, 0.0, "Ch 4 level", "%", 0, 100); | |||
configOutput(OUT1_OUTPUT, "Main"); | |||
configOutput(OUT2_OUTPUT, "Inverted"); | |||
} | |||
void process(const ProcessArgs& args) override { | |||
@@ -88,29 +88,22 @@ struct Morphader : Module { | |||
constexpr static float slewMin = 2.0 / 15.f; | |||
constexpr static float slewMax = 2.0 / 0.01f; | |||
struct AudioCVModeParam : ParamQuantity { | |||
std::string getDisplayValueString() override { | |||
switch (static_cast<CrossfadeMode>(getValue())) { | |||
case AUDIO_MODE: return "Audio"; | |||
case CV_MODE: return "CV"; | |||
default: assert(false); | |||
} | |||
} | |||
}; | |||
Morphader() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
configParam(CV_PARAM, 0.f, 1.f, 1.f, "CV"); | |||
configParam(CV_PARAM, 0.f, 1.f, 1.f, "CV attenuator"); | |||
for (int i = 0; i < NUM_MIXER_CHANNELS; i++) { | |||
configParam(A_LEVEL + i, 0.f, 1.f, 0.f, "A level " + std::to_string(i + 1)); | |||
configParam(A_LEVEL + i, 0.f, 1.f, 0.f, string::f("A level %d", i + 1)); | |||
configInput(A_INPUT + i, string::f("A%d", i + 1)); | |||
} | |||
for (int i = 0; i < NUM_MIXER_CHANNELS; i++) { | |||
configParam(B_LEVEL + i, 0.f, 1.f, 0.f, "B level " + std::to_string(i + 1)); | |||
configParam(B_LEVEL + i, 0.f, 1.f, 0.f, string::f("B level %d", i + 1)); | |||
configInput(B_INPUT + i, string::f("B%d", i + 1)); | |||
} | |||
for (int i = 0; i < NUM_MIXER_CHANNELS; i++) { | |||
configParam<AudioCVModeParam>(MODE + i, AUDIO_MODE, CV_MODE, AUDIO_MODE, "Mode " + std::to_string(i + 1)); | |||
configSwitch(MODE + i, AUDIO_MODE, CV_MODE, AUDIO_MODE, string::f("Mode %d", i + 1), {"Audio", "CV"}); | |||
configInput(CV_INPUT + i, string::f("CV channel %d", i + 1)); | |||
} | |||
configParam(FADER_LAG_PARAM, 2.0f / slewMax, 2.0f / slewMin, 2.0f / slewMax, "Fader lag", "s"); | |||
@@ -905,14 +905,6 @@ struct MuxlicerWidget : ModuleWidget { | |||
} | |||
}; | |||
struct OutputClockStopStartItem : MenuItem { | |||
Muxlicer* module; | |||
void onAction(const event::Action& e) override { | |||
module->outputClockFollowsPlayMode ^= true; | |||
module->updateParamFromMainClockMultDiv(); | |||
} | |||
}; | |||
struct TapTempoItem : MenuItem { | |||
Muxlicer* module; | |||
void onAction(const event::Action& e) override { | |||
@@ -930,7 +922,6 @@ struct MuxlicerWidget : ModuleWidget { | |||
if (module->usingExternalClock) { | |||
menu->addChild(createMenuLabel<MenuLabel>("Using external clock")); | |||
} | |||
else { | |||
TapTempoItem* tapTempoItem = createMenuItem<TapTempoItem>("Tap to set internal tempo..."); | |||
@@ -962,10 +953,7 @@ struct MuxlicerWidget : ModuleWidget { | |||
menu->addChild(createMenuLabel<MenuLabel>("All In Normalled Value (disabled)")); | |||
} | |||
OutputClockStopStartItem* outputClockStopStartItem = | |||
createMenuItem<OutputClockStopStartItem>("Output clock follows play/stop", CHECKMARK(module->quadraticGatesOnly)); | |||
outputClockStopStartItem->module = module; | |||
menu->addChild(outputClockStopStartItem); | |||
menu->addChild(createBoolPtrMenuItem("Output clock follows play/stop", &module->outputClockFollowsPlayMode)); | |||
menu->addChild(new MenuSeparator()); | |||
menu->addChild(createMenuLabel<MenuLabel>("Input/Output mode")); | |||
@@ -44,12 +44,19 @@ struct Percall : Module { | |||
for (int i = 0; i < 4; i++) { | |||
configParam(VOL_PARAMS + i, 0.f, 1.f, 1.f, string::f("Channel %d level", i + 1), "%", 0, 100); | |||
configParam(DECAY_PARAMS + i, 0.f, 1.f, 0.f, string::f("Channel %d decay time", i + 1)); | |||
configInput(CH_INPUTS + i, string::f("Channel %d", i + 1)); | |||
configInput(TRIG_INPUTS + i, string::f("Channel %d trigger", i + 1)); | |||
configInput(CV_INPUTS + i, string::f("Channel %d CV", i + 1)); | |||
configOutput(ENV_OUTPUTS + i, string::f("Channel %d envelope", i + 1)); | |||
envs[i].attackTime = attackTime; | |||
envs[i].attackShape = 0.5f; | |||
envs[i].decayShape = 2.0f; | |||
} | |||
for (int i = 0; i < 2; i++) { | |||
configInput(STRENGTH_INPUT, string::f("Overall gain (also affects Env Outs)")); | |||
for (int i = 0; i < 2; i++) { | |||
configParam(CHOKE_PARAMS + i, 0.f, 1.f, 0.f, string::f("Choke %d to %d", 2 * i + 1, 2 * i + 2)); | |||
} | |||
@@ -85,19 +85,44 @@ struct Rampage : Module { | |||
Rampage() { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
configParam(RANGE_A_PARAM, 0.0, 2.0, 0.0, "Ch 1 range"); | |||
configSwitch(RANGE_A_PARAM, 0.0, 2.0, 0.0, "Ch 1 range", {"Medium", "Fast", "Slow"}); | |||
configParam(SHAPE_A_PARAM, -1.0, 1.0, 0.0, "Ch 1 shape"); | |||
configParam(TRIGG_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 trigger"); | |||
configButton(TRIGG_A_PARAM, "Ch 1 trigger"); | |||
configParam(RISE_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 rise time"); | |||
configParam(FALL_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 fall time"); | |||
configParam(CYCLE_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 cycle"); | |||
configParam(RANGE_B_PARAM, 0.0, 2.0, 0.0, "Ch 2 range"); | |||
configSwitch(CYCLE_A_PARAM, 0.0, 1.0, 0.0, "Ch 1 cycle", {"Off", "On"}); | |||
configSwitch(RANGE_B_PARAM, 0.0, 2.0, 0.0, "Ch 2 range", {"Medium", "Fast", "Slow"}); | |||
configParam(SHAPE_B_PARAM, -1.0, 1.0, 0.0, "Ch 2 shape"); | |||
configParam(TRIGG_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 trigger"); | |||
configButton(TRIGG_B_PARAM, "Ch 2 trigger"); | |||
configParam(RISE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 rise time"); | |||
configParam(FALL_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 fall time"); | |||
configParam(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle"); | |||
configSwitch(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle", {"Off", "On"}); | |||
configParam(BALANCE_PARAM, 0.0, 1.0, 0.5, "Balance"); | |||
configInput(IN_A_INPUT, "A"); | |||
configInput(IN_B_INPUT, "B"); | |||
configInput(TRIGG_A_INPUT, "Trigger A"); | |||
configInput(TRIGG_B_INPUT, "Trigger B"); | |||
configInput(RISE_CV_A_INPUT, "Rise CV A"); | |||
configInput(RISE_CV_B_INPUT, "Rise CV B"); | |||
configInput(FALL_CV_A_INPUT, "Fall CV A"); | |||
configInput(FALL_CV_B_INPUT, "Fall CV B"); | |||
configInput(EXP_CV_A_INPUT, "Exponetial CV A"); | |||
configInput(EXP_CV_B_INPUT, "Exponetial CV B"); | |||
configInput(CYCLE_A_INPUT, "Cycle A"); | |||
configInput(CYCLE_B_INPUT, "Cycle B"); | |||
configOutput(RISING_A_OUTPUT, "Rising A"); | |||
configOutput(RISING_B_OUTPUT, "Rising B"); | |||
configOutput(FALLING_A_OUTPUT, "Falling A"); | |||
configOutput(FALLING_B_OUTPUT, "Falling B"); | |||
configOutput(EOC_A_OUTPUT, "End of cycle A"); | |||
configOutput(EOC_B_OUTPUT, "End of cycle B"); | |||
configOutput(OUT_A_OUTPUT, "A"); | |||
configOutput(OUT_B_OUTPUT, "B"); | |||
configOutput(COMPARATOR_OUTPUT, "B > A"); | |||
configOutput(MIN_OUTPUT, "Minimum of A and B"); | |||
configOutput(MAX_OUTPUT, "Maximum of A and B"); | |||
} | |||
void process(const ProcessArgs& args) override { | |||
@@ -32,7 +32,13 @@ struct STMix : Module { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
for (int i = 0; i < numMixerChannels; ++i) { | |||
configParam(GAIN_PARAM + i, 0.f, 1.f, 0.f, string::f("Gain %d", i + 1)); | |||
configInput(LEFT_INPUT + i, string::f("Channel %d left", i + 1)); | |||
configInput(RIGHT_INPUT + i, string::f("Channel %d right", i + 1)); | |||
} | |||
configInput(LEFT_INPUT + numMixerChannels, "Channel left (aux)"); | |||
configInput(RIGHT_INPUT + numMixerChannels, "Channel right (aux)"); | |||
configOutput(LEFT_OUTPUT, "Left"); | |||
configOutput(RIGHT_OUTPUT, "Right"); | |||
} | |||
void process(const ProcessArgs& args) override { | |||
@@ -41,38 +41,6 @@ struct SamplingModulator : Module { | |||
CLOCK_INTERNAL | |||
}; | |||
struct ClockTypeParam : ParamQuantity { | |||
std::string getDisplayValueString() override { | |||
if (module != nullptr && paramId == INT_EXT_PARAM) { | |||
return (module->params[INT_EXT_PARAM].getValue() == CLOCK_EXTERNAL) ? "External" : "Internal"; | |||
} | |||
else { | |||
return ""; | |||
} | |||
} | |||
}; | |||
struct StepTypeParam : ParamQuantity { | |||
std::string getDisplayValueString() override { | |||
if (module != nullptr && STEP_PARAM <= paramId && STEP_PARAM < STEP_PARAM_LAST) { | |||
StepState stepState = (StepState) module->params[paramId].getValue(); | |||
if (stepState == STATE_RESET) { | |||
return "Reset"; | |||
} | |||
else if (stepState == STATE_OFF) { | |||
return "Off"; | |||
} | |||
else { | |||
return "On"; | |||
} | |||
} | |||
else { | |||
return ""; | |||
} | |||
} | |||
}; | |||
int numEffectiveSteps = numSteps; | |||
int currentStep = 0; | |||
StepState stepStates[numSteps]; | |||
@@ -94,10 +62,10 @@ struct SamplingModulator : Module { | |||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
configParam(RATE_PARAM, 0.0f, 1.f, 0.f, "Rate"); | |||
configParam(FINE_PARAM, 0.f, 1.f, 0.f, "Fine tune"); | |||
configParam<ClockTypeParam>(INT_EXT_PARAM, 0.f, 1.f, CLOCK_INTERNAL, "Clock"); | |||
configSwitch(INT_EXT_PARAM, 0.f, 1.f, CLOCK_INTERNAL, "Clock", {"External", "Internal"}); | |||
for (int i = 0; i < numSteps; i++) { | |||
configParam<StepTypeParam>(STEP_PARAM + i, 0.f, 2.f, STATE_ON, "Step " + std::to_string(i + 1)); | |||
configSwitch(STEP_PARAM + i, 0.f, 2.f, STATE_ON, string::f("Step %d", i + 1), {"Reset", "Off", "On"}); | |||
} | |||
} | |||
@@ -287,22 +255,12 @@ struct SamplingModulatorWidget : ModuleWidget { | |||
addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(23.722, 94.617)), module, SamplingModulator::STEP_LIGHT + 7)); | |||
} | |||
struct DCMenuItem : MenuItem { | |||
SamplingModulator* module; | |||
void onAction(const event::Action& e) override { | |||
module->removeDC ^= true; | |||
} | |||
}; | |||
void appendContextMenu(Menu* menu) override { | |||
SamplingModulator* module = dynamic_cast<SamplingModulator*>(this->module); | |||
assert(module); | |||
menu->addChild(new MenuSeparator()); | |||
DCMenuItem* dcItem = createMenuItem<DCMenuItem>("Remove DC Offset", CHECKMARK(module->removeDC)); | |||
dcItem->module = module; | |||
menu->addChild(dcItem); | |||
menu->addChild(createBoolPtrMenuItem("Remove DC Offset", &module->removeDC)); | |||
} | |||
}; | |||
@@ -28,6 +28,9 @@ struct SlewLimiter : Module { | |||
configParam(RISE_PARAM, 0.0, 1.0, 0.0, "Rise time"); | |||
configParam(FALL_PARAM, 0.0, 1.0, 0.0, "Fall time"); | |||
configBypass(IN_INPUT, OUT_OUTPUT); | |||
configInput(RISE_INPUT, "Rise CV"); | |||
configInput(FALL_INPUT, "Fall CV"); | |||
} | |||
void process(const ProcessArgs& args) override { | |||