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.

299 lines
8.9KB

  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 Splash : 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. Splash();
  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. return rootJ;
  65. }
  66. void fromJson(json_t *rootJ) override {
  67. json_t *modeJ = json_object_get(rootJ, "mode");
  68. if (modeJ) {
  69. generator.set_mode((tides::GeneratorMode) json_integer_value(modeJ));
  70. }
  71. json_t *rangeJ = json_object_get(rootJ, "range");
  72. if (rangeJ) {
  73. generator.set_range((tides::GeneratorRange) json_integer_value(rangeJ));
  74. }
  75. json_t *sheepJ = json_object_get(rootJ, "sheep");
  76. if (sheepJ) {
  77. sheep = json_boolean_value(sheepJ);
  78. }
  79. }
  80. };
  81. Splash::Splash() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  82. memset(&generator, 0, sizeof(generator));
  83. generator.Init();
  84. generator.set_sync(false);
  85. reset();
  86. }
  87. void Splash::step() {
  88. tides::GeneratorMode mode = generator.mode();
  89. if (modeTrigger.process(params[MODE_PARAM].value)) {
  90. mode = (tides::GeneratorMode) (((int)mode - 1 + 3) % 3);
  91. generator.set_mode(mode);
  92. }
  93. lights[MODE_GREEN_LIGHT].value = (mode == 2) ? 1.0 : 0.0;
  94. lights[MODE_GREEN_LIGHT].value = (mode == 0) ? 0.0 : 1.0;
  95. lights[MODE_RED_LIGHT].value = (mode == 0) ? 1.0 : 0.0;
  96. lights[MODE_RED_LIGHT].value = (mode == 2) ? 0.0 : 1.0;
  97. tides::GeneratorRange range = generator.range();
  98. if (rangeTrigger.process(params[RANGE_PARAM].value)) {
  99. range = (tides::GeneratorRange) (((int)range - 1 + 3) % 3);
  100. generator.set_range(range);
  101. }
  102. lights[RANGE_GREEN_LIGHT].value = (range == 2) ? 1.0 : 0.0;
  103. lights[RANGE_GREEN_LIGHT].value = (range == 0) ? 0.0 : 1.0;
  104. lights[RANGE_RED_LIGHT].value = (range == 0) ? 1.0 : 0.0;
  105. lights[RANGE_RED_LIGHT].value = (range == 2) ? 0.0 : 1.0;
  106. // Buffer loop
  107. if (++frame >= 16) {
  108. frame = 0;
  109. // Pitch
  110. float pitch = params[FREQUENCY_PARAM].value;
  111. pitch += 12.0 * inputs[PITCH_INPUT].value;
  112. pitch += params[FM_PARAM].value * inputs[FM_INPUT].normalize(0.1) / 5.0;
  113. pitch += 60.0;
  114. // Scale to the global sample rate
  115. pitch += log2f(48000.0 / engineGetSampleRate()) * 12.0;
  116. generator.set_pitch(clampf(pitch * 0x80, -0x8000, 0x7fff));
  117. // Slope, smoothness, pitch
  118. int16_t shape = clampf(params[SHAPE_PARAM].value + inputs[SHAPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  119. int16_t slope = clampf(params[SLOPE_PARAM].value + inputs[SLOPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  120. int16_t smoothness = clampf(params[SMOOTHNESS_PARAM].value + inputs[SMOOTHNESS_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  121. generator.set_shape(shape);
  122. generator.set_slope(slope);
  123. generator.set_smoothness(smoothness);
  124. // Sync
  125. // Slight deviation from spec here.
  126. // Instead of toggling sync by holding the range button, just enable it if the clock port is plugged in.
  127. generator.set_sync(inputs[CLOCK_INPUT].active);
  128. // Generator
  129. generator.Process(sheep);
  130. }
  131. // Level
  132. uint16_t level = clampf(inputs[LEVEL_INPUT].normalize(8.0) / 8.0, 0.0, 1.0) * 0xffff;
  133. if (level < 32)
  134. level = 0;
  135. uint8_t gate = 0;
  136. if (inputs[FREEZE_INPUT].value >= 0.7)
  137. gate |= tides::CONTROL_FREEZE;
  138. if (inputs[TRIG_INPUT].value >= 0.7)
  139. gate |= tides::CONTROL_GATE;
  140. if (inputs[CLOCK_INPUT].value >= 0.7)
  141. gate |= tides::CONTROL_CLOCK;
  142. if (!(lastGate & tides::CONTROL_CLOCK) && (gate & tides::CONTROL_CLOCK))
  143. gate |= tides::CONTROL_GATE_RISING;
  144. if (!(lastGate & tides::CONTROL_GATE) && (gate & tides::CONTROL_GATE))
  145. gate |= tides::CONTROL_GATE_RISING;
  146. if ((lastGate & tides::CONTROL_GATE) && !(gate & tides::CONTROL_GATE))
  147. gate |= tides::CONTROL_GATE_FALLING;
  148. lastGate = gate;
  149. const tides::GeneratorSample& sample = generator.Process(gate);
  150. uint32_t uni = sample.unipolar;
  151. int32_t bi = sample.bipolar;
  152. uni = uni * level >> 16;
  153. bi = -bi * level >> 16;
  154. float unif = (float) uni / 0xffff;
  155. float bif = (float) bi / 0x8000;
  156. outputs[HIGH_OUTPUT].value = sample.flags & tides::FLAG_END_OF_ATTACK ? 0.0 : 5.0;
  157. outputs[LOW_OUTPUT].value = sample.flags & tides::FLAG_END_OF_RELEASE ? 0.0 : 5.0;
  158. outputs[UNI_OUTPUT].value = unif * 8.0;
  159. outputs[BI_OUTPUT].value = bif * 5.0;
  160. if (sample.flags & tides::FLAG_END_OF_ATTACK)
  161. unif *= -1.0;
  162. lights[PHASE_GREEN_LIGHT].setBrightnessSmooth(fmaxf(0.0, unif));
  163. lights[PHASE_RED_LIGHT].setBrightnessSmooth(fmaxf(0.0, -unif));
  164. }
  165. SplashWidget::SplashWidget() {
  166. Splash *module = new Splash();
  167. setModule(module);
  168. box.size = Vec(15 * 8, 380);
  169. {
  170. tidesPanel = new SVGPanel();
  171. tidesPanel->setBackground(SVG::load(assetPlugin(plugin, "res/Splash.svg")));
  172. tidesPanel->box.size = box.size;
  173. addChild(tidesPanel);
  174. }
  175. {
  176. sheepPanel = new SVGPanel();
  177. sheepPanel->setBackground(SVG::load(assetPlugin(plugin, "res/Lambs.svg")));
  178. sheepPanel->box.size = box.size;
  179. addChild(sheepPanel);
  180. }
  181. const float x1 = 0.5*RACK_GRID_WIDTH;
  182. const float x2 = 3.25*RACK_GRID_WIDTH;
  183. const float x3 = 5.75*RACK_GRID_WIDTH;
  184. const float y1 = 40.0f;
  185. const float y2 = 25.0f;
  186. const float yh = 38.0f;
  187. addParam(createParam<CKD6>(Vec(x3-3,y1-3), module, Splash::MODE_PARAM, 0.0, 1.0, 0.0));
  188. addChild(createLight<MediumLight<GreenRedLight>>(Vec(x3+7, y1+7), module, Splash::MODE_GREEN_LIGHT));
  189. addParam(createParam<CKD6>(Vec(x3-3,y1+1.45*yh), module, Splash::RANGE_PARAM, 0.0, 1.0, 0.0));
  190. addChild(createLight<MediumLight<GreenRedLight>>(Vec(x3+7, y1+2*yh-10), module, Splash::RANGE_GREEN_LIGHT));
  191. addChild(createLight<MediumLight<GreenRedLight>>(Vec(x2-20, y2+2*yh), module, Splash::PHASE_GREEN_LIGHT));
  192. addParam(createParam<sp_BlackKnob>(Vec(x2-7,y2+1.75*yh), module, Splash::FREQUENCY_PARAM, -48.0, 48.0, 0.0));
  193. addParam(createParam<sp_SmallBlackKnob>(Vec(x3, y2+4*yh), module, Splash::SHAPE_PARAM, -1.0, 1.0, 0.0));
  194. addParam(createParam<sp_SmallBlackKnob>(Vec(x3, y2+4.75*yh), module, Splash::SLOPE_PARAM, -1.0, 1.0, 0.0));
  195. addParam(createParam<sp_SmallBlackKnob>(Vec(x3, y2+5.5*yh), module, Splash::SMOOTHNESS_PARAM, -1.0, 1.0, 0.0));
  196. addInput(createInput<sp_Port>(Vec(x1, y1), module, Splash::TRIG_INPUT));
  197. addInput(createInput<sp_Port>(Vec(x2, y1), module, Splash::FREEZE_INPUT));
  198. addInput(createInput<sp_Port>(Vec(x1, y2+2*yh), module, Splash::PITCH_INPUT));
  199. addInput(createInput<sp_Port>(Vec(x1, y2+3.25*yh), module, Splash::FM_INPUT));
  200. addParam(createParam<sp_Trimpot>(Vec(x2,y2+3.25*yh), module, Splash::FM_PARAM, -12.0, 12.0, 0.0));
  201. addInput(createInput<sp_Port>(Vec(x1, y2+4*yh), module, Splash::SHAPE_INPUT));
  202. addInput(createInput<sp_Port>(Vec(x1, y2+4.75*yh), module, Splash::SLOPE_INPUT));
  203. addInput(createInput<sp_Port>(Vec(x1, y2+5.5*yh), module, Splash::SMOOTHNESS_INPUT));
  204. addInput(createInput<sp_Port>(Vec(x3, y1+5.9*yh), module, Splash::LEVEL_INPUT));
  205. addInput(createInput<sp_Port>(Vec(x1, y1+5.9*yh), module, Splash::CLOCK_INPUT));
  206. addOutput(createOutput<sp_Port>(Vec(x1, y1+7.125*yh), module, Splash::HIGH_OUTPUT));
  207. addOutput(createOutput<sp_Port>(Vec(x1+1*28., y1+7.125*yh), module, Splash::LOW_OUTPUT));
  208. addOutput(createOutput<sp_Port>(Vec(x1+2*28., y1+7.125*yh), module, Splash::UNI_OUTPUT));
  209. addOutput(createOutput<sp_Port>(Vec(x1+3*28., y1+7.125*yh), module, Splash::BI_OUTPUT));
  210. }
  211. void SplashWidget::step() {
  212. Splash *tides = dynamic_cast<Splash*>(module);
  213. assert(tides);
  214. tidesPanel->visible = !tides->sheep;
  215. sheepPanel->visible = tides->sheep;
  216. ModuleWidget::step();
  217. }
  218. struct SplashSheepItem : MenuItem {
  219. Splash *tides;
  220. void onAction(EventAction &e) override {
  221. tides->sheep ^= true;
  222. }
  223. void step() override {
  224. rightText = (tides->sheep) ? "✔" : "";
  225. MenuItem::step();
  226. }
  227. };
  228. Menu *SplashWidget::createContextMenu() {
  229. Menu *menu = ModuleWidget::createContextMenu();
  230. Splash *tides = dynamic_cast<Splash*>(module);
  231. assert(tides);
  232. menu->addChild(construct<MenuEntry>());
  233. menu->addChild(construct<SplashSheepItem>(&MenuEntry::text, "Lambs", &SplashSheepItem::tides, tides));
  234. return menu;
  235. }