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.

357 lines
12KB

  1. #include <string.h>
  2. #include "Southpole.hpp"
  3. #include "dsp/samplerate.hpp"
  4. #include "dsp/digital.hpp"
  5. #include "tides/generator.h"
  6. struct SplashParasite : Module {
  7. enum ParamIds {
  8. MODE_PARAM,
  9. RANGE_PARAM,
  10. FREQUENCY_PARAM,
  11. FM_PARAM,
  12. SHAPE_PARAM,
  13. SLOPE_PARAM,
  14. SMOOTHNESS_PARAM,
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. SHAPE_INPUT,
  19. SLOPE_INPUT,
  20. SMOOTHNESS_INPUT,
  21. TRIG_INPUT,
  22. FREEZE_INPUT,
  23. PITCH_INPUT,
  24. FM_INPUT,
  25. LEVEL_INPUT,
  26. CLOCK_INPUT,
  27. NUM_INPUTS
  28. };
  29. enum OutputIds {
  30. HIGH_OUTPUT,
  31. LOW_OUTPUT,
  32. UNI_OUTPUT,
  33. BI_OUTPUT,
  34. NUM_OUTPUTS
  35. };
  36. enum LightIds {
  37. MODE_GREEN_LIGHT, MODE_RED_LIGHT,
  38. PHASE_GREEN_LIGHT, PHASE_RED_LIGHT,
  39. RANGE_GREEN_LIGHT, RANGE_RED_LIGHT,
  40. NUM_LIGHTS
  41. };
  42. bool sheep;
  43. tides::Generator generator;
  44. int frame = 0;
  45. uint8_t lastGate;
  46. SchmittTrigger modeTrigger;
  47. SchmittTrigger rangeTrigger;
  48. SplashParasite();
  49. void step() override;
  50. void reset() override {
  51. generator.set_range(tides::GENERATOR_RANGE_MEDIUM);
  52. generator.set_mode(tides::GENERATOR_MODE_LOOPING);
  53. sheep = false;
  54. }
  55. void randomize() override {
  56. generator.set_range((tides::GeneratorRange) (randomu32() % 3));
  57. generator.set_mode((tides::GeneratorMode) (randomu32() % 3));
  58. }
  59. json_t *toJson() override {
  60. json_t *rootJ = json_object();
  61. json_object_set_new(rootJ, "mode", json_integer((int) generator.mode()));
  62. json_object_set_new(rootJ, "range", json_integer((int) generator.range()));
  63. json_object_set_new(rootJ, "sheep", json_boolean(sheep));
  64. json_object_set_new(rootJ, "featureMode", json_integer((int)generator.feature_mode_));
  65. return rootJ;
  66. }
  67. void fromJson(json_t *rootJ) override {
  68. json_t *featModeJ = json_object_get(rootJ, "featureMode");
  69. if(featModeJ)
  70. generator.feature_mode_ = (tides::Generator::FeatureMode) json_integer_value(featModeJ);
  71. json_t *modeJ = json_object_get(rootJ, "mode");
  72. if (modeJ) {
  73. generator.set_mode((tides::GeneratorMode) json_integer_value(modeJ));
  74. }
  75. json_t *rangeJ = json_object_get(rootJ, "range");
  76. if (rangeJ) {
  77. generator.set_range((tides::GeneratorRange) json_integer_value(rangeJ));
  78. }
  79. json_t *sheepJ = json_object_get(rootJ, "sheep");
  80. if (sheepJ) {
  81. sheep = json_boolean_value(sheepJ);
  82. }
  83. }
  84. };
  85. SplashParasite::SplashParasite() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  86. memset(&generator, 0, sizeof(generator));
  87. generator.Init();
  88. generator.set_sync(false);
  89. reset();
  90. }
  91. void SplashParasite::step() {
  92. tides::GeneratorMode mode = generator.mode();
  93. if (modeTrigger.process(params[MODE_PARAM].value)) {
  94. mode = (tides::GeneratorMode) (((int)mode - 1 + 3) % 3);
  95. generator.set_mode(mode);
  96. }
  97. lights[MODE_GREEN_LIGHT].value = (mode == 2) ? 1.0 : 0.0;
  98. lights[MODE_GREEN_LIGHT].value = (mode == 0) ? 0.0 : 1.0;
  99. lights[MODE_RED_LIGHT].value = (mode == 0) ? 1.0 : 0.0;
  100. lights[MODE_RED_LIGHT].value = (mode == 2) ? 0.0 : 1.0;
  101. tides::GeneratorRange range = generator.range();
  102. if (rangeTrigger.process(params[RANGE_PARAM].value)) {
  103. range = (tides::GeneratorRange) (((int)range - 1 + 3) % 3);
  104. generator.set_range(range);
  105. }
  106. lights[RANGE_GREEN_LIGHT].value = (range == 2) ? 1.0 : 0.0;
  107. lights[RANGE_GREEN_LIGHT].value = (range == 0) ? 0.0 : 1.0;
  108. lights[RANGE_RED_LIGHT].value = (range == 0) ? 1.0 : 0.0;
  109. lights[RANGE_RED_LIGHT].value = (range == 2) ? 0.0 : 1.0;
  110. //Buffer loop
  111. if (generator.writable_block()) {
  112. // Pitch
  113. float pitch = params[FREQUENCY_PARAM].value;
  114. pitch += 12.0 * inputs[PITCH_INPUT].value;
  115. //pitch += params[FM_PARAM].value * inputs[FM_INPUT].normalize(0.1) / 5.0;
  116. float fm = clamp(inputs[FM_INPUT].value /5.0 * params[FM_PARAM].value /12.0, -1.0, 1.0) * 0x600;
  117. pitch += 60.0;
  118. if (generator.feature_mode_ == tides::Generator::FEAT_MODE_HARMONIC) {
  119. //this is probably not original but seems useful
  120. pitch -= 12;
  121. // Scale to the global sample rate
  122. pitch += log2f(48000.0 / engineGetSampleRate()) * 12.0;
  123. generator.set_pitch_high_range(clamp(pitch * 0x80, -0x8000, 0x7fff), fm);
  124. }
  125. else {
  126. pitch += log2f(48000.0 / engineGetSampleRate()) * 12.0;
  127. generator.set_pitch(clamp(pitch * 0x80, -0x8000, 0x7fff),fm);
  128. }
  129. if (generator.feature_mode_ == tides::Generator::FEAT_MODE_RANDOM) {
  130. //TODO: should this be inverted?
  131. generator.set_pulse_width(clamp(1.0 - params[FM_PARAM].value /12.0, 0.0, 2.0) * 0x7fff);
  132. }
  133. // Slope, smoothness, pitch
  134. int16_t shape = clamp(params[SHAPE_PARAM].value + inputs[SHAPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  135. int16_t slope = clamp(params[SLOPE_PARAM].value + inputs[SLOPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  136. int16_t smoothness = clamp(params[SMOOTHNESS_PARAM].value + inputs[SMOOTHNESS_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  137. generator.set_shape(shape);
  138. generator.set_slope(slope);
  139. generator.set_smoothness(smoothness);
  140. // Sync
  141. // Slight deviation from spec here.
  142. // Instead of toggling sync by holding the range button, just enable it if the clock port is plugged in.
  143. generator.set_sync(inputs[CLOCK_INPUT].active);
  144. generator.FillBuffer();
  145. #ifdef WAVETABLE_HACK
  146. generator.Process(sheep);
  147. #endif
  148. }
  149. // Level
  150. uint16_t level = clamp(inputs[LEVEL_INPUT].normalize(8.0) / 8.0, 0.0, 1.0) * 0xffff;
  151. if (level < 32)
  152. level = 0;
  153. uint8_t gate = 0;
  154. if (inputs[FREEZE_INPUT].value >= 0.7)
  155. gate |= tides::CONTROL_FREEZE;
  156. if (inputs[TRIG_INPUT].value >= 0.7)
  157. gate |= tides::CONTROL_GATE;
  158. if (inputs[CLOCK_INPUT].value >= 0.7)
  159. gate |= tides::CONTROL_CLOCK;
  160. if (!(lastGate & tides::CONTROL_CLOCK) && (gate & tides::CONTROL_CLOCK))
  161. gate |= tides::CONTROL_GATE_RISING;
  162. if (!(lastGate & tides::CONTROL_GATE) && (gate & tides::CONTROL_GATE))
  163. gate |= tides::CONTROL_GATE_RISING;
  164. if ((lastGate & tides::CONTROL_GATE) && !(gate & tides::CONTROL_GATE))
  165. gate |= tides::CONTROL_GATE_FALLING;
  166. lastGate = gate;
  167. const tides::GeneratorSample& sample = generator.Process(gate);
  168. uint32_t uni = sample.unipolar;
  169. int32_t bi = sample.bipolar;
  170. uni = uni * level >> 16;
  171. bi = -bi * level >> 16;
  172. float unif = (float) uni / 0xffff;
  173. float bif = (float) bi / 0x8000;
  174. outputs[HIGH_OUTPUT].value = sample.flags & tides::FLAG_END_OF_ATTACK ? 0.0 : 5.0;
  175. outputs[LOW_OUTPUT].value = sample.flags & tides::FLAG_END_OF_RELEASE ? 0.0 : 5.0;
  176. outputs[UNI_OUTPUT].value = unif * 8.0;
  177. outputs[BI_OUTPUT].value = bif * 5.0;
  178. if (sample.flags & tides::FLAG_END_OF_ATTACK)
  179. unif *= -1.0;
  180. lights[PHASE_GREEN_LIGHT].setBrightnessSmooth(fmaxf(0.0, unif));
  181. lights[PHASE_RED_LIGHT].setBrightnessSmooth(fmaxf(0.0, -unif));
  182. }
  183. struct SplashParasiteWidget : ModuleWidget {
  184. SVGPanel *panel0;
  185. SVGPanel *panel1;
  186. SVGPanel *panel2;
  187. void step() override;
  188. Menu *createContextMenu() override;
  189. SplashParasiteWidget(SplashParasite *module) : ModuleWidget(module) {
  190. box.size = Vec(15 * 8, 380);
  191. {
  192. panel0 = new SVGPanel();
  193. panel0->setBackground(SVG::load(assetPlugin(plugin, "res/SplashParasite.svg")));
  194. panel0->box.size = box.size;
  195. addChild(panel0);
  196. }
  197. {
  198. panel1 = new SVGPanel();
  199. panel1->setBackground(SVG::load(assetPlugin(plugin, "res/TwoBumps.svg")));
  200. panel1->box.size = box.size;
  201. addChild(panel1);
  202. }
  203. {
  204. panel2 = new SVGPanel();
  205. panel2->setBackground(SVG::load(assetPlugin(plugin, "res/TwoDrunks.svg")));
  206. panel2->box.size = box.size;
  207. addChild(panel2);
  208. }
  209. const float x1 = 0.5*RACK_GRID_WIDTH;
  210. const float x2 = 3.25*RACK_GRID_WIDTH;
  211. const float x3 = 5.75*RACK_GRID_WIDTH;
  212. const float y1 = 40.0f;
  213. const float y2 = 25.0f;
  214. const float yh = 38.0f;
  215. addParam(ParamWidget::create<CKD6>(Vec(x3-3,y1-3), module, SplashParasite::MODE_PARAM, 0.0, 1.0, 0.0));
  216. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(x3+7, y1+7), module, SplashParasite::MODE_GREEN_LIGHT));
  217. addParam(ParamWidget::create<CKD6>(Vec(x3-3,y1+1.45*yh), module, SplashParasite::RANGE_PARAM, 0.0, 1.0, 0.0));
  218. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(x3+7, y1+2*yh-10), module, SplashParasite::RANGE_GREEN_LIGHT));
  219. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(x2-20, y2+2*yh), module, SplashParasite::PHASE_GREEN_LIGHT));
  220. addParam(ParamWidget::create<sp_BlackKnob>(Vec(x2-7,y2+1.75*yh), module, SplashParasite::FREQUENCY_PARAM, -48.0, 48.0, 0.0));
  221. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x3, y2+4*yh), module, SplashParasite::SHAPE_PARAM, -1.0, 1.0, 0.0));
  222. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x3, y2+4.75*yh), module, SplashParasite::SLOPE_PARAM, -1.0, 1.0, 0.0));
  223. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x3, y2+5.5*yh), module, SplashParasite::SMOOTHNESS_PARAM, -1.0, 1.0, 0.0));
  224. addInput(Port::create<sp_Port>(Vec(x1, y1), Port::INPUT, module, SplashParasite::TRIG_INPUT));
  225. addInput(Port::create<sp_Port>(Vec(x2, y1), Port::INPUT, module, SplashParasite::FREEZE_INPUT));
  226. addInput(Port::create<sp_Port>(Vec(x1, y2+2*yh), Port::INPUT, module, SplashParasite::PITCH_INPUT));
  227. addInput(Port::create<sp_Port>(Vec(x1, y2+3.25*yh), Port::INPUT, module, SplashParasite::FM_INPUT));
  228. addParam(ParamWidget::create<sp_Trimpot>(Vec(x2,y2+3.25*yh), module, SplashParasite::FM_PARAM, -12.0, 12.0, 0.0));
  229. addInput(Port::create<sp_Port>(Vec(x1, y2+4*yh), Port::INPUT, module, SplashParasite::SHAPE_INPUT));
  230. addInput(Port::create<sp_Port>(Vec(x1, y2+4.75*yh), Port::INPUT, module, SplashParasite::SLOPE_INPUT));
  231. addInput(Port::create<sp_Port>(Vec(x1, y2+5.5*yh), Port::INPUT, module, SplashParasite::SMOOTHNESS_INPUT));
  232. addInput(Port::create<sp_Port>(Vec(x3, y1+5.9*yh), Port::INPUT, module, SplashParasite::LEVEL_INPUT));
  233. addInput(Port::create<sp_Port>(Vec(x1, y1+5.9*yh), Port::INPUT, module, SplashParasite::CLOCK_INPUT));
  234. addOutput(Port::create<sp_Port>(Vec(x1, y1+7.125*yh), Port::OUTPUT, module, SplashParasite::HIGH_OUTPUT));
  235. addOutput(Port::create<sp_Port>(Vec(x1+1*28., y1+7.125*yh), Port::OUTPUT, module, SplashParasite::LOW_OUTPUT));
  236. addOutput(Port::create<sp_Port>(Vec(x1+2*28., y1+7.125*yh), Port::OUTPUT, module, SplashParasite::UNI_OUTPUT));
  237. addOutput(Port::create<sp_Port>(Vec(x1+3*28., y1+7.125*yh), Port::OUTPUT, module, SplashParasite::BI_OUTPUT));
  238. }
  239. };
  240. void SplashParasiteWidget::step() {
  241. SplashParasite *tides = dynamic_cast<SplashParasite*>(module);
  242. assert(tides);
  243. if (tides->generator.feature_mode_ == tides::Generator::FEAT_MODE_HARMONIC) {
  244. panel0->visible = false;
  245. panel1->visible = true;
  246. panel2->visible = false;
  247. } else
  248. if (tides->generator.feature_mode_ == tides::Generator::FEAT_MODE_RANDOM) {
  249. panel0->visible = false;
  250. panel1->visible = false;
  251. panel2->visible = true;
  252. } else {
  253. panel0->visible = true;
  254. panel1->visible = false;
  255. panel2->visible = false;
  256. }
  257. ModuleWidget::step();
  258. }
  259. struct SplashParasiteSheepItem : MenuItem {
  260. SplashParasite *tides;
  261. void onAction(EventAction &e) override {
  262. tides->sheep ^= true;
  263. }
  264. void step() override {
  265. rightText = (tides->sheep) ? "✔" : "";
  266. MenuItem::step();
  267. }
  268. };
  269. struct SplashParasiteModeItem : MenuItem {
  270. SplashParasite *module;
  271. tides::Generator::FeatureMode mode;
  272. void onAction(EventAction &e) override {
  273. //module->playback = playback;
  274. module->generator.feature_mode_ = mode;
  275. }
  276. void step() override {
  277. rightText = (module->generator.feature_mode_ == mode) ? "✔" : "";
  278. MenuItem::step();
  279. }
  280. };
  281. Menu *SplashParasiteWidget::createContextMenu() {
  282. Menu *menu = ModuleWidget::createContextMenu();
  283. SplashParasite *tides = dynamic_cast<SplashParasite*>(module);
  284. assert(tides);
  285. #ifdef WAVETABLE_HACK
  286. menu->addChild(construct<MenuLabel>());
  287. menu->addChild(construct<SplashParasiteSheepItem>(&MenuLabel::text, "Sheep", &SplashParasiteSheepItem::tides, tides));
  288. #endif
  289. menu->addChild(construct<MenuLabel>());
  290. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Mode"));
  291. menu->addChild(construct<SplashParasiteModeItem>(&MenuLabel::text, "Original", &SplashParasiteModeItem::module, tides, &SplashParasiteModeItem::mode, tides::Generator::FEAT_MODE_FUNCTION));
  292. menu->addChild(construct<SplashParasiteModeItem>(&MenuLabel::text, "Harmonic", &SplashParasiteModeItem::module, tides, &SplashParasiteModeItem::mode, tides::Generator::FEAT_MODE_HARMONIC));
  293. menu->addChild(construct<SplashParasiteModeItem>(&MenuLabel::text, "Random", &SplashParasiteModeItem::module, tides, &SplashParasiteModeItem::mode, tides::Generator::FEAT_MODE_RANDOM));
  294. return menu;
  295. }