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.

412 lines
13KB

  1. #include "plugin.hpp"
  2. #include "frames/keyframer.h"
  3. #include "frames/poly_lfo.h"
  4. struct Frames : Module {
  5. enum ParamIds {
  6. GAIN1_PARAM,
  7. GAIN2_PARAM,
  8. GAIN3_PARAM,
  9. GAIN4_PARAM,
  10. ADD_PARAM,
  11. DEL_PARAM,
  12. FRAME_PARAM,
  13. MODULATION_PARAM,
  14. OFFSET_PARAM,
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. ALL_INPUT,
  19. IN1_INPUT,
  20. IN2_INPUT,
  21. IN3_INPUT,
  22. IN4_INPUT,
  23. FRAME_INPUT,
  24. NUM_INPUTS
  25. };
  26. enum OutputIds {
  27. MIX_OUTPUT,
  28. OUT1_OUTPUT,
  29. OUT2_OUTPUT,
  30. OUT3_OUTPUT,
  31. OUT4_OUTPUT,
  32. FRAME_STEP_OUTPUT,
  33. NUM_OUTPUTS
  34. };
  35. enum LightIds {
  36. GAIN1_LIGHT,
  37. EDIT_LIGHT = GAIN1_LIGHT + 4,
  38. FRAME_LIGHT,
  39. NUM_LIGHTS = FRAME_LIGHT + 3
  40. };
  41. frames::Keyframer keyframer;
  42. frames::PolyLfo poly_lfo;
  43. bool poly_lfo_mode = false;
  44. uint16_t lastControls[4] = {};
  45. dsp::SchmittTrigger addTrigger;
  46. dsp::SchmittTrigger delTrigger;
  47. Frames() {
  48. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  49. configParam(GAIN1_PARAM, 0.0, 1.0, 0.0, "Channel 1 gain");
  50. configParam(GAIN2_PARAM, 0.0, 1.0, 0.0, "Channel 2 gain");
  51. configParam(GAIN3_PARAM, 0.0, 1.0, 0.0, "Channel 3 gain");
  52. configParam(GAIN4_PARAM, 0.0, 1.0, 0.0, "Channel 4 gain");
  53. configParam(FRAME_PARAM, 0.0, 1.0, 0.0, "Frame");
  54. configParam(MODULATION_PARAM, -1.0, 1.0, 0.0, "Animation attenuverter");
  55. configButton(ADD_PARAM, "Add keyframe");
  56. configButton(DEL_PARAM, "Delete keyframe");
  57. configSwitch(OFFSET_PARAM, 0.0, 1.0, 0.0, "Offset", {"+0V", "+10V"});
  58. configInput(ALL_INPUT, "All");
  59. configInput(IN1_INPUT, "Channel 1");
  60. configInput(IN2_INPUT, "Channel 2");
  61. configInput(IN3_INPUT, "Channel 3");
  62. configInput(IN4_INPUT, "Channel 4");
  63. configInput(FRAME_INPUT, "Frame");
  64. configOutput(MIX_OUTPUT, "Mix");
  65. configOutput(OUT1_OUTPUT, "Channel 1");
  66. configOutput(OUT2_OUTPUT, "Channel 2");
  67. configOutput(OUT3_OUTPUT, "Channel 3");
  68. configOutput(OUT4_OUTPUT, "Channel 4");
  69. configOutput(FRAME_STEP_OUTPUT, "Frame step");
  70. memset(&keyframer, 0, sizeof(keyframer));
  71. keyframer.Init();
  72. memset(&poly_lfo, 0, sizeof(poly_lfo));
  73. poly_lfo.Init();
  74. onReset();
  75. }
  76. void process(const ProcessArgs& args) override {
  77. // Set gain and timestamp knobs
  78. uint16_t controls[4];
  79. for (int i = 0; i < 4; i++) {
  80. controls[i] = params[GAIN1_PARAM + i].getValue() * 65535.0;
  81. }
  82. int32_t timestamp = params[FRAME_PARAM].getValue() * 65535.0;
  83. int32_t timestampMod = timestamp + params[MODULATION_PARAM].getValue() * inputs[FRAME_INPUT].getVoltage() / 10.0 * 65535.0;
  84. timestamp = clamp(timestamp, 0, 65535);
  85. timestampMod = clamp(timestampMod, 0, 65535);
  86. int16_t nearestIndex = -1;
  87. if (!poly_lfo_mode) {
  88. nearestIndex = keyframer.FindNearestKeyframe(timestamp, 2048);
  89. }
  90. // Render, handle buttons
  91. if (poly_lfo_mode) {
  92. if (controls[0] != lastControls[0])
  93. poly_lfo.set_shape(controls[0]);
  94. if (controls[1] != lastControls[1])
  95. poly_lfo.set_shape_spread(controls[1]);
  96. if (controls[2] != lastControls[2])
  97. poly_lfo.set_spread(controls[2]);
  98. if (controls[3] != lastControls[3])
  99. poly_lfo.set_coupling(controls[3]);
  100. poly_lfo.Render(timestampMod);
  101. }
  102. else {
  103. for (int i = 0; i < 4; i++) {
  104. if (controls[i] != lastControls[i]) {
  105. // Update recently moved control
  106. if (keyframer.num_keyframes() == 0) {
  107. keyframer.set_immediate(i, controls[i]);
  108. }
  109. if (nearestIndex >= 0) {
  110. frames::Keyframe* nearestKeyframe = keyframer.mutable_keyframe(nearestIndex);
  111. nearestKeyframe->values[i] = controls[i];
  112. }
  113. }
  114. }
  115. if (addTrigger.process(params[ADD_PARAM].getValue())) {
  116. if (nearestIndex < 0) {
  117. keyframer.AddKeyframe(timestamp, controls);
  118. }
  119. }
  120. if (delTrigger.process(params[DEL_PARAM].getValue())) {
  121. if (nearestIndex >= 0) {
  122. int32_t nearestTimestamp = keyframer.keyframe(nearestIndex).timestamp;
  123. keyframer.RemoveKeyframe(nearestTimestamp);
  124. }
  125. }
  126. keyframer.Evaluate(timestampMod);
  127. }
  128. // Get gains
  129. float gains[4];
  130. for (int i = 0; i < 4; i++) {
  131. if (poly_lfo_mode) {
  132. // gains[i] = poly_lfo.level(i) / 255.0;
  133. gains[i] = poly_lfo.level16(i) / 65535.0;
  134. }
  135. else {
  136. float lin = keyframer.level(i) / 65535.0;
  137. gains[i] = lin;
  138. }
  139. // Simulate SSM2164
  140. if (keyframer.mutable_settings(i)->response > 0) {
  141. const float expBase = 200.0;
  142. float expGain = rescale(powf(expBase, gains[i]), 1.0f, expBase, 0.0f, 1.0f);
  143. gains[i] = crossfade(gains[i], expGain, keyframer.mutable_settings(i)->response / 255.0f);
  144. }
  145. }
  146. // Update last controls
  147. for (int i = 0; i < 4; i++) {
  148. lastControls[i] = controls[i];
  149. }
  150. // Get inputs
  151. float all = ((int)params[OFFSET_PARAM].getValue() == 1) ? 10.0 : 0.0;
  152. if (inputs[ALL_INPUT].isConnected()) {
  153. all = inputs[ALL_INPUT].getVoltage();
  154. }
  155. float ins[4];
  156. for (int i = 0; i < 4; i++) {
  157. ins[i] = inputs[IN1_INPUT + i].getNormalVoltage(all) * gains[i];
  158. }
  159. // Set outputs
  160. float mix = 0.0;
  161. for (int i = 0; i < 4; i++) {
  162. if (outputs[OUT1_OUTPUT + i].isConnected()) {
  163. outputs[OUT1_OUTPUT + i].setVoltage(ins[i]);
  164. }
  165. else {
  166. mix += ins[i];
  167. }
  168. }
  169. outputs[MIX_OUTPUT].setVoltage(clamp(mix / 2.0, -10.0f, 10.0f));
  170. // Set lights
  171. for (int i = 0; i < 4; i++) {
  172. lights[GAIN1_LIGHT + i].setBrightness(gains[i]);
  173. }
  174. if (poly_lfo_mode) {
  175. lights[EDIT_LIGHT].value = (poly_lfo.level(0) > 128 ? 1.0 : 0.0);
  176. }
  177. else {
  178. lights[EDIT_LIGHT].value = (nearestIndex >= 0 ? 1.0 : 0.0);
  179. }
  180. // Set frame light colors
  181. const uint8_t* colors;
  182. if (poly_lfo_mode) {
  183. colors = poly_lfo.color();
  184. }
  185. else {
  186. colors = keyframer.color();
  187. }
  188. for (int i = 0; i < 3; i++) {
  189. float c = colors[i] / 255.f;
  190. // c = 1.f - (1.f - c) * 1.25f;
  191. lights[FRAME_LIGHT + i].setBrightness(c);
  192. }
  193. }
  194. json_t* dataToJson() override {
  195. json_t* rootJ = json_object();
  196. json_object_set_new(rootJ, "polyLfo", json_boolean(poly_lfo_mode));
  197. json_t* keyframesJ = json_array();
  198. for (int i = 0; i < keyframer.num_keyframes(); i++) {
  199. json_t* keyframeJ = json_array();
  200. frames::Keyframe* keyframe = keyframer.mutable_keyframe(i);
  201. json_array_append_new(keyframeJ, json_integer(keyframe->timestamp));
  202. for (int k = 0; k < 4; k++) {
  203. json_array_append_new(keyframeJ, json_integer(keyframe->values[k]));
  204. }
  205. json_array_append_new(keyframesJ, keyframeJ);
  206. }
  207. json_object_set_new(rootJ, "keyframes", keyframesJ);
  208. json_t* channelsJ = json_array();
  209. for (int i = 0; i < 4; i++) {
  210. json_t* channelJ = json_object();
  211. json_object_set_new(channelJ, "curve", json_integer((int) keyframer.mutable_settings(i)->easing_curve));
  212. json_object_set_new(channelJ, "response", json_integer(keyframer.mutable_settings(i)->response));
  213. json_array_append_new(channelsJ, channelJ);
  214. }
  215. json_object_set_new(rootJ, "channels", channelsJ);
  216. return rootJ;
  217. }
  218. void dataFromJson(json_t* rootJ) override {
  219. json_t* polyLfoJ = json_object_get(rootJ, "polyLfo");
  220. if (polyLfoJ)
  221. poly_lfo_mode = json_boolean_value(polyLfoJ);
  222. json_t* keyframesJ = json_object_get(rootJ, "keyframes");
  223. if (keyframesJ) {
  224. json_t* keyframeJ;
  225. size_t i;
  226. json_array_foreach(keyframesJ, i, keyframeJ) {
  227. uint16_t timestamp = json_integer_value(json_array_get(keyframeJ, 0));
  228. uint16_t values[4];
  229. for (int k = 0; k < 4; k++) {
  230. values[k] = json_integer_value(json_array_get(keyframeJ, k + 1));
  231. }
  232. keyframer.AddKeyframe(timestamp, values);
  233. }
  234. }
  235. json_t* channelsJ = json_object_get(rootJ, "channels");
  236. if (channelsJ) {
  237. for (int i = 0; i < 4; i++) {
  238. json_t* channelJ = json_array_get(channelsJ, i);
  239. if (channelJ) {
  240. json_t* curveJ = json_object_get(channelJ, "curve");
  241. if (curveJ)
  242. keyframer.mutable_settings(i)->easing_curve = (frames::EasingCurve) json_integer_value(curveJ);
  243. json_t* responseJ = json_object_get(channelJ, "response");
  244. if (responseJ)
  245. keyframer.mutable_settings(i)->response = json_integer_value(responseJ);
  246. }
  247. }
  248. }
  249. }
  250. void onReset() override {
  251. poly_lfo_mode = false;
  252. keyframer.Clear();
  253. for (int i = 0; i < 4; i++) {
  254. keyframer.mutable_settings(i)->easing_curve = frames::EASING_CURVE_LINEAR;
  255. keyframer.mutable_settings(i)->response = 0;
  256. }
  257. }
  258. void onRandomize() override {
  259. // TODO
  260. // Maybe something useful should go in here?
  261. }
  262. };
  263. struct CKSSRot : SVGSwitch {
  264. CKSSRot() {
  265. addFrame(Svg::load(asset::plugin(pluginInstance, "res/CKSS_rot_0.svg")));
  266. addFrame(Svg::load(asset::plugin(pluginInstance, "res/CKSS_rot_1.svg")));
  267. }
  268. };
  269. struct FramesWidget : ModuleWidget {
  270. FramesWidget(Frames* module) {
  271. setModule(module);
  272. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Frames.svg")));
  273. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  274. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  275. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  276. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  277. addParam(createParam<Rogan1PSWhite>(Vec(14, 52), module, Frames::GAIN1_PARAM));
  278. addParam(createParam<Rogan1PSWhite>(Vec(81, 52), module, Frames::GAIN2_PARAM));
  279. addParam(createParam<Rogan1PSWhite>(Vec(149, 52), module, Frames::GAIN3_PARAM));
  280. addParam(createParam<Rogan1PSWhite>(Vec(216, 52), module, Frames::GAIN4_PARAM));
  281. addParam(createParamCentered<Rogan6PSWhite>(Vec(133.556641, 159.560532), module, Frames::FRAME_PARAM));
  282. addParam(createParam<Rogan1PSGreen>(Vec(208, 141), module, Frames::MODULATION_PARAM));
  283. addParam(createParam<CKD6>(Vec(19, 123), module, Frames::ADD_PARAM));
  284. addParam(createParam<CKD6>(Vec(19, 172), module, Frames::DEL_PARAM));
  285. addParam(createParam<CKSSRot>(Vec(18, 239), module, Frames::OFFSET_PARAM));
  286. addInput(createInput<PJ301MPort>(Vec(16, 273), module, Frames::ALL_INPUT));
  287. addInput(createInput<PJ301MPort>(Vec(59, 273), module, Frames::IN1_INPUT));
  288. addInput(createInput<PJ301MPort>(Vec(102, 273), module, Frames::IN2_INPUT));
  289. addInput(createInput<PJ301MPort>(Vec(145, 273), module, Frames::IN3_INPUT));
  290. addInput(createInput<PJ301MPort>(Vec(188, 273), module, Frames::IN4_INPUT));
  291. addInput(createInput<PJ301MPort>(Vec(231, 273), module, Frames::FRAME_INPUT));
  292. addOutput(createOutput<PJ301MPort>(Vec(16, 315), module, Frames::MIX_OUTPUT));
  293. addOutput(createOutput<PJ301MPort>(Vec(59, 315), module, Frames::OUT1_OUTPUT));
  294. addOutput(createOutput<PJ301MPort>(Vec(102, 315), module, Frames::OUT2_OUTPUT));
  295. addOutput(createOutput<PJ301MPort>(Vec(145, 315), module, Frames::OUT3_OUTPUT));
  296. addOutput(createOutput<PJ301MPort>(Vec(188, 315), module, Frames::OUT4_OUTPUT));
  297. addOutput(createOutput<PJ301MPort>(Vec(231, 315), module, Frames::FRAME_STEP_OUTPUT));
  298. addChild(createLight<SmallLight<GreenLight>>(Vec(30, 101), module, Frames::GAIN1_LIGHT + 0));
  299. addChild(createLight<SmallLight<GreenLight>>(Vec(97, 101), module, Frames::GAIN1_LIGHT + 1));
  300. addChild(createLight<SmallLight<GreenLight>>(Vec(165, 101), module, Frames::GAIN1_LIGHT + 2));
  301. addChild(createLight<SmallLight<GreenLight>>(Vec(232, 101), module, Frames::GAIN1_LIGHT + 3));
  302. addChild(createLight<MediumLight<GreenLight>>(Vec(61, 155), module, Frames::EDIT_LIGHT));
  303. addChild(createLightCentered<Rogan6PSLight<RedGreenBlueLight>>(Vec(133.556641, 159.560532), module, Frames::FRAME_LIGHT));
  304. }
  305. void appendContextMenu(Menu* menu) override {
  306. Frames* module = dynamic_cast<Frames*>(this->module);
  307. assert(module);
  308. menu->addChild(new MenuSeparator);
  309. menu->addChild(createMenuLabel("Channel settings"));
  310. for (int c = 0; c < 4; c++) {
  311. menu->addChild(createSubmenuItem(string::f("Channel %d", c + 1), "",
  312. [=](Menu* menu) {
  313. menu->addChild(createMenuLabel("Interpolation curve"));
  314. static const std::vector<std::string> curveLabels = {
  315. "Step",
  316. "Linear",
  317. "Accelerating",
  318. "Decelerating",
  319. "Departure/arrival",
  320. "Bouncing",
  321. };
  322. for (int i = 0; i < (int) curveLabels.size(); i++) {
  323. menu->addChild(createCheckMenuItem(curveLabels[i],
  324. [=]() {return module->keyframer.mutable_settings(c)->easing_curve == i;},
  325. [=]() {module->keyframer.mutable_settings(c)->easing_curve = (frames::EasingCurve) i;}
  326. ));
  327. }
  328. menu->addChild(new MenuSeparator);
  329. menu->addChild(createMenuLabel("Response curve"));
  330. menu->addChild(createCheckMenuItem("Linear",
  331. [=]() {return module->keyframer.mutable_settings(c)->response == 0;},
  332. [=]() {module->keyframer.mutable_settings(c)->response = 0;}
  333. ));
  334. menu->addChild(createCheckMenuItem("Exponential",
  335. [=]() {return module->keyframer.mutable_settings(c)->response == 255;},
  336. [=]() {module->keyframer.mutable_settings(c)->response = 255;}
  337. ));
  338. }
  339. ));
  340. }
  341. menu->addChild(createMenuItem("Clear keyframes", "",
  342. [=]() {module->keyframer.Clear();}
  343. ));
  344. menu->addChild(new MenuSeparator);
  345. menu->addChild(createMenuLabel("Alternate modes"));
  346. static const std::vector<std::string> modeLabels = {
  347. "Keyframer",
  348. "Poly LFO",
  349. };
  350. for (int i = 0; i < (int) modeLabels.size(); i++) {
  351. menu->addChild(createCheckMenuItem(modeLabels[i],
  352. [=]() {return module->poly_lfo_mode == i;},
  353. [=]() {module->poly_lfo_mode = i;}
  354. ));
  355. }
  356. }
  357. };
  358. Model* modelFrames = createModel<Frames, FramesWidget>("Frames");