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.

506 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 * simd::pow(2.f, pitch);
  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 syncCrossing = -lastSyncValue / (syncValue - lastSyncValue);
  106. lastSyncValue = syncValue;
  107. T sync = (0.f < syncCrossing) & (syncCrossing <= 1.f);
  108. int syncMask = simd::movemask(sync);
  109. if (syncMask) {
  110. if (soft) {
  111. syncDirection = simd::ifelse(sync, -syncDirection, syncDirection);
  112. }
  113. else {
  114. T newPhase = simd::ifelse(sync, syncCrossing * deltaPhase, phase);
  115. // Insert minBLEP for sync
  116. for (int i = 0; i < channels; i++) {
  117. if (syncMask & (1 << i)) {
  118. T mask = simd::movemaskInverse<T>(1 << i);
  119. float p = syncCrossing[i] - 1.f;
  120. T x;
  121. x = mask & (sqr(newPhase) - sqr(phase));
  122. sqrMinBlep.insertDiscontinuity(p, x);
  123. x = mask & (saw(newPhase) - saw(phase));
  124. sawMinBlep.insertDiscontinuity(p, x);
  125. x = mask & (tri(newPhase) - tri(phase));
  126. triMinBlep.insertDiscontinuity(p, x);
  127. x = mask & (sin(newPhase) - sin(phase));
  128. sinMinBlep.insertDiscontinuity(p, x);
  129. }
  130. }
  131. phase = newPhase;
  132. }
  133. }
  134. }
  135. // Square
  136. sqrValue = sqr(phase);
  137. sqrValue += sqrMinBlep.process();
  138. if (analog) {
  139. sqrFilter.setCutoffFreq(20.f * deltaTime);
  140. sqrFilter.process(sqrValue);
  141. sqrValue = sqrFilter.highpass() * 0.95f;
  142. }
  143. // Saw
  144. sawValue = saw(phase);
  145. sawValue += sawMinBlep.process();
  146. // Tri
  147. triValue = tri(phase);
  148. triValue += triMinBlep.process();
  149. // Sin
  150. sinValue = sin(phase);
  151. sinValue += sinMinBlep.process();
  152. }
  153. T sin(T phase) {
  154. T v;
  155. if (analog) {
  156. // Quadratic approximation of sine, slightly richer harmonics
  157. T halfPhase = (phase < 0.5f);
  158. T x = phase - simd::ifelse(halfPhase, 0.25f, 0.75f);
  159. v = 1.f - 16.f * simd::pow(x, 2);
  160. v *= simd::ifelse(halfPhase, 1.f, -1.f);
  161. }
  162. else {
  163. v = sin2pi_pade_05_5_4(phase);
  164. // v = sin2pi_pade_05_7_6(phase);
  165. // v = simd::sin(2 * T(M_PI) * phase);
  166. }
  167. return v;
  168. }
  169. T sin() {
  170. return sinValue;
  171. }
  172. T tri(T phase) {
  173. T v;
  174. if (analog) {
  175. T x = phase + 0.25f;
  176. x -= simd::trunc(x);
  177. T halfX = (x < 0.5f);
  178. x = 2 * x - simd::ifelse(halfX, 0.f, 1.f);
  179. v = expCurve(x) * simd::ifelse(halfX, 1.f, -1.f);
  180. }
  181. else {
  182. v = 1 - 4 * simd::fmin(simd::fabs(phase - 0.25f), simd::fabs(phase - 1.25f));
  183. }
  184. return v;
  185. }
  186. T tri() {
  187. return triValue;
  188. }
  189. T saw(T phase) {
  190. T v;
  191. T x = phase + 0.5f;
  192. x -= simd::trunc(x);
  193. if (analog) {
  194. v = -expCurve(x);
  195. }
  196. else {
  197. v = 2 * x - 1;
  198. }
  199. return v;
  200. }
  201. T saw() {
  202. return sawValue;
  203. }
  204. T sqr(T phase) {
  205. T v = simd::ifelse(phase < pulseWidth, 1.f, -1.f);
  206. return v;
  207. }
  208. T sqr() {
  209. return sqrValue;
  210. }
  211. T light() {
  212. return simd::sin(2 * T(M_PI) * phase);
  213. }
  214. };
  215. struct VCO : Module {
  216. enum ParamIds {
  217. MODE_PARAM,
  218. SYNC_PARAM,
  219. FREQ_PARAM,
  220. FINE_PARAM,
  221. FM_PARAM,
  222. PW_PARAM,
  223. PWM_PARAM,
  224. NUM_PARAMS
  225. };
  226. enum InputIds {
  227. PITCH_INPUT,
  228. FM_INPUT,
  229. SYNC_INPUT,
  230. PW_INPUT,
  231. NUM_INPUTS
  232. };
  233. enum OutputIds {
  234. SIN_OUTPUT,
  235. TRI_OUTPUT,
  236. SAW_OUTPUT,
  237. SQR_OUTPUT,
  238. NUM_OUTPUTS
  239. };
  240. enum LightIds {
  241. ENUMS(PHASE_LIGHT, 3),
  242. NUM_LIGHTS
  243. };
  244. VoltageControlledOscillator<16, 16, float_4> oscillators[4];
  245. dsp::ClockDivider lightDivider;
  246. VCO() {
  247. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  248. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  249. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  250. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  251. configParam(FINE_PARAM, -1.f, 1.f, 0.f, "Fine frequency");
  252. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  253. configParam(PW_PARAM, 0.f, 1.f, 0.5f, "Pulse width", "%", 0.f, 100.f);
  254. configParam(PWM_PARAM, 0.f, 1.f, 0.f, "Pulse width modulation", "%", 0.f, 100.f);
  255. lightDivider.setDivision(16);
  256. }
  257. void process(const ProcessArgs &args) override {
  258. float freqParam = params[FREQ_PARAM].getValue() / 12.f;
  259. freqParam += dsp::quadraticBipolar(params[FINE_PARAM].getValue()) * 3.f / 12.f;
  260. float fmParam = dsp::quadraticBipolar(params[FM_PARAM].getValue());
  261. int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
  262. for (int c = 0; c < channels; c += 4) {
  263. auto *oscillator = &oscillators[c / 4];
  264. oscillator->channels = std::min(channels - c, 4);
  265. oscillator->analog = params[MODE_PARAM].getValue() > 0.f;
  266. oscillator->soft = params[SYNC_PARAM].getValue() <= 0.f;
  267. float_4 pitch = freqParam;
  268. pitch += inputs[PITCH_INPUT].getVoltageSimd<float_4>(c);
  269. if (inputs[FM_INPUT].isConnected()) {
  270. pitch += fmParam * inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c);
  271. }
  272. oscillator->setPitch(pitch);
  273. oscillator->setPulseWidth(params[PW_PARAM].getValue() + params[PWM_PARAM].getValue() * inputs[PW_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f);
  274. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  275. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  276. // Set output
  277. if (outputs[SIN_OUTPUT].isConnected())
  278. outputs[SIN_OUTPUT].setVoltageSimd(5.f * oscillator->sin(), c);
  279. if (outputs[TRI_OUTPUT].isConnected())
  280. outputs[TRI_OUTPUT].setVoltageSimd(5.f * oscillator->tri(), c);
  281. if (outputs[SAW_OUTPUT].isConnected())
  282. outputs[SAW_OUTPUT].setVoltageSimd(5.f * oscillator->saw(), c);
  283. if (outputs[SQR_OUTPUT].isConnected())
  284. outputs[SQR_OUTPUT].setVoltageSimd(5.f * oscillator->sqr(), c);
  285. }
  286. outputs[SIN_OUTPUT].setChannels(channels);
  287. outputs[TRI_OUTPUT].setChannels(channels);
  288. outputs[SAW_OUTPUT].setChannels(channels);
  289. outputs[SQR_OUTPUT].setChannels(channels);
  290. // Light
  291. if (lightDivider.process()) {
  292. if (channels == 1) {
  293. float lightValue = oscillators[0].light()[0];
  294. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  295. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  296. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  297. }
  298. else {
  299. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  300. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  301. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  302. }
  303. }
  304. }
  305. };
  306. struct VCOWidget : ModuleWidget {
  307. VCOWidget(VCO *module) {
  308. setModule(module);
  309. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-1.svg")));
  310. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  311. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  312. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  313. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  314. addParam(createParam<CKSS>(Vec(15, 77), module, VCO::MODE_PARAM));
  315. addParam(createParam<CKSS>(Vec(119, 77), module, VCO::SYNC_PARAM));
  316. addParam(createParam<RoundHugeBlackKnob>(Vec(47, 61), module, VCO::FREQ_PARAM));
  317. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 143), module, VCO::FINE_PARAM));
  318. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 143), module, VCO::PW_PARAM));
  319. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 208), module, VCO::FM_PARAM));
  320. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 208), module, VCO::PWM_PARAM));
  321. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO::PITCH_INPUT));
  322. addInput(createInput<PJ301MPort>(Vec(45, 276), module, VCO::FM_INPUT));
  323. addInput(createInput<PJ301MPort>(Vec(80, 276), module, VCO::SYNC_INPUT));
  324. addInput(createInput<PJ301MPort>(Vec(114, 276), module, VCO::PW_INPUT));
  325. addOutput(createOutput<PJ301MPort>(Vec(11, 320), module, VCO::SIN_OUTPUT));
  326. addOutput(createOutput<PJ301MPort>(Vec(45, 320), module, VCO::TRI_OUTPUT));
  327. addOutput(createOutput<PJ301MPort>(Vec(80, 320), module, VCO::SAW_OUTPUT));
  328. addOutput(createOutput<PJ301MPort>(Vec(114, 320), module, VCO::SQR_OUTPUT));
  329. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(99, 42.5f), module, VCO::PHASE_LIGHT));
  330. }
  331. };
  332. Model *modelVCO = createModel<VCO, VCOWidget>("VCO");
  333. struct VCO2 : Module {
  334. enum ParamIds {
  335. MODE_PARAM,
  336. SYNC_PARAM,
  337. FREQ_PARAM,
  338. WAVE_PARAM,
  339. FM_PARAM,
  340. NUM_PARAMS
  341. };
  342. enum InputIds {
  343. FM_INPUT,
  344. SYNC_INPUT,
  345. WAVE_INPUT,
  346. NUM_INPUTS
  347. };
  348. enum OutputIds {
  349. OUT_OUTPUT,
  350. NUM_OUTPUTS
  351. };
  352. enum LightIds {
  353. ENUMS(PHASE_LIGHT, 3),
  354. NUM_LIGHTS
  355. };
  356. VoltageControlledOscillator<8, 8, float_4> oscillators[4];
  357. dsp::ClockDivider lightDivider;
  358. VCO2() {
  359. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  360. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  361. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  362. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  363. configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave");
  364. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  365. lightDivider.setDivision(16);
  366. }
  367. void process(const ProcessArgs &args) override {
  368. float freqParam = params[FREQ_PARAM].getValue() / 12.f;
  369. float fmParam = dsp::quadraticBipolar(params[FM_PARAM].getValue());
  370. float waveParam = params[WAVE_PARAM].getValue();
  371. int channels = std::max(inputs[FM_INPUT].getChannels(), 1);
  372. for (int c = 0; c < channels; c += 4) {
  373. auto *oscillator = &oscillators[c / 4];
  374. oscillator->channels = std::min(channels - c, 4);
  375. oscillator->analog = (params[MODE_PARAM].getValue() > 0.f);
  376. oscillator->soft = (params[SYNC_PARAM].getValue() <= 0.f);
  377. float_4 pitch = freqParam;
  378. pitch += fmParam * inputs[FM_INPUT].getVoltageSimd<float_4>(c);
  379. oscillator->setPitch(pitch);
  380. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  381. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  382. // Outputs
  383. if (outputs[OUT_OUTPUT].isConnected()) {
  384. float_4 wave = simd::clamp(waveParam + inputs[WAVE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * 3.f, 0.f, 3.f);
  385. float_4 v = 0.f;
  386. v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f));
  387. v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f));
  388. v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f));
  389. v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f));
  390. outputs[OUT_OUTPUT].setVoltageSimd(5.f * v, c);
  391. }
  392. }
  393. outputs[OUT_OUTPUT].setChannels(channels);
  394. // Light
  395. if (lightDivider.process()) {
  396. if (channels == 1) {
  397. float lightValue = oscillators[0].light()[0];
  398. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  399. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  400. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  401. }
  402. else {
  403. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  404. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  405. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  406. }
  407. }
  408. }
  409. };
  410. struct VCO2Widget : ModuleWidget {
  411. VCO2Widget(VCO2 *module) {
  412. setModule(module);
  413. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-2.svg")));
  414. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  415. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  416. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  417. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  418. addParam(createParam<CKSS>(Vec(62, 150), module, VCO2::MODE_PARAM));
  419. addParam(createParam<CKSS>(Vec(62, 215), module, VCO2::SYNC_PARAM));
  420. addParam(createParam<RoundHugeBlackKnob>(Vec(17, 60), module, VCO2::FREQ_PARAM));
  421. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 143), module, VCO2::WAVE_PARAM));
  422. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 208), module, VCO2::FM_PARAM));
  423. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO2::FM_INPUT));
  424. addInput(createInput<PJ301MPort>(Vec(54, 276), module, VCO2::SYNC_INPUT));
  425. addInput(createInput<PJ301MPort>(Vec(11, 320), module, VCO2::WAVE_INPUT));
  426. addOutput(createOutput<PJ301MPort>(Vec(54, 320), module, VCO2::OUT_OUTPUT));
  427. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(68, 42.5f), module, VCO2::PHASE_LIGHT));
  428. }
  429. };
  430. Model *modelVCO2 = createModel<VCO2, VCO2Widget>("VCO2");