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.

450 lines
14KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  3. BINARY(src_sawTable_bin);
  4. BINARY(src_triTable_bin);
  5. // Accurate only on [0, 1]
  6. template <typename T>
  7. T sin2pi_pade_05_7_6(T x) {
  8. x -= 0.5f;
  9. 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))
  10. / (1 + T(0.953136) * simd::pow(x, 2) + T(0.430238) * simd::pow(x, 4) + T(0.0981408) * simd::pow(x, 6));
  11. }
  12. template <typename T>
  13. T sin2pi_pade_05_5_4(T x) {
  14. x -= 0.5f;
  15. return (T(-6.283185307) * x + T(33.19863968) * simd::pow(x, 3) - T(32.44191367) * simd::pow(x, 5))
  16. / (1 + T(1.296008659) * simd::pow(x, 2) + T(0.7028072946) * simd::pow(x, 4));
  17. }
  18. template <int OVERSAMPLE, int QUALITY, typename T>
  19. struct VoltageControlledOscillator {
  20. bool analog = false;
  21. bool soft = false;
  22. T lastSyncValue = 0.f;
  23. T phase = 0.f;
  24. T freq;
  25. T pw = 0.5f;
  26. bool syncEnabled = false;
  27. T syncDirection = T::zero();
  28. dsp::Decimator<OVERSAMPLE, QUALITY, T> sinDecimator;
  29. dsp::Decimator<OVERSAMPLE, QUALITY, T> triDecimator;
  30. dsp::Decimator<OVERSAMPLE, QUALITY, T> sawDecimator;
  31. dsp::Decimator<OVERSAMPLE, QUALITY, T> sqrDecimator;
  32. dsp::TRCFilter<T> sqrFilter;
  33. // For analog detuning effect
  34. T pitchSlew = 0.f;
  35. int pitchSlewIndex = 0;
  36. T sinBuffer[OVERSAMPLE];
  37. T triBuffer[OVERSAMPLE];
  38. T sawBuffer[OVERSAMPLE];
  39. T sqrBuffer[OVERSAMPLE];
  40. void setPitch(T pitchKnob, T pitchCv) {
  41. // Compute frequency
  42. T pitch = pitchKnob + pitchCv;
  43. if (analog) {
  44. // Apply pitch slew
  45. const float pitchSlewAmount = 3.f;
  46. pitch += pitchSlew * pitchSlewAmount;
  47. }
  48. freq = dsp::FREQ_C4 * simd::pow(2.f, pitch / 12.f);
  49. }
  50. void setPulseWidth(T pulseWidth) {
  51. const float pwMin = 0.01f;
  52. pw = simd::clamp(pulseWidth, pwMin, 1.f - pwMin);
  53. }
  54. void process(float deltaTime, T syncValue) {
  55. if (analog) {
  56. // Adjust pitch slew
  57. if (++pitchSlewIndex > 32) {
  58. const float pitchSlewTau = 100.f; // Time constant for leaky integrator in seconds
  59. T r;
  60. for (int i = 0; i < T::size; i++) {
  61. r.s[i] = random::uniform();
  62. }
  63. pitchSlew += ((2.f * r - 1.f) - pitchSlew / pitchSlewTau) * deltaTime;
  64. pitchSlewIndex = 0;
  65. }
  66. }
  67. // Advance phase
  68. T deltaPhase = simd::clamp(freq * deltaTime, 1e-6f, 0.5f);
  69. if (!soft) {
  70. syncDirection = T::zero();
  71. }
  72. // Detect sync
  73. // Might be NAN or outside of [0, 1) range
  74. T syncCrossing = lastSyncValue / (lastSyncValue - syncValue);
  75. lastSyncValue = syncValue;
  76. for (int i = 0; i < OVERSAMPLE; i++) {
  77. // Reset if synced in this time interval
  78. T reset = (T(i) / OVERSAMPLE <= syncCrossing) & (syncCrossing < T(i + 1) / OVERSAMPLE);
  79. if (simd::movemask(reset)) {
  80. if (soft) {
  81. syncDirection = simd::ifelse(reset, ~syncDirection, syncDirection);
  82. }
  83. else {
  84. phase = simd::ifelse(reset, (syncCrossing - T(i) / OVERSAMPLE) * deltaPhase, phase);
  85. // This also works as a good approximation.
  86. // phase = 0.f;
  87. }
  88. }
  89. // Sin
  90. if (analog) {
  91. // Quadratic approximation of sine, slightly richer harmonics
  92. T halfPhase = (phase < 0.5f);
  93. T x = phase - simd::ifelse(halfPhase, 0.25f, 0.75f);
  94. sinBuffer[i] = 1.f - 16.f * simd::pow(x, 2);
  95. sinBuffer[i] *= simd::ifelse(halfPhase, 1.f, -1.f);
  96. // sinBuffer[i] *= 1.08f;
  97. }
  98. else {
  99. sinBuffer[i] = sin2pi_pade_05_5_4(phase);
  100. // sinBuffer[i] = sin2pi_pade_05_7_6(phase);
  101. // sinBuffer[i] = simd::sin(2 * T(M_PI) * phase);
  102. }
  103. // Tri
  104. if (analog) {
  105. const float *triTable = (const float*) BINARY_START(src_triTable_bin);
  106. T p = phase * (BINARY_SIZE(src_triTable_bin) / sizeof(float) - 1);
  107. simd::Vector<int32_t, T::size> index = p;
  108. p -= index;
  109. T v0, v1;
  110. for (int i = 0; i < T::size; i++) {
  111. v0.s[i] = triTable[index.s[i]];
  112. v1.s[i] = triTable[index.s[i] + 1];
  113. }
  114. triBuffer[i] = 1.129f * simd::crossfade(v0, v1, p);
  115. }
  116. else {
  117. triBuffer[i] = 1 - 4 * simd::fmin(simd::fabs(phase - 0.25f), simd::fabs(phase - 1.25f));
  118. }
  119. // Saw
  120. if (analog) {
  121. const float *sawTable = (const float*) BINARY_START(src_sawTable_bin);
  122. T p = phase * (BINARY_SIZE(src_sawTable_bin) / sizeof(float) - 1);
  123. simd::Vector<int32_t, T::size> index = p;
  124. p -= index;
  125. T v0, v1;
  126. for (int i = 0; i < T::size; i++) {
  127. v0.s[i] = sawTable[index.s[i]];
  128. v1.s[i] = sawTable[index.s[i] + 1];
  129. }
  130. sawBuffer[i] = 1.376f * simd::crossfade(v0, v1, p);
  131. }
  132. else {
  133. sawBuffer[i] = simd::ifelse(phase < 0.5f, 0.f, -2.f) + 2.f * phase;
  134. }
  135. // Sqr
  136. sqrBuffer[i] = simd::ifelse(phase < pw, 1.f, -1.f);
  137. if (analog) {
  138. // Add a highpass filter here
  139. sqrFilter.setCutoff(10.f * deltaTime);
  140. sqrFilter.process(sqrBuffer[i]);
  141. sqrBuffer[i] = 0.771f * sqrFilter.highpass();
  142. }
  143. // Advance phase
  144. phase += simd::ifelse(syncDirection, -1.f, 1.f) * deltaPhase / OVERSAMPLE;
  145. // Wrap phase to [0, 1)
  146. phase -= simd::floor(phase);
  147. }
  148. }
  149. T sin() {
  150. return sinDecimator.process(sinBuffer);
  151. // sinBuffer[0];
  152. }
  153. T tri() {
  154. return triDecimator.process(triBuffer);
  155. // triBuffer[0];
  156. }
  157. T saw() {
  158. return sawDecimator.process(sawBuffer);
  159. // sawBuffer[0];
  160. }
  161. T sqr() {
  162. return sqrDecimator.process(sqrBuffer);
  163. // sqrBuffer[0];
  164. }
  165. T light() {
  166. return simd::sin(2 * T(M_PI) * phase);
  167. }
  168. };
  169. struct VCO : Module {
  170. enum ParamIds {
  171. MODE_PARAM,
  172. SYNC_PARAM,
  173. FREQ_PARAM,
  174. FINE_PARAM,
  175. FM_PARAM,
  176. PW_PARAM,
  177. PWM_PARAM,
  178. NUM_PARAMS
  179. };
  180. enum InputIds {
  181. PITCH_INPUT,
  182. FM_INPUT,
  183. SYNC_INPUT,
  184. PW_INPUT,
  185. NUM_INPUTS
  186. };
  187. enum OutputIds {
  188. SIN_OUTPUT,
  189. TRI_OUTPUT,
  190. SAW_OUTPUT,
  191. SQR_OUTPUT,
  192. NUM_OUTPUTS
  193. };
  194. enum LightIds {
  195. ENUMS(PHASE_LIGHT, 3),
  196. NUM_LIGHTS
  197. };
  198. VoltageControlledOscillator<16, 16, float_4> oscillators[4];
  199. dsp::ClockDivider lightDivider;
  200. VCO() {
  201. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  202. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  203. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  204. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  205. configParam(FINE_PARAM, -1.f, 1.f, 0.f, "Fine frequency");
  206. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  207. configParam(PW_PARAM, 0.f, 1.f, 0.5f, "Pulse width", "%", 0.f, 100.f);
  208. configParam(PWM_PARAM, 0.f, 1.f, 0.f, "Pulse width modulation", "%", 0.f, 100.f);
  209. lightDivider.setDivision(16);
  210. }
  211. void process(const ProcessArgs &args) override {
  212. int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
  213. for (int c = 0; c < channels; c += 4) {
  214. auto *oscillator = &oscillators[c / 4];
  215. oscillator->analog = params[MODE_PARAM].getValue() > 0.f;
  216. oscillator->soft = params[SYNC_PARAM].getValue() <= 0.f;
  217. float pitchFine = 3.f * dsp::quadraticBipolar(params[FINE_PARAM].getValue());
  218. float_4 pitchCv = 12.f * inputs[PITCH_INPUT].getVoltageSimd<float_4>(c);
  219. if (inputs[FM_INPUT].isConnected()) {
  220. pitchCv += dsp::quadraticBipolar(params[FM_PARAM].getValue()) * 12.f * inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c);
  221. }
  222. oscillator->setPitch(params[FREQ_PARAM].getValue(), pitchFine + pitchCv);
  223. oscillator->setPulseWidth(params[PW_PARAM].getValue() + params[PWM_PARAM].getValue() * inputs[PW_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f);
  224. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  225. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  226. // Set output
  227. if (outputs[SIN_OUTPUT].isConnected())
  228. outputs[SIN_OUTPUT].setVoltageSimd(5.f * oscillator->sin(), c);
  229. if (outputs[TRI_OUTPUT].isConnected())
  230. outputs[TRI_OUTPUT].setVoltageSimd(5.f * oscillator->tri(), c);
  231. if (outputs[SAW_OUTPUT].isConnected())
  232. outputs[SAW_OUTPUT].setVoltageSimd(5.f * oscillator->saw(), c);
  233. if (outputs[SQR_OUTPUT].isConnected())
  234. outputs[SQR_OUTPUT].setVoltageSimd(5.f * oscillator->sqr(), c);
  235. }
  236. outputs[SIN_OUTPUT].setChannels(channels);
  237. outputs[TRI_OUTPUT].setChannels(channels);
  238. outputs[SAW_OUTPUT].setChannels(channels);
  239. outputs[SQR_OUTPUT].setChannels(channels);
  240. // Light
  241. if (lightDivider.process()) {
  242. if (channels == 1) {
  243. float lightValue = oscillators[0].light().s[0];
  244. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  245. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  246. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  247. }
  248. else {
  249. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  250. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  251. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  252. }
  253. }
  254. }
  255. };
  256. struct VCOWidget : ModuleWidget {
  257. VCOWidget(VCO *module) {
  258. setModule(module);
  259. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-1.svg")));
  260. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  261. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  262. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  263. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  264. addParam(createParam<CKSS>(Vec(15, 77), module, VCO::MODE_PARAM));
  265. addParam(createParam<CKSS>(Vec(119, 77), module, VCO::SYNC_PARAM));
  266. addParam(createParam<RoundHugeBlackKnob>(Vec(47, 61), module, VCO::FREQ_PARAM));
  267. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 143), module, VCO::FINE_PARAM));
  268. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 143), module, VCO::PW_PARAM));
  269. addParam(createParam<RoundLargeBlackKnob>(Vec(23, 208), module, VCO::FM_PARAM));
  270. addParam(createParam<RoundLargeBlackKnob>(Vec(91, 208), module, VCO::PWM_PARAM));
  271. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO::PITCH_INPUT));
  272. addInput(createInput<PJ301MPort>(Vec(45, 276), module, VCO::FM_INPUT));
  273. addInput(createInput<PJ301MPort>(Vec(80, 276), module, VCO::SYNC_INPUT));
  274. addInput(createInput<PJ301MPort>(Vec(114, 276), module, VCO::PW_INPUT));
  275. addOutput(createOutput<PJ301MPort>(Vec(11, 320), module, VCO::SIN_OUTPUT));
  276. addOutput(createOutput<PJ301MPort>(Vec(45, 320), module, VCO::TRI_OUTPUT));
  277. addOutput(createOutput<PJ301MPort>(Vec(80, 320), module, VCO::SAW_OUTPUT));
  278. addOutput(createOutput<PJ301MPort>(Vec(114, 320), module, VCO::SQR_OUTPUT));
  279. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(99, 42.5f), module, VCO::PHASE_LIGHT));
  280. }
  281. };
  282. Model *modelVCO = createModel<VCO, VCOWidget>("VCO");
  283. struct VCO2 : Module {
  284. enum ParamIds {
  285. MODE_PARAM,
  286. SYNC_PARAM,
  287. FREQ_PARAM,
  288. WAVE_PARAM,
  289. FM_PARAM,
  290. NUM_PARAMS
  291. };
  292. enum InputIds {
  293. FM_INPUT,
  294. SYNC_INPUT,
  295. WAVE_INPUT,
  296. NUM_INPUTS
  297. };
  298. enum OutputIds {
  299. OUT_OUTPUT,
  300. NUM_OUTPUTS
  301. };
  302. enum LightIds {
  303. ENUMS(PHASE_LIGHT, 3),
  304. NUM_LIGHTS
  305. };
  306. VoltageControlledOscillator<8, 8, float_4> oscillators[4];
  307. dsp::ClockDivider lightDivider;
  308. VCO2() {
  309. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  310. configParam(MODE_PARAM, 0.f, 1.f, 1.f, "Analog mode");
  311. configParam(SYNC_PARAM, 0.f, 1.f, 1.f, "Hard sync");
  312. configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4);
  313. configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave");
  314. configParam(FM_PARAM, 0.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f);
  315. lightDivider.setDivision(16);
  316. }
  317. void process(const ProcessArgs &args) override {
  318. float freqParam = params[FREQ_PARAM].getValue();
  319. float fmParam = params[FM_PARAM].getValue();
  320. float waveParam = params[WAVE_PARAM].getValue();
  321. int channels = std::max(inputs[FM_INPUT].getChannels(), 1);
  322. for (int c = 0; c < channels; c += 4) {
  323. auto *oscillator = &oscillators[c / 4];
  324. oscillator->analog = (params[MODE_PARAM].getValue() > 0.f);
  325. oscillator->soft = (params[SYNC_PARAM].getValue() <= 0.f);
  326. float_4 pitch = freqParam + dsp::quadraticBipolar(fmParam) * 12.f * inputs[FM_INPUT].getVoltageSimd<float_4>(c);
  327. oscillator->setPitch(0.f, pitch);
  328. oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected();
  329. oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
  330. // Outputs
  331. if (outputs[OUT_OUTPUT].isConnected()) {
  332. float_4 wave = simd::clamp(waveParam + inputs[WAVE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * 3.f, 0.f, 3.f);
  333. float_4 v = 0.f;
  334. v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f));
  335. v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f));
  336. v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f));
  337. v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f));
  338. outputs[OUT_OUTPUT].setVoltageSimd(5.f * v, c);
  339. }
  340. }
  341. outputs[OUT_OUTPUT].setChannels(channels);
  342. // Light
  343. if (lightDivider.process()) {
  344. if (channels == 1) {
  345. float lightValue = oscillators[0].light().s[0];
  346. lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
  347. lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
  348. lights[PHASE_LIGHT + 2].setBrightness(0.f);
  349. }
  350. else {
  351. lights[PHASE_LIGHT + 0].setBrightness(0.f);
  352. lights[PHASE_LIGHT + 1].setBrightness(0.f);
  353. lights[PHASE_LIGHT + 2].setBrightness(1.f);
  354. }
  355. }
  356. }
  357. };
  358. struct VCO2Widget : ModuleWidget {
  359. VCO2Widget(VCO2 *module) {
  360. setModule(module);
  361. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCO-2.svg")));
  362. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  363. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  364. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  365. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  366. addParam(createParam<CKSS>(Vec(62, 150), module, VCO2::MODE_PARAM));
  367. addParam(createParam<CKSS>(Vec(62, 215), module, VCO2::SYNC_PARAM));
  368. addParam(createParam<RoundHugeBlackKnob>(Vec(17, 60), module, VCO2::FREQ_PARAM));
  369. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 143), module, VCO2::WAVE_PARAM));
  370. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 208), module, VCO2::FM_PARAM));
  371. addInput(createInput<PJ301MPort>(Vec(11, 276), module, VCO2::FM_INPUT));
  372. addInput(createInput<PJ301MPort>(Vec(54, 276), module, VCO2::SYNC_INPUT));
  373. addInput(createInput<PJ301MPort>(Vec(11, 320), module, VCO2::WAVE_INPUT));
  374. addOutput(createOutput<PJ301MPort>(Vec(54, 320), module, VCO2::OUT_OUTPUT));
  375. addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(68, 42.5f), module, VCO2::PHASE_LIGHT));
  376. }
  377. };
  378. Model *modelVCO2 = createModel<VCO2, VCO2Widget>("VCO2");