@@ -0,0 +1,50 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | |||||
<svg version="1.0" | |||||
id="svg64581" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" | |||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="36.02325px" | |||||
height="36.0188px" viewBox="0 0 36.02325 36.0188" enable-background="new 0 0 36.02325 36.0188" xml:space="preserve"> | |||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" fit-margin-bottom="0" fit-margin-left="0" fit-margin-right="0" fit-margin-top="0" id="base" inkscape:current-layer="layer1" inkscape:cx="-100.74496" inkscape:cy="9.8683694" inkscape:document-units="mm" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="1422" inkscape:window-maximized="0" inkscape:window-width="2560" inkscape:window-x="0" inkscape:window-y="18" inkscape:zoom="1.979899" pagecolor="#ffffff" showgrid="false"> | |||||
</sodipodi:namedview> | |||||
<g> | |||||
<g id="g5959-5_106_" transform="translate(301.93513,1189.951)"> | |||||
<path id="path5961-3_112_" inkscape:connector-curvature="0" d="M-265.91-1171.93701 | |||||
c0,9.94739-8.06683,18.01147-18.01407,18.01147c-9.94595,0-18.01144-8.06409-18.01144-18.01147 | |||||
c0-9.95129,8.06549-18.01404,18.01141-18.01404C-273.97684-1189.95105-265.91-1181.88831-265.91-1171.93701"/> | |||||
</g> | |||||
<g id="g5959-5_105_" transform="translate(301.93513,1189.951)"> | |||||
<linearGradient id="path5961-3_1_" gradientUnits="userSpaceOnUse" x1="-590.1001" y1="-2191.85449" x2="-557.04907" y2="-2191.85449" gradientTransform="matrix(-3.267949e-07 1 -1 -3.267949e-07 -2475.77734 -598.36444)"> | |||||
<stop offset="0" style="stop-color:#787878"/> | |||||
<stop offset="1" style="stop-color:#474747"/> | |||||
</linearGradient> | |||||
<path id="path5961-3_111_" inkscape:connector-curvature="0" fill="url(#path5961-3_1_)" d="M-300.44827-1171.93945 | |||||
c0-9.1261,7.40079-16.52441,16.52676-16.52441c9.12473,0,16.52429,7.39832,16.52429,16.52441 | |||||
c0,9.12964-7.39957,16.52673-16.52432,16.52673C-293.04749-1155.41272-300.44827-1162.80981-300.44827-1171.93945"/> | |||||
</g> | |||||
<g opacity="0.21"> | |||||
<g id="g5959-5_104_" transform="translate(301.93513,1189.951)"> | |||||
<linearGradient id="path5961-3_2_" gradientUnits="userSpaceOnUse" x1="-299.58844" y1="-1111.948" x2="-268.70374" y2="-1111.948" gradientTransform="matrix(-6.535898e-07 -1 1 -6.535898e-07 828.02509 -1456.08508)"> | |||||
<stop offset="0.00559" style="stop-color:#6B6B6B"/> | |||||
<stop offset="1" style="stop-color:#DEDEDE"/> | |||||
</linearGradient> | |||||
<path id="path5961-3_110_" inkscape:connector-curvature="0" fill="url(#path5961-3_2_)" d="M-283.92389-1156.49597 | |||||
c-8.52792,0-15.44122-6.91565-15.44122-15.44348c0-8.52661,6.9133-15.44116,15.44122-15.44116 | |||||
c8.53131,0,15.44348,6.91455,15.44348,15.44116C-268.48041-1163.41162-275.39255-1156.49597-283.92389-1156.49597"/> | |||||
</g> | |||||
</g> | |||||
<g> | |||||
<g id="g5959-5_103_" transform="translate(301.93513,1189.951)"> | |||||
<linearGradient id="path5961-3_3_" gradientUnits="userSpaceOnUse" x1="-298.98605" y1="-1111.948" x2="-269.30612" y2="-1111.948" gradientTransform="matrix(-6.535898e-07 -1 1 -6.535898e-07 828.02509 -1456.08508)"> | |||||
<stop offset="0.00559" style="stop-color:#5B5B5B"/> | |||||
<stop offset="1" style="stop-color:#6C6C6C"/> | |||||
</linearGradient> | |||||
<path id="path5961-3_109_" inkscape:connector-curvature="0" fill="url(#path5961-3_3_)" d="M-283.92386-1157.09839 | |||||
c-8.19525,0-14.83887-6.64587-14.83887-14.84094c0-8.19409,6.64362-14.83899,14.83887-14.83899 | |||||
c8.19852,0,14.84106,6.6449,14.84106,14.83887C-269.08279-1163.74426-275.72531-1157.09839-283.92386-1157.09839"/> | |||||
</g> | |||||
</g> | |||||
</g> | |||||
</svg> |
@@ -30,6 +30,8 @@ struct Compare : Module { | |||||
LIGHTS_LEN | LIGHTS_LEN | ||||
}; | }; | ||||
dsp::ClockDivider lightDivider; | |||||
Compare() { | Compare() { | ||||
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); | ||||
configParam(B_PARAM, -10.f, 10.f, 0.f, "B offset", " V"); | configParam(B_PARAM, -10.f, 10.f, 0.f, "B offset", " V"); | ||||
@@ -43,6 +45,8 @@ struct Compare : Module { | |||||
configOutput(LIMGATE_OUTPUT, "Limit gate"); | configOutput(LIMGATE_OUTPUT, "Limit gate"); | ||||
configOutput(GREATER_OUTPUT, "A>B"); | configOutput(GREATER_OUTPUT, "A>B"); | ||||
configOutput(LESS_OUTPUT, "A<B"); | configOutput(LESS_OUTPUT, "A<B"); | ||||
lightDivider.setDivision(32); | |||||
} | } | ||||
void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
@@ -76,18 +80,14 @@ struct Compare : Module { | |||||
outputs[LIM_OUTPUT].setVoltage(a - clip, c); | outputs[LIM_OUTPUT].setVoltage(a - clip, c); | ||||
outputs[CLIPGATE_OUTPUT].setVoltage(clipped ? 10.f : 0.f, c); | outputs[CLIPGATE_OUTPUT].setVoltage(clipped ? 10.f : 0.f, c); | ||||
if (clipped) | |||||
anyClipped = true; | |||||
if (!clipped) | |||||
anyLimmed = true; | |||||
anyClipped = anyClipped || clipped; | |||||
anyLimmed = anyLimmed || !clipped; | |||||
outputs[LIMGATE_OUTPUT].setVoltage(!clipped ? 10.f : 0.f, c); | outputs[LIMGATE_OUTPUT].setVoltage(!clipped ? 10.f : 0.f, c); | ||||
outputs[GREATER_OUTPUT].setVoltage(a > b ? 10.f : 0.f, c); | outputs[GREATER_OUTPUT].setVoltage(a > b ? 10.f : 0.f, c); | ||||
if (a > b) | |||||
anyGreater = true; | |||||
anyGreater = anyGreater || (a > b); | |||||
outputs[LESS_OUTPUT].setVoltage(a < b ? 10.f : 0.f, c); | outputs[LESS_OUTPUT].setVoltage(a < b ? 10.f : 0.f, c); | ||||
if (a < b) | |||||
anyLess = true; | |||||
anyLess = anyLess || (a < b); | |||||
} | } | ||||
outputs[MAX_OUTPUT].setChannels(channels); | outputs[MAX_OUTPUT].setChannels(channels); | ||||
@@ -99,14 +99,17 @@ struct Compare : Module { | |||||
outputs[GREATER_OUTPUT].setChannels(channels); | outputs[GREATER_OUTPUT].setChannels(channels); | ||||
outputs[LESS_OUTPUT].setChannels(channels); | outputs[LESS_OUTPUT].setChannels(channels); | ||||
lights[CLIP_LIGHT + 0].setBrightnessSmooth(anyClipped && channels <= 1, args.sampleTime); | |||||
lights[CLIP_LIGHT + 1].setBrightnessSmooth(anyClipped && channels > 1, args.sampleTime); | |||||
lights[LIM_LIGHT + 0].setBrightnessSmooth(anyLimmed && channels <= 1, args.sampleTime); | |||||
lights[LIM_LIGHT + 1].setBrightnessSmooth(anyLimmed && channels > 1, args.sampleTime); | |||||
lights[GREATER_LIGHT + 0].setBrightnessSmooth(anyGreater && channels <= 1, args.sampleTime); | |||||
lights[GREATER_LIGHT + 1].setBrightnessSmooth(anyGreater && channels > 1, args.sampleTime); | |||||
lights[LESS_LIGHT + 0].setBrightnessSmooth(anyLess && channels <= 1, args.sampleTime); | |||||
lights[LESS_LIGHT + 1].setBrightnessSmooth(anyLess && channels > 1, args.sampleTime); | |||||
if (lightDivider.process()) { | |||||
float lightTime = args.sampleTime * lightDivider.getDivision(); | |||||
lights[CLIP_LIGHT + 0].setBrightnessSmooth(anyClipped && channels <= 1, lightTime); | |||||
lights[CLIP_LIGHT + 1].setBrightnessSmooth(anyClipped && channels > 1, lightTime); | |||||
lights[LIM_LIGHT + 0].setBrightnessSmooth(anyLimmed && channels <= 1, lightTime); | |||||
lights[LIM_LIGHT + 1].setBrightnessSmooth(anyLimmed && channels > 1, lightTime); | |||||
lights[GREATER_LIGHT + 0].setBrightnessSmooth(anyGreater && channels <= 1, lightTime); | |||||
lights[GREATER_LIGHT + 1].setBrightnessSmooth(anyGreater && channels > 1, lightTime); | |||||
lights[LESS_LIGHT + 0].setBrightnessSmooth(anyLess && channels <= 1, lightTime); | |||||
lights[LESS_LIGHT + 1].setBrightnessSmooth(anyLess && channels > 1, lightTime); | |||||
} | |||||
} | } | ||||
}; | }; | ||||
@@ -88,16 +88,55 @@ struct Logic : Module { | |||||
// Set lights | // Set lights | ||||
if (lightDivider.process()) { | if (lightDivider.process()) { | ||||
float lightTime = args.sampleTime * lightDivider.getDivision(); | |||||
lights[B_BUTTON_LIGHT].setBrightness(bPush); | lights[B_BUTTON_LIGHT].setBrightness(bPush); | ||||
for (int i = 0; i < 8; i++) { | for (int i = 0; i < 8; i++) { | ||||
lights[NOTA_LIGHT + 2 * i + 0].setBrightness(anyState[i] && channels == 1); | |||||
lights[NOTA_LIGHT + 2 * i + 1].setBrightness(anyState[i] && channels > 1); | |||||
lights[NOTA_LIGHT + 2 * i + 0].setBrightnessSmooth(anyState[i] && channels == 1, lightTime); | |||||
lights[NOTA_LIGHT + 2 * i + 1].setBrightnessSmooth(anyState[i] && channels > 1, lightTime); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
struct VCVBezelBig : app::SvgSwitch { | |||||
VCVBezelBig() { | |||||
momentary = true; | |||||
addFrame(Svg::load(asset::plugin(pluginInstance, "res/VCVBezelBig.svg"))); | |||||
} | |||||
}; | |||||
template <typename TBase> | |||||
struct VCVBezelLightBig : TBase { | |||||
VCVBezelLightBig() { | |||||
this->borderColor = color::BLACK_TRANSPARENT; | |||||
this->bgColor = color::BLACK_TRANSPARENT; | |||||
this->box.size = mm2px(math::Vec(9.53, 9.53)); | |||||
} | |||||
}; | |||||
template <typename TBase, typename TLight = WhiteLight> | |||||
struct LightButton : TBase { | |||||
app::ModuleLightWidget* light; | |||||
LightButton() { | |||||
light = new TLight; | |||||
// Move center of light to center of box | |||||
light->box.pos = this->box.size.div(2).minus(light->box.size.div(2)); | |||||
this->addChild(light); | |||||
} | |||||
app::ModuleLightWidget* getLight() { | |||||
return light; | |||||
} | |||||
}; | |||||
using VCVBezelLightBigWhite = LightButton<VCVBezelBig, VCVBezelLightBig<WhiteLight>>; | |||||
struct LogicWidget : ModuleWidget { | struct LogicWidget : ModuleWidget { | ||||
LogicWidget(Logic* module) { | LogicWidget(Logic* module) { | ||||
setModule(module); | setModule(module); | ||||
@@ -108,7 +147,7 @@ struct LogicWidget : ModuleWidget { | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addParam(createLightParamCentered<VCVLightBezel<>>(mm2px(Vec(12.7, 26.755)), module, Logic::B_PARAM, Logic::B_BUTTON_LIGHT)); | |||||
addParam(createLightParamCentered<VCVBezelLightBigWhite>(mm2px(Vec(12.7, 26.755)), module, Logic::B_PARAM, Logic::B_BUTTON_LIGHT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.299, 52.31)), module, Logic::A_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.299, 52.31)), module, Logic::A_INPUT)); | ||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.136, 52.31)), module, Logic::B_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.136, 52.31)), module, Logic::B_INPUT)); | ||||
@@ -4,7 +4,7 @@ | |||||
struct Process : Module { | struct Process : Module { | ||||
enum ParamId { | enum ParamId { | ||||
SLEW_PARAM, | SLEW_PARAM, | ||||
PUSH_PARAM, | |||||
GATE_PARAM, | |||||
PARAMS_LEN | PARAMS_LEN | ||||
}; | }; | ||||
enum InputId { | enum InputId { | ||||
@@ -23,25 +23,94 @@ struct Process : Module { | |||||
OUTPUTS_LEN | OUTPUTS_LEN | ||||
}; | }; | ||||
enum LightId { | enum LightId { | ||||
GATE_LIGHT, | |||||
LIGHTS_LEN | LIGHTS_LEN | ||||
}; | }; | ||||
bool state[16] = {}; | |||||
float sample1[16] = {}; | |||||
float sample2[16] = {}; | |||||
float holdValue[16] = {}; | |||||
float slewValue[16] = {}; | |||||
float glideValue[16] = {}; | |||||
Process() { | Process() { | ||||
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); | ||||
configParam(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(0.1f), "Slew", " ms/V", 2, 1000); | |||||
configButton(PUSH_PARAM, "Gate"); | |||||
configParam(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(1e-3f), "Slew", " ms/V", 2, 1000); | |||||
configButton(GATE_PARAM, "Gate"); | |||||
configInput(SLEW_INPUT, "Slew"); | configInput(SLEW_INPUT, "Slew"); | ||||
configInput(IN_INPUT, "Voltage"); | configInput(IN_INPUT, "Voltage"); | ||||
configInput(GATE_INPUT, "Gate"); | configInput(GATE_INPUT, "Gate"); | ||||
configOutput(SH1_OUTPUT, "Sample & hold"); | configOutput(SH1_OUTPUT, "Sample & hold"); | ||||
configOutput(SH2_OUTPUT, "Sample & hold 2"); | configOutput(SH2_OUTPUT, "Sample & hold 2"); | ||||
configOutput(TH_OUTPUT, "Trigger & hold"); | |||||
configOutput(HT_OUTPUT, "Hold & trigger"); | |||||
configOutput(TH_OUTPUT, "Track & hold"); | |||||
configOutput(HT_OUTPUT, "Hold & track"); | |||||
configOutput(SLEW_OUTPUT, "Slew"); | configOutput(SLEW_OUTPUT, "Slew"); | ||||
configOutput(GLIDE_OUTPUT, "Glide"); | configOutput(GLIDE_OUTPUT, "Glide"); | ||||
} | } | ||||
void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
int channels = inputs[IN_INPUT].getChannels(); | |||||
bool gateButton = params[GATE_PARAM].getValue() > 0.f; | |||||
for (int c = 0; c < channels; c++) { | |||||
float in = inputs[IN_INPUT].getVoltage(c); | |||||
float slewPitch = -params[SLEW_PARAM].getValue() - inputs[SLEW_INPUT].getPolyVoltage(c); | |||||
// V/s | |||||
float slew = dsp::approxExp2_taylor5(slewPitch + 30.f) / 1073741824; | |||||
float gateValue = inputs[GATE_INPUT].getPolyVoltage(c); | |||||
if (!state[c]) { | |||||
if (gateValue >= 2.f || gateButton) { | |||||
// Triggered | |||||
state[c] = true; | |||||
// Hold and track | |||||
holdValue[c] = in; | |||||
// Sample and hold | |||||
sample2[c] = sample1[c]; | |||||
sample1[c] = in; | |||||
// Glide | |||||
// TODO delay timer | |||||
glideValue[c] = in; | |||||
} | |||||
} | |||||
else { | |||||
if (gateValue <= 0.1f && !gateButton) { | |||||
// Untriggered | |||||
state[c] = false; | |||||
// Track and hold | |||||
holdValue[c] = in; | |||||
} | |||||
} | |||||
// Slew each value | |||||
float slewDelta = slew * args.sampleTime; | |||||
if (state[c]) { | |||||
slewValue[c] = in; | |||||
} | |||||
else { | |||||
slewValue[c] += clamp(in - slewValue[c], -slewDelta, slewDelta); | |||||
} | |||||
glideValue[c] += clamp(in - glideValue[c], -slewDelta, slewDelta); | |||||
outputs[SH1_OUTPUT].setVoltage(sample1[c], c); | |||||
outputs[SH2_OUTPUT].setVoltage(sample2[c], c); | |||||
outputs[TH_OUTPUT].setVoltage(state[c] ? holdValue[c] : in, c); | |||||
outputs[HT_OUTPUT].setVoltage(state[c] ? in : holdValue[c], c); | |||||
outputs[SLEW_OUTPUT].setVoltage(slewValue[c], c); | |||||
outputs[GLIDE_OUTPUT].setVoltage(glideValue[c], c); | |||||
} | |||||
outputs[SH1_OUTPUT].setChannels(channels); | |||||
outputs[SH2_OUTPUT].setChannels(channels); | |||||
outputs[TH_OUTPUT].setChannels(channels); | |||||
outputs[HT_OUTPUT].setChannels(channels); | |||||
outputs[SLEW_OUTPUT].setChannels(channels); | |||||
outputs[GLIDE_OUTPUT].setChannels(channels); | |||||
lights[GATE_LIGHT].setBrightness(gateButton); | |||||
} | } | ||||
}; | }; | ||||
@@ -57,7 +126,7 @@ struct ProcessWidget : ModuleWidget { | |||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(12.646, 26.755)), module, Process::SLEW_PARAM)); | addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(12.646, 26.755)), module, Process::SLEW_PARAM)); | ||||
// addParam(createParamCentered<LEDLightBezel>(mm2px(Vec(18.136, 52.31)), module, Process::PUSH_PARAM)); | |||||
addParam(createLightParamCentered<VCVLightBezel<>>(mm2px(Vec(18.136, 52.31)), module, Process::GATE_PARAM, Process::GATE_LIGHT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.299, 52.31)), module, Process::SLEW_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.299, 52.31)), module, Process::SLEW_INPUT)); | ||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.297, 67.53)), module, Process::IN_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.297, 67.53)), module, Process::IN_INPUT)); | ||||