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.

359 lines
11KB

  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 = simd::pow(2.f, pitch);
  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) p -= 0.25f;
  36. T v = simd::sin(2*M_PI * p);
  37. if (invert) v *= -1.f;
  38. if (!bipolar) v += 1.f;
  39. return v;
  40. }
  41. T tri() {
  42. T p = phase;
  43. if (bipolar) p += 0.25f;
  44. T v = 4.f * simd::fabs(p - simd::round(p)) - 1.f;
  45. if (invert) v *= -1.f;
  46. if (!bipolar) v += 1.f;
  47. return v;
  48. }
  49. T saw() {
  50. T p = phase;
  51. if (!bipolar) p -= 0.5f;
  52. T v = 2.f * (p - simd::round(p));
  53. if (invert) v *= -1.f;
  54. if (!bipolar) v += 1.f;
  55. return v;
  56. }
  57. T sqr() {
  58. T v = simd::ifelse(phase < pw, 1.f, -1.f);
  59. if (invert) v *= -1.f;
  60. if (!bipolar) v += 1.f;
  61. return v;
  62. }
  63. T light() {
  64. return 1.f - 2.f * phase;
  65. }
  66. };
  67. struct LFO : Module {
  68. enum ParamIds {
  69. OFFSET_PARAM,
  70. INVERT_PARAM,
  71. FREQ_PARAM,
  72. FM1_PARAM,
  73. FM2_PARAM,
  74. PW_PARAM,
  75. PWM_PARAM,
  76. NUM_PARAMS
  77. };
  78. enum InputIds {
  79. FM1_INPUT,
  80. FM2_INPUT,
  81. RESET_INPUT,
  82. PW_INPUT,
  83. NUM_INPUTS
  84. };
  85. enum OutputIds {
  86. SIN_OUTPUT,
  87. TRI_OUTPUT,
  88. SAW_OUTPUT,
  89. SQR_OUTPUT,
  90. NUM_OUTPUTS
  91. };
  92. enum LightIds {
  93. ENUMS(PHASE_LIGHT, 3),
  94. NUM_LIGHTS
  95. };
  96. LowFrequencyOscillator<float_4> oscillators[4];
  97. dsp::ClockDivider lightDivider;
  98. LFO() {
  99. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  100. configParam(OFFSET_PARAM, 0.f, 1.f, 1.f, "Offset");
  101. configParam(INVERT_PARAM, 0.f, 1.f, 1.f, "Invert");
  102. configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1);
  103. configParam(FM1_PARAM, 0.f, 1.f, 0.f, "Frequency modulation 1", "%", 0.f, 100.f);
  104. configParam(PW_PARAM, 0.f, 1.f, 0.5f, "Pulse width", "%", 0.f, 100.f);
  105. configParam(FM2_PARAM, 0.f, 1.f, 0.f, "Frequency modulation 2", "%", 0.f, 100.f);
  106. configParam(PWM_PARAM, 0.f, 1.f, 0.f, "Pulse width modulation", "%", 0.f, 100.f);
  107. lightDivider.setDivision(16);
  108. }
  109. void process(const ProcessArgs &args) override {
  110. float freqParam = params[FREQ_PARAM].getValue();
  111. float fm1Param = params[FM1_PARAM].getValue();
  112. float fm2Param = params[FM2_PARAM].getValue();
  113. float pwParam = params[PW_PARAM].getValue();
  114. float pwmParam = params[PWM_PARAM].getValue();
  115. int channels = std::max(1, inputs[FM1_INPUT].getChannels());
  116. for (int c = 0; c < channels; c += 4) {
  117. auto *oscillator = &oscillators[c / 4];
  118. float_4 pitch = freqParam;
  119. // FM1, polyphonic
  120. pitch += float_4::load(inputs[FM1_INPUT].getVoltages(c)) * fm1Param;
  121. // FM2, polyphonic or monophonic
  122. if (inputs[FM2_INPUT].isPolyphonic())
  123. pitch += float_4::load(inputs[FM2_INPUT].getVoltages(c)) * fm2Param;
  124. else
  125. pitch += inputs[FM2_INPUT].getVoltage() * fm2Param;
  126. oscillator->setPitch(pitch);
  127. // Pulse width
  128. float_4 pw = pwParam;
  129. if (inputs[PW_INPUT].isPolyphonic())
  130. pw += float_4::load(inputs[PW_INPUT].getVoltages(c)) / 10.f * pwmParam;
  131. else
  132. pw += inputs[PW_INPUT].getVoltage() / 10.f * pwmParam;
  133. oscillator->setPulseWidth(pw);
  134. // Settings
  135. oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
  136. oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
  137. oscillator->step(args.sampleTime);
  138. oscillator->setReset(inputs[RESET_INPUT].getVoltage());
  139. // Outputs
  140. if (outputs[SIN_OUTPUT].isConnected()) {
  141. outputs[SIN_OUTPUT].setChannels(channels);
  142. float_4 v = 5.f * oscillator->sin();
  143. v.store(outputs[SIN_OUTPUT].getVoltages(c));
  144. }
  145. if (outputs[TRI_OUTPUT].isConnected()) {
  146. outputs[TRI_OUTPUT].setChannels(channels);
  147. float_4 v = 5.f * oscillator->tri();
  148. v.store(outputs[TRI_OUTPUT].getVoltages(c));
  149. }
  150. if (outputs[SAW_OUTPUT].isConnected()) {
  151. outputs[SAW_OUTPUT].setChannels(channels);
  152. float_4 v = 5.f * oscillator->saw();
  153. v.store(outputs[SAW_OUTPUT].getVoltages(c));
  154. }
  155. if (outputs[SQR_OUTPUT].isConnected()) {
  156. outputs[SQR_OUTPUT].setChannels(channels);
  157. float_4 v = 5.f * oscillator->sqr();
  158. v.store(outputs[SQR_OUTPUT].getVoltages(c));
  159. }
  160. }
  161. // Light
  162. if (lightDivider.process()) {
  163. if (channels == 1) {
  164. float lightValue = oscillators[0].light().s[0];
  165. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  166. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  167. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  168. }
  169. else {
  170. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  171. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  172. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  173. }
  174. }
  175. }
  176. };
  177. struct LFOWidget : ModuleWidget {
  178. LFOWidget(LFO *module) {
  179. setModule(module);
  180. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/LFO-1.svg")));
  181. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  182. addChild(createWidget<ScrewSilver>(Vec(box.size.x-30, 0)));
  183. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  184. addChild(createWidget<ScrewSilver>(Vec(box.size.x-30, 365)));
  185. addParam(createParam<CKSS>(Vec(15, 77), module, LFO::OFFSET_PARAM));
  186. addParam(createParam<CKSS>(Vec(119, 77), module, LFO::INVERT_PARAM));
  187. addParam(createParam<RoundHugeBlackKnob>(Vec(47, 61), module, LFO::FREQ_PARAM));
  188. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 143), module, LFO::FM1_PARAM));
  189. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 143), module, LFO::PW_PARAM));
  190. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 208), module, LFO::FM2_PARAM));
  191. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 208), module, LFO::PWM_PARAM));
  192. addInput(createInput<PJ301MPort>(Vec(11, 276), module, LFO::FM1_INPUT));
  193. addInput(createInput<PJ301MPort>(Vec(45, 276), module, LFO::FM2_INPUT));
  194. addInput(createInput<PJ301MPort>(Vec(80, 276), module, LFO::RESET_INPUT));
  195. addInput(createInput<PJ301MPort>(Vec(114, 276), module, LFO::PW_INPUT));
  196. addOutput(createOutput<PJ301MPort>(Vec(11, 320), module, LFO::SIN_OUTPUT));
  197. addOutput(createOutput<PJ301MPort>(Vec(45, 320), module, LFO::TRI_OUTPUT));
  198. addOutput(createOutput<PJ301MPort>(Vec(80, 320), module, LFO::SAW_OUTPUT));
  199. addOutput(createOutput<PJ301MPort>(Vec(114, 320), module, LFO::SQR_OUTPUT));
  200. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(99, 42.5f), module, LFO::PHASE_LIGHT));
  201. }
  202. };
  203. Model *modelLFO = createModel<LFO, LFOWidget>("LFO");
  204. struct LFO2 : Module {
  205. enum ParamIds {
  206. OFFSET_PARAM,
  207. INVERT_PARAM,
  208. FREQ_PARAM,
  209. WAVE_PARAM,
  210. FM_PARAM,
  211. NUM_PARAMS
  212. };
  213. enum InputIds {
  214. FM_INPUT,
  215. RESET_INPUT,
  216. WAVE_INPUT,
  217. NUM_INPUTS
  218. };
  219. enum OutputIds {
  220. INTERP_OUTPUT,
  221. NUM_OUTPUTS
  222. };
  223. enum LightIds {
  224. ENUMS(PHASE_LIGHT, 3),
  225. NUM_LIGHTS
  226. };
  227. LowFrequencyOscillator<float_4> oscillators[4];
  228. dsp::ClockDivider lightDivider;
  229. LFO2() {
  230. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  231. configParam(OFFSET_PARAM, 0.f, 1.f, 1.f, "Offset");
  232. configParam(INVERT_PARAM, 0.f, 1.f, 1.f, "Invert");
  233. configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1);
  234. configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave");
  235. configParam(FM_PARAM, 0.f, 1.f, 0.5f, "Frequency modulation", "%", 0.f, 100.f);
  236. lightDivider.setDivision(16);
  237. }
  238. void process(const ProcessArgs &args) override {
  239. float freqParam = params[FREQ_PARAM].getValue();
  240. float waveParam = params[WAVE_PARAM].getValue();
  241. float fmParam = params[FM_PARAM].getValue();
  242. int channels = std::max(1, inputs[FM_INPUT].getChannels());
  243. for (int c = 0; c < channels; c += 4) {
  244. auto *oscillator = &oscillators[c / 4];
  245. float_4 pitch = freqParam;
  246. // FM, polyphonic
  247. pitch += float_4::load(inputs[FM_INPUT].getVoltages(c)) * fmParam;
  248. oscillator->setPitch(pitch);
  249. // Wave
  250. float_4 wave = waveParam;
  251. inputs[WAVE_INPUT].getVoltage();
  252. if (inputs[WAVE_INPUT].isPolyphonic())
  253. wave += float_4::load(inputs[WAVE_INPUT].getVoltages(c)) / 10.f * 3.f;
  254. else
  255. wave += inputs[WAVE_INPUT].getVoltage() / 10.f * 3.f;
  256. wave = clamp(wave, 0.f, 3.f);
  257. // Settings
  258. oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
  259. oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
  260. oscillator->step(args.sampleTime);
  261. oscillator->setReset(inputs[RESET_INPUT].getVoltage());
  262. // Outputs
  263. if (outputs[INTERP_OUTPUT].isConnected()) {
  264. outputs[INTERP_OUTPUT].setChannels(channels);
  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. v *= 5.f;
  271. v.store(outputs[INTERP_OUTPUT].getVoltages(c));
  272. }
  273. }
  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(APP->window->loadSvg(asset::plugin(pluginInstance, "res/LFO-2.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");