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.

261 lines
8.4KB

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