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.

390 lines
12KB

  1. #include "util/common.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "dsp/noise.hpp"
  4. #include "AH.hpp"
  5. #include "Core.hpp"
  6. #include "UI.hpp"
  7. #include "VCO.hpp"
  8. namespace rack_plugin_AmalgamatedHarmonics {
  9. struct Generative : AHModule {
  10. enum ParamIds {
  11. FREQ_PARAM,
  12. WAVE_PARAM,
  13. FM_PARAM,
  14. AM_PARAM,
  15. NOISE_PARAM,
  16. CLOCK_PARAM,
  17. PROB_PARAM,
  18. DELAYL_PARAM,
  19. DELAYS_PARAM,
  20. GATEL_PARAM,
  21. GATES_PARAM,
  22. SLOPE_PARAM,
  23. SPEED_PARAM,
  24. ATTN_PARAM,
  25. NUM_PARAMS
  26. };
  27. enum InputIds {
  28. WAVE_INPUT,
  29. FM_INPUT,
  30. AM_INPUT,
  31. NOISE_INPUT,
  32. SAMPLE_INPUT,
  33. CLOCK_INPUT,
  34. PROB_INPUT,
  35. HOLD_INPUT,
  36. NUM_INPUTS
  37. };
  38. enum OutputIds {
  39. LFO_OUTPUT,
  40. MIXED_OUTPUT,
  41. NOISE_OUTPUT,
  42. OUT_OUTPUT,
  43. GATE_OUTPUT,
  44. NUM_OUTPUTS
  45. };
  46. enum LightIds {
  47. ENUMS(GATE_LIGHT,2),
  48. NUM_LIGHTS
  49. };
  50. Generative() : AHModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { }
  51. void step() override;
  52. json_t *toJson() override {
  53. json_t *rootJ = json_object();
  54. // quantise
  55. json_t *quantiseJ = json_boolean(quantise);
  56. json_object_set_new(rootJ, "quantise", quantiseJ);
  57. // offset
  58. json_t *offsetJ = json_boolean(offset);
  59. json_object_set_new(rootJ, "offset", offsetJ);
  60. return rootJ;
  61. }
  62. void fromJson(json_t *rootJ) override {
  63. // quantise
  64. json_t *quantiseJ = json_object_get(rootJ, "quantise");
  65. if (quantiseJ) {
  66. quantise = json_boolean_value(quantiseJ);
  67. }
  68. // offset
  69. json_t *offsetJ = json_object_get(rootJ, "offset");
  70. if (offsetJ) {
  71. offset = json_boolean_value(offsetJ);
  72. }
  73. }
  74. Core core;
  75. SchmittTrigger sampleTrigger;
  76. SchmittTrigger holdTrigger;
  77. SchmittTrigger clockTrigger;
  78. bogaudio_dsp::PinkNoiseGenerator pink;
  79. LowFrequencyOscillator oscillator;
  80. LowFrequencyOscillator clock;
  81. AHPulseGenerator delayPhase;
  82. AHPulseGenerator gatePhase;
  83. float target = 0.0f;
  84. float current = 0.0f;
  85. bool quantise = false;
  86. bool offset = false;
  87. bool delayState = false;
  88. bool gateState = false;
  89. // minimum and maximum slopes in volts per second
  90. const float slewMin = 0.1;
  91. const float slewMax = 10000.0;
  92. const float slewRatio = slewMin / slewMax;
  93. // Amount of extra slew per voltage difference
  94. const float shapeScale = 1.0 / 10.0;
  95. float delayTime;
  96. float gateTime;
  97. };
  98. void Generative::step() {
  99. AHModule::step();
  100. oscillator.setPitch(params[FREQ_PARAM].value + params[FM_PARAM].value * inputs[FM_INPUT].value);
  101. oscillator.offset = offset;
  102. oscillator.step(delta);
  103. clock.setPitch(clamp(params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value, -2.0f, 6.0f));
  104. clock.step(delta);
  105. float wavem = fabs(fmodf(params[WAVE_PARAM].value + inputs[WAVE_INPUT].value, 4.0f));
  106. float interp = 0.0f;
  107. bool toss = false;
  108. if (wavem < 1.0f)
  109. interp = crossfade(oscillator.sin(), oscillator.tri(), wavem) * 5.0;
  110. else if (wavem < 2.0f)
  111. interp = crossfade(oscillator.tri(), oscillator.saw(), wavem - 1.0f) * 5.0;
  112. else if (wavem < 3.0f)
  113. interp = crossfade(oscillator.saw(), oscillator.sqr(), wavem - 2.0f) * 5.0;
  114. else
  115. interp = crossfade(oscillator.sqr(), oscillator.sin(), wavem - 3.0f) * 5.0;
  116. // Capture (pink) noise
  117. float noise = clamp(pink.next() * 7.5f, -5.0f, 5.0f); // -5V to 5V
  118. float range = params[ATTN_PARAM].value;
  119. // Shift the noise floor
  120. if (offset) {
  121. noise += 5.0f;
  122. }
  123. float noiseLevel = clamp(params[NOISE_PARAM].value + inputs[NOISE_INPUT].value, 0.0f, 1.0f);
  124. // Mixed the input AM signal or noise
  125. if (inputs[AM_INPUT].active) {
  126. interp = crossfade(interp, inputs[AM_INPUT].value, params[AM_PARAM].value) * range;
  127. } else {
  128. interp *= range;
  129. }
  130. // Mix noise
  131. float mixedSignal = (noise * noiseLevel + interp * (1.0f - noiseLevel));
  132. // Process gate
  133. bool sampleActive = inputs[SAMPLE_INPUT].active;
  134. float sample = inputs[SAMPLE_INPUT].value;
  135. bool hold = inputs[HOLD_INPUT].value > 0.000001f;
  136. bool isClocked = false;
  137. if (!sampleActive) {
  138. if (clockTrigger.process(clock.sqr())) {
  139. isClocked = true;
  140. }
  141. } else {
  142. if (sampleTrigger.process(sample)) {
  143. isClocked = true;
  144. }
  145. }
  146. // If we have no input or we have been a trigger on the sample input
  147. if (isClocked) {
  148. // If we are not in a delay or gate state process the tick, otherwise eat it
  149. if (!delayPhase.ishigh() && !gatePhase.ishigh()) {
  150. // Check against prob control
  151. float threshold = clamp(params[PROB_PARAM].value + inputs[PROB_INPUT].value / 10.f, 0.f, 1.f);
  152. toss = (randomUniform() < threshold);
  153. // Tick is valid
  154. if (toss) {
  155. // Determine delay time
  156. float dlyLen = log2(params[DELAYL_PARAM].value);
  157. float dlySpr = log2(params[DELAYS_PARAM].value);
  158. double rndD = clamp(core.gaussrand(), -2.0f, 2.0f);
  159. delayTime = clamp(dlyLen + dlySpr * rndD, 0.0f, 100.0f);
  160. // Trigger the respective delay pulse generator
  161. delayState = true;
  162. delayPhase.trigger(delayTime);
  163. }
  164. }
  165. }
  166. // In delay state and finished waiting
  167. if (delayState && !delayPhase.process(delta)) {
  168. // set the target voltage
  169. target = mixedSignal;
  170. // Determine gate time
  171. float gateLen = log2(params[GATEL_PARAM].value);
  172. float gateSpr = log2(params[GATES_PARAM].value);
  173. double rndG = clamp(core.gaussrand(), -2.0f, 2.0f);
  174. gateTime = clamp(gateLen + gateSpr * rndG, Core::TRIGGER, 100.0f);
  175. // Open the gate and set flags
  176. gatePhase.trigger(gateTime);
  177. gateState = true;
  178. delayState = false;
  179. }
  180. // If not held slew voltages
  181. if (!hold) {
  182. // Curve calc
  183. float shape = params[SLOPE_PARAM].value;
  184. float speed = params[SPEED_PARAM].value;
  185. float slew = slewMax * powf(slewRatio, speed);
  186. // Rise
  187. if (target > current) {
  188. current += slew * crossfade(1.0f, shapeScale * (target - current), shape) * delta;
  189. if (current > target) // Trap overshoot
  190. current = target;
  191. }
  192. // Fall
  193. else if (target < current) {
  194. current -= slew * crossfade(1.0f, shapeScale * (current - target), shape) * delta;
  195. if (current < target) // Trap overshoot
  196. current = target;
  197. }
  198. }
  199. // Quantise or not
  200. float out;
  201. if (quantise) {
  202. int i;
  203. int d;
  204. out = CoreUtil().getPitchFromVolts(current, Core::NOTE_C, Core::SCALE_CHROMATIC, &i, &d);
  205. } else {
  206. out = current;
  207. }
  208. // If the gate is open, set output to high
  209. if (gatePhase.process(delta)) {
  210. outputs[GATE_OUTPUT].value = 10.0f;
  211. lights[GATE_LIGHT].setBrightnessSmooth(1.0f);
  212. lights[GATE_LIGHT + 1].setBrightnessSmooth(0.0f);
  213. } else {
  214. outputs[GATE_OUTPUT].value = 0.0f;
  215. gateState = false;
  216. if (delayState) {
  217. lights[GATE_LIGHT].setBrightnessSmooth(0.0f);
  218. lights[GATE_LIGHT + 1].setBrightnessSmooth(1.0f);
  219. } else {
  220. lights[GATE_LIGHT].setBrightnessSmooth(0.0f);
  221. lights[GATE_LIGHT + 1].setBrightnessSmooth(0.0f);
  222. }
  223. }
  224. outputs[OUT_OUTPUT].value = out;
  225. outputs[NOISE_OUTPUT].value = noise * 2.0;
  226. outputs[LFO_OUTPUT].value = interp;
  227. outputs[MIXED_OUTPUT].value = mixedSignal;
  228. }
  229. struct GenerativeWidget : ModuleWidget {
  230. GenerativeWidget(Generative *module) : ModuleWidget(module) {
  231. UI ui;
  232. box.size = Vec(240, 380);
  233. {
  234. SVGPanel *panel = new SVGPanel();
  235. panel->box.size = box.size;
  236. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Generative.svg")));
  237. addChild(panel);
  238. }
  239. // LFO section
  240. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 0, 0, false, false), module, Generative::FREQ_PARAM, -8.0f, 10.0f, 1.0f));
  241. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 1, 0, false, false), module, Generative::WAVE_PARAM, 0.0f, 4.0f, 1.5f));
  242. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 2, 0, false, false), module, Generative::FM_PARAM, 0.0f, 1.0f, 0.5f));
  243. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 3, 0, false, false), module, Generative::AM_PARAM, 0.0f, 1.0f, 0.5f));
  244. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, 0, false, false), module, Generative::NOISE_PARAM, 0.0f, 1.0f, 0.5f));
  245. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 1, 1, false, false), Port::INPUT, module, Generative::WAVE_INPUT));
  246. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 2, 1, false, false), Port::INPUT, module, Generative::FM_INPUT));
  247. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 3, 1, false, false), Port::INPUT, module, Generative::AM_INPUT));
  248. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 4, 1, false, false), Port::INPUT, module, Generative::NOISE_INPUT));
  249. // Gate Section
  250. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 0, 2, false, false), Port::INPUT, module, Generative::SAMPLE_INPUT));
  251. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 1, 2, false, false), module, Generative::CLOCK_PARAM, -2.0, 6.0, 1.0));
  252. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 2, 2, false, false), module, Generative::PROB_PARAM, 0.0, 1.0, 1.0));
  253. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 3, 2, false, false), module, Generative::DELAYL_PARAM, 1.0f, 2.0f, 1.0f));
  254. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, 2, false, false), module, Generative::GATEL_PARAM, 1.0f, 2.0f, 1.0f));
  255. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 1, 3, false, false), Port::INPUT, module, Generative::CLOCK_INPUT));
  256. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 2, 3, false, false), Port::INPUT, module, Generative::PROB_INPUT));
  257. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 3, 3, false, false), module, Generative::DELAYS_PARAM, 1.0f, 2.0f, 1.0f));
  258. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, 3, false, false), module, Generative::GATES_PARAM, 1.0f, 2.0f, 1.0f));
  259. // Curve Section
  260. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 0, 4, false, false), module, Generative::SLOPE_PARAM, 0.0, 1.0, 0.0));
  261. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 1, 4, false, false), module, Generative::SPEED_PARAM, 0.0, 1.0, 0.0));
  262. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 2, 4, false, false), Port::INPUT, module, Generative::HOLD_INPUT));
  263. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(ui.getPosition(UI::LIGHT, 3, 4, false, false), module, Generative::GATE_LIGHT));
  264. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, 4, false, false), module, Generative::ATTN_PARAM, 0.0, 1.0, 1.0));
  265. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 0, 5, false, false), Port::OUTPUT, module, Generative::LFO_OUTPUT));
  266. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 1, 5, false, false), Port::OUTPUT, module, Generative::MIXED_OUTPUT));
  267. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 2, 5, false, false), Port::OUTPUT, module, Generative::NOISE_OUTPUT));
  268. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 3, 5, false, false), Port::OUTPUT, module, Generative::GATE_OUTPUT));
  269. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 4, 5, false, false), Port::OUTPUT, module, Generative::OUT_OUTPUT));
  270. }
  271. void appendContextMenu(Menu *menu) override {
  272. Generative *gen = dynamic_cast<Generative*>(module);
  273. assert(gen);
  274. struct GenModeItem : MenuItem {
  275. Generative *gen;
  276. void onAction(EventAction &e) override {
  277. gen->quantise ^= 1;
  278. }
  279. void step() override {
  280. rightText = gen->quantise ? "Quantised" : "Unquantised";
  281. MenuItem::step();
  282. }
  283. };
  284. struct GenOffsetItem : MenuItem {
  285. Generative *gen;
  286. void onAction(EventAction &e) override {
  287. gen->offset ^= 1;
  288. }
  289. void step() override {
  290. rightText = gen->offset ? "0V - 10V" : "-5V to 5V";
  291. MenuItem::step();
  292. }
  293. };
  294. menu->addChild(construct<MenuLabel>());
  295. menu->addChild(construct<GenModeItem>(&MenuItem::text, "Quantise", &GenModeItem::gen, gen));
  296. menu->addChild(construct<GenOffsetItem>(&MenuItem::text, "CV Offset", &GenOffsetItem::gen, gen));
  297. }
  298. };
  299. } // namespace rack_plugin_AmalgamatedHarmonics
  300. using namespace rack_plugin_AmalgamatedHarmonics;
  301. RACK_PLUGIN_MODEL_INIT(AmalgamatedHarmonics, Generative) {
  302. Model *modelGenerative = Model::create<Generative, GenerativeWidget>( "Amalgamated Harmonics", "Generative", "Generative", NOISE_TAG, SAMPLE_AND_HOLD_TAG, LFO_TAG, RANDOM_TAG);
  303. return modelGenerative;
  304. }