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.

238 lines
8.4KB

  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 TheOneRingModulator : Module {
  7. enum ParamIds {
  8. FORWARD_BIAS_PARAM,
  9. LINEAR_VOLTAGE_PARAM,
  10. SLOPE_PARAM,
  11. FORWARD_BIAS_ATTENUVERTER_PARAM,
  12. LINEAR_VOLTAGE_ATTENUVERTER_PARAM,
  13. SLOPE_ATTENUVERTER_PARAM,
  14. MIX_PARAM,
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. CARRIER_INPUT,
  19. SIGNAL_INPUT,
  20. FORWARD_BIAS_CV_INPUT,
  21. LINEAR_VOLTAGE_CV_INPUT,
  22. SLOPE_CV_INPUT,
  23. NUM_INPUTS
  24. };
  25. enum OutputIds {
  26. WET_OUTPUT,
  27. MIX_OUTPUT,
  28. NUM_OUTPUTS
  29. };
  30. enum LightIds {
  31. BLINK_LIGHT_1,
  32. BLINK_LIGHT_2,
  33. BLINK_LIGHT_3,
  34. BLINK_LIGHT_4,
  35. NUM_LIGHTS
  36. };
  37. float bufferX1[BUFFER_SIZE] = {};
  38. float bufferY1[BUFFER_SIZE] = {};
  39. float bufferX2[BUFFER_SIZE] = {};
  40. float bufferY2[BUFFER_SIZE] = {};
  41. int bufferIndex = 0;
  42. float frameIndex = 0;
  43. float deltaTime = powf(2.0, -8);
  44. float voltageBias = 0;
  45. float voltageLinear = 0.5;
  46. float h = 1; //Slope
  47. //SchmittTrigger resetTrigger;
  48. inline float diode_sim(float inVoltage )
  49. {
  50. //Original
  51. //if( inVoltage < 0 ) return 0;
  52. // else return 0.2 * log( 1.0 + exp( 10 * ( inVoltage - 1 ) ) );
  53. //Mine
  54. if( inVoltage <= voltageBias )
  55. return 0;
  56. if( inVoltage <= voltageLinear) {
  57. return h * (inVoltage - voltageBias) * (inVoltage - voltageBias) / ((2.0 * voltageLinear) - (2.0 * voltageBias));
  58. } else {
  59. return (h * inVoltage) - (h * voltageLinear) + (h * ((voltageLinear - voltageBias) * (voltageLinear - voltageBias) / ((2.0 * voltageLinear) - (2.0 * voltageBias))));
  60. }
  61. }
  62. TheOneRingModulator() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
  63. void step() override;
  64. // For more advanced Module features, read Rack's engine.hpp header file
  65. // - toJson, fromJson: serialization of internal data
  66. // - onSampleRateChange: event triggered by a change of sample rate
  67. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  68. };
  69. void TheOneRingModulator::step() {
  70. float vIn = inputs[ SIGNAL_INPUT ].value;
  71. float vC = inputs[ CARRIER_INPUT ].value;
  72. float wd = params[ MIX_PARAM ].value;
  73. voltageBias = clamp(params[FORWARD_BIAS_PARAM].value + (inputs[FORWARD_BIAS_CV_INPUT].value * params[FORWARD_BIAS_ATTENUVERTER_PARAM].value),0.0,10.0);
  74. voltageLinear = clamp(params[LINEAR_VOLTAGE_PARAM].value + (inputs[LINEAR_VOLTAGE_CV_INPUT].value * params[LINEAR_VOLTAGE_ATTENUVERTER_PARAM].value),voltageBias + 0.001f,10.0);
  75. h = clamp(params[SLOPE_PARAM].value + (inputs[SLOPE_CV_INPUT].value / 10.0 * params[SLOPE_ATTENUVERTER_PARAM].value),0.1f,1.0f);
  76. float A = 0.5 * vIn + vC;
  77. float B = vC - 0.5 * vIn;
  78. float dPA = diode_sim( A );
  79. float dMA = diode_sim( -A );
  80. float dPB = diode_sim( B );
  81. float dMB = diode_sim( -B );
  82. float res = dPA + dMA - dPB - dMB;
  83. //outputs[WET_OUTPUT].value = res;
  84. outputs[MIX_OUTPUT].value = wd * res + ( 1.0 - wd ) * vIn;
  85. }
  86. struct DiodeResponseDisplay : TransparentWidget {
  87. TheOneRingModulator *module;
  88. int frame = 0;
  89. std::shared_ptr<Font> font;
  90. struct Stats {
  91. float vrms, vpp, vmin, vmax;
  92. void calculate(float *values) {
  93. vrms = 0.0;
  94. vmax = -INFINITY;
  95. vmin = INFINITY;
  96. for (int i = 0; i < BUFFER_SIZE; i++) {
  97. float v = values[i];
  98. vrms += v*v;
  99. vmax = fmaxf(vmax, v);
  100. vmin = fminf(vmin, v);
  101. }
  102. vrms = sqrtf(vrms / BUFFER_SIZE);
  103. vpp = vmax - vmin;
  104. }
  105. };
  106. Stats statsX, statsY;
  107. DiodeResponseDisplay() {
  108. font = Font::load(assetPlugin(plugin, "res/fonts/Sudo.ttf"));
  109. }
  110. void drawWaveform(NVGcontext *vg, float vB, float vL, float h) {
  111. nvgStrokeWidth(vg, 2);
  112. nvgBeginPath(vg);
  113. nvgMoveTo(vg, 10, 122);
  114. nvgLineTo(vg, 10 + vB/10.0*127, 122);
  115. //Draw response as a bunch of small lines for now until I can convert to bezier
  116. for (float inX=vB+.1;inX<=vL;inX+=.1) {
  117. float nonLinearY = h * (inX - vB) * (inX - vB) / ((2.0 * vL) - (2.0 * vB));
  118. nvgLineTo(vg, 10 + inX/10*127.0,10+(1-nonLinearY/10.0)*112.0);
  119. }
  120. float voltLinearConstant = 0 - (h * vL) + (h * ((vL - vB) * (vL - vB) / ((2.0 * vL) - (2.0 * vB))));
  121. for (float inX=vL+.1;inX<=10;inX+=.1) {
  122. float linearY = (h * inX) + voltLinearConstant;
  123. nvgLineTo(vg, 10 + inX/10*127.0,10+(1-linearY/10.0)*112.0);
  124. }
  125. //nvgLineTo(vg, 137, 12 + (1-h) * 122);
  126. nvgStroke(vg);
  127. }
  128. void drawStats(NVGcontext *vg, Vec pos, const char *title, Stats *stats) {
  129. nvgFontSize(vg, 13);
  130. nvgFontFaceId(vg, font->handle);
  131. nvgTextLetterSpacing(vg, -2);
  132. nvgFillColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
  133. nvgText(vg, pos.x + 6, pos.y + 11, title, NULL);
  134. nvgFillColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x80));
  135. char text[128];
  136. snprintf(text, sizeof(text), "pp % 06.2f max % 06.2f min % 06.2f", stats->vpp, stats->vmax, stats->vmin);
  137. nvgText(vg, pos.x + 22, pos.y + 11, text, NULL);
  138. }
  139. void draw(NVGcontext *vg) override {
  140. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0x20, 0xff));
  141. drawWaveform(vg, module->voltageBias, module->voltageLinear, module->h);
  142. }
  143. };
  144. struct TheOneRingModulatorWidget : ModuleWidget {
  145. TheOneRingModulatorWidget(TheOneRingModulator *module);
  146. };
  147. TheOneRingModulatorWidget::TheOneRingModulatorWidget(TheOneRingModulator *module) : ModuleWidget(module) {
  148. box.size = Vec(15*10, RACK_GRID_HEIGHT);
  149. {
  150. SVGPanel *panel = new SVGPanel();
  151. panel->box.size = box.size;
  152. panel->setBackground(SVG::load(assetPlugin(plugin, "res/TheOneRingModulator.svg")));
  153. addChild(panel);
  154. }
  155. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, 0)));
  156. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  157. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  158. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  159. {
  160. DiodeResponseDisplay *display = new DiodeResponseDisplay();
  161. display->module = module;
  162. display->box.pos = Vec(0, 35);
  163. display->box.size = Vec(box.size.x-10, 90);
  164. addChild(display);
  165. }
  166. addParam(ParamWidget::create<RoundBlackKnob>(Vec(10, 190), module, TheOneRingModulator::FORWARD_BIAS_PARAM, 0.0, 10.0, 0.0));
  167. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(12, 254), module, TheOneRingModulator::FORWARD_BIAS_ATTENUVERTER_PARAM, -1.0, 1.0, 0.0));
  168. addParam(ParamWidget::create<RoundBlackKnob>(Vec(60, 190), module, TheOneRingModulator::LINEAR_VOLTAGE_PARAM, 0.0, 10.0, 0.5));
  169. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(62, 254), module, TheOneRingModulator::LINEAR_VOLTAGE_ATTENUVERTER_PARAM, -1.0, 1.0, 0.0));
  170. addParam(ParamWidget::create<RoundBlackKnob>(Vec(110, 190), module, TheOneRingModulator::SLOPE_PARAM, 0.1, 1.0, 1.0));
  171. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(112, 254), module, TheOneRingModulator::SLOPE_ATTENUVERTER_PARAM, -1.0, 1.0, 0.0));
  172. addParam(ParamWidget::create<RoundBlackKnob>(Vec(90, 325), module, TheOneRingModulator::MIX_PARAM, -0.0, 1.0, 0.5));
  173. addInput(Port::create<PJ301MPort>(Vec(14, 330), Port::INPUT, module, TheOneRingModulator::CARRIER_INPUT));
  174. addInput(Port::create<PJ301MPort>(Vec(50, 330), Port::INPUT, module, TheOneRingModulator::SIGNAL_INPUT));
  175. addInput(Port::create<PJ301MPort>(Vec(13, 225), Port::INPUT, module, TheOneRingModulator::FORWARD_BIAS_CV_INPUT));
  176. addInput(Port::create<PJ301MPort>(Vec(63, 225), Port::INPUT, module, TheOneRingModulator::LINEAR_VOLTAGE_CV_INPUT));
  177. addInput(Port::create<PJ301MPort>(Vec(113, 225), Port::INPUT, module, TheOneRingModulator::SLOPE_CV_INPUT));
  178. //addOutput(Port::create<PJ301MPort>(Vec(51, 330), Port::OUTPUT, module, TheOneRingModulator::WET_OUTPUT));
  179. addOutput(Port::create<PJ301MPort>(Vec(122, 330), Port::OUTPUT, module, TheOneRingModulator::MIX_OUTPUT));
  180. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(21, 59), module, LissajousLFO::BLINK_LIGHT_1));
  181. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(41, 59), module, LissajousLFO::BLINK_LIGHT_2));
  182. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(61, 59), module, LissajousLFO::BLINK_LIGHT_3));
  183. //addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(81, 59), module, LissajousLFO::BLINK_LIGHT_4));
  184. }
  185. } // namespace rack_plugin_FrozenWasteland
  186. using namespace rack_plugin_FrozenWasteland;
  187. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, TheOneRingModulator) {
  188. Model *modelTheOneRingModulator = Model::create<TheOneRingModulator, TheOneRingModulatorWidget>("Frozen Wasteland", "TheOneRingModulator", "The One Ring Modulator", RING_MODULATOR_TAG);
  189. return modelTheOneRingModulator;
  190. }