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
9.1KB

  1. #include "plugin.hpp"
  2. #include "tides/generator.h"
  3. struct Tides : Module {
  4. enum ParamIds {
  5. MODE_PARAM,
  6. RANGE_PARAM,
  7. FREQUENCY_PARAM,
  8. FM_PARAM,
  9. SHAPE_PARAM,
  10. SLOPE_PARAM,
  11. SMOOTHNESS_PARAM,
  12. NUM_PARAMS
  13. };
  14. enum InputIds {
  15. SHAPE_INPUT,
  16. SLOPE_INPUT,
  17. SMOOTHNESS_INPUT,
  18. TRIG_INPUT,
  19. FREEZE_INPUT,
  20. PITCH_INPUT,
  21. FM_INPUT,
  22. LEVEL_INPUT,
  23. CLOCK_INPUT,
  24. NUM_INPUTS
  25. };
  26. enum OutputIds {
  27. HIGH_OUTPUT,
  28. LOW_OUTPUT,
  29. UNI_OUTPUT,
  30. BI_OUTPUT,
  31. NUM_OUTPUTS
  32. };
  33. enum LightIds {
  34. MODE_GREEN_LIGHT, MODE_RED_LIGHT,
  35. PHASE_GREEN_LIGHT, PHASE_RED_LIGHT,
  36. RANGE_GREEN_LIGHT, RANGE_RED_LIGHT,
  37. NUM_LIGHTS
  38. };
  39. bool sheep;
  40. tides::Generator generator;
  41. int frame = 0;
  42. uint8_t lastGate;
  43. dsp::SchmittTrigger modeTrigger;
  44. dsp::SchmittTrigger rangeTrigger;
  45. Tides() {
  46. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  47. configParam(MODE_PARAM, 0.0, 1.0, 0.0, "Output mode");
  48. configParam(RANGE_PARAM, 0.0, 1.0, 0.0, "Frequency range");
  49. configParam(FREQUENCY_PARAM, -48.0, 48.0, 0.0, "Main frequency");
  50. configParam(FM_PARAM, -12.0, 12.0, 0.0, "FM input attenuverter");
  51. configParam(SHAPE_PARAM, -1.0, 1.0, 0.0, "Shape");
  52. configParam(SLOPE_PARAM, -1.0, 1.0, 0.0, "Slope");
  53. configParam(SMOOTHNESS_PARAM, -1.0, 1.0, 0.0, "Smoothness");
  54. memset(&generator, 0, sizeof(generator));
  55. generator.Init();
  56. generator.set_sync(false);
  57. onReset();
  58. }
  59. void process(const ProcessArgs& args) override {
  60. tides::GeneratorMode mode = generator.mode();
  61. if (modeTrigger.process(params[MODE_PARAM].getValue())) {
  62. mode = (tides::GeneratorMode)(((int)mode - 1 + 3) % 3);
  63. generator.set_mode(mode);
  64. }
  65. lights[MODE_GREEN_LIGHT].value = (mode == 2) ? 1.0 : 0.0;
  66. lights[MODE_RED_LIGHT].value = (mode == 0) ? 1.0 : 0.0;
  67. tides::GeneratorRange range = generator.range();
  68. if (rangeTrigger.process(params[RANGE_PARAM].getValue())) {
  69. range = (tides::GeneratorRange)(((int)range - 1 + 3) % 3);
  70. generator.set_range(range);
  71. }
  72. lights[RANGE_GREEN_LIGHT].value = (range == 2) ? 1.0 : 0.0;
  73. lights[RANGE_RED_LIGHT].value = (range == 0) ? 1.0 : 0.0;
  74. // Buffer loop
  75. if (++frame >= 16) {
  76. frame = 0;
  77. // Pitch
  78. float pitch = params[FREQUENCY_PARAM].getValue();
  79. pitch += 12.0 * inputs[PITCH_INPUT].getVoltage();
  80. pitch += params[FM_PARAM].getValue() * inputs[FM_INPUT].getNormalVoltage(0.1) / 5.0;
  81. pitch += 60.0;
  82. // Scale to the global sample rate
  83. pitch += log2f(48000.0 / args.sampleRate) * 12.0;
  84. generator.set_pitch((int) clamp(pitch * 0x80, (float) -0x8000, (float) 0x7fff));
  85. // Slope, smoothness, pitch
  86. int16_t shape = clamp(params[SHAPE_PARAM].getValue() + inputs[SHAPE_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f) * 0x7fff;
  87. int16_t slope = clamp(params[SLOPE_PARAM].getValue() + inputs[SLOPE_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f) * 0x7fff;
  88. int16_t smoothness = clamp(params[SMOOTHNESS_PARAM].getValue() + inputs[SMOOTHNESS_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f) * 0x7fff;
  89. generator.set_shape(shape);
  90. generator.set_slope(slope);
  91. generator.set_smoothness(smoothness);
  92. // Sync
  93. // Slight deviation from spec here.
  94. // Instead of toggling sync by holding the range button, just enable it if the clock port is plugged in.
  95. generator.set_sync(inputs[CLOCK_INPUT].isConnected() && !sheep);
  96. // Generator
  97. generator.Process(sheep);
  98. }
  99. // Level
  100. uint16_t level = clamp(inputs[LEVEL_INPUT].getNormalVoltage(8.0) / 8.0f, 0.0f, 1.0f) * 0xffff;
  101. if (level < 32)
  102. level = 0;
  103. uint8_t gate = 0;
  104. if (inputs[FREEZE_INPUT].getVoltage() >= 0.7)
  105. gate |= tides::CONTROL_FREEZE;
  106. if (inputs[TRIG_INPUT].getVoltage() >= 0.7)
  107. gate |= tides::CONTROL_GATE;
  108. if (inputs[CLOCK_INPUT].getVoltage() >= 0.7)
  109. gate |= tides::CONTROL_CLOCK;
  110. if (!(lastGate & tides::CONTROL_CLOCK) && (gate & tides::CONTROL_CLOCK))
  111. gate |= tides::CONTROL_GATE_RISING;
  112. if (!(lastGate & tides::CONTROL_GATE) && (gate & tides::CONTROL_GATE))
  113. gate |= tides::CONTROL_GATE_RISING;
  114. if ((lastGate & tides::CONTROL_GATE) && !(gate & tides::CONTROL_GATE))
  115. gate |= tides::CONTROL_GATE_FALLING;
  116. lastGate = gate;
  117. const tides::GeneratorSample& sample = generator.Process(gate);
  118. uint32_t uni = sample.unipolar;
  119. int32_t bi = sample.bipolar;
  120. uni = uni * level >> 16;
  121. bi = -bi * level >> 16;
  122. float unif = (float) uni / 0xffff;
  123. float bif = (float) bi / 0x8000;
  124. outputs[HIGH_OUTPUT].setVoltage(sample.flags & tides::FLAG_END_OF_ATTACK ? 0.0 : 5.0);
  125. outputs[LOW_OUTPUT].setVoltage(sample.flags & tides::FLAG_END_OF_RELEASE ? 0.0 : 5.0);
  126. outputs[UNI_OUTPUT].setVoltage(unif * 8.0);
  127. outputs[BI_OUTPUT].setVoltage(bif * 5.0);
  128. if (sample.flags & tides::FLAG_END_OF_ATTACK)
  129. unif *= -1.0;
  130. lights[PHASE_GREEN_LIGHT].setSmoothBrightness(fmaxf(0.0, unif), args.sampleTime);
  131. lights[PHASE_RED_LIGHT].setSmoothBrightness(fmaxf(0.0, -unif), args.sampleTime);
  132. }
  133. void onReset() override {
  134. generator.set_range(tides::GENERATOR_RANGE_MEDIUM);
  135. generator.set_mode(tides::GENERATOR_MODE_LOOPING);
  136. sheep = false;
  137. }
  138. void onRandomize() override {
  139. generator.set_range((tides::GeneratorRange)(random::u32() % 3));
  140. generator.set_mode((tides::GeneratorMode)(random::u32() % 3));
  141. }
  142. json_t* dataToJson() override {
  143. json_t* rootJ = json_object();
  144. json_object_set_new(rootJ, "mode", json_integer((int) generator.mode()));
  145. json_object_set_new(rootJ, "range", json_integer((int) generator.range()));
  146. json_object_set_new(rootJ, "sheep", json_boolean(sheep));
  147. return rootJ;
  148. }
  149. void dataFromJson(json_t* rootJ) override {
  150. json_t* modeJ = json_object_get(rootJ, "mode");
  151. if (modeJ) {
  152. generator.set_mode((tides::GeneratorMode) json_integer_value(modeJ));
  153. }
  154. json_t* rangeJ = json_object_get(rootJ, "range");
  155. if (rangeJ) {
  156. generator.set_range((tides::GeneratorRange) json_integer_value(rangeJ));
  157. }
  158. json_t* sheepJ = json_object_get(rootJ, "sheep");
  159. if (sheepJ) {
  160. sheep = json_boolean_value(sheepJ);
  161. }
  162. }
  163. };
  164. struct TidesWidget : ModuleWidget {
  165. SvgPanel* tidesPanel;
  166. SvgPanel* sheepPanel;
  167. TidesWidget(Tides* module) {
  168. setModule(module);
  169. box.size = Vec(15 * 14, 380);
  170. {
  171. tidesPanel = new SvgPanel();
  172. tidesPanel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Tides.svg")));
  173. tidesPanel->box.size = box.size;
  174. addChild(tidesPanel);
  175. }
  176. {
  177. sheepPanel = new SvgPanel();
  178. sheepPanel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Sheep.svg")));
  179. sheepPanel->box.size = box.size;
  180. addChild(sheepPanel);
  181. }
  182. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  183. addChild(createWidget<ScrewSilver>(Vec(180, 0)));
  184. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  185. addChild(createWidget<ScrewSilver>(Vec(180, 365)));
  186. addParam(createParam<CKD6>(Vec(19, 52), module, Tides::MODE_PARAM));
  187. addParam(createParam<CKD6>(Vec(19, 93), module, Tides::RANGE_PARAM));
  188. addParam(createParam<Rogan3PSGreen>(Vec(78, 60), module, Tides::FREQUENCY_PARAM));
  189. addParam(createParam<Rogan1PSGreen>(Vec(156, 66), module, Tides::FM_PARAM));
  190. addParam(createParam<Rogan1PSWhite>(Vec(13, 155), module, Tides::SHAPE_PARAM));
  191. addParam(createParam<Rogan1PSWhite>(Vec(85, 155), module, Tides::SLOPE_PARAM));
  192. addParam(createParam<Rogan1PSWhite>(Vec(156, 155), module, Tides::SMOOTHNESS_PARAM));
  193. addInput(createInput<PJ301MPort>(Vec(21, 219), module, Tides::SHAPE_INPUT));
  194. addInput(createInput<PJ301MPort>(Vec(93, 219), module, Tides::SLOPE_INPUT));
  195. addInput(createInput<PJ301MPort>(Vec(164, 219), module, Tides::SMOOTHNESS_INPUT));
  196. addInput(createInput<PJ301MPort>(Vec(21, 274), module, Tides::TRIG_INPUT));
  197. addInput(createInput<PJ301MPort>(Vec(57, 274), module, Tides::FREEZE_INPUT));
  198. addInput(createInput<PJ301MPort>(Vec(93, 274), module, Tides::PITCH_INPUT));
  199. addInput(createInput<PJ301MPort>(Vec(128, 274), module, Tides::FM_INPUT));
  200. addInput(createInput<PJ301MPort>(Vec(164, 274), module, Tides::LEVEL_INPUT));
  201. addInput(createInput<PJ301MPort>(Vec(21, 316), module, Tides::CLOCK_INPUT));
  202. addOutput(createOutput<PJ301MPort>(Vec(57, 316), module, Tides::HIGH_OUTPUT));
  203. addOutput(createOutput<PJ301MPort>(Vec(93, 316), module, Tides::LOW_OUTPUT));
  204. addOutput(createOutput<PJ301MPort>(Vec(128, 316), module, Tides::UNI_OUTPUT));
  205. addOutput(createOutput<PJ301MPort>(Vec(164, 316), module, Tides::BI_OUTPUT));
  206. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 61), module, Tides::MODE_GREEN_LIGHT));
  207. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 82), module, Tides::PHASE_GREEN_LIGHT));
  208. addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 102), module, Tides::RANGE_GREEN_LIGHT));
  209. }
  210. void step() override {
  211. Tides* tides = dynamic_cast<Tides*>(module);
  212. if (tides) {
  213. tidesPanel->visible = !tides->sheep;
  214. sheepPanel->visible = tides->sheep;
  215. }
  216. ModuleWidget::step();
  217. }
  218. void appendContextMenu(Menu* menu) override {
  219. Tides* module = dynamic_cast<Tides*>(this->module);
  220. struct SheepItem : MenuItem {
  221. Tides* module;
  222. void onAction(const event::Action& e) override {
  223. module->sheep ^= true;
  224. }
  225. };
  226. menu->addChild(new MenuSeparator);
  227. SheepItem* sheepItem = createMenuItem<SheepItem>("Sheep", CHECKMARK(module->sheep));
  228. sheepItem->module = module;
  229. menu->addChild(sheepItem);
  230. }
  231. };
  232. Model* modelTides = createModel<Tides, TidesWidget>("Tides");