You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
12KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  3. template <typename T>
  4. struct LowFrequencyOscillator {
  5. T phase = 0.f;
  6. T pw = 0.5f;
  7. T freq = 1.f;
  8. bool invert = false;
  9. bool bipolar = false;
  10. T resetState = T::mask();
  11. void setPitch(T pitch) {
  12. pitch = simd::fmin(pitch, 10.f);
  13. freq = dsp::approxExp2_taylor5(pitch + 30.f) / std::pow(2.f, 30.f);
  14. }
  15. void setPulseWidth(T pw) {
  16. const T pwMin = 0.01f;
  17. this->pw = clamp(pw, pwMin, 1.f - pwMin);
  18. }
  19. void setReset(T reset) {
  20. reset = simd::rescale(reset, 0.1f, 2.f, 0.f, 1.f);
  21. T on = (reset >= 1.f);
  22. T off = (reset <= 0.f);
  23. T triggered = ~resetState & on;
  24. resetState = simd::ifelse(off, 0.f, resetState);
  25. resetState = simd::ifelse(on, T::mask(), resetState);
  26. phase = simd::ifelse(triggered, 0.f, phase);
  27. }
  28. void step(float dt) {
  29. T deltaPhase = simd::fmin(freq * dt, 0.5f);
  30. phase += deltaPhase;
  31. phase -= (phase >= 1.f) & 1.f;
  32. }
  33. T sin() {
  34. T p = phase;
  35. if (!bipolar)
  36. p -= 0.25f;
  37. T v = simd::sin(2 * M_PI * p);
  38. if (invert)
  39. v *= -1.f;
  40. if (!bipolar)
  41. v += 1.f;
  42. return v;
  43. }
  44. T tri() {
  45. T p = phase;
  46. if (bipolar)
  47. p += 0.25f;
  48. T v = 4.f * simd::fabs(p - simd::round(p)) - 1.f;
  49. if (invert)
  50. v *= -1.f;
  51. if (!bipolar)
  52. v += 1.f;
  53. return v;
  54. }
  55. T saw() {
  56. T p = phase;
  57. if (!bipolar)
  58. p -= 0.5f;
  59. T v = 2.f * (p - simd::round(p));
  60. if (invert)
  61. v *= -1.f;
  62. if (!bipolar)
  63. v += 1.f;
  64. return v;
  65. }
  66. T sqr() {
  67. T v = simd::ifelse(phase < pw, 1.f, -1.f);
  68. if (invert)
  69. v *= -1.f;
  70. if (!bipolar)
  71. v += 1.f;
  72. return v;
  73. }
  74. T light() {
  75. return simd::sin(2 * T(M_PI) * phase);
  76. }
  77. };
  78. struct LFO : Module {
  79. enum ParamIds {
  80. OFFSET_PARAM,
  81. INVERT_PARAM,
  82. FREQ_PARAM,
  83. FM1_PARAM,
  84. FM2_PARAM, // removed
  85. PW_PARAM,
  86. PWM_PARAM,
  87. NUM_PARAMS
  88. };
  89. enum InputIds {
  90. FM1_INPUT,
  91. FM2_INPUT, // removed
  92. RESET_INPUT,
  93. PW_INPUT,
  94. NUM_INPUTS
  95. };
  96. enum OutputIds {
  97. SIN_OUTPUT,
  98. TRI_OUTPUT,
  99. SAW_OUTPUT,
  100. SQR_OUTPUT,
  101. NUM_OUTPUTS
  102. };
  103. enum LightIds {
  104. ENUMS(PHASE_LIGHT, 3),
  105. NUM_LIGHTS
  106. };
  107. LowFrequencyOscillator<float_4> oscillators[4];
  108. dsp::ClockDivider lightDivider;
  109. LFO() {
  110. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  111. configSwitch(OFFSET_PARAM, 0.f, 1.f, 1.f, "Offset", {"Bipolar", "Unipolar"});
  112. configSwitch(INVERT_PARAM, 0.f, 1.f, 1.f, "Orientation", {"Inverted", "Normal"});
  113. configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1);
  114. configParam(FM1_PARAM, 0.f, 1.f, 0.f, "Frequency modulation 1", "%", 0.f, 100.f);
  115. configParam(PW_PARAM, 0.01f, 0.99f, 0.5f, "Pulse width", "%", 0.f, 100.f);
  116. configParam(FM2_PARAM, 0.f, 1.f, 0.f, "Frequency modulation 2", "%", 0.f, 100.f);
  117. configParam(PWM_PARAM, 0.f, 1.f, 0.f, "Pulse width modulation", "%", 0.f, 100.f);
  118. configInput(FM1_INPUT, "Frequency modulation 1");
  119. configInput(FM2_INPUT, "Frequency modulation 2");
  120. configInput(RESET_INPUT, "Reset");
  121. configInput(PW_INPUT, "Pulse width modulation");
  122. configOutput(SIN_OUTPUT, "Sine");
  123. configOutput(TRI_OUTPUT, "Triangle");
  124. configOutput(SAW_OUTPUT, "Sawtooth");
  125. configOutput(SQR_OUTPUT, "Square");
  126. configLight(PHASE_LIGHT, "Phase");
  127. lightInfos[PHASE_LIGHT]->description = "Tracks the sine output.\nGreen if positive, red if negative, blue if polyphonic.";
  128. lightDivider.setDivision(16);
  129. }
  130. void process(const ProcessArgs& args) override {
  131. float freqParam = params[FREQ_PARAM].getValue();
  132. float fm1Param = params[FM1_PARAM].getValue();
  133. float fm2Param = params[FM2_PARAM].getValue();
  134. float pwParam = params[PW_PARAM].getValue();
  135. float pwmParam = params[PWM_PARAM].getValue();
  136. int channels = std::max(1, inputs[FM1_INPUT].getChannels());
  137. for (int c = 0; c < channels; c += 4) {
  138. auto* oscillator = &oscillators[c / 4];
  139. oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
  140. oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
  141. float_4 pitch = freqParam;
  142. // FM1, polyphonic
  143. pitch += inputs[FM1_INPUT].getVoltageSimd<float_4>(c) * fm1Param;
  144. // FM2, polyphonic or monophonic
  145. pitch += inputs[FM2_INPUT].getPolyVoltageSimd<float_4>(c) * fm2Param;
  146. oscillator->setPitch(pitch);
  147. // Pulse width
  148. float_4 pw = pwParam + inputs[PW_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * pwmParam;
  149. oscillator->setPulseWidth(pw);
  150. oscillator->step(args.sampleTime);
  151. oscillator->setReset(inputs[RESET_INPUT].getPolyVoltageSimd<float_4>(c));
  152. // Outputs
  153. if (outputs[SIN_OUTPUT].isConnected())
  154. outputs[SIN_OUTPUT].setVoltageSimd(5.f * oscillator->sin(), c);
  155. if (outputs[TRI_OUTPUT].isConnected())
  156. outputs[TRI_OUTPUT].setVoltageSimd(5.f * oscillator->tri(), c);
  157. if (outputs[SAW_OUTPUT].isConnected())
  158. outputs[SAW_OUTPUT].setVoltageSimd(5.f * oscillator->saw(), c);
  159. if (outputs[SQR_OUTPUT].isConnected())
  160. outputs[SQR_OUTPUT].setVoltageSimd(5.f * oscillator->sqr(), c);
  161. }
  162. outputs[SIN_OUTPUT].setChannels(channels);
  163. outputs[TRI_OUTPUT].setChannels(channels);
  164. outputs[SAW_OUTPUT].setChannels(channels);
  165. outputs[SQR_OUTPUT].setChannels(channels);
  166. // Light
  167. if (lightDivider.process()) {
  168. if (channels == 1) {
  169. float lightValue = oscillators[0].light().s[0];
  170. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  171. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  172. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  173. }
  174. else {
  175. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  176. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  177. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  178. }
  179. }
  180. }
  181. };
  182. struct LFOWidget : ModuleWidget {
  183. LFOWidget(LFO* module) {
  184. setModule(module);
  185. setPanel(createPanel(asset::plugin(pluginInstance, "res/LFO.svg")));
  186. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  187. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  188. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  189. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  190. addParam(createParamCentered<RoundHugeBlackKnob>(mm2px(Vec(22.902, 29.803)), module, LFO::FREQ_PARAM));
  191. addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(22.861, 56.388)), module, LFO::PW_PARAM));
  192. addParam(createParamCentered<Trimpot>(mm2px(Vec(6.604, 80.603)), module, LFO::FM1_PARAM));
  193. // addParam(createParamCentered<LEDButton>(mm2px(Vec(17.441, 80.603)), module, LFO::INV_PARAM));
  194. // addParam(createParamCentered<LEDButton>(mm2px(Vec(28.279, 80.603)), module, LFO::OFST_PARAM));
  195. addParam(createParamCentered<Trimpot>(mm2px(Vec(39.116, 80.603)), module, LFO::PWM_PARAM));
  196. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.604, 96.859)), module, LFO::FM1_INPUT));
  197. // addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.441, 96.859)), module, LFO::CLK_INPUT));
  198. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.279, 96.819)), module, LFO::RESET_INPUT));
  199. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(39.116, 96.819)), module, LFO::PW_INPUT));
  200. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.604, 113.115)), module, LFO::SIN_OUTPUT));
  201. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(17.441, 113.115)), module, LFO::TRI_OUTPUT));
  202. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(28.279, 113.115)), module, LFO::SAW_OUTPUT));
  203. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(39.116, 113.115)), module, LFO::SQR_OUTPUT));
  204. addChild(createLightCentered<SmallLight<RedGreenBlueLight>>(mm2px(Vec(31.085, 16.428)), module, LFO::PHASE_LIGHT));
  205. }
  206. };
  207. Model* modelLFO = createModel<LFO, LFOWidget>("LFO");
  208. #if 0
  209. struct LFO2 : Module {
  210. enum ParamIds {
  211. OFFSET_PARAM,
  212. INVERT_PARAM,
  213. FREQ_PARAM,
  214. WAVE_PARAM,
  215. FM_PARAM,
  216. NUM_PARAMS
  217. };
  218. enum InputIds {
  219. FM_INPUT,
  220. RESET_INPUT,
  221. WAVE_INPUT,
  222. NUM_INPUTS
  223. };
  224. enum OutputIds {
  225. INTERP_OUTPUT,
  226. NUM_OUTPUTS
  227. };
  228. enum LightIds {
  229. ENUMS(PHASE_LIGHT, 3),
  230. NUM_LIGHTS
  231. };
  232. LowFrequencyOscillator<float_4> oscillators[4];
  233. dsp::ClockDivider lightDivider;
  234. LFO2() {
  235. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  236. configSwitch(OFFSET_PARAM, 0.f, 1.f, 1.f, "Offset", {"Bipolar", "Unipolar"});
  237. configSwitch(INVERT_PARAM, 0.f, 1.f, 1.f, "Orientation", {"Inverted", "Normal"});
  238. configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1);
  239. configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave");
  240. configParam(FM_PARAM, 0.f, 1.f, 1.f, "Frequency modulation", "%", 0.f, 100.f);
  241. configInput(FM_INPUT, "Frequency modulation");
  242. configInput(RESET_INPUT, "Reset");
  243. configInput(WAVE_INPUT, "Wave type");
  244. configOutput(INTERP_OUTPUT, "Audio");
  245. configLight(PHASE_LIGHT, "Phase");
  246. lightInfos[PHASE_LIGHT]->description = "Tracks the sine output.\nGreen if positive, red if negative, blue if polyphonic.";
  247. lightDivider.setDivision(16);
  248. }
  249. void process(const ProcessArgs& args) override {
  250. float freqParam = params[FREQ_PARAM].getValue();
  251. float fmParam = params[FM_PARAM].getValue();
  252. float waveParam = params[WAVE_PARAM].getValue();
  253. int channels = std::max(1, inputs[FM_INPUT].getChannels());
  254. for (int c = 0; c < channels; c += 4) {
  255. auto* oscillator = &oscillators[c / 4];
  256. oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
  257. oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
  258. float_4 pitch = freqParam + inputs[FM_INPUT].getVoltageSimd<float_4>(c) * fmParam;
  259. oscillator->setPitch(pitch);
  260. oscillator->step(args.sampleTime);
  261. oscillator->setReset(inputs[RESET_INPUT].getPolyVoltageSimd<float_4>(c));
  262. // Outputs
  263. if (outputs[INTERP_OUTPUT].isConnected()) {
  264. float_4 wave = simd::clamp(waveParam + inputs[WAVE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * 3.f, 0.f, 3.f);
  265. float_4 v = 0.f;
  266. v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f));
  267. v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f));
  268. v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f));
  269. v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f));
  270. outputs[INTERP_OUTPUT].setVoltageSimd(5.f * v, c);
  271. }
  272. }
  273. outputs[INTERP_OUTPUT].setChannels(channels);
  274. // Light
  275. if (lightDivider.process()) {
  276. if (channels == 1) {
  277. float lightValue = oscillators[0].light().s[0];
  278. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  279. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  280. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  281. }
  282. else {
  283. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  284. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  285. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  286. }
  287. }
  288. }
  289. };
  290. struct LFO2Widget : ModuleWidget {
  291. LFO2Widget(LFO2* module) {
  292. setModule(module);
  293. setPanel(createPanel(asset::plugin(pluginInstance, "res/WTLFO.svg")));
  294. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  295. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  296. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  297. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  298. addParam(createParam<CKSS>(Vec(62, 150), module, LFO2::OFFSET_PARAM));
  299. addParam(createParam<CKSS>(Vec(62, 215), module, LFO2::INVERT_PARAM));
  300. addParam(createParam<RoundHugeBlackKnob>(Vec(18, 60), module, LFO2::FREQ_PARAM));
  301. addParam(createParam<RoundLargeBlackKnob>(Vec(11, 142), module, LFO2::WAVE_PARAM));
  302. addParam(createParam<RoundLargeBlackKnob>(Vec(11, 207), module, LFO2::FM_PARAM));
  303. addInput(createInput<PJ301MPort>(Vec(11, 276), module, LFO2::FM_INPUT));
  304. addInput(createInput<PJ301MPort>(Vec(54, 276), module, LFO2::RESET_INPUT));
  305. addInput(createInput<PJ301MPort>(Vec(11, 319), module, LFO2::WAVE_INPUT));
  306. addOutput(createOutput<PJ301MPort>(Vec(54, 319), module, LFO2::INTERP_OUTPUT));
  307. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(68, 42.5f), module, LFO2::PHASE_LIGHT));
  308. }
  309. };
  310. Model* modelLFO2 = createModel<LFO2, LFO2Widget>("LFO2");
  311. #endif