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.

283 lines
8.5KB

  1. #include <string.h>
  2. #include "AudibleInstruments.hpp"
  3. #include "dsp/samplerate.hpp"
  4. #include "dsp/digital.hpp"
  5. #include "tides/generator.h"
  6. struct Tides : 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. Tides();
  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. Tides::Tides() : 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 Tides::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_RED_LIGHT].value = (mode == 0) ? 1.0 : 0.0;
  95. tides::GeneratorRange range = generator.range();
  96. if (rangeTrigger.process(params[RANGE_PARAM].value)) {
  97. range = (tides::GeneratorRange) (((int)range - 1 + 3) % 3);
  98. generator.set_range(range);
  99. }
  100. lights[RANGE_GREEN_LIGHT].value = (range == 2) ? 1.0 : 0.0;
  101. lights[RANGE_RED_LIGHT].value = (range == 0) ? 1.0 : 0.0;
  102. // Buffer loop
  103. if (++frame >= 16) {
  104. frame = 0;
  105. // Pitch
  106. float pitch = params[FREQUENCY_PARAM].value;
  107. pitch += 12.0 * inputs[PITCH_INPUT].value;
  108. pitch += params[FM_PARAM].value * inputs[FM_INPUT].normalize(0.1) / 5.0;
  109. pitch += 60.0;
  110. // Scale to the global sample rate
  111. pitch += log2f(48000.0 / engineGetSampleRate()) * 12.0;
  112. generator.set_pitch(clampf(pitch * 0x80, -0x8000, 0x7fff));
  113. // Slope, smoothness, pitch
  114. int16_t shape = clampf(params[SHAPE_PARAM].value + inputs[SHAPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  115. int16_t slope = clampf(params[SLOPE_PARAM].value + inputs[SLOPE_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  116. int16_t smoothness = clampf(params[SMOOTHNESS_PARAM].value + inputs[SMOOTHNESS_INPUT].value / 5.0, -1.0, 1.0) * 0x7fff;
  117. generator.set_shape(shape);
  118. generator.set_slope(slope);
  119. generator.set_smoothness(smoothness);
  120. // Sync
  121. // Slight deviation from spec here.
  122. // Instead of toggling sync by holding the range button, just enable it if the clock port is plugged in.
  123. generator.set_sync(inputs[CLOCK_INPUT].active);
  124. // Generator
  125. generator.Process(sheep);
  126. }
  127. // Level
  128. uint16_t level = clampf(inputs[LEVEL_INPUT].normalize(8.0) / 8.0, 0.0, 1.0) * 0xffff;
  129. if (level < 32)
  130. level = 0;
  131. uint8_t gate = 0;
  132. if (inputs[FREEZE_INPUT].value >= 0.7)
  133. gate |= tides::CONTROL_FREEZE;
  134. if (inputs[TRIG_INPUT].value >= 0.7)
  135. gate |= tides::CONTROL_GATE;
  136. if (inputs[CLOCK_INPUT].value >= 0.7)
  137. gate |= tides::CONTROL_CLOCK;
  138. if (!(lastGate & tides::CONTROL_CLOCK) && (gate & tides::CONTROL_CLOCK))
  139. gate |= tides::CONTROL_GATE_RISING;
  140. if (!(lastGate & tides::CONTROL_GATE) && (gate & tides::CONTROL_GATE))
  141. gate |= tides::CONTROL_GATE_RISING;
  142. if ((lastGate & tides::CONTROL_GATE) && !(gate & tides::CONTROL_GATE))
  143. gate |= tides::CONTROL_GATE_FALLING;
  144. lastGate = gate;
  145. const tides::GeneratorSample& sample = generator.Process(gate);
  146. uint32_t uni = sample.unipolar;
  147. int32_t bi = sample.bipolar;
  148. uni = uni * level >> 16;
  149. bi = -bi * level >> 16;
  150. float unif = (float) uni / 0xffff;
  151. float bif = (float) bi / 0x8000;
  152. outputs[HIGH_OUTPUT].value = sample.flags & tides::FLAG_END_OF_ATTACK ? 0.0 : 5.0;
  153. outputs[LOW_OUTPUT].value = sample.flags & tides::FLAG_END_OF_RELEASE ? 0.0 : 5.0;
  154. outputs[UNI_OUTPUT].value = unif * 8.0;
  155. outputs[BI_OUTPUT].value = bif * 5.0;
  156. if (sample.flags & tides::FLAG_END_OF_ATTACK)
  157. unif *= -1.0;
  158. lights[PHASE_GREEN_LIGHT].setBrightnessSmooth(fmaxf(0.0, unif));
  159. lights[PHASE_RED_LIGHT].setBrightnessSmooth(fmaxf(0.0, -unif));
  160. }
  161. TidesWidget::TidesWidget() {
  162. Tides *module = new Tides();
  163. setModule(module);
  164. box.size = Vec(15 * 14, 380);
  165. {
  166. tidesPanel = new LightPanel();
  167. tidesPanel->backgroundImage = Image::load(assetPlugin(plugin, "res/Tides.png"));
  168. tidesPanel->box.size = box.size;
  169. addChild(tidesPanel);
  170. }
  171. {
  172. sheepPanel = new LightPanel();
  173. sheepPanel->backgroundImage = Image::load(assetPlugin(plugin, "res/Sheep.png"));
  174. sheepPanel->box.size = box.size;
  175. addChild(sheepPanel);
  176. }
  177. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  178. addChild(createScrew<ScrewSilver>(Vec(180, 0)));
  179. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  180. addChild(createScrew<ScrewSilver>(Vec(180, 365)));
  181. addParam(createParam<CKD6>(Vec(19, 52), module, Tides::MODE_PARAM, 0.0, 1.0, 0.0));
  182. addParam(createParam<CKD6>(Vec(19, 93), module, Tides::RANGE_PARAM, 0.0, 1.0, 0.0));
  183. addParam(createParam<Rogan3PSGreen>(Vec(78, 60), module, Tides::FREQUENCY_PARAM, -48.0, 48.0, 0.0));
  184. addParam(createParam<Rogan1PSGreen>(Vec(156, 66), module, Tides::FM_PARAM, -12.0, 12.0, 0.0));
  185. addParam(createParam<Rogan1PSWhite>(Vec(13, 155), module, Tides::SHAPE_PARAM, -1.0, 1.0, 0.0));
  186. addParam(createParam<Rogan1PSWhite>(Vec(85, 155), module, Tides::SLOPE_PARAM, -1.0, 1.0, 0.0));
  187. addParam(createParam<Rogan1PSWhite>(Vec(156, 155), module, Tides::SMOOTHNESS_PARAM, -1.0, 1.0, 0.0));
  188. addInput(createInput<PJ301MPort>(Vec(21, 219), module, Tides::SHAPE_INPUT));
  189. addInput(createInput<PJ301MPort>(Vec(93, 219), module, Tides::SLOPE_INPUT));
  190. addInput(createInput<PJ301MPort>(Vec(164, 219), module, Tides::SMOOTHNESS_INPUT));
  191. addInput(createInput<PJ301MPort>(Vec(21, 274), module, Tides::TRIG_INPUT));
  192. addInput(createInput<PJ301MPort>(Vec(57, 274), module, Tides::FREEZE_INPUT));
  193. addInput(createInput<PJ301MPort>(Vec(93, 274), module, Tides::PITCH_INPUT));
  194. addInput(createInput<PJ301MPort>(Vec(128, 274), module, Tides::FM_INPUT));
  195. addInput(createInput<PJ301MPort>(Vec(164, 274), module, Tides::LEVEL_INPUT));
  196. addInput(createInput<PJ301MPort>(Vec(21, 316), module, Tides::CLOCK_INPUT));
  197. addOutput(createOutput<PJ301MPort>(Vec(57, 316), module, Tides::HIGH_OUTPUT));
  198. addOutput(createOutput<PJ301MPort>(Vec(93, 316), module, Tides::LOW_OUTPUT));
  199. addOutput(createOutput<PJ301MPort>(Vec(128, 316), module, Tides::UNI_OUTPUT));
  200. addOutput(createOutput<PJ301MPort>(Vec(164, 316), module, Tides::BI_OUTPUT));
  201. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 61), module, Tides::MODE_GREEN_LIGHT));
  202. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 82), module, Tides::PHASE_GREEN_LIGHT));
  203. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 102), module, Tides::RANGE_GREEN_LIGHT));
  204. }
  205. void TidesWidget::step() {
  206. Tides *tides = dynamic_cast<Tides*>(module);
  207. assert(tides);
  208. tidesPanel->visible = !tides->sheep;
  209. sheepPanel->visible = tides->sheep;
  210. ModuleWidget::step();
  211. }
  212. struct TidesSheepItem : MenuItem {
  213. Tides *tides;
  214. void onAction(EventAction &e) override {
  215. tides->sheep ^= true;
  216. }
  217. void step() override {
  218. rightText = (tides->sheep) ? "✔" : "";
  219. MenuItem::step();
  220. }
  221. };
  222. Menu *TidesWidget::createContextMenu() {
  223. Menu *menu = ModuleWidget::createContextMenu();
  224. Tides *tides = dynamic_cast<Tides*>(module);
  225. assert(tides);
  226. menu->addChild(construct<MenuEntry>());
  227. menu->addChild(construct<TidesSheepItem>(&MenuEntry::text, "Sheep", &TidesSheepItem::tides, tides));
  228. return menu;
  229. }