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.

345 lines
12KB

  1. #include "dsp/digital.hpp"
  2. #include <iostream>
  3. #include "RJModules.hpp"
  4. namespace rack_plugin_RJModules {
  5. struct Osc {
  6. float phase = 0.0;
  7. float pw = 0.5;
  8. float freq = 1.0;
  9. bool offset = false;
  10. bool invert = false;
  11. SchmittTrigger resetTrigger;
  12. Osc() {}
  13. void setPitch(float pitch) {
  14. pitch = fminf(pitch, 8.0);
  15. freq = powf(2.0, pitch);
  16. }
  17. void setFreq(float freqi){
  18. freq = freqi;
  19. }
  20. float getFreq() {
  21. return freq;
  22. }
  23. void setReset(float reset) {
  24. if (resetTrigger.process(reset)) {
  25. phase = 0.0;
  26. }
  27. }
  28. void step(float dt) {
  29. float deltaPhase = fminf(freq * dt, 0.5);
  30. phase += deltaPhase;
  31. if (phase >= 1.0)
  32. phase -= 1.0;
  33. }
  34. float saw(float x) {
  35. return 2.0 * (x - roundf(x));
  36. }
  37. float saw() {
  38. if (offset)
  39. return invert ? 2.0 * (1.0 - phase) : 2.0 * phase;
  40. else
  41. return saw(phase) * (invert ? -1.0 : 1.0);
  42. }
  43. float light() {
  44. return sinf(2*M_PI * phase);
  45. }
  46. };
  47. struct ThreeXOSC : Module {
  48. enum ParamIds {
  49. SHAPE_PARAM_1,
  50. ATTACK_PARAM_1,
  51. DECAY_PARAM_1,
  52. MIX_PARAM_1,
  53. SHAPE_PARAM_2,
  54. ATTACK_PARAM_2,
  55. DECAY_PARAM_2,
  56. MIX_PARAM_2,
  57. SHAPE_PARAM_3,
  58. ATTACK_PARAM_3,
  59. DECAY_PARAM_3,
  60. MIX_PARAM_3,
  61. NUM_PARAMS
  62. };
  63. enum InputIds {
  64. SHAPE_CV1_1,
  65. ATTACK_CV1_1,
  66. DECAY_CV1_1,
  67. MIX_CV1_1,
  68. VOCT_1,
  69. SHAPE_CV1_2,
  70. ATTACK_CV1_2,
  71. DECAY_CV1_2,
  72. MIX_CV1_2,
  73. VOCT_2,
  74. SHAPE_CV1_3,
  75. ATTACK_CV1_3,
  76. DECAY_CV1_3,
  77. MIX_CV1_3,
  78. VOCT_3,
  79. NUM_INPUTS
  80. };
  81. enum OutputIds {
  82. OUTPUT_1,
  83. OUTPUT_2,
  84. OUTPUT_3,
  85. OUTPUT_ALL,
  86. NUM_OUTPUTS
  87. };
  88. enum LightIds {
  89. PHASE_POS_LIGHT_1,
  90. PHASE_POS_LIGHT_2,
  91. PHASE_POS_LIGHT_3,
  92. NUM_LIGHTS
  93. };
  94. // Pitchies
  95. float referenceFrequency = 261.626; // C4; frequency at which Rack 1v/octave CVs are zero.
  96. float referenceSemitone = 60.0; // C4; value of C4 in semitones is arbitrary here, so have it match midi note numbers when rounded to integer.
  97. float twelfthRootTwo = 1.0594630943592953;
  98. float logTwelfthRootTwo = logf(1.0594630943592953);
  99. int referencePitch = 0;
  100. int referenceOctave = 4;
  101. float frequencyToSemitone(float frequency) {
  102. return logf(frequency / referenceFrequency) / logTwelfthRootTwo + referenceSemitone;
  103. }
  104. float semitoneToFrequency(float semitone) {
  105. return powf(twelfthRootTwo, semitone - referenceSemitone) * referenceFrequency;
  106. }
  107. float frequencyToCV(float frequency) {
  108. return log2f(frequency / referenceFrequency);
  109. }
  110. float cvToFrequency(float cv) {
  111. return powf(2.0, cv) * referenceFrequency;
  112. }
  113. float cvToSemitone(float cv) {
  114. return frequencyToSemitone(cvToFrequency(cv));
  115. }
  116. float semitoneToCV(float semitone) {
  117. return frequencyToCV(semitoneToFrequency(semitone));
  118. }
  119. Osc osc1;
  120. Osc osc2;
  121. Osc osc3;
  122. float last_1 = 0.0;
  123. bool gated1 = false;
  124. float DETUNE_STEP = .075;
  125. bool decaying1 = false;
  126. float env1 = 0.0f;
  127. SchmittTrigger trigger1;
  128. ThreeXOSC() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  129. void step() override;
  130. };
  131. void ThreeXOSC::step() {
  132. float attack1 = clamp(params[ATTACK_PARAM_1].value + inputs[ATTACK_CV1_1].value / 10.0f, 0.0f, 1.0f);
  133. float decay1 = 0.0f;
  134. float sustain1 = 1.0f;
  135. float release1 = clamp(params[DECAY_PARAM_1].value + inputs[DECAY_CV1_1].value / 10.0f, 0.0f, 1.0f);
  136. float osc1_pitch = inputs[VOCT_1].value;
  137. osc1.setFreq(cvToFrequency(osc1_pitch));
  138. osc1.step(1.0 / engineGetSampleRate());
  139. // Gate
  140. if (last_1 == inputs[VOCT_1].value){
  141. gated1 = false;
  142. } else{
  143. gated1 = true;
  144. last_1 = inputs[VOCT_1].value;
  145. }
  146. // ADSR
  147. const float base = 20000.0f;
  148. const float maxTime = 10.0f;
  149. if (gated1) {
  150. if (decaying1) {
  151. // Decay
  152. if (decay1 < 1e-4) {
  153. env1 = sustain1;
  154. }
  155. else {
  156. env1 += powf(base, 1 - decay1) / maxTime * (sustain1 - env1) * engineGetSampleTime();
  157. }
  158. }
  159. else {
  160. // Attack
  161. // Skip ahead if attack is all the way down (infinitely fast)
  162. if (attack1 < 1e-4) {
  163. env1 = 1.0f;
  164. }
  165. else {
  166. env1 += powf(base, 1 - attack1) / maxTime * (1.01f - env1) * engineGetSampleTime();
  167. }
  168. if (env1 >= 1.0f) {
  169. env1 = 1.0f;
  170. decaying1 = true;
  171. }
  172. }
  173. }
  174. else {
  175. // Release
  176. if (release1 < 1e-4) {
  177. env1 = 0.0f;
  178. }
  179. else {
  180. env1 += powf(base, 1 - release1) / maxTime * (0.0f - env1) * engineGetSampleTime();
  181. }
  182. decaying1 = false;
  183. }
  184. float env_out1 = 10.0f * env1;
  185. // Osci
  186. float osc_out1 = osc1.saw();
  187. printf("env_out1 %f \n", env_out1);
  188. // VCA
  189. float cv = 1.f;
  190. cv = fmaxf(env_out1 / 10.f, 0.f);
  191. cv = powf(cv, 4.f);
  192. printf("cv %f \n", cv);
  193. printf("output %f\n", osc_out1 * cv);
  194. //lastCv = cv;
  195. outputs[OUTPUT_1].value = osc_out1 - cv;
  196. //outputs[OUTPUT_1].value = osc1.saw() * env_out1;
  197. // float root_pitch = params[FREQ_PARAM].value * clamp(inputs[FREQ_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  198. // oscillator.setPitch(root_pitch);
  199. // oscillator.offset = (params[OFFSET_PARAM].value > 0.0);
  200. // oscillator.invert = (params[INVERT_PARAM].value <= 0.0);
  201. // oscillator.step(1.0 / engineGetSampleRate());
  202. // oscillator.setReset(inputs[RESET_INPUT].value);
  203. // oscillator2.setPitch(root_pitch + (params[DETUNE_PARAM].value * DETUNE_STEP * clamp(inputs[DETUNE_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f)));
  204. // oscillator2.offset = (params[OFFSET_PARAM].value > 0.0);
  205. // oscillator2.invert = (params[INVERT_PARAM].value <= 0.0);
  206. // oscillator2.step(1.0 / engineGetSampleRate());
  207. // oscillator2.setReset(inputs[RESET_INPUT].value);
  208. // oscillator3.setPitch(root_pitch - (params[DETUNE_PARAM].value * DETUNE_STEP * clamp(inputs[DETUNE_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f)));
  209. // oscillator3.offset = (params[OFFSET_PARAM].value > 0.0);
  210. // oscillator3.invert = (params[INVERT_PARAM].value <= 0.0);
  211. // oscillator3.step(1.0 / engineGetSampleRate());
  212. // oscillator3.setReset(inputs[RESET_INPUT].value);
  213. // float osc3_saw = oscillator3.saw();
  214. // if (params[OFFSET_PARAM].value < 1){
  215. // osc3_saw = 0;
  216. // } else{
  217. // osc3_saw = oscillator3.saw();
  218. // }
  219. // float mix_percent = params[MIX_PARAM].value * clamp(inputs[MIX_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  220. // outputs[SAW_OUTPUT].value = 5.0 * (( oscillator.saw() + (oscillator2.saw() * mix_percent) + (osc3_saw * mix_percent) / 3));
  221. // lights[PHASE_POS_LIGHT].setBrightnessSmooth(fmaxf(0.0, oscillator.light()));
  222. // lights[PHASE_NEG_LIGHT].setBrightnessSmooth(fmaxf(0.0, -oscillator.light()));
  223. }
  224. struct ThreeXOSCWidget: ModuleWidget {
  225. ThreeXOSCWidget(ThreeXOSC *module);
  226. };
  227. ThreeXOSCWidget::ThreeXOSCWidget(ThreeXOSC *module) : ModuleWidget(module) {
  228. box.size = Vec(240, 380);
  229. {
  230. SVGPanel *panel = new SVGPanel();
  231. panel->box.size = box.size;
  232. panel->setBackground(SVG::load(assetPlugin(plugin, "res/ThreeXOSC.svg")));
  233. addChild(panel);
  234. }
  235. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  236. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  237. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  238. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  239. int row_base = 30;
  240. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(25, row_base), module, ThreeXOSC::SHAPE_PARAM_1, 0.0, 8.0, 5.0));
  241. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(75, row_base), module, ThreeXOSC::ATTACK_PARAM_1, 0.0, 1.0, 0.1));
  242. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(125, row_base), module, ThreeXOSC::DECAY_PARAM_1, 0.0, 1.0, 1.0));
  243. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(175, row_base), module, ThreeXOSC::MIX_PARAM_1, 0.0, 1.0, 1.0));
  244. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 45), Port::INPUT, module, ThreeXOSC::SHAPE_CV1_1));
  245. addInput(Port::create<PJ301MPort>(Vec(80, row_base + 45), Port::INPUT, module, ThreeXOSC::ATTACK_CV1_1));
  246. addInput(Port::create<PJ301MPort>(Vec(130, row_base + 45), Port::INPUT, module, ThreeXOSC::DECAY_CV1_1));
  247. addInput(Port::create<PJ301MPort>(Vec(180, row_base + 45), Port::INPUT, module, ThreeXOSC::MIX_CV1_1));
  248. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 75), Port::INPUT, module, ThreeXOSC::VOCT_1));
  249. addOutput(Port::create<PJ301MPort>(Vec(180, row_base + 75), Port::OUTPUT, module, ThreeXOSC::OUTPUT_1));
  250. addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(210, row_base + 80), module, ThreeXOSC::PHASE_POS_LIGHT_1));
  251. row_base = 140;
  252. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(25, row_base), module, ThreeXOSC::SHAPE_PARAM_2, 0.0, 8.0, 5.0));
  253. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(75, row_base), module, ThreeXOSC::ATTACK_PARAM_2, 0.0, 1.0, 0.1));
  254. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(125, row_base), module, ThreeXOSC::DECAY_PARAM_2, 0.0, 1.0, 1.0));
  255. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(175, row_base), module, ThreeXOSC::MIX_PARAM_2, 0.0, 1.0, 1.0));
  256. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 45), Port::INPUT, module, ThreeXOSC::SHAPE_CV1_2));
  257. addInput(Port::create<PJ301MPort>(Vec(80, row_base + 45), Port::INPUT, module, ThreeXOSC::ATTACK_CV1_2));
  258. addInput(Port::create<PJ301MPort>(Vec(130, row_base + 45), Port::INPUT, module, ThreeXOSC::DECAY_CV1_2));
  259. addInput(Port::create<PJ301MPort>(Vec(180, row_base + 45), Port::INPUT, module, ThreeXOSC::MIX_CV1_2));
  260. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 75), Port::INPUT, module, ThreeXOSC::VOCT_2));
  261. addOutput(Port::create<PJ301MPort>(Vec(180, row_base + 75), Port::OUTPUT, module, ThreeXOSC::OUTPUT_2));
  262. addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(210, row_base + 80), module, ThreeXOSC::PHASE_POS_LIGHT_2));
  263. row_base = 250;
  264. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(25, row_base), module, ThreeXOSC::SHAPE_PARAM_3, 0.0, 8.0, 5.0));
  265. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(75, row_base), module, ThreeXOSC::ATTACK_PARAM_3, 0.0, 1.0, 0.1));
  266. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(125, row_base), module, ThreeXOSC::DECAY_PARAM_3, 0.0, 1.0, 1.0));
  267. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(175, row_base), module, ThreeXOSC::MIX_PARAM_3, 0.0, 1.0, 1.0));
  268. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 45), Port::INPUT, module, ThreeXOSC::SHAPE_CV1_3));
  269. addInput(Port::create<PJ301MPort>(Vec(80, row_base + 45), Port::INPUT, module, ThreeXOSC::ATTACK_CV1_3));
  270. addInput(Port::create<PJ301MPort>(Vec(130, row_base + 45), Port::INPUT, module, ThreeXOSC::DECAY_CV1_3));
  271. addInput(Port::create<PJ301MPort>(Vec(180, row_base + 45), Port::INPUT, module, ThreeXOSC::MIX_CV1_3));
  272. addInput(Port::create<PJ301MPort>(Vec(30, row_base + 75), Port::INPUT, module, ThreeXOSC::VOCT_3));
  273. addOutput(Port::create<PJ301MPort>(Vec(180, row_base + 75), Port::OUTPUT, module, ThreeXOSC::OUTPUT_3));
  274. // addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(210, row_base + 80), module, ThreeXOSC::PHASE_POS_LIGHT_3));
  275. // addInput(Port::create<PJ301MPort>(Vec(22, 100), Port::INPUT, module, ThreeXOSC::FREQ_CV_INPUT));
  276. // addInput(Port::create<PJ301MPort>(Vec(22, 190), Port::INPUT, module, ThreeXOSC::DETUNE_CV_INPUT));
  277. // addInput(Port::create<PJ301MPort>(Vec(22, 270), Port::INPUT, module, ThreeXOSC::MIX_CV_INPUT));
  278. // addInput(Port::create<PJ301MPort>(Vec(38, 310), Port::INPUT, module, ThreeXOSC::RESET_INPUT));
  279. // addOutput(Port::create<PJ301MPort>(Vec(100, 310), Port::OUTPUT, module, ThreeXOSC::SAW_OUTPUT));
  280. }
  281. } // namespace rack_plugin_RJModules
  282. using namespace rack_plugin_RJModules;
  283. RACK_PLUGIN_MODEL_INIT(RJModules, ThreeXOSC) {
  284. Model *modelThreeXOSC = Model::create<ThreeXOSC, ThreeXOSCWidget>("RJModules", "ThreeXOSC", "[GEN] 3xOSC", LFO_TAG);
  285. return modelThreeXOSC;
  286. }