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.

341 lines
9.3KB

  1. /* Portions of this code derive from Fundamental/src/Scope.cpp - Copyright © 2016 by Andrew Belt */
  2. #include <string.h>
  3. #include "DS.hpp"
  4. namespace rack_plugin_SubmarineFree {
  5. #define BUFFER_SIZE 512
  6. struct LA_108 : DS_Module {
  7. enum ParamIds {
  8. PARAM_TRIGGER,
  9. PARAM_EDGE,
  10. PARAM_TIME,
  11. PARAM_INDEX_1,
  12. PARAM_INDEX_2,
  13. PARAM_RUN,
  14. PARAM_RESET,
  15. PARAM_PRE,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. INPUT_1,
  20. INPUT_2,
  21. INPUT_3,
  22. INPUT_4,
  23. INPUT_5,
  24. INPUT_6,
  25. INPUT_7,
  26. INPUT_8,
  27. INPUT_EXT,
  28. NUM_INPUTS
  29. };
  30. enum OutputIds {
  31. NUM_OUTPUTS
  32. };
  33. enum LightIds {
  34. LIGHT_1,
  35. LIGHT_2,
  36. LIGHT_3,
  37. LIGHT_4,
  38. LIGHT_5,
  39. LIGHT_6,
  40. LIGHT_7,
  41. LIGHT_8,
  42. LIGHT_EXT,
  43. NUM_LIGHTS
  44. };
  45. float buffer[8][BUFFER_SIZE] = {};
  46. int bufferIndex = 0;
  47. float frameIndex = 0;
  48. float preBuffer[8][32] = {};
  49. int preBufferIndex = 0;
  50. float preFrameIndex = 0;
  51. int preCount = 0;
  52. DS_Schmitt trigger;
  53. LA_108() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  54. void step() override;
  55. void startFrame(void);
  56. };
  57. void LA_108::startFrame() {
  58. frameIndex = 0;
  59. preCount = (int)(params[PARAM_PRE].value + 0.5f);
  60. if (preCount) {
  61. for (int i = 0; i < 8; i++) {
  62. for (int s = 0; s < preCount; s++) {
  63. buffer[i][s] = preBuffer[i][(preBufferIndex + 64 - preCount + s) % 32];
  64. }
  65. }
  66. bufferIndex = preCount;
  67. return;
  68. }
  69. bufferIndex = 0;
  70. }
  71. void LA_108::step() {
  72. // Set trigger lights
  73. for (int i = 0; i < 9; i++)
  74. lights[LIGHT_1 + i].value = (params[PARAM_TRIGGER].value == i);
  75. // Compute time
  76. float deltaTime = powf(2.0f, params[PARAM_TIME].value);
  77. int frameCount = (int)ceilf(deltaTime * engineGetSampleRate());
  78. // Add frame to preBuffer
  79. if (++preFrameIndex >= frameCount) {
  80. preFrameIndex = 0;
  81. for (int i = 0; i < 8; i++)
  82. preBuffer[i][preBufferIndex] = inputs[INPUT_1 + i].value;
  83. preBufferIndex++;
  84. if (preBufferIndex >= 32)
  85. preBufferIndex = 0;
  86. }
  87. // Add frame to buffer
  88. if (bufferIndex < BUFFER_SIZE) {
  89. if (++frameIndex >= frameCount) {
  90. frameIndex = 0;
  91. for (int i = 0; i < 8; i++)
  92. buffer[i][bufferIndex] = inputs[INPUT_1 + i].value;
  93. bufferIndex++;
  94. }
  95. }
  96. int triggerInput = LA_108::INPUT_1 + (int)(clamp(params[PARAM_TRIGGER].value, 0.0f, 8.0f));
  97. int edge = (params[PARAM_EDGE].value > 0.5f);
  98. // Are we waiting on the next trigger?
  99. if (bufferIndex >= BUFFER_SIZE) {
  100. // Trigger immediately if nothing connected to trigger input
  101. if (!inputs[triggerInput].active) {
  102. startFrame();
  103. return;
  104. }
  105. // Reset the Schmitt trigger so we don't trigger immediately if the input is high
  106. if (frameIndex == 0) {
  107. //trigger.set(edge);
  108. }
  109. frameIndex++;
  110. float gate = inputs[triggerInput].value;
  111. int triggered = trigger.edge(this, gate, edge);
  112. if (params[PARAM_RUN].value < 0.5f) { // Continuous run mode
  113. engineSetParam(this, PARAM_RESET, 0.0f);
  114. // Reset if triggered
  115. float holdTime = 0.1f;
  116. if (triggered) {
  117. startFrame();
  118. return;
  119. }
  120. // Reset if we've waited too long
  121. if (frameIndex >= engineGetSampleRate() * holdTime) {
  122. startFrame();
  123. return;
  124. }
  125. }
  126. else {
  127. if (params[PARAM_RESET].value > 0.5f) {
  128. if (triggered) {
  129. startFrame();
  130. engineSetParam(this, PARAM_RESET, 0.0f);
  131. return;
  132. }
  133. }
  134. }
  135. }
  136. }
  137. struct LA_Display : TransparentWidget {
  138. LA_108 *module;
  139. void drawTrace(NVGcontext *vg, float *values, float offset) {
  140. if (!values)
  141. return;
  142. nvgSave(vg);
  143. Rect b = Rect(Vec(0, 0), box.size);
  144. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  145. nvgBeginPath(vg);
  146. for (int i = 0; i < BUFFER_SIZE; i++) {
  147. float x, y;
  148. x = (float)i / (BUFFER_SIZE - 1) * b.size.x;
  149. y = module->voltage0 -clamp(values[i], module->voltage0, module->voltage1);
  150. y *= 29.0f / (module->voltage1 - module->voltage0);
  151. y += offset;
  152. if (i == 0)
  153. nvgMoveTo(vg, x, y);
  154. else
  155. nvgLineTo(vg, x, y);
  156. }
  157. nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0));
  158. nvgLineCap(vg, NVG_ROUND);
  159. nvgMiterLimit(vg, 2.0f);
  160. nvgStrokeWidth(vg, 1.5f);
  161. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  162. nvgStroke(vg);
  163. nvgResetScissor(vg);
  164. nvgRestore(vg);
  165. }
  166. void drawIndex(NVGcontext *vg, float value) {
  167. Rect b = Rect(Vec(0, 0), box.size);
  168. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  169. value = value * b.size.x;
  170. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
  171. {
  172. nvgBeginPath(vg);
  173. nvgMoveTo(vg, value, 0);
  174. nvgLineTo(vg, value, b.size.y);
  175. nvgClosePath(vg);
  176. }
  177. nvgStroke(vg);
  178. nvgResetScissor(vg);
  179. }
  180. void drawPre(NVGcontext *vg, float value) {
  181. if (value == 0.0f)
  182. return;
  183. Rect b = Rect(Vec(0, 0), box.size);
  184. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  185. value = value * b.size.x;
  186. nvgStrokeColor(vg, nvgRGBA(0xff, 0x40, 0x40, 0x80));
  187. {
  188. nvgBeginPath(vg);
  189. nvgMoveTo(vg, value, 0);
  190. nvgLineTo(vg, value, b.size.y);
  191. nvgClosePath(vg);
  192. }
  193. nvgStroke(vg);
  194. nvgResetScissor(vg);
  195. }
  196. void drawMask(NVGcontext *vg, float value) {
  197. if (value == 0.0f)
  198. return;
  199. Rect b = Rect(Vec(0, 0), box.size);
  200. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  201. value = value * b.size.x;
  202. nvgFillColor(vg, nvgRGBA(0xff, 0x40, 0x40, 0x40));
  203. {
  204. nvgBeginPath(vg);
  205. nvgRect(vg, 0, 0, value, b.size.y);
  206. nvgClosePath(vg);
  207. }
  208. nvgFill(vg);
  209. nvgResetScissor(vg);
  210. }
  211. void draw(NVGcontext *vg) override {
  212. for (int i = 0; i < 8; i++) {
  213. if (module->inputs[LA_108::INPUT_1 + i].active) {
  214. drawTrace(vg, module->buffer[i], 32.5f + 35 * i);
  215. }
  216. }
  217. drawIndex(vg, clamp(module->params[LA_108::PARAM_INDEX_1].value, 0.0f, 1.0f));
  218. drawIndex(vg, clamp(module->params[LA_108::PARAM_INDEX_2].value, 0.0f, 1.0f));
  219. drawMask(vg, clamp(module->params[LA_108::PARAM_PRE].value, 0.0f, 32.0f) / BUFFER_SIZE);
  220. drawPre(vg, 1.0f * module->preCount / BUFFER_SIZE);
  221. }
  222. };
  223. struct LA_Measure : TransparentWidget {
  224. std::shared_ptr<Font> font;
  225. LA_108 *module;
  226. char measureText[41];
  227. LA_Measure() {
  228. font = Font::load(assetGlobal( "res/fonts/DejaVuSans.ttf"));
  229. }
  230. void draw(NVGcontext *vg) override {
  231. float deltaTime = powf(2.0f, module->params[LA_108::PARAM_TIME].value);
  232. int frameCount = (int)ceilf(deltaTime * engineGetSampleRate());
  233. frameCount *= BUFFER_SIZE;
  234. float width = (float)frameCount * fabs(module->params[LA_108::PARAM_INDEX_1].value - module->params[LA_108::PARAM_INDEX_2].value) / engineGetSampleRate();
  235. if (width < 0.00000995f)
  236. sprintf(measureText, "%4.3f\xc2\xb5s", width * 1000000.0f);
  237. else if (width < 0.0000995f)
  238. sprintf(measureText, "%4.2f\xc2\xb5s", width * 1000000.0f);
  239. else if (width < 0.000995f)
  240. sprintf(measureText, "%4.1f\xc2\xb5s", width * 1000000.0f);
  241. else if (width < 0.00995f)
  242. sprintf(measureText, "%4.3fms", width * 1000.0f);
  243. else if (width < 0.0995f)
  244. sprintf(measureText, "%4.2fms", width * 1000.0f);
  245. else if (width < 0.995f)
  246. sprintf(measureText, "%4.1fms", width * 1000.0f);
  247. else if (width < 9.95f)
  248. sprintf(measureText, "%4.3fs", width);
  249. else if (width < 99.5f)
  250. sprintf(measureText, "%4.2fs", width);
  251. else
  252. sprintf(measureText, "%4.1fs", width);
  253. nvgFontSize(vg, 14);
  254. nvgFontFaceId(vg, font->handle);
  255. nvgFillColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xff));
  256. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  257. nvgText(vg, 27, 12, measureText, NULL);
  258. }
  259. };
  260. struct LA108 : ModuleWidget {
  261. LA108(LA_108 *module) : ModuleWidget(module) {
  262. setPanel(SVG::load(assetPlugin(plugin, "res/LA-108.svg")));
  263. {
  264. LA_Display * display = new LA_Display();
  265. display->module = module;
  266. display->box.pos = Vec(42, 15);
  267. display->box.size = Vec(box.size.x - 44, 280);
  268. addChild(display);
  269. }
  270. {
  271. LA_Measure * display = new LA_Measure();
  272. display->module = module;
  273. display->box.pos = Vec(213, 297);
  274. display->box.size = Vec(54, 16);
  275. addChild(display);
  276. }
  277. for (int i = 0; i < 8; i++) {
  278. addInput(Port::create<BluePort>(Vec(4, 20 + 35 * i), Port::INPUT, module, LA_108::INPUT_1 + i));
  279. addChild(ModuleLightWidget::create<TinyLight<BlueLight>>(Vec(30, 22 + 35 * i), module, LA_108::LIGHT_1 + i));
  280. }
  281. addInput(Port::create<BluePort>(Vec(4, 310), Port::INPUT, module, LA_108::INPUT_EXT));
  282. addChild(ModuleLightWidget::create<TinyLight<BlueLight>>(Vec(30, 312), module, LA_108::LIGHT_EXT));
  283. addParam(ParamWidget::create<SnapKnob<MedKnob<LightKnob>>>(Vec(39, 301), module, LA_108::PARAM_TRIGGER, 0.0f, 8.0f, 0.0f));
  284. addParam(ParamWidget::create<sub_sw_2>(Vec(82, 308), module, LA_108::PARAM_EDGE, 0.0f, 1.0f, 0.0f));
  285. addParam(ParamWidget::create<sub_sw_2>(Vec(108, 308), module, LA_108::PARAM_RUN, 0.0f, 1.0f, 0.0f));
  286. addParam(ParamWidget::create<sub_btn>(Vec(151, 312), module, LA_108::PARAM_RESET, 0.0f, 1.0f, 0.0f));
  287. addParam(ParamWidget::create<MedKnob<LightKnob>>(Vec(171, 301), module, LA_108::PARAM_TIME, -6.0f, -16.0f, -14.0f));
  288. addParam(ParamWidget::create<SmallKnob<LightKnob>>(Vec(214, 315), module, LA_108::PARAM_INDEX_1, 0.0f, 1.0f, 0.0f));
  289. addParam(ParamWidget::create<SmallKnob<LightKnob>>(Vec(242, 315), module, LA_108::PARAM_INDEX_2, 0.0f, 1.0f, 1.0f));
  290. addParam(ParamWidget::create<SnapKnob<SmallKnob<LightKnob>>>(Vec(271, 315), module, LA_108::PARAM_PRE, 0.0f, 32.0f, 0.0f));
  291. }
  292. void appendContextMenu(Menu *menu) override {
  293. ((DS_Module *)module)->appendContextMenu(menu);
  294. }
  295. };
  296. } // namespace rack_plugin_SubmarineFree
  297. using namespace rack_plugin_SubmarineFree;
  298. RACK_PLUGIN_MODEL_INIT(SubmarineFree, LA108) {
  299. Model *modelLA108 = Model::create<LA_108, LA108>("Submarine (Free)", "LA-108", "LA-108 Logic Analyser", LOGIC_TAG, VISUAL_TAG);
  300. return modelLA108;
  301. }