Greetings,
I am new to development in rack. I have a background in SuperCollider and Swift, but not too much in C++ or DSP for that matter but I’ve been working through some resources on my own and trying to see if I could Frankenstein some code together to make a module.
I cannot figure out why my module is not working and I would really appreciate it if anyone might have some time to shed a little light on it.
I used some of Andrew’s fundamental code and borrowed some from some other libraries, but my module is only outputting a constant 1V right now and I don’t know why.
Any help would be greatly appreciated! I’m trying my best to learn and I hate asking for this kind of thing. The end goal is making a variable feedback sine oscillator but I’m just making one without right now. I can easily write some code for the feedback portion, just sending it to the phase and limiting it to pi/2.
#include “plugin.hpp”
using simd::float_4;
template <int OVERSAMPLE, int QUALITY, typename T> struct VoltageControlledOscillator {
int channels = 0; T phase = 0.f; T freq = 0.f; dsp::MinBlepGenerator<QUALITY, OVERSAMPLE, T> sinMinBlep; T sinValue = 0.f; void process(float deltaTime, T) { // Advance phase T deltaPhase = simd::clamp(freq * deltaTime, 0.f, 0.35f); phase += deltaPhase; // Wrap phase phase -= simd::floor(phase); sinValue = sin(phase); sinValue += sinMinBlep.process(); } T sin(T phase) { T v; T halfPhase = (phase < 0.5f); T x = phase - simd::ifelse(halfPhase, 0.25f, 0.75f); v = 1.f - 16.f * simd::pow(x, 2); v *= simd::ifelse(halfPhase, 1.f, -1.f); return v; } T sin() { return sinValue; } T light() { return simd::sin(2 * T(M_PI) * phase); }
};
struct Boomerang : Module { enum ParamId { FREQ_PARAM, FEEDBACK_PARAM, FEEDBACK_CV_PARAM, FM_PARAM, PARAMS_LEN }; enum InputId { FM_INPUT, FB_INPUT, VOCT_INPUT, INPUTS_LEN }; enum OutputId { SIN_OUTPUT, OUTPUTS_LEN }; enum LightId { ENUMS(PHASE_LIGHT, 3), LIGHTS_LEN };
VoltageControlledOscillator<16, 16, float_4> oscillators[1]; dsp::ClockDivider lightDivider; Boomerang() { config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4); configParam(FEEDBACK_PARAM, 0.f, 1.f, 0.f, "Feedback", "%", 0.f, 100.f); configParam(FEEDBACK_CV_PARAM, 0.f, 1.f, 0.f, "Feedback CV depth", "%", 0.f, 100.f); configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f); configInput(FM_INPUT, "Frequency modulation"); configInput(FB_INPUT, "Feedback"); configInput(VOCT_INPUT, "1V/oct"); configOutput(SIN_OUTPUT, "Signal"); lightDivider.setDivision(16); } void process(const ProcessArgs& args) override { float freqParam = params[FREQ_PARAM].getValue() / 12.f; float fmParam = params[FM_PARAM].getValue(); int channels = std::max(inputs[VOCT_INPUT].getChannels(), 1); for (int c = 0; c < channels; c += 1) { auto& oscillator = oscillators[c / 1]; oscillator.channels = std::min(channels - c, 1); //get frequency float_4 pitch = freqParam + inputs[VOCT_INPUT].getPolyVoltageSimd<float_4>(c); float_4 freq; pitch += inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c) * fmParam; freq = dsp::FREQ_C4 * dsp::approxExp2_taylor5(pitch + 30.f) / std::pow(2.f, 30.f); freq = clamp(freq, 0.f, args.sampleRate / 2.f); oscillator.freq = freq; if (outputs[SIN_OUTPUT].isConnected()) outputs[SIN_OUTPUT].setVoltageSimd(5.f * oscillator.sin(), c); } outputs[SIN_OUTPUT].setVoltage(channels); // Light if (lightDivider.process()) { if (channels == 1) { float lightValue = oscillators[0].light()[0]; lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision()); lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision()); lights[PHASE_LIGHT + 2].setBrightness(0.f); } else { lights[PHASE_LIGHT + 0].setBrightness(0.f); lights[PHASE_LIGHT + 1].setBrightness(0.f); lights[PHASE_LIGHT + 2].setBrightness(1.f); } } }
};
struct BoomerangWidget : ModuleWidget { BoomerangWidget(Boomerang* module) { setModule(module); setPanel(createPanel(asset::plugin(pluginInstance, “res/Boomerang.svg”)));
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); 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))); //defaults not working, check coordinates in illustrator and use mm addParam(createParamCentered<RoundHugeBlackKnob>(mm2px(Vec(15.24, 35.122)), module, Boomerang::FREQ_PARAM)); addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(15.24, 61.912)), module, Boomerang::FEEDBACK_PARAM)); addParam(createParamCentered<Trimpot>(mm2px(Vec(21.943, 81.86)), module, Boomerang::FEEDBACK_CV_PARAM)); addParam(createParamCentered<Trimpot>(mm2px(Vec(8.537, 81.86)), module, Boomerang::FM_PARAM)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.537, 94.918)), module, Boomerang::FM_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(21.943, 94.918)), module, Boomerang::FB_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.537, 108.265)), module, Boomerang::VOCT_INPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(21.943, 108.265)), module, Boomerang::SIN_OUTPUT)); addChild(createLightCentered<MediumLight<WhiteLight>>(mm2px(Vec(15.24, 18.754)), module, Boomerang::PHASE_LIGHT)); }
};
Model* modelBoomerang = createModel<Boomerang, BoomerangWidget>(“Boomerang”);