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.

256 lines
8.0KB

  1. #include <string.h>
  2. #include "FrozenWasteland.hpp"
  3. #include "dsp/digital.hpp"
  4. #define BUFFER_SIZE 512
  5. namespace rack_plugin_FrozenWasteland {
  6. struct RouletteLFO : Module {
  7. enum ParamIds {
  8. FIXED_RADIUS_PARAM,
  9. ROTATING_RADIUS_PARAM,
  10. DISTANCE_PARAM,
  11. FREQUENCY_PARAM,
  12. EPI_HYPO_PARAM,
  13. FIXED_D_PARAM,
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. FIXED_RADIUS_INPUT,
  18. ROATATING_RADIUS_INPUT,
  19. DISTANCE_INPUT,
  20. FREQUENCY_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. OUTPUT_X,
  25. OUTPUT_Y,
  26. NUM_OUTPUTS
  27. };
  28. enum RouletteTypes {
  29. HYPOTROCHOID_ROULETTE,
  30. EPITROCHIID_ROULETTE
  31. };
  32. float bufferX1[BUFFER_SIZE] = {};
  33. float bufferY1[BUFFER_SIZE] = {};
  34. int bufferIndex = 0;
  35. float frameIndex = 0;
  36. float scopeDeltaTime = powf(2.0, -8);
  37. //SchmittTrigger resetTrigger;
  38. float x1 = 0.0;
  39. float y1 = 0.0;
  40. float phase = 0.0;
  41. RouletteLFO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
  42. void step() override;
  43. // For more advanced Module features, read Rack's engine.hpp header file
  44. // - toJson, fromJson: serialization of internal data
  45. // - onSampleRateChange: event triggered by a change of sample rate
  46. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  47. };
  48. void RouletteLFO::step() {
  49. float pitch = fminf(params[FREQUENCY_PARAM].value + inputs[FREQUENCY_INPUT].value, 8.0);
  50. float freq = powf(2.0, pitch);
  51. float deltaTime = 1.0 / engineGetSampleRate();
  52. float deltaPhase = fminf(freq * deltaTime, 0.5);
  53. phase += deltaPhase;
  54. if (phase >= 1.0)
  55. phase -= 1.0;
  56. if(params[EPI_HYPO_PARAM].value == HYPOTROCHOID_ROULETTE) {
  57. float r = clamp(params[ROTATING_RADIUS_PARAM].value + inputs[ROATATING_RADIUS_INPUT].value,1.0,10.0);
  58. float R = clamp(params[FIXED_RADIUS_PARAM].value +inputs[FIXED_RADIUS_INPUT].value,r,20.0);
  59. float d = clamp(params[DISTANCE_PARAM].value + inputs[DISTANCE_INPUT].value,1.0,10.0);
  60. if(params[FIXED_D_PARAM].value) {
  61. d=r;
  62. }
  63. float amplitudeScaling = 5.0 / (R-r+d);
  64. float theta = phase * 2 * M_PI;
  65. x1 = amplitudeScaling * (((R-r) * cosf(theta)) + (d * cosf((R-r)/r * theta)));
  66. y1 = amplitudeScaling * (((R-r) * sinf(theta)) - (d * sinf((R-r)/r * theta)));
  67. } else {
  68. float R = clamp(params[FIXED_RADIUS_PARAM].value +inputs[FIXED_RADIUS_INPUT].value,1.0,20.0);
  69. float r = clamp(params[ROTATING_RADIUS_PARAM].value + inputs[ROATATING_RADIUS_INPUT].value,1.0,10.0);
  70. float d = clamp(params[DISTANCE_PARAM].value + inputs[DISTANCE_INPUT].value,1.0,20.0);
  71. if(params[FIXED_D_PARAM].value) {
  72. d=r;
  73. }
  74. float amplitudeScaling = 5.0 / (R+r+d);
  75. float theta = phase * 2 * M_PI;
  76. x1 = amplitudeScaling * (((R+r) * cosf(theta)) - (d * cosf((R+r)/r * theta)));
  77. y1 = amplitudeScaling * (((R+r) * sinf(theta)) - (d * sinf((R+r)/r * theta)));
  78. }
  79. outputs[OUTPUT_X].value = x1;
  80. outputs[OUTPUT_Y].value = y1;
  81. //Update scope.
  82. int frameCount = (int)ceilf(scopeDeltaTime * engineGetSampleRate());
  83. // Add frame to buffers
  84. if (bufferIndex < BUFFER_SIZE) {
  85. if (++frameIndex > frameCount) {
  86. frameIndex = 0;
  87. bufferX1[bufferIndex] = x1;
  88. bufferY1[bufferIndex] = y1;
  89. bufferIndex++;
  90. }
  91. }
  92. // Are we waiting on the next trigger?
  93. if (bufferIndex >= BUFFER_SIZE) {
  94. bufferIndex = 0;
  95. frameIndex = 0;
  96. }
  97. }
  98. struct RouletteScopeDisplay : TransparentWidget {
  99. RouletteLFO *module;
  100. int frame = 0;
  101. std::shared_ptr<Font> font;
  102. RouletteScopeDisplay() {
  103. }
  104. void drawWaveform(NVGcontext *vg, float *valuesX, float *valuesY) {
  105. if (!valuesX)
  106. return;
  107. nvgSave(vg);
  108. Rect b = Rect(Vec(0, 15), box.size.minus(Vec(0, 15*2)));
  109. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  110. nvgBeginPath(vg);
  111. // Draw maximum display left to right
  112. for (int i = 0; i < BUFFER_SIZE; i++) {
  113. float x, y;
  114. if (valuesY) {
  115. x = valuesX[i] / 2.0 + 0.5;
  116. y = valuesY[i] / 2.0 + 0.5;
  117. }
  118. else {
  119. x = (float)i / (BUFFER_SIZE - 1);
  120. y = valuesX[i] / 2.0 + 0.5;
  121. }
  122. Vec p;
  123. p.x = b.pos.x + b.size.x * x;
  124. p.y = b.pos.y + b.size.y * (1.0 - y);
  125. if (i == 0)
  126. nvgMoveTo(vg, p.x, p.y);
  127. else
  128. nvgLineTo(vg, p.x, p.y);
  129. }
  130. nvgLineCap(vg, NVG_ROUND);
  131. nvgMiterLimit(vg, 2.0);
  132. nvgStrokeWidth(vg, 1.5);
  133. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  134. nvgStroke(vg);
  135. nvgResetScissor(vg);
  136. nvgRestore(vg);
  137. }
  138. void draw(NVGcontext *vg) override {
  139. //float gainX = powf(2.0, 1);
  140. //float gainY = powf(2.0, 1);
  141. //float offsetX = module->x1;
  142. //float offsetY = module->y1;
  143. float valuesX[BUFFER_SIZE];
  144. float valuesY[BUFFER_SIZE];
  145. for (int i = 0; i < BUFFER_SIZE; i++) {
  146. int j = i;
  147. // Lock display to buffer if buffer update deltaTime <= 2^-11
  148. j = (i + module->bufferIndex) % BUFFER_SIZE;
  149. valuesX[i] = (module->bufferX1[j]) / 5.0;
  150. valuesY[i] = (module->bufferY1[j]) / 5.0;
  151. }
  152. // Draw waveforms for LFO 1
  153. // X x Y
  154. nvgStrokeColor(vg, nvgRGBA(0x9f, 0xe4, 0x36, 0xc0));
  155. drawWaveform(vg, valuesX, valuesY);
  156. }
  157. };
  158. struct RouletteLFOWidget : ModuleWidget {
  159. RouletteLFOWidget(RouletteLFO *module);
  160. };
  161. RouletteLFOWidget::RouletteLFOWidget(RouletteLFO *module) : ModuleWidget(module) {
  162. box.size = Vec(15*13, RACK_GRID_HEIGHT);
  163. {
  164. SVGPanel *panel = new SVGPanel();
  165. panel->box.size = box.size;
  166. panel->setBackground(SVG::load(assetPlugin(plugin, "res/RouletteLFO.svg")));
  167. addChild(panel);
  168. }
  169. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, 0)));
  170. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  171. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  172. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  173. {
  174. RouletteScopeDisplay *display = new RouletteScopeDisplay();
  175. display->module = module;
  176. display->box.pos = Vec(0, 35);
  177. display->box.size = Vec(box.size.x, 140);
  178. addChild(display);
  179. }
  180. addParam(ParamWidget::create<RoundBlackKnob>(Vec(10, 186), module, RouletteLFO::FIXED_RADIUS_PARAM, 1, 20.0, 5));
  181. addParam(ParamWidget::create<RoundBlackKnob>(Vec(60, 186), module, RouletteLFO::ROTATING_RADIUS_PARAM, 1, 10.0, 3));
  182. addParam(ParamWidget::create<RoundBlackKnob>(Vec(113, 186), module, RouletteLFO::DISTANCE_PARAM, 1, 10.0, 5.0));
  183. addParam(ParamWidget::create<RoundBlackKnob>(Vec(160, 186), module, RouletteLFO::FREQUENCY_PARAM, -8.0, 4.0, 0.0));
  184. addParam(ParamWidget::create<CKSS>(Vec(55, 265), module, RouletteLFO::EPI_HYPO_PARAM, 0.0, 1.0, 0.0));
  185. addParam(ParamWidget::create<CKSS>(Vec(130, 265), module, RouletteLFO::FIXED_D_PARAM, 0.0, 1.0, 0.0));
  186. //addParam(ParamWidget::create<RoundBlackKnob>(Vec(87, 265), module, RouletteLFO::FREQX2_PARAM, -8.0, 3.0, 0.0));
  187. //addParam(ParamWidget::create<RoundBlackKnob>(Vec(137, 265), module, RouletteLFO::FREQY2_PARAM, -8.0, 3.0, 1.0));
  188. addInput(Port::create<PJ301MPort>(Vec(13, 219), Port::INPUT, module, RouletteLFO::FIXED_RADIUS_INPUT));
  189. addInput(Port::create<PJ301MPort>(Vec(63, 219), Port::INPUT, module, RouletteLFO::ROATATING_RADIUS_INPUT));
  190. addInput(Port::create<PJ301MPort>(Vec(116, 219), Port::INPUT, module, RouletteLFO::DISTANCE_INPUT));
  191. addInput(Port::create<PJ301MPort>(Vec(163, 219), Port::INPUT, module, RouletteLFO::FREQUENCY_INPUT));
  192. addOutput(Port::create<PJ301MPort>(Vec(57, 335), Port::OUTPUT, module, RouletteLFO::OUTPUT_X));
  193. addOutput(Port::create<PJ301MPort>(Vec(113, 335), Port::OUTPUT, module, RouletteLFO::OUTPUT_Y));
  194. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(21, 59), module, LissajousLFO::BLINK_LIGHT_1));
  195. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(41, 59), module, LissajousLFO::BLINK_LIGHT_2));
  196. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(61, 59), module, LissajousLFO::BLINK_LIGHT_3));
  197. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(81, 59), module, LissajousLFO::BLINK_LIGHT_4));
  198. }
  199. } // namespace rack_plugin_FrozenWasteland
  200. using namespace rack_plugin_FrozenWasteland;
  201. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, RouletteLFO) {
  202. Model *modelRouletteLFO = Model::create<RouletteLFO, RouletteLFOWidget>("Frozen Wasteland", "RouletteLFO", "Roulette LFO", LFO_TAG);
  203. return modelRouletteLFO;
  204. }