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.

274 lines
9.0KB

  1. #include "RJModules.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "common.hpp"
  4. #include <iostream>
  5. #include <cmath>
  6. #include <sstream>
  7. #include <iomanip>
  8. namespace rack_plugin_RJModules {
  9. struct LowFrequencyOscillator {
  10. float phase = 0.0;
  11. float pw = 0.5;
  12. float freq = 1.0;
  13. bool offset = false;
  14. bool invert = false;
  15. SchmittTrigger resetTrigger;
  16. LowFrequencyOscillator() {}
  17. void setPitch(float pitch) {
  18. pitch = fminf(pitch, 8.0);
  19. freq = powf(2.0, pitch);
  20. }
  21. void setPulseWidth(float pw_) {
  22. const float pwMin = 0.01;
  23. pw = clamp(pw_, pwMin, 1.0 - pwMin);
  24. }
  25. void setReset(float reset) {
  26. if (resetTrigger.process(reset)) {
  27. phase = 0.0;
  28. }
  29. }
  30. void step(float dt) {
  31. float deltaPhase = fminf(freq * dt, 0.5);
  32. phase += deltaPhase;
  33. if (phase >= 1.0)
  34. phase -= 1.0;
  35. }
  36. float sin() {
  37. if (offset)
  38. return 1.0 - cosf(2*M_PI * phase) * (invert ? -1.0 : 1.0);
  39. else
  40. return sinf(2*M_PI * phase) * (invert ? -1.0 : 1.0);
  41. }
  42. float tri(float x) {
  43. return 4.0 * fabsf(x - roundf(x));
  44. }
  45. float tri() {
  46. if (offset)
  47. return tri(invert ? phase - 0.5 : phase);
  48. else
  49. return -1.0 + tri(invert ? phase - 0.25 : phase - 0.75);
  50. }
  51. float saw(float x) {
  52. return 2.0 * (x - roundf(x));
  53. }
  54. float saw() {
  55. if (offset)
  56. return invert ? 2.0 * (1.0 - phase) : 2.0 * phase;
  57. else
  58. return saw(phase) * (invert ? -1.0 : 1.0);
  59. }
  60. float sqr() {
  61. float sqr = (phase < pw) ^ invert ? 1.0 : -1.0;
  62. return offset ? sqr + 1.0 : sqr;
  63. }
  64. float light() {
  65. return sinf(2*M_PI * phase);
  66. }
  67. };
  68. /*
  69. Display
  70. */
  71. struct SmallIntegerDisplayWidgeter : TransparentWidget {
  72. float *value;
  73. std::shared_ptr<Font> font;
  74. SmallIntegerDisplayWidgeter() {
  75. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  76. };
  77. void draw(NVGcontext *vg) override
  78. {
  79. // Background
  80. NVGcolor backgroundColor = nvgRGB(0xC0, 0xC0, 0xC0);
  81. nvgBeginPath(vg);
  82. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  83. nvgFillColor(vg, backgroundColor);
  84. nvgFill(vg);
  85. // text
  86. nvgFontSize(vg, 16);
  87. nvgFontFaceId(vg, font->handle);
  88. nvgTextLetterSpacing(vg, 0.5);
  89. std::stringstream to_display;
  90. to_display = format4display(*value);
  91. Vec textPos = Vec(8.0f, 33.0f);
  92. NVGcolor textColor = nvgRGB(0x00, 0x00, 0x00);
  93. nvgFillColor(vg, textColor);
  94. nvgText(vg, textPos.x, textPos.y, to_display.str().substr(0, 4).c_str(), NULL);
  95. nvgFontSize(vg, 8);
  96. textPos = Vec(1.0f, (*value<0?28.0f:32.0f));
  97. nvgText(vg, textPos.x, textPos.y, (*value<0?"-":"+"), NULL);
  98. }
  99. };
  100. /*
  101. Widget
  102. */
  103. struct RangeLFO : Module {
  104. enum ParamIds {
  105. OFFSET_PARAM,
  106. INVERT_PARAM,
  107. FREQ_PARAM,
  108. FM1_PARAM,
  109. FM2_PARAM,
  110. PW_PARAM,
  111. PWM_PARAM,
  112. NUM_PARAMS,
  113. CH1_PARAM,
  114. CH2_PARAM,
  115. };
  116. enum InputIds {
  117. FM1_INPUT,
  118. FM2_INPUT,
  119. RESET_INPUT,
  120. PW_INPUT,
  121. RATE_CV_INPUT,
  122. FROM_CV_INPUT,
  123. TO_CV_INPUT,
  124. NUM_INPUTS
  125. };
  126. enum OutputIds {
  127. SIN_OUTPUT,
  128. TRI_OUTPUT,
  129. SAW_OUTPUT,
  130. SQR_OUTPUT,
  131. NUM_OUTPUTS
  132. };
  133. enum LightIds {
  134. PHASE_POS_LIGHT,
  135. PHASE_NEG_LIGHT,
  136. NUM_LIGHTS
  137. };
  138. LowFrequencyOscillator oscillator;
  139. float display1_val;
  140. float display2_val;
  141. float display3_val;
  142. float display4_val;
  143. float display5_val;
  144. float display6_val;
  145. RangeLFO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  146. void step() override;
  147. };
  148. void RangeLFO::step() {
  149. // display
  150. display1_val = params[CH1_PARAM].value * clamp(inputs[FROM_CV_INPUT].normalize(10.0f) / 10.0f, -1.0f, 1.0f);
  151. display2_val = params[CH2_PARAM].value * clamp(inputs[TO_CV_INPUT].normalize(10.0f) / 10.0f, -1.0f, 1.0f);
  152. float osc_pitch = params[FREQ_PARAM].value + params[FM1_PARAM].value * inputs[FM1_INPUT].value + params[FM2_PARAM].value * inputs[FM2_INPUT].value;
  153. osc_pitch = osc_pitch * clamp(inputs[RATE_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  154. oscillator.setPitch(osc_pitch);
  155. oscillator.setPulseWidth(params[PW_PARAM].value + params[PWM_PARAM].value * inputs[PW_INPUT].value / 10.0);
  156. oscillator.offset = (params[OFFSET_PARAM].value > 0.0);
  157. oscillator.invert = (params[INVERT_PARAM].value <= 0.0);
  158. oscillator.step(1.0 / engineGetSampleRate());
  159. oscillator.setReset(inputs[RESET_INPUT].value);
  160. float sin = oscillator.sin();
  161. float tri = oscillator.tri();
  162. float saw = oscillator.saw();
  163. float sqr = oscillator.sqr();
  164. // new_value = ( (old_value - old_min) / (old_max - old_min) ) * (new_max - new_min) + new_min
  165. float sin_output = ( (sin - (-1)) / (1 - (-1)) ) * (display2_val - display1_val) + display1_val;
  166. float tri_output = ( (tri - (-1)) / (1 - (-1)) ) * (display2_val - display1_val) + display1_val;
  167. float saw_output = ( (saw - (-1)) / (1 - (-1)) ) * (display2_val - display1_val) + display1_val;
  168. float sqr_output = ( (sqr - (-1)) / (1 - (-1)) ) * (display2_val - display1_val) + display1_val;
  169. outputs[SIN_OUTPUT].value = sin_output;
  170. outputs[TRI_OUTPUT].value = tri_output;
  171. outputs[SAW_OUTPUT].value = saw_output;
  172. outputs[SQR_OUTPUT].value = sqr_output;
  173. lights[PHASE_POS_LIGHT].setBrightnessSmooth(fmaxf(0.0, oscillator.light()));
  174. lights[PHASE_NEG_LIGHT].setBrightnessSmooth(fmaxf(0.0, -oscillator.light()));
  175. }
  176. struct RangeLFOWidget: ModuleWidget {
  177. RangeLFOWidget(RangeLFO *module);
  178. };
  179. RangeLFOWidget::RangeLFOWidget(RangeLFO *module) : ModuleWidget(module) {
  180. box.size = Vec(15*10, 380);
  181. {
  182. SVGPanel *panel = new SVGPanel();
  183. panel->box.size = box.size;
  184. panel->setBackground(SVG::load(assetPlugin(plugin, "res/RangeLFO.svg")));
  185. addChild(panel);
  186. }
  187. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  188. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  189. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  190. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  191. SmallIntegerDisplayWidgeter *display = new SmallIntegerDisplayWidgeter();
  192. display->box.pos = Vec(23, 160);
  193. display->box.size = Vec(50, 40);
  194. display->value = &module->display1_val;
  195. addChild(display);
  196. addParam(ParamWidget::create<RoundBlackKnob>(Vec(28, 205), module, RangeLFO::CH1_PARAM, -12.0, 12.0, -12.0));
  197. addInput(Port::create<PJ301MPort>(Vec(5, 235), Port::INPUT, module, RangeLFO::FROM_CV_INPUT));
  198. SmallIntegerDisplayWidgeter *display2 = new SmallIntegerDisplayWidgeter();
  199. display2->box.pos = Vec(83, 160);
  200. display2->box.size = Vec(50, 40);
  201. display2->value = &module->display2_val;
  202. addChild(display2);
  203. addParam(ParamWidget::create<RoundBlackKnob>(Vec(88, 205), module, RangeLFO::CH2_PARAM, -12.0, 12.0, 12.0));
  204. addInput(Port::create<PJ301MPort>(Vec(62, 235), Port::INPUT, module, RangeLFO::TO_CV_INPUT));
  205. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(47, 61), module, RangeLFO::FREQ_PARAM, -8.0, 6.0, -1.0));
  206. // addParam(ParamWidget::create<RoundBlackKnob>(Vec(23, 143), module, RangeLFO::FM1_PARAM, 0.0, 1.0, 0.0));
  207. // addParam(ParamWidget::create<RoundBlackKnob>(Vec(91, 143), module, RangeLFO::PW_PARAM, 0.0, 1.0, 0.5));
  208. // addParam(ParamWidget::create<RoundBlackKnob>(Vec(23, 208), module, RangeLFO::FM2_PARAM, 0.0, 1.0, 0.0));
  209. // addParam(ParamWidget::create<RoundBlackKnob>(Vec(91, 208), module, RangeLFO::PWM_PARAM, 0.0, 1.0, 0.0));
  210. addInput(Port::create<PJ301MPort>(Vec(22, 100), Port::INPUT, module, RangeLFO::RATE_CV_INPUT));
  211. addInput(Port::create<PJ301MPort>(Vec(11, 276), Port::INPUT, module, RangeLFO::FM1_INPUT));
  212. addInput(Port::create<PJ301MPort>(Vec(45, 276), Port::INPUT, module, RangeLFO::RESET_INPUT));
  213. // addInput(Port::create<PJ301MPort>(Vec(80, 276), Port::INPUT, module, RangeLFO::RESET_INPUT));
  214. // addInput(Port::create<PJ301MPort>(Vec(114, 276), Port::INPUT, module, RangeLFO::PW_INPUT));
  215. addParam(ParamWidget::create<CKSS>(Vec(85, 276), module, RangeLFO::INVERT_PARAM, 0.0, 1.0, 0.0));
  216. //addParam(ParamWidget::create<CKSS>(Vec(119, 276), module, RangeLFO::OFFSET_PARAM, 0.0, 1.0, 0.0));
  217. addOutput(Port::create<PJ301MPort>(Vec(11, 320), Port::OUTPUT, module, RangeLFO::SIN_OUTPUT));
  218. addOutput(Port::create<PJ301MPort>(Vec(45, 320), Port::OUTPUT, module, RangeLFO::TRI_OUTPUT));
  219. addOutput(Port::create<PJ301MPort>(Vec(80, 320), Port::OUTPUT, module, RangeLFO::SAW_OUTPUT));
  220. addOutput(Port::create<PJ301MPort>(Vec(114, 320), Port::OUTPUT, module, RangeLFO::SQR_OUTPUT));
  221. addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(99, 60), module, RangeLFO::PHASE_POS_LIGHT));
  222. }
  223. } // namespace rack_plugin_RJModules
  224. using namespace rack_plugin_RJModules;
  225. RACK_PLUGIN_MODEL_INIT(RJModules, RangeLFO) {
  226. Model *modelRangeLFO = Model::create<RangeLFO, RangeLFOWidget>("RJModules", "RangeLFO", "[GEN] RangeLFO", LFO_TAG);
  227. return modelRangeLFO;
  228. }