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.

214 lines
6.0KB

  1. #include "ESeries.hpp"
  2. #include "dsp/filter.hpp"
  3. #include "dsp/minblep.hpp"
  4. struct E340 : Module {
  5. enum ParamIds {
  6. COARSE_PARAM,
  7. FINE_PARAM,
  8. FM_PARAM,
  9. SPREAD_PARAM,
  10. CHAOS_PARAM,
  11. CHAOS_BW_PARAM,
  12. DENSITY_PARAM,
  13. NUM_PARAMS
  14. };
  15. enum InputIds {
  16. PITCH_INPUT,
  17. FM_INPUT,
  18. SYNC_INPUT,
  19. SPREAD_INPUT,
  20. CHAOS_INPUT,
  21. CHAOS_BW_INPUT,
  22. NUM_INPUTS
  23. };
  24. enum OutputIds {
  25. SAW_OUTPUT,
  26. SINE_OUTPUT,
  27. NUM_OUTPUTS
  28. };
  29. float phases[8] = {};
  30. RCFilter noiseFilters[8];
  31. float sync = 0.0;
  32. MinBLEP<16> sineMinBLEP;
  33. MinBLEP<16> sawMinBLEP;
  34. // For removing DC
  35. RCFilter sineFilter;
  36. RCFilter sawFilter;
  37. E340();
  38. void step() override;
  39. };
  40. E340::E340() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  41. sineMinBLEP.minblep = minblep_16_32;
  42. sineMinBLEP.oversample = 32;
  43. sawMinBLEP.minblep = minblep_16_32;
  44. sawMinBLEP.oversample = 32;
  45. // Randomize initial phases
  46. for (int i = 0; i < 8; i++) {
  47. phases[i] = randomUniform();
  48. }
  49. }
  50. void E340::step() {
  51. // Base pitch
  52. float basePitch = params[COARSE_PARAM].value + 12.0 * inputs[PITCH_INPUT].value;
  53. if (inputs[FM_INPUT].active) {
  54. basePitch += 12.0 / 4.0 * params[FM_PARAM].value * inputs[FM_INPUT].value;
  55. }
  56. basePitch += params[FINE_PARAM].value;
  57. // Spread
  58. float spread = params[SPREAD_PARAM].value + inputs[SPREAD_INPUT].value / 10.0;
  59. spread = clamp(spread, 0.0f, 1.0f);
  60. const float spreadPower = 50.0;
  61. spread = (powf(spreadPower + 1.0, spread) - 1.0) / spreadPower;
  62. // Chaos
  63. float chaos = params[CHAOS_PARAM].value + inputs[CHAOS_INPUT].value / 10.0;
  64. chaos = clamp(chaos, 0.0f, 1.0f);
  65. const float chaosPower = 50.0;
  66. chaos = 8.0 * (powf(chaosPower + 1.0, chaos) - 1.0) / chaosPower;
  67. // Chaos BW
  68. float chaosBW = params[CHAOS_BW_PARAM].value + inputs[CHAOS_BW_INPUT].value / 10.0;
  69. chaosBW = clamp(chaosBW, 0.0f, 1.0f);
  70. chaosBW = 6.0 * powf(100.0, chaosBW);
  71. // This shouldn't scale with the global sample rate, because of reasons.
  72. float filterCutoff = chaosBW / 44100.0;
  73. // Check sync input
  74. float newSync = inputs[SYNC_INPUT].value - 0.25;
  75. float syncCrossing = INFINITY;
  76. if (sync < 0.0 && newSync >= 0.0) {
  77. float deltaSync = newSync - sync;
  78. syncCrossing = -newSync / deltaSync;
  79. }
  80. sync = newSync;
  81. // Density
  82. int density;
  83. switch ((int)roundf(params[DENSITY_PARAM].value)) {
  84. case 0: density = 2; break;
  85. case 1: density = 4; break;
  86. default: density = 8; break;
  87. }
  88. // Detuning amounts, in note value
  89. const static float detunings[8] = {-21, 21, -9, 9, -3, 3, -15, 15}; // Perfect fourths
  90. // const static float detunings[8] = {-24, 24, -12, 12, -5, 7, -17, 19}; // Fifths
  91. // Oscillator block
  92. float sines = 0.0;
  93. float saws = 0.0;
  94. for (int i = 0; i < density; i++) {
  95. // Noise
  96. float noise = 0.0;
  97. if (chaos > 0.0) {
  98. noise = randomNormal();
  99. noiseFilters[i].setCutoff(filterCutoff);
  100. noiseFilters[i].process(noise);
  101. noise = noiseFilters[i].lowpass();
  102. noise *= chaos;
  103. }
  104. // Frequency
  105. float pitch = basePitch + spread * detunings[i] + 12.0 * noise;
  106. pitch = clamp(pitch, -72.0f, 72.0f);
  107. float freq = 261.626 * powf(2.0, pitch / 12.0);
  108. // Advance phase
  109. float deltaPhase = freq * engineGetSampleTime();
  110. float phase = phases[i] + deltaPhase;
  111. // Reset phase
  112. if (phase >= 1.0) {
  113. phase -= 1.0;
  114. float crossing = -phase / deltaPhase;
  115. sawMinBLEP.jump(crossing, -2.0);
  116. }
  117. // Compute output
  118. float sine = -cosf(2*M_PI * phase);
  119. float saw = 2.0*phase - 1.0;
  120. // Sync
  121. if (syncCrossing <= 0.0) {
  122. phase = deltaPhase * -syncCrossing;
  123. float newSine = -cosf(2*M_PI * phase);
  124. float newSaw = 2.0*phase - 1.0;
  125. sineMinBLEP.jump(syncCrossing, newSine - sine);
  126. sawMinBLEP.jump(syncCrossing, newSaw - saw);
  127. sine = newSine;
  128. saw = newSaw;
  129. }
  130. phases[i] = phase;
  131. sines += sine;
  132. saws += saw;
  133. }
  134. sines += sineMinBLEP.shift();
  135. saws += sawMinBLEP.shift();
  136. sines /= density;
  137. saws /= density;
  138. // Apply HP filter at 20Hz
  139. float r = 20.0 * engineGetSampleTime();
  140. sineFilter.setCutoff(r);
  141. sawFilter.setCutoff(r);
  142. sineFilter.process(sines);
  143. sawFilter.process(saws);
  144. outputs[SINE_OUTPUT].value = 5.0 * sineFilter.highpass();
  145. outputs[SAW_OUTPUT].value = 5.0 * sawFilter.highpass();
  146. }
  147. struct E340Widget : ModuleWidget {
  148. E340Widget(E340 *module);
  149. };
  150. E340Widget::E340Widget(E340 *module) : ModuleWidget(module) {
  151. setPanel(SVG::load(assetPlugin(plugin, "res/E340.svg")));
  152. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  153. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  154. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  155. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  156. addParam(ParamWidget::create<SynthTechAlco>(Vec(26, 43), module, E340::COARSE_PARAM, -48.0, 48.0, 0.0));
  157. addParam(ParamWidget::create<SynthTechAlco>(Vec(137, 43), module, E340::FINE_PARAM, -1.0, 1.0, 0.0));
  158. addParam(ParamWidget::create<SynthTechAlco>(Vec(26, 109), module, E340::FM_PARAM, 0.0, 1.0, 0.0));
  159. addParam(ParamWidget::create<SynthTechAlco>(Vec(137, 109), module, E340::SPREAD_PARAM, 0.0, 1.0, 0.0));
  160. addParam(ParamWidget::create<SynthTechAlco>(Vec(26, 175), module, E340::CHAOS_PARAM, 0.0, 1.0, 0.0));
  161. addParam(ParamWidget::create<SynthTechAlco>(Vec(137, 175), module, E340::CHAOS_BW_PARAM, 0.0, 1.0, 0.5));
  162. addParam(ParamWidget::create<NKK>(Vec(89, 140), module, E340::DENSITY_PARAM, 0.0, 2.0, 2.0));
  163. addInput(Port::create<CL1362Port>(Vec(13, 243), Port::INPUT, module, E340::PITCH_INPUT));
  164. addInput(Port::create<CL1362Port>(Vec(63, 243), Port::INPUT, module, E340::FM_INPUT));
  165. addInput(Port::create<CL1362Port>(Vec(113, 243), Port::INPUT, module, E340::SYNC_INPUT));
  166. addInput(Port::create<CL1362Port>(Vec(163, 243), Port::INPUT, module, E340::SPREAD_INPUT));
  167. addInput(Port::create<CL1362Port>(Vec(13, 301), Port::INPUT, module, E340::CHAOS_INPUT));
  168. addInput(Port::create<CL1362Port>(Vec(63, 301), Port::INPUT, module, E340::CHAOS_BW_INPUT));
  169. addOutput(Port::create<CL1362Port>(Vec(113, 301), Port::OUTPUT, module, E340::SAW_OUTPUT));
  170. addOutput(Port::create<CL1362Port>(Vec(163, 301), Port::OUTPUT, module, E340::SINE_OUTPUT));
  171. }
  172. Model *modelE340 = Model::create<E340, E340Widget>("E-Series", "E340", "E340 Cloud Generator", OSCILLATOR_TAG);