@@ -116,6 +116,12 @@ | |||
"name": "ChoppingKinky", | |||
"description": "", | |||
"tags": [] | |||
}, | |||
{ | |||
"slug": "Kickall", | |||
"name": "Kickall", | |||
"description": "", | |||
"tags": [] | |||
} | |||
] | |||
} |
@@ -0,0 +1,85 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="9.0000019mm" | |||
height="9.0000801mm" | |||
viewBox="0 0 9.0000016 9.00008" | |||
version="1.1" | |||
id="svg113936" | |||
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)" | |||
sodipodi:docname="BefacoTinyKnobGrey.svg"> | |||
<defs | |||
id="defs113930" /> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="7.919596" | |||
inkscape:cx="-34.401622" | |||
inkscape:cy="28.745327" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
fit-margin-top="0" | |||
fit-margin-left="0" | |||
fit-margin-right="0" | |||
fit-margin-bottom="0" | |||
inkscape:window-width="2560" | |||
inkscape:window-height="1393" | |||
inkscape:window-x="0" | |||
inkscape:window-y="0" | |||
inkscape:window-maximized="0" | |||
inkscape:document-rotation="0" /> | |||
<metadata | |||
id="metadata113933"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(-111.86932,-85.795053)"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="fill:#d4d4d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28209424" | |||
d="m 120.10511,92.798549 c 1.38512,-2.062815 0.83525,-4.854005 -1.22757,-6.23803 -2.0617,-1.384026 -4.86062,-0.840773 -6.24463,1.222041 -1.38514,2.062814 -0.83306,4.866126 1.22866,6.251255 2.06282,1.384023 4.8595,0.827547 6.24354,-1.235266" | |||
id="path109730" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="fill:#606060;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28209424" | |||
d="m 120.02026,91.143448 c -0.46281,2.022043 -2.47716,3.285957 -4.49919,2.823148 -2.02205,-0.462813 -3.28598,-2.47714 -2.82314,-4.499182 0.46281,-2.020944 2.47713,-3.285958 4.49918,-2.82315 2.02093,0.462812 3.28594,2.477141 2.82315,4.499184" | |||
id="path109732" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="fill:none;stroke:#7f7878;stroke-width:0.11481237;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" | |||
d="m 120.02026,91.143463 c -0.46281,2.022042 -2.47715,3.285956 -4.49919,2.823145 -2.02204,-0.462811 -3.28595,-2.47714 -2.82314,-4.499183 0.46281,-2.020941 2.47714,-3.285957 4.49918,-2.823145 2.02094,0.46281 3.28596,2.47714 2.82315,4.499183 z m 0,0" | |||
id="path109734" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28209424" | |||
d="m 116.20648,88.009558 c -0.11239,-0.07603 -0.14328,-0.229201 -0.0672,-0.342701 0.0761,-0.113495 0.23029,-0.143251 0.34379,-0.06722 0.11356,0.07603 0.14325,0.229201 0.0672,0.342701 -0.076,0.113495 -0.23031,0.14325 -0.34379,0.06722" | |||
id="path109736" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.46912277;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;fill-opacity:1" | |||
d="m 116.20647,88.009571 c -0.11239,-0.07603 -0.14326,-0.229202 -0.0672,-0.3427 0.076,-0.113495 0.23032,-0.14325 0.34381,-0.06722 0.11356,0.07602 0.14325,0.229201 0.0672,0.3427 -0.076,0.113495 -0.2303,0.143251 -0.3438,0.06722 z m 0,0" | |||
id="path109738" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,105 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="54" | |||
height="54.002399" | |||
viewBox="0 0 14.2875 14.288134" | |||
version="1.1" | |||
id="svg16908" | |||
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)" | |||
sodipodi:docname="Davies1900hLargeGrey.svg"> | |||
<defs | |||
id="defs16902"> | |||
<clipPath | |||
clipPathUnits="userSpaceOnUse" | |||
id="clipPath6367"> | |||
<path | |||
d="M 0,3193 H 2089 V 0 H 0 Z" | |||
id="path6365" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="15.839192" | |||
inkscape:cx="4.7207333" | |||
inkscape:cy="8.7180405" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
fit-margin-top="0" | |||
fit-margin-left="0" | |||
fit-margin-right="0" | |||
fit-margin-bottom="0" | |||
inkscape:window-width="2560" | |||
inkscape:window-height="1393" | |||
inkscape:window-x="43" | |||
inkscape:window-y="161" | |||
inkscape:window-maximized="0" | |||
units="px" | |||
inkscape:document-rotation="0" /> | |||
<metadata | |||
id="metadata16905"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title /> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(-231.61418,-118.28792)"> | |||
<g | |||
id="g2004" | |||
transform="matrix(0.39687059,0,0,-0.39687059,-305.70597,1064.5469)" | |||
style="stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none"> | |||
<g | |||
transform="translate(1389.8926,2366.2998)" | |||
id="g6455" | |||
style="stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path6457" | |||
style="fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none" | |||
d="m 0,0 c 0,-9.941 -8.059,-18 -18,-18 -9.941,0 -18,8.059 -18,18 0,9.941 8.059,18 18,18 C -8.059,18 0,9.941 0,0" /> | |||
</g> | |||
<g | |||
transform="translate(1389.791,2368.2021)" | |||
id="g6459" | |||
style="stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path6461" | |||
style="fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none" | |||
d="m 0,0 c -0.79,1.279 -2.12,2.537 -2.86,4.327 -0.74,1.784 -0.691,3.609 -1.033,5.067 -0.805,0.997 -1.711,1.902 -2.709,2.708 -1.459,0.341 -3.282,0.293 -5.068,1.033 -1.79,0.742 -3.048,2.069 -4.325,2.86 -0.625,0.066 -1.26,0.104 -1.902,0.104 -0.644,0 -1.279,-0.038 -1.903,-0.104 -1.279,-0.791 -2.537,-2.118 -4.327,-2.86 -1.784,-0.74 -3.607,-0.692 -5.067,-1.033 -0.997,-0.806 -1.904,-1.711 -2.708,-2.708 -0.342,-1.458 -0.294,-3.283 -1.035,-5.067 -0.74,-1.79 -2.069,-3.048 -2.858,-4.327 -0.066,-0.625 -0.103,-1.26 -0.103,-1.902 0,-0.643 0.037,-1.278 0.103,-1.905 0.789,-1.277 2.118,-2.535 2.858,-4.325 0.741,-1.784 0.693,-3.609 1.035,-5.066 0.804,-0.998 1.711,-1.904 2.708,-2.709 1.46,-0.342 3.283,-0.293 5.067,-1.032 1.79,-0.743 3.048,-2.071 4.327,-2.861 0.624,-0.065 1.259,-0.103 1.903,-0.103 0.642,0 1.277,0.038 1.902,0.103 1.277,0.79 2.535,2.118 4.325,2.861 1.786,0.739 3.609,0.69 5.068,1.032 0.998,0.805 1.904,1.711 2.709,2.709 0.342,1.457 0.293,3.282 1.033,5.066 0.74,1.79 2.07,3.048 2.86,4.325 0.065,0.627 0.102,1.262 0.102,1.905 C 0.102,-1.26 0.065,-0.625 0,0" /> | |||
</g> | |||
<g | |||
transform="translate(1372.4912,2384.2314)" | |||
id="g6463" | |||
style="stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path6465" | |||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.8894587;stroke-miterlimit:4;stroke-dasharray:none" | |||
d="M 0,0 C -0.195,0.045 -0.393,0.069 -0.598,0.069 -0.804,0.069 -1.002,0.045 -1.196,0 V -18.157 H 0 Z" /> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@@ -19,14 +19,28 @@ struct BefacoTinyKnobWhite : app::SvgKnob { | |||
} | |||
}; | |||
struct BefacoTinyKnobGrey : app::SvgKnob { | |||
BefacoTinyKnobGrey() { | |||
minAngle = -0.8 * M_PI; | |||
maxAngle = 0.8 * M_PI; | |||
setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoTinyKnobGrey.svg"))); | |||
} | |||
}; | |||
struct Davies1900hLargeGreyKnob : Davies1900hKnob { | |||
Davies1900hLargeGreyKnob() { | |||
setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Davies1900hLargeGrey.svg"))); | |||
} | |||
}; | |||
struct BefacoOutputPort : app::SvgPort { | |||
BefacoOutputPort() { | |||
BefacoOutputPort() { | |||
setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoOutputPort.svg"))); | |||
} | |||
}; | |||
struct BefacoInputPort : app::SvgPort { | |||
BefacoInputPort() { | |||
BefacoInputPort() { | |||
setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoInputPort.svg"))); | |||
} | |||
}; | |||
@@ -36,7 +50,7 @@ struct BefacoInputPort : app::SvgPort { | |||
The template parameter N should be 1/2 the desired filter order. | |||
Currently uses an 2*N-th order Butterworth filter. | |||
@TODO: implement Chebyshev, Elliptic filter options. | |||
source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp | |||
*/ | |||
template<int N> | |||
class AAFilter { | |||
@@ -86,6 +100,7 @@ private: | |||
/** | |||
* Base class for oversampling of any order | |||
* source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/oversampling.hpp | |||
*/ | |||
class BaseOversampling { | |||
public: | |||
@@ -170,6 +185,8 @@ private: | |||
osBuffer[k] = processSample(osBuffer[k]); | |||
float y = oversample.downsample(); | |||
@endcode | |||
source (modified): https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/VariableOversampling.hpp | |||
*/ | |||
template<int filtN = 4> | |||
class VariableOversampling { | |||
@@ -0,0 +1,187 @@ | |||
#include "plugin.hpp" | |||
#include "Common.hpp" | |||
struct ADEnvelope { | |||
enum Stage { | |||
STAGE_OFF, | |||
STAGE_ATTACK, | |||
STAGE_DECAY | |||
}; | |||
Stage stage = STAGE_OFF; | |||
float env = 0.f; | |||
float attackTime = 0.1, decayTime = 0.1; | |||
ADEnvelope() { }; | |||
void process(const float& sampleTime) { | |||
if (stage == STAGE_OFF) { | |||
env = 0.0f; | |||
} | |||
else if (stage == STAGE_ATTACK) { | |||
env += sampleTime / attackTime; | |||
} | |||
else if (stage == STAGE_DECAY) { | |||
env -= sampleTime / decayTime; | |||
} | |||
if (env >= 1.0f) { | |||
stage = STAGE_DECAY; | |||
env = 1.0f; | |||
} | |||
else if (env <= 0.0f) { | |||
stage = STAGE_OFF; | |||
env = 0.0f; | |||
} | |||
} | |||
}; | |||
struct Kickall : Module { | |||
enum ParamIds { | |||
TUNE_PARAM, | |||
TRIGG_BUTTON_PARAM, | |||
SHAPE_PARAM, | |||
DECAY_PARAM, | |||
TIME_PARAM, | |||
BEND_PARAM, | |||
NUM_PARAMS | |||
}; | |||
enum InputIds { | |||
TRIGG_INPUT, | |||
VOLUME_INPUT, | |||
TUNE_INPUT, | |||
SHAPE_INPUT, | |||
DECAY_INPUT, | |||
NUM_INPUTS | |||
}; | |||
enum OutputIds { | |||
OUT_OUTPUT, | |||
NUM_OUTPUTS | |||
}; | |||
enum LightIds { | |||
ENV_LIGHT, | |||
NUM_LIGHTS | |||
}; | |||
Kickall() { | |||
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"); | |||
configParam(SHAPE_PARAM, 0.f, 1.f, 0.f, ""); | |||
configParam(DECAY_PARAM, 0.01f, 1.f, 0.01f, "VCA Envelope decay time"); | |||
configParam(TIME_PARAM, 0.0079f, 0.076f, 0.0079f, "Pitch envelope decay time", "ms", 0.0f, 1000.f); | |||
configParam(BEND_PARAM, 0.f, 1.f, 0.f, "Pitch envelope attenuator"); | |||
volume.attackTime = 0.00165; | |||
pitch.attackTime = 0.00165; | |||
// calculate up/downsampling rates | |||
onSampleRateChange(); | |||
} | |||
void onSampleRateChange() override { | |||
// SampleRateConverter needs integer value | |||
int sampleRate = APP->engine->getSampleRate(); | |||
oversampler.reset(sampleRate); | |||
} | |||
float bendRange = 600; | |||
float phase = 0.f; | |||
ADEnvelope volume; | |||
ADEnvelope pitch; | |||
dsp::SchmittTrigger trigger; | |||
static constexpr float FREQ_A0 = 27.5f; | |||
static constexpr float FREQ_B2 = 123.471f; | |||
static const int UPSAMPLE = 4; | |||
Oversampling<UPSAMPLE> oversampler; | |||
float shaperBuf[UPSAMPLE]; | |||
void process(const ProcessArgs& args) override { | |||
// TODO: check values | |||
if (trigger.process(inputs[TRIGG_INPUT].getVoltage() / 2.0f + params[TRIGG_BUTTON_PARAM].getValue() * 10.0)) { | |||
volume.stage = ADEnvelope::STAGE_ATTACK; | |||
pitch.stage = ADEnvelope::STAGE_ATTACK; | |||
} | |||
float vcaGain = clamp(inputs[VOLUME_INPUT].getNormalVoltage(10.f) / 10.f, 0.f, 1.0f); | |||
// pitch envelope | |||
float bend = params[BEND_PARAM].getValue(); | |||
pitch.decayTime = params[TIME_PARAM].getValue(); | |||
pitch.process(args.sampleTime); | |||
// volume envelope TODO: wire in decay CV | |||
volume.decayTime = params[DECAY_PARAM].getValue(); | |||
volume.process(args.sampleTime); | |||
float freq = params[TUNE_PARAM].getValue(); | |||
freq *= std::pow(2, inputs[TUNE_INPUT].getVoltage()); | |||
//float kickFrequency = std::max(50.0f, params[TUNE_PARAM].getValue() + bendRange * bend * pitch.env); | |||
float kickFrequency = std::max(50.0f, freq + bendRange * bend * pitch.env); | |||
phase += args.sampleTime * kickFrequency; | |||
if (phase > 1.0f) { | |||
phase -= 1.0f; | |||
} | |||
// TODO: faster approximation | |||
float wave = std::sin(2.0 * M_PI * phase); | |||
oversampler.upsample(wave); | |||
float* inputBuf = oversampler.getOSBuffer(); | |||
float shape = clamp(inputs[SHAPE_INPUT].getVoltage() / 2.f + params[SHAPE_PARAM].getValue() * 5.0f, 0.0f, 10.0f); | |||
shape = clamp(shape, 0.f, 5.0f) * 0.2f; | |||
shape *= 0.99f; | |||
const float shapeB = (1.0f - shape) / (1.0f + shape); | |||
const float shapeA = (4.0f * shape) / ((1.0f - shape) * (1.0f + shape)); | |||
for (int i = 0; i < UPSAMPLE; ++i) { | |||
inputBuf[i] = inputBuf[i] * (shapeA + shapeB) / ((std::abs(inputBuf[i]) * shapeA) + shapeB); | |||
} | |||
float out = volume.env * oversampler.downsample() * 5.0f * vcaGain; | |||
outputs[OUT_OUTPUT].setVoltage(out); | |||
lights[ENV_LIGHT].setBrightness(volume.stage); | |||
} | |||
}; | |||
struct KickallWidget : ModuleWidget { | |||
KickallWidget(Kickall* module) { | |||
setModule(module); | |||
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Kickall.svg"))); | |||
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, 0))); | |||
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
addParam(createParamCentered<BefacoTinyKnobGrey>(mm2px(Vec(8.76, 29.068)), module, Kickall::TUNE_PARAM)); | |||
addParam(createParamCentered<BefacoPush>(mm2px(Vec(22.651, 29.068)), module, Kickall::TRIGG_BUTTON_PARAM)); | |||
addParam(createParamCentered<Davies1900hLargeGreyKnob>(mm2px(Vec(15.781, 49.278)), module, Kickall::SHAPE_PARAM)); | |||
addParam(createParam<BefacoSlidePot>(mm2px(Vec(19.913, 64.004)), module, Kickall::DECAY_PARAM)); | |||
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(8.977, 71.626)), module, Kickall::TIME_PARAM)); | |||
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(8.977, 93.549)), module, Kickall::BEND_PARAM)); | |||
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.715, 14.651)), module, Kickall::TRIGG_INPUT)); | |||
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.748, 14.651)), module, Kickall::VOLUME_INPUT)); | |||
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.697, 113.286)), module, Kickall::TUNE_INPUT)); | |||
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.794, 113.286)), module, Kickall::SHAPE_INPUT)); | |||
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.891, 113.286)), module, Kickall::DECAY_INPUT)); | |||
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(25.781, 14.651)), module, Kickall::OUT_OUTPUT)); | |||
addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(15.723, 34.971)), module, Kickall::ENV_LIGHT)); | |||
} | |||
}; | |||
Model* modelKickall = createModel<Kickall, KickallWidget>("Kickall"); |
@@ -16,4 +16,5 @@ void init(rack::Plugin *p) { | |||
p->addModel(modelPercall); | |||
p->addModel(modelHexmixVCA); | |||
p->addModel(modelChoppingKinky); | |||
p->addModel(modelKickall); | |||
} |
@@ -16,6 +16,7 @@ extern Model *modelDualAtenuverter; | |||
extern Model *modelPercall; | |||
extern Model *modelHexmixVCA; | |||
extern Model *modelChoppingKinky; | |||
extern Model *modelKickall; | |||
struct Knurlie : SvgScrew { | |||
Knurlie() { | |||