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.

255 lines
8.3KB

  1. //**************************************************************************************
  2. //Signal Delay module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS
  3. //
  4. //Code taken from the Fundamentals plugins by Andrew Belt http://www.vcvrack.com
  5. //**************************************************************************************
  6. #include "AS.hpp"
  7. #include "dsp/samplerate.hpp"
  8. #include "dsp/ringbuffer.hpp"
  9. #include <sstream>
  10. #include <iomanip>
  11. #define HISTORY_SIZE (1<<21)
  12. struct SignalDelay : Module {
  13. enum ParamIds {
  14. TIME_1_PARAM,
  15. TIME_2_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. TIME_1_INPUT,
  20. TIME_2_INPUT,
  21. IN_1_INPUT,
  22. IN_2_INPUT,
  23. NUM_INPUTS
  24. };
  25. enum OutputIds {
  26. THRU_1_OUTPUT,
  27. THRU_2_OUTPUT,
  28. OUT_1_OUTPUT,
  29. OUT_2_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. DoubleRingBuffer<float, HISTORY_SIZE> historyBuffer1;
  33. DoubleRingBuffer<float, 16> outBuffer1;
  34. SampleRateConverter<1> src1;
  35. float lastWet1 = 0.0f;
  36. int lcd_tempo1 = 0;
  37. DoubleRingBuffer<float, HISTORY_SIZE> historyBuffer2;
  38. DoubleRingBuffer<float, 16> outBuffer2;
  39. SampleRateConverter<1> src2;
  40. float lastWet2 = 0.0f;
  41. int lcd_tempo2 = 0;
  42. SignalDelay() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  43. }
  44. void step() override;
  45. };
  46. void SignalDelay::step() {
  47. // DELAY 1 Get input to delay block
  48. float in1 = inputs[IN_1_INPUT].value;
  49. float feedback1 = 0;//only one repetition, for regular use: clampf(params[FEEDBACK_PARAM].value + inputs[FEEDBACK_INPUT].value / 10.0, 0.0, 1.0);
  50. float dry1 = in1 + lastWet1 * feedback1;
  51. // Compute delay time in seconds
  52. //delay time in seconds. Linear reading, now easy to setup any value by the digit
  53. float delay1 = clamp(params[TIME_1_PARAM].value + inputs[TIME_1_INPUT].value, 0.001f, 10.0f);
  54. //LCD display tempo - show value as ms
  55. lcd_tempo1 = std::round(delay1*1000);
  56. // Number of delay samples
  57. float index1 = delay1 * engineGetSampleRate();
  58. // Push dry1 sample into history buffer
  59. if (!historyBuffer1.full()) {
  60. historyBuffer1.push(dry1);
  61. }
  62. // How many samples do we need consume1 to catch up?
  63. float consume1 = index1 - historyBuffer1.size();
  64. if (outBuffer1.empty()) {
  65. double ratio1 = 1.0;
  66. if (consume1 <= -16)
  67. ratio1 = 0.5;
  68. else if (consume1 >= 16)
  69. ratio1 = 2.0;
  70. float inSR1 = engineGetSampleRate();
  71. float outSR1 = ratio1 * inSR1;
  72. int inFrames1 = min(historyBuffer1.size(), 16);
  73. int outFrames1 = outBuffer1.capacity();
  74. src1.setRates(inSR1, outSR1);
  75. src1.process((const Frame<1>*)historyBuffer1.startData(), &inFrames1, (Frame<1>*)outBuffer1.endData(), &outFrames1);
  76. historyBuffer1.startIncr(inFrames1);
  77. outBuffer1.endIncr(outFrames1);
  78. }
  79. float wet1 = 0.0f;
  80. if (!outBuffer1.empty()) {
  81. wet1 = outBuffer1.shift();
  82. }
  83. outputs[THRU_1_OUTPUT].value = in1;
  84. outputs[OUT_1_OUTPUT].value = wet1;
  85. lastWet1 = wet1;
  86. // DELAY 2 Get input to delay block
  87. float in2 = inputs[IN_2_INPUT].value;
  88. float feedback2 = 0;//only one repetition, for regular use: clamp(params[FEEDBACK_PARAM].value + inputs[FEEDBACK_INPUT].value / 10.0, 0.0, 1.0);
  89. float dry2 = in2 + lastWet2 * feedback2;
  90. // Compute delay time in seconds
  91. //delay time in seconds. Linear reading, now easy to setup any value by the digit
  92. float delay2 = clamp(params[TIME_2_PARAM].value + inputs[TIME_2_INPUT].value, 0.001f, 10.0f);
  93. //LCD display tempo - show value as ms
  94. lcd_tempo2 = std::round(delay2*1000);
  95. // Number of delay samples
  96. float index2 = delay2 * engineGetSampleRate();
  97. // Push dry sample into history buffer
  98. if (!historyBuffer2.full()) {
  99. historyBuffer2.push(dry2);
  100. }
  101. // How many samples do we need consume1 to catch up?
  102. float consume2 = index2 - historyBuffer2.size();
  103. if (outBuffer2.empty()) {
  104. double ratio2 = 1.0;
  105. if (consume2 <= -16)
  106. ratio2 = 0.5;
  107. else if (consume2 >= 16)
  108. ratio2 = 2.0;
  109. float inSR2 = engineGetSampleRate();
  110. float outSR2 = ratio2 * inSR2;
  111. int inFrames2 = min(historyBuffer2.size(), 16);
  112. int outFrames2 = outBuffer2.capacity();
  113. src2.setRates(inSR2, outSR2);
  114. src2.process((const Frame<1>*)historyBuffer2.startData(), &inFrames2, (Frame<1>*)outBuffer2.endData(), &outFrames2);
  115. historyBuffer2.startIncr(inFrames2);
  116. outBuffer2.endIncr(outFrames2);
  117. }
  118. float wet2 = 0.0;
  119. if (!outBuffer2.empty()) {
  120. wet2 = outBuffer2.shift();
  121. }
  122. outputs[THRU_2_OUTPUT].value = in2;
  123. outputs[OUT_2_OUTPUT].value = wet2;
  124. lastWet2 = wet2;
  125. }
  126. ///////////////////////////////////
  127. struct MsDisplayWidget : TransparentWidget {
  128. int *value;
  129. std::shared_ptr<Font> font;
  130. MsDisplayWidget() {
  131. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  132. };
  133. void draw(NVGcontext *vg) override
  134. {
  135. // Background
  136. //NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  137. NVGcolor backgroundColor = nvgRGB(0x20, 0x10, 0x10);
  138. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  139. nvgBeginPath(vg);
  140. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  141. nvgFillColor(vg, backgroundColor);
  142. nvgFill(vg);
  143. nvgStrokeWidth(vg, 1.5);
  144. nvgStrokeColor(vg, borderColor);
  145. nvgStroke(vg);
  146. // text
  147. nvgFontSize(vg, 18);
  148. nvgFontFaceId(vg, font->handle);
  149. nvgTextLetterSpacing(vg, 2.5);
  150. std::stringstream to_display;
  151. to_display << std::right << std::setw(5) << *value;
  152. Vec textPos = Vec(4.0f, 17.0f);
  153. NVGcolor textColor = nvgRGB(0xdf, 0xd2, 0x2c);
  154. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  155. nvgText(vg, textPos.x, textPos.y, "~~~~~", NULL);
  156. textColor = nvgRGB(0xda, 0xe9, 0x29);
  157. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  158. nvgText(vg, textPos.x, textPos.y, "\\\\\\\\\\", NULL);
  159. textColor = nvgRGB(0xf0, 0x00, 0x00);
  160. nvgFillColor(vg, textColor);
  161. nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL);
  162. }
  163. };
  164. ////////////////////////////////////
  165. struct SignalDelayWidget : ModuleWidget
  166. {
  167. SignalDelayWidget(SignalDelay *module);
  168. };
  169. SignalDelayWidget::SignalDelayWidget(SignalDelay *module) : ModuleWidget(module) {
  170. setPanel(SVG::load(assetPlugin(plugin, "res/SignalDelay.svg")));
  171. //DELAY 1
  172. //MS DISPLAY
  173. MsDisplayWidget *display1 = new MsDisplayWidget();
  174. display1->box.pos = Vec(10,50);
  175. display1->box.size = Vec(70, 20);
  176. display1->value = &module->lcd_tempo1;
  177. addChild(display1);
  178. static const float posX[3] = {3,33,63};
  179. //SCREWS
  180. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  181. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  182. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  183. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  184. //KNOBS
  185. addParam(ParamWidget::create<as_KnobBlack>(Vec(47, 80), module, SignalDelay::TIME_1_PARAM, 0.001f, 10.0f, 0.350f));
  186. //CV INPUT
  187. addInput(Port::create<as_PJ301MPort>(Vec(posX[0]+5, 87), Port::INPUT, module, SignalDelay::TIME_1_INPUT));
  188. //INPUT
  189. addInput(Port::create<as_PJ301MPort>(Vec(posX[0], 160), Port::INPUT, module, SignalDelay::IN_1_INPUT));
  190. //OUTPUTS
  191. addOutput(Port::create<as_PJ301MPort>(Vec(posX[1], 160), Port::OUTPUT, module, SignalDelay::THRU_1_OUTPUT));
  192. addOutput(Port::create<as_PJ301MPort>(Vec(posX[2], 160), Port::OUTPUT, module, SignalDelay::OUT_1_OUTPUT));
  193. //DELAY 2
  194. //MS DISPLAY
  195. static const int mod_offset=160;
  196. MsDisplayWidget *display2 = new MsDisplayWidget();
  197. display2->box.pos = Vec(10,50+mod_offset);
  198. display2->box.size = Vec(70, 20);
  199. display2->value = &module->lcd_tempo2;
  200. addChild(display2);
  201. //KNOBS
  202. addParam(ParamWidget::create<as_KnobBlack>(Vec(47, 80+mod_offset), module, SignalDelay::TIME_2_PARAM, 0.001f, 10.0f, 0.350f));
  203. //CV INPUT
  204. addInput(Port::create<as_PJ301MPort>(Vec(posX[0]+5, 87+mod_offset), Port::INPUT, module, SignalDelay::TIME_2_INPUT));
  205. //INPUT
  206. addInput(Port::create<as_PJ301MPort>(Vec(posX[0], 160+mod_offset), Port::INPUT, module, SignalDelay::IN_2_INPUT));
  207. //OUTPUTS
  208. addOutput(Port::create<as_PJ301MPort>(Vec(posX[1], 160+mod_offset), Port::OUTPUT, module, SignalDelay::THRU_2_OUTPUT));
  209. addOutput(Port::create<as_PJ301MPort>(Vec(posX[2], 160+mod_offset), Port::OUTPUT, module, SignalDelay::OUT_2_OUTPUT));
  210. }
  211. RACK_PLUGIN_MODEL_INIT(AS, SignalDelay) {
  212. Model *modelSignalDelay = Model::create<SignalDelay, SignalDelayWidget>("AS", "SignalDelay", "Signal Delay", UTILITY_TAG, DELAY_TAG);
  213. return modelSignalDelay;
  214. }