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.

508 lines
15KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  3. // Accurate only on [0, 1]
  4. template <typename T>
  5. T sin2pi_pade_05_7_6(T x) {
  6. x -= 0.5f;
  7. return (T(-6.28319) * x + T(35.353) * simd::pow(x, 3) - T(44.9043) * simd::pow(x, 5) + T(16.0951) * simd::pow(x, 7))
  8. / (1 + T(0.953136) * simd::pow(x, 2) + T(0.430238) * simd::pow(x, 4) + T(0.0981408) * simd::pow(x, 6));
  9. }
  10. template <typename T>
  11. T sin2pi_pade_05_5_4(T x) {
  12. x -= 0.5f;
  13. return (T(-6.283185307) * x + T(33.19863968) * simd::pow(x, 3) - T(32.44191367) * simd::pow(x, 5))
  14. / (1 + T(1.296008659) * simd::pow(x, 2) + T(0.7028072946) * simd::pow(x, 4));
  15. }
  16. template <typename T>
  17. T expCurve(T x) {
  18. return (3 + x * (-13 + 5 * x)) / (3 + 2 * x);
  19. }
  20. template <int OVERSAMPLE, int QUALITY, typename T>
  21. struct VoltageControlledOscillator {
  22. bool analog = false;
  23. bool soft = false;
  24. bool syncEnabled = false;
  25. // For optimizing in serial code
  26. int channels = 0;
  27. T lastSyncValue = 0.f;
  28. T phase = 0.f;
  29. T freq;
  30. T pulseWidth = 0.5f;
  31. T syncDirection = 1.f;
  32. dsp::TRCFilter<T> sqrFilter;
  33. dsp::MinBlepGenerator<QUALITY, OVERSAMPLE, T> sqrMinBlep;
  34. dsp::MinBlepGenerator<QUALITY, OVERSAMPLE, T> sawMinBlep;
  35. dsp::MinBlepGenerator<QUALITY, OVERSAMPLE, T> triMinBlep;
  36. dsp::MinBlepGenerator<QUALITY, OVERSAMPLE, T> sinMinBlep;
  37. T sqrValue = 0.f;
  38. T sawValue = 0.f;
  39. T triValue = 0.f;
  40. T sinValue = 0.f;
  41. void setPitch(T pitch) {
  42. freq = dsp::FREQ_C4 * dsp::approxExp2_taylor5(pitch + 30) / 1073741824;
  43. }
  44. void setPulseWidth(T pulseWidth) {
  45. const float pwMin = 0.01f;
  46. this->pulseWidth = simd::clamp(pulseWidth, pwMin, 1.f - pwMin);
  47. }
  48. void process(float deltaTime, T syncValue) {
  49. // Advance phase
  50. T deltaPhase = simd::clamp(freq * deltaTime, 1e-6f, 0.35f);
  51. if (soft) {
  52. // Reverse direction
  53. deltaPhase *= syncDirection;
  54. }
  55. else {
  56. // Reset back to forward
  57. syncDirection = 1.f;
  58. }
  59. phase += deltaPhase;
  60. // Wrap phase
  61. phase -= simd::floor(phase);
  62. // Jump sqr when crossing 0, or 1 if backwards
  63. T wrapPhase = (syncDirection == -1.f) & 1.f;
  64. T wrapCrossing = (wrapPhase - (phase - deltaPhase)) / deltaPhase;
  65. int wrapMask = simd::movemask((0 < wrapCrossing) & (wrapCrossing <= 1.f));
  66. if (wrapMask) {
  67. for (int i = 0; i < channels; i++) {
  68. if (wrapMask & (1 << i)) {
  69. T mask = simd::movemaskInverse<T>(1 << i);
  70. float p = wrapCrossing[i] - 1.f;
  71. T x = mask & (2.f * syncDirection);
  72. sqrMinBlep.insertDiscontinuity(p, x);
  73. }
  74. }
  75. }
  76. // Jump sqr when crossing `pulseWidth`
  77. T pulseCrossing = (pulseWidth - (phase - deltaPhase)) / deltaPhase;
  78. int pulseMask = simd::movemask((0 < pulseCrossing) & (pulseCrossing <= 1.f));
  79. if (pulseMask) {
  80. for (int i = 0; i < channels; i++) {
  81. if (pulseMask & (1 << i)) {
  82. T mask = simd::movemaskInverse<T>(1 << i);
  83. float p = pulseCrossing[i] - 1.f;
  84. T x = mask & (-2.f * syncDirection);
  85. sqrMinBlep.insertDiscontinuity(p, x);
  86. }
  87. }
  88. }
  89. // Jump saw when crossing 0.5
  90. T halfCrossing = (0.5f - (phase - deltaPhase)) / deltaPhase;
  91. int halfMask = simd::movemask((0 < halfCrossing) & (halfCrossing <= 1.f));
  92. if (halfMask) {
  93. for (int i = 0; i < channels; i++) {
  94. if (halfMask & (1 << i)) {
  95. T mask = simd::movemaskInverse<T>(1 << i);
  96. float p = halfCrossing[i] - 1.f;
  97. T x = mask & (-2.f * syncDirection);
  98. sawMinBlep.insertDiscontinuity(p, x);
  99. }
  100. }
  101. }
  102. // Detect sync
  103. // Might be NAN or outside of [0, 1) range
  104. if (syncEnabled) {
  105. T deltaSync = syncValue - lastSyncValue;
  106. T syncCrossing = -lastSyncValue / deltaSync;
  107. lastSyncValue = syncValue;
  108. T sync = (0.f < syncCrossing) & (syncCrossing <= 1.f) & (syncValue >= 0.f);
  109. int syncMask = simd::movemask(sync);
  110. if (syncMask) {
  111. if (soft) {
  112. syncDirection = simd::ifelse(sync, -syncDirection, syncDirection);
  113. }
  114. else {
  115. T newPhase = simd::ifelse(sync, (1.f - syncCrossing) * deltaPhase, phase);
  116. // Insert minBLEP for sync
  117. for (int i = 0; i < channels; i++) {
  118. if (syncMask & (1 << i)) {
  119. T mask = simd::movemaskInverse<T>(1 << i);
  120. float p = syncCrossing[i] - 1.f;
  121. T x;
  122. x = mask & (sqr(newPhase) - sqr(phase));
  123. sqrMinBlep.insertDiscontinuity(p, x);
  124. x = mask & (saw(newPhase) - saw(phase));
  125. sawMinBlep.insertDiscontinuity(p, x);
  126. x = mask & (tri(newPhase) - tri(phase));
  127. triMinBlep.insertDiscontinuity(p, x);
  128. x = mask & (sin(newPhase) - sin(phase));
  129. sinMinBlep.insertDiscontinuity(p, x);
  130. }
  131. }
  132. phase = newPhase;
  133. }
  134. }
  135. }
  136. // Square
  137. sqrValue = sqr(phase);
  138. sqrValue += sqrMinBlep.process();
  139. if (analog) {
  140. sqrFilter.setCutoffFreq(20.f * deltaTime);
  141. sqrFilter.process(sqrValue);
  142. sqrValue = sqrFilter.highpass() * 0.95f;
  143. }
  144. // Saw
  145. sawValue = saw(phase);
  146. sawValue += sawMinBlep.process();
  147. // Tri
  148. triValue = tri(phase);
  149. triValue += triMinBlep.process();
  150. // Sin
  151. sinValue = sin(phase);
  152. sinValue += sinMinBlep.process();
  153. }
  154. T sin(T phase) {
  155. T v;
  156. if (analog) {
  157. // Quadratic approximation of sine, slightly richer harmonics
  158. T halfPhase = (phase < 0.5f);
  159. T x = phase - simd::ifelse(halfPhase, 0.25f, 0.75f);
  160. v = 1.f - 16.f * simd::pow(x, 2);
  161. v *= simd::ifelse(halfPhase, 1.f, -1.f);
  162. }
  163. else {
  164. v = sin2pi_pade_05_5_4(phase);
  165. // v = sin2pi_pade_05_7_6(phase);
  166. // v = simd::sin(2 * T(M_PI) * phase);
  167. }
  168. return v;
  169. }
  170. T sin() {
  171. return sinValue;
  172. }
  173. T tri(T phase) {
  174. T v;
  175. if (analog) {
  176. T x = phase + 0.25f;
  177. x -= simd::trunc(x);
  178. T halfX = (x >= 0.5f);
  179. x *= 2;
  180. x -= simd::trunc(x);
  181. v = expCurve(x) * simd::ifelse(halfX, 1.f, -1.f);
  182. }
  183. else {
  184. v = 1 - 4 * simd::fmin(simd::fabs(phase - 0.25f), simd::fabs(phase - 1.25f));
  185. }
  186. return v;
  187. }
  188. T tri() {
  189. return triValue;
  190. }
  191. T saw(T phase) {
  192. T v;
  193. T x = phase + 0.5f;
  194. x -= simd::trunc(x);
  195. if (analog) {
  196. v = -expCurve(x);
  197. }
  198. else {
  199. v = 2 * x - 1;
  200. }
  201. return v;
  202. }
  203. T saw() {
  204. return sawValue;
  205. }
  206. T sqr(T phase) {
  207. T v = simd::ifelse(phase < pulseWidth, 1.f, -1.f);
  208. return v;
  209. }
  210. T sqr() {
  211. return sqrValue;
  212. }
  213. T light() {
  214. return simd::sin(2 * T(M_PI) * phase);
  215. }
  216. };
  217. struct VCO : Module {
  218. enum ParamIds {
  219. MODE_PARAM,
  220. SYNC_PARAM,
  221. FREQ_PARAM,
  222. FINE_PARAM,
  223. FM_PARAM,
  224. PW_PARAM,
  225. PWM_PARAM,
  226. NUM_PARAMS
  227. };
  228. enum InputIds {
  229. PITCH_INPUT,
  230. FM_INPUT,
  231. SYNC_INPUT,
  232. PW_INPUT,
  233. NUM_INPUTS
  234. };
  235. enum OutputIds {
  236. SIN_OUTPUT,
  237. TRI_OUTPUT,
  238. SAW_OUTPUT,
  239. SQR_OUTPUT,
  240. NUM_OUTPUTS
  241. };
  242. enum LightIds {
  243. ENUMS(PHASE_LIGHT, 3),
  244. NUM_LIGHTS
  245. };
  246. VoltageControlledOscillator<16, 16, float_4> oscillators[4];
  247. dsp::ClockDivider lightDivider;
  248. VCO() {
  249. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  250. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  251. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  252. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  253. configParam(FINE_PARAM, -1.f, 1.f, 0.f, "Fine frequency");
  254. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  255. configParam(PW_PARAM, 0.01f, 0.99f, 0.5f, "Pulse width", "%", 0.f, 100.f);
  256. configParam(PWM_PARAM, 0.f, 1.f, 0.f, "Pulse width modulation", "%", 0.f, 100.f);
  257. lightDivider.setDivision(16);
  258. }
  259. void process(const ProcessArgs &args) override {
  260. float freqParam = params[FREQ_PARAM].getValue() / 12.f;
  261. freqParam += dsp::quadraticBipolar(params[FINE_PARAM].getValue()) * 3.f / 12.f;
  262. float fmParam = dsp::quadraticBipolar(params[FM_PARAM].getValue());
  263. int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
  264. for (int c = 0; c < channels; c += 4) {
  265. auto *oscillator = &oscillators[c / 4];
  266. oscillator->channels = std::min(channels - c, 4);
  267. oscillator->analog = params[MODE_PARAM].getValue() > 0.f;
  268. oscillator->soft = params[SYNC_PARAM].getValue() <= 0.f;
  269. float_4 pitch = freqParam;
  270. pitch += inputs[PITCH_INPUT].getVoltageSimd<float_4>(c);
  271. if (inputs[FM_INPUT].isConnected()) {
  272. pitch += fmParam * inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c);
  273. }
  274. oscillator->setPitch(pitch);
  275. oscillator->setPulseWidth(params[PW_PARAM].getValue() + params[PWM_PARAM].getValue() * inputs[PW_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f);
  276. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  277. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  278. // Set output
  279. if (outputs[SIN_OUTPUT].isConnected())
  280. outputs[SIN_OUTPUT].setVoltageSimd(5.f * oscillator->sin(), c);
  281. if (outputs[TRI_OUTPUT].isConnected())
  282. outputs[TRI_OUTPUT].setVoltageSimd(5.f * oscillator->tri(), c);
  283. if (outputs[SAW_OUTPUT].isConnected())
  284. outputs[SAW_OUTPUT].setVoltageSimd(5.f * oscillator->saw(), c);
  285. if (outputs[SQR_OUTPUT].isConnected())
  286. outputs[SQR_OUTPUT].setVoltageSimd(5.f * oscillator->sqr(), c);
  287. }
  288. outputs[SIN_OUTPUT].setChannels(channels);
  289. outputs[TRI_OUTPUT].setChannels(channels);
  290. outputs[SAW_OUTPUT].setChannels(channels);
  291. outputs[SQR_OUTPUT].setChannels(channels);
  292. // Light
  293. if (lightDivider.process()) {
  294. if (channels == 1) {
  295. float lightValue = oscillators[0].light()[0];
  296. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  297. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  298. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  299. }
  300. else {
  301. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  302. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  303. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  304. }
  305. }
  306. }
  307. };
  308. struct VCOWidget : ModuleWidget {
  309. VCOWidget(VCO *module) {
  310. setModule(module);
  311. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-1.svg")));
  312. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  313. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  314. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  315. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  316. addParam(createParam<CKSS>(Vec(15, 77), module, VCO::MODE_PARAM));
  317. addParam(createParam<CKSS>(Vec(119, 77), module, VCO::SYNC_PARAM));
  318. addParam(createParam<RoundHugeBlackKnob>(Vec(47, 61), module, VCO::FREQ_PARAM));
  319. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 143), module, VCO::FINE_PARAM));
  320. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 143), module, VCO::PW_PARAM));
  321. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 208), module, VCO::FM_PARAM));
  322. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 208), module, VCO::PWM_PARAM));
  323. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO::PITCH_INPUT));
  324. addInput(createInput<PJ301MPort>(Vec(45, 276), module, VCO::FM_INPUT));
  325. addInput(createInput<PJ301MPort>(Vec(80, 276), module, VCO::SYNC_INPUT));
  326. addInput(createInput<PJ301MPort>(Vec(114, 276), module, VCO::PW_INPUT));
  327. addOutput(createOutput<PJ301MPort>(Vec(11, 320), module, VCO::SIN_OUTPUT));
  328. addOutput(createOutput<PJ301MPort>(Vec(45, 320), module, VCO::TRI_OUTPUT));
  329. addOutput(createOutput<PJ301MPort>(Vec(80, 320), module, VCO::SAW_OUTPUT));
  330. addOutput(createOutput<PJ301MPort>(Vec(114, 320), module, VCO::SQR_OUTPUT));
  331. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(99, 42.5f), module, VCO::PHASE_LIGHT));
  332. }
  333. };
  334. Model *modelVCO = createModel<VCO, VCOWidget>("VCO");
  335. struct VCO2 : Module {
  336. enum ParamIds {
  337. MODE_PARAM,
  338. SYNC_PARAM,
  339. FREQ_PARAM,
  340. WAVE_PARAM,
  341. FM_PARAM,
  342. NUM_PARAMS
  343. };
  344. enum InputIds {
  345. FM_INPUT,
  346. SYNC_INPUT,
  347. WAVE_INPUT,
  348. NUM_INPUTS
  349. };
  350. enum OutputIds {
  351. OUT_OUTPUT,
  352. NUM_OUTPUTS
  353. };
  354. enum LightIds {
  355. ENUMS(PHASE_LIGHT, 3),
  356. NUM_LIGHTS
  357. };
  358. VoltageControlledOscillator<8, 8, float_4> oscillators[4];
  359. dsp::ClockDivider lightDivider;
  360. VCO2() {
  361. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  362. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  363. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  364. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  365. configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave");
  366. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  367. lightDivider.setDivision(16);
  368. }
  369. void process(const ProcessArgs &args) override {
  370. float freqParam = params[FREQ_PARAM].getValue() / 12.f;
  371. float fmParam = dsp::quadraticBipolar(params[FM_PARAM].getValue());
  372. float waveParam = params[WAVE_PARAM].getValue();
  373. int channels = std::max(inputs[FM_INPUT].getChannels(), 1);
  374. for (int c = 0; c < channels; c += 4) {
  375. auto *oscillator = &oscillators[c / 4];
  376. oscillator->channels = std::min(channels - c, 4);
  377. oscillator->analog = (params[MODE_PARAM].getValue() > 0.f);
  378. oscillator->soft = (params[SYNC_PARAM].getValue() <= 0.f);
  379. float_4 pitch = freqParam;
  380. pitch += fmParam * inputs[FM_INPUT].getVoltageSimd<float_4>(c);
  381. oscillator->setPitch(pitch);
  382. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  383. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  384. // Outputs
  385. if (outputs[OUT_OUTPUT].isConnected()) {
  386. float_4 wave = simd::clamp(waveParam + inputs[WAVE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * 3.f, 0.f, 3.f);
  387. float_4 v = 0.f;
  388. v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f));
  389. v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f));
  390. v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f));
  391. v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f));
  392. outputs[OUT_OUTPUT].setVoltageSimd(5.f * v, c);
  393. }
  394. }
  395. outputs[OUT_OUTPUT].setChannels(channels);
  396. // Light
  397. if (lightDivider.process()) {
  398. if (channels == 1) {
  399. float lightValue = oscillators[0].light()[0];
  400. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  401. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  402. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  403. }
  404. else {
  405. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  406. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  407. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  408. }
  409. }
  410. }
  411. };
  412. struct VCO2Widget : ModuleWidget {
  413. VCO2Widget(VCO2 *module) {
  414. setModule(module);
  415. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-2.svg")));
  416. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  417. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  418. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  419. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  420. addParam(createParam<CKSS>(Vec(62, 150), module, VCO2::MODE_PARAM));
  421. addParam(createParam<CKSS>(Vec(62, 215), module, VCO2::SYNC_PARAM));
  422. addParam(createParam<RoundHugeBlackKnob>(Vec(17, 60), module, VCO2::FREQ_PARAM));
  423. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 143), module, VCO2::WAVE_PARAM));
  424. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 208), module, VCO2::FM_PARAM));
  425. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO2::FM_INPUT));
  426. addInput(createInput<PJ301MPort>(Vec(54, 276), module, VCO2::SYNC_INPUT));
  427. addInput(createInput<PJ301MPort>(Vec(11, 320), module, VCO2::WAVE_INPUT));
  428. addOutput(createOutput<PJ301MPort>(Vec(54, 320), module, VCO2::OUT_OUTPUT));
  429. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(68, 42.5f), module, VCO2::PHASE_LIGHT));
  430. }
  431. };
  432. Model *modelVCO2 = createModel<VCO2, VCO2Widget>("VCO2");