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.

497 lines
19KB

  1. // Mutable Instruments Streams emulation for VCV Rack
  2. // Copyright (C) 2020 Tyler Coy
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. #include <string>
  17. #include <algorithm>
  18. #include "plugin.hpp"
  19. #include "Streams/streams.hpp"
  20. namespace streams
  21. {
  22. struct StreamsChannelMode
  23. {
  24. ProcessorFunction function;
  25. bool alternate;
  26. std::string label;
  27. };
  28. static constexpr int kNumChannelModes = 10;
  29. static const StreamsChannelMode kChannelModeTable[kNumChannelModes] =
  30. {
  31. {PROCESSOR_FUNCTION_ENVELOPE, false, "Envelope"},
  32. {PROCESSOR_FUNCTION_VACTROL, false, "Vactrol"},
  33. {PROCESSOR_FUNCTION_FOLLOWER, false, "Follower"},
  34. {PROCESSOR_FUNCTION_COMPRESSOR, false, "Compressor"},
  35. {PROCESSOR_FUNCTION_ENVELOPE, true, "AR envelope"},
  36. {PROCESSOR_FUNCTION_VACTROL, true, "Plucked vactrol"},
  37. {PROCESSOR_FUNCTION_FOLLOWER, true, "Cutoff controller"},
  38. {PROCESSOR_FUNCTION_COMPRESSOR, true, "Slow compressor"},
  39. {PROCESSOR_FUNCTION_FILTER_CONTROLLER, true, "Direct VCF controller"},
  40. {PROCESSOR_FUNCTION_LORENZ_GENERATOR, false, "Lorenz generator"},
  41. };
  42. struct StreamsMonitorMode
  43. {
  44. MonitorMode mode;
  45. std::string label;
  46. };
  47. static constexpr int kNumMonitorModes = 4;
  48. static const StreamsMonitorMode kMonitorModeTable[kNumMonitorModes] =
  49. {
  50. {MONITOR_MODE_EXCITE_IN, "Excite"},
  51. {MONITOR_MODE_VCA_CV, "Level"},
  52. {MONITOR_MODE_AUDIO_IN, "In"},
  53. {MONITOR_MODE_OUTPUT, "Out"},
  54. };
  55. }
  56. struct Streams : Module
  57. {
  58. enum ParamIds
  59. {
  60. CH1_SHAPE_PARAM,
  61. CH1_MOD_PARAM,
  62. CH1_LEVEL_MOD_PARAM,
  63. CH1_RESPONSE_PARAM,
  64. CH2_SHAPE_PARAM,
  65. CH2_MOD_PARAM,
  66. CH2_LEVEL_MOD_PARAM,
  67. CH2_RESPONSE_PARAM,
  68. CH1_FUNCTION_BUTTON_PARAM,
  69. CH2_FUNCTION_BUTTON_PARAM,
  70. METERING_BUTTON_PARAM,
  71. NUM_PARAMS
  72. };
  73. enum InputIds
  74. {
  75. CH1_EXCITE_INPUT,
  76. CH1_SIGNAL_INPUT,
  77. CH1_LEVEL_INPUT,
  78. CH2_EXCITE_INPUT,
  79. CH2_SIGNAL_INPUT,
  80. CH2_LEVEL_INPUT,
  81. NUM_INPUTS
  82. };
  83. enum OutputIds
  84. {
  85. CH1_SIGNAL_OUTPUT,
  86. CH2_SIGNAL_OUTPUT,
  87. NUM_OUTPUTS
  88. };
  89. enum LightIds
  90. {
  91. CH1_LIGHT_1_G,
  92. CH1_LIGHT_1_R,
  93. CH1_LIGHT_2_G,
  94. CH1_LIGHT_2_R,
  95. CH1_LIGHT_3_G,
  96. CH1_LIGHT_3_R,
  97. CH1_LIGHT_4_G,
  98. CH1_LIGHT_4_R,
  99. CH2_LIGHT_1_G,
  100. CH2_LIGHT_1_R,
  101. CH2_LIGHT_2_G,
  102. CH2_LIGHT_2_R,
  103. CH2_LIGHT_3_G,
  104. CH2_LIGHT_3_R,
  105. CH2_LIGHT_4_G,
  106. CH2_LIGHT_4_R,
  107. NUM_LIGHTS
  108. };
  109. static constexpr int kNumEngines = 16;
  110. streams::StreamsEngine engine_[kNumEngines];
  111. float brightness_[NUM_LIGHTS][kNumEngines];
  112. int prev_num_channels_;
  113. Streams()
  114. {
  115. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  116. configParam(CH1_SHAPE_PARAM, 0.f, 1.f, 0.f);
  117. configParam(CH1_MOD_PARAM, 0.f, 1.f, 0.5f);
  118. configParam(CH1_LEVEL_MOD_PARAM, 0.f, 1.f, 0.f);
  119. configParam(CH2_SHAPE_PARAM, 0.f, 1.f, 0.f);
  120. configParam(CH2_MOD_PARAM, 0.f, 1.f, 0.5f);
  121. configParam(CH2_LEVEL_MOD_PARAM, 0.f, 1.f, 0.f);
  122. configParam(CH1_RESPONSE_PARAM, 0.f, 1.f, 0.f);
  123. configParam(CH2_RESPONSE_PARAM, 0.f, 1.f, 0.f);
  124. configParam(CH1_FUNCTION_BUTTON_PARAM, 0.f, 1.f, 0.f);
  125. configParam(CH2_FUNCTION_BUTTON_PARAM, 0.f, 1.f, 0.f);
  126. configParam(METERING_BUTTON_PARAM, 0.f, 1.f, 0.f);
  127. onReset();
  128. }
  129. void onReset() override
  130. {
  131. for (int c = 0; c < kNumEngines; c++)
  132. {
  133. engine_[c].Reset();
  134. for (int i = 0; i < NUM_LIGHTS; i++)
  135. {
  136. brightness_[c][i] = 0.f;
  137. }
  138. }
  139. prev_num_channels_ = 1;
  140. onSampleRateChange();
  141. }
  142. void onSampleRateChange() override
  143. {
  144. float sample_rate = APP->engine->getSampleRate();
  145. for (int c = 0; c < kNumEngines; c++)
  146. {
  147. engine_[c].SetSampleRate(sample_rate);
  148. }
  149. }
  150. json_t* dataToJson() override
  151. {
  152. streams::UiSettings settings = engine_[0].ui_settings();
  153. json_t* root_j = json_object();
  154. json_object_set_new(root_j, "function1", json_integer(settings.function[0]));
  155. json_object_set_new(root_j, "function2", json_integer(settings.function[1]));
  156. json_object_set_new(root_j, "alternate1", json_integer(settings.alternate[0]));
  157. json_object_set_new(root_j, "alternate2", json_integer(settings.alternate[1]));
  158. json_object_set_new(root_j, "monitor_mode", json_integer(settings.monitor_mode));
  159. json_object_set_new(root_j, "linked", json_integer(settings.linked));
  160. return root_j;
  161. }
  162. void dataFromJson(json_t* root_j) override
  163. {
  164. json_t* function1_j = json_object_get(root_j, "function1");
  165. json_t* function2_j = json_object_get(root_j, "function2");
  166. json_t* alternate1_j = json_object_get(root_j, "alternate1");
  167. json_t* alternate2_j = json_object_get(root_j, "alternate2");
  168. json_t* monitor_mode_j = json_object_get(root_j, "monitor_mode");
  169. json_t* linked_j = json_object_get(root_j, "linked");
  170. streams::UiSettings settings = {};
  171. if (function1_j) settings.function[0] = json_integer_value(function1_j);
  172. if (function2_j) settings.function[1] = json_integer_value(function2_j);
  173. if (alternate1_j) settings.alternate[0] = json_integer_value(alternate1_j);
  174. if (alternate2_j) settings.alternate[1] = json_integer_value(alternate2_j);
  175. if (monitor_mode_j) settings.monitor_mode = json_integer_value(monitor_mode_j);
  176. if (linked_j) settings.linked = json_integer_value(linked_j);
  177. for (int c = 0; c < kNumEngines; c++)
  178. {
  179. engine_[c].ApplySettings(settings);
  180. }
  181. }
  182. void onRandomize() override
  183. {
  184. for (int c = 0; c < kNumEngines; c++)
  185. {
  186. engine_[c].Randomize();
  187. }
  188. }
  189. void ToggleLink()
  190. {
  191. streams::UiSettings settings = engine_[0].ui_settings();
  192. settings.linked ^= 1;
  193. for (int c = 0; c < kNumEngines; c++)
  194. {
  195. engine_[c].ApplySettings(settings);
  196. }
  197. }
  198. void SetChannelMode(int channel, int mode_id)
  199. {
  200. streams::UiSettings settings = engine_[0].ui_settings();
  201. settings.function[channel] = streams::kChannelModeTable[mode_id].function;
  202. settings.alternate[channel] = streams::kChannelModeTable[mode_id].alternate;
  203. for (int c = 0; c < kNumEngines; c++)
  204. {
  205. engine_[c].ApplySettings(settings);
  206. }
  207. }
  208. void SetMonitorMode(int mode_id)
  209. {
  210. streams::UiSettings settings = engine_[0].ui_settings();
  211. settings.monitor_mode = streams::kMonitorModeTable[mode_id].mode;
  212. for (int c = 0; c < kNumEngines; c++)
  213. {
  214. engine_[c].ApplySettings(settings);
  215. }
  216. }
  217. int function(int channel)
  218. {
  219. return engine_[0].ui_settings().function[channel];
  220. }
  221. int alternate(int channel)
  222. {
  223. return engine_[0].ui_settings().alternate[channel];
  224. }
  225. bool linked()
  226. {
  227. return engine_[0].ui_settings().linked;
  228. }
  229. int monitor_mode()
  230. {
  231. return engine_[0].ui_settings().monitor_mode;
  232. }
  233. void process(const ProcessArgs& args) override
  234. {
  235. int num_channels = std::max(inputs[CH1_SIGNAL_INPUT].getChannels(),
  236. inputs[CH2_SIGNAL_INPUT].getChannels());
  237. num_channels = std::max(num_channels, 1);
  238. if (num_channels > prev_num_channels_)
  239. {
  240. for (int c = prev_num_channels_; c < num_channels; c++)
  241. {
  242. engine_[c].SyncUI(engine_[0]);
  243. }
  244. }
  245. prev_num_channels_ = num_channels;
  246. // Reuse the same frame object for multiple engines because the params
  247. // aren't touched.
  248. streams::StreamsEngine::Frame frame;
  249. frame.ch1.shape_knob = params[CH1_SHAPE_PARAM] .getValue();
  250. frame.ch1.mod_knob = params[CH1_MOD_PARAM] .getValue();
  251. frame.ch1.level_mod_knob = params[CH1_LEVEL_MOD_PARAM].getValue();
  252. frame.ch1.response_knob = params[CH1_RESPONSE_PARAM] .getValue();
  253. frame.ch2.shape_knob = params[CH2_SHAPE_PARAM] .getValue();
  254. frame.ch2.mod_knob = params[CH2_MOD_PARAM] .getValue();
  255. frame.ch2.level_mod_knob = params[CH2_LEVEL_MOD_PARAM].getValue();
  256. frame.ch2.response_knob = params[CH2_RESPONSE_PARAM] .getValue();
  257. frame.ch1.signal_in_connected = inputs[CH1_SIGNAL_INPUT].isConnected();
  258. frame.ch1.level_cv_connected = inputs[CH1_LEVEL_INPUT] .isConnected();
  259. frame.ch2.signal_in_connected = inputs[CH2_SIGNAL_INPUT].isConnected();
  260. frame.ch2.level_cv_connected = inputs[CH2_LEVEL_INPUT] .isConnected();
  261. frame.ch1.function_button = params[CH1_FUNCTION_BUTTON_PARAM].getValue();
  262. frame.ch2.function_button = params[CH2_FUNCTION_BUTTON_PARAM].getValue();
  263. frame.metering_button = params[METERING_BUTTON_PARAM].getValue();
  264. bool lights_updated = false;
  265. for (int c = 0; c < num_channels; c++)
  266. {
  267. frame.ch1.excite_in = inputs[CH1_EXCITE_INPUT].getPolyVoltage(c);
  268. frame.ch1.signal_in = inputs[CH1_SIGNAL_INPUT].getPolyVoltage(c);
  269. frame.ch1.level_cv = inputs[CH1_LEVEL_INPUT] .getPolyVoltage(c);
  270. frame.ch2.excite_in = inputs[CH2_EXCITE_INPUT].getPolyVoltage(c);
  271. frame.ch2.signal_in = inputs[CH2_SIGNAL_INPUT].getPolyVoltage(c);
  272. frame.ch2.level_cv = inputs[CH2_LEVEL_INPUT] .getPolyVoltage(c);
  273. engine_[c].Process(frame);
  274. outputs[CH1_SIGNAL_OUTPUT].setVoltage(frame.ch1.signal_out, c);
  275. outputs[CH2_SIGNAL_OUTPUT].setVoltage(frame.ch2.signal_out, c);
  276. if (frame.lights_updated)
  277. {
  278. brightness_[CH1_LIGHT_1_G][c] = frame.ch1.led_green[0];
  279. brightness_[CH1_LIGHT_2_G][c] = frame.ch1.led_green[1];
  280. brightness_[CH1_LIGHT_3_G][c] = frame.ch1.led_green[2];
  281. brightness_[CH1_LIGHT_4_G][c] = frame.ch1.led_green[3];
  282. brightness_[CH1_LIGHT_1_R][c] = frame.ch1.led_red[0];
  283. brightness_[CH1_LIGHT_2_R][c] = frame.ch1.led_red[1];
  284. brightness_[CH1_LIGHT_3_R][c] = frame.ch1.led_red[2];
  285. brightness_[CH1_LIGHT_4_R][c] = frame.ch1.led_red[3];
  286. brightness_[CH2_LIGHT_1_G][c] = frame.ch2.led_green[0];
  287. brightness_[CH2_LIGHT_2_G][c] = frame.ch2.led_green[1];
  288. brightness_[CH2_LIGHT_3_G][c] = frame.ch2.led_green[2];
  289. brightness_[CH2_LIGHT_4_G][c] = frame.ch2.led_green[3];
  290. brightness_[CH2_LIGHT_1_R][c] = frame.ch2.led_red[0];
  291. brightness_[CH2_LIGHT_2_R][c] = frame.ch2.led_red[1];
  292. brightness_[CH2_LIGHT_3_R][c] = frame.ch2.led_red[2];
  293. brightness_[CH2_LIGHT_4_R][c] = frame.ch2.led_red[3];
  294. }
  295. lights_updated |= frame.lights_updated;
  296. }
  297. outputs[CH1_SIGNAL_OUTPUT].setChannels(num_channels);
  298. outputs[CH2_SIGNAL_OUTPUT].setChannels(num_channels);
  299. if (lights_updated)
  300. {
  301. // Drive lights according to maximum brightness across engines
  302. for (int i = 0; i < NUM_LIGHTS; i++)
  303. {
  304. float brightness = 0.f;
  305. for (int c = 0; c < num_channels; c++)
  306. {
  307. brightness = std::max(brightness_[i][c], brightness);
  308. }
  309. lights[i].setBrightness(brightness);
  310. }
  311. }
  312. }
  313. };
  314. struct StreamsWidget : ModuleWidget
  315. {
  316. StreamsWidget(Streams* module)
  317. {
  318. setModule(module);
  319. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Streams.svg")));
  320. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  321. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  322. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  323. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  324. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 107.695)), module, Streams::CH1_SHAPE_PARAM));
  325. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 84.196)), module, Streams::CH1_MOD_PARAM));
  326. addParam(createParamCentered<Rogan1PSRed> (mm2px(Vec(11.065, 128.75 - 60.706)), module, Streams::CH1_LEVEL_MOD_PARAM));
  327. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 107.695)), module, Streams::CH2_SHAPE_PARAM));
  328. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 84.196)), module, Streams::CH2_MOD_PARAM));
  329. addParam(createParamCentered<Rogan1PSGreen>(mm2px(Vec(49.785, 128.75 - 60.706)), module, Streams::CH2_LEVEL_MOD_PARAM));
  330. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 68.006)), module, Streams::CH1_RESPONSE_PARAM));
  331. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 53.406)), module, Streams::CH2_RESPONSE_PARAM));
  332. addParam(createParamCentered<TL1105>(mm2px(Vec(24.715, 128.75 - 113.726)), module, Streams::CH1_FUNCTION_BUTTON_PARAM));
  333. addParam(createParamCentered<TL1105>(mm2px(Vec(36.135, 128.75 - 113.726)), module, Streams::CH2_FUNCTION_BUTTON_PARAM));
  334. addParam(createParamCentered<TL1105>(mm2px(Vec(30.425, 128.75 - 81.976)), module, Streams::METERING_BUTTON_PARAM));
  335. addInput(createInputCentered<PJ301MPort>(mm2px(Vec( 8.506, 128.75 - 32.136)), module, Streams::CH1_EXCITE_INPUT));
  336. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 32.136)), module, Streams::CH1_SIGNAL_INPUT));
  337. addInput(createInputCentered<PJ301MPort>(mm2px(Vec( 8.506, 128.75 - 17.526)), module, Streams::CH1_LEVEL_INPUT));
  338. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 32.136)), module, Streams::CH2_EXCITE_INPUT));
  339. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 32.136)), module, Streams::CH2_SIGNAL_INPUT));
  340. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 17.526)), module, Streams::CH2_LEVEL_INPUT));
  341. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 17.526)), module, Streams::CH1_SIGNAL_OUTPUT));
  342. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 17.526)), module, Streams::CH2_SIGNAL_OUTPUT));
  343. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 106.746)), module, Streams::CH1_LIGHT_1_G));
  344. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 101.026)), module, Streams::CH1_LIGHT_2_G));
  345. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 95.305)), module, Streams::CH1_LIGHT_3_G));
  346. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 89.585)), module, Streams::CH1_LIGHT_4_G));
  347. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 106.746)), module, Streams::CH2_LIGHT_1_G));
  348. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 101.026)), module, Streams::CH2_LIGHT_2_G));
  349. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 95.305)), module, Streams::CH2_LIGHT_3_G));
  350. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 89.585)), module, Streams::CH2_LIGHT_4_G));
  351. }
  352. void appendContextMenu(Menu* menu) override
  353. {
  354. Streams* module = dynamic_cast<Streams*>(this->module);
  355. struct LinkItem : MenuItem
  356. {
  357. Streams* module;
  358. void onAction(const event::Action & e) override
  359. {
  360. module->ToggleLink();
  361. }
  362. };
  363. struct ChannelModeItem : MenuItem
  364. {
  365. Streams* module;
  366. int channel;
  367. int mode;
  368. void onAction(const event::Action& e) override
  369. {
  370. module->SetChannelMode(channel, mode);
  371. }
  372. };
  373. struct MonitorModeItem : MenuItem
  374. {
  375. Streams* module;
  376. int mode;
  377. void onAction(const event::Action& e) override
  378. {
  379. module->SetMonitorMode(mode);
  380. }
  381. };
  382. menu->addChild(new MenuSeparator);
  383. LinkItem* link_item = createMenuItem<LinkItem>(
  384. "Link channels", CHECKMARK(module->linked()));
  385. link_item->module = module;
  386. menu->addChild(link_item);
  387. menu->addChild(new MenuSeparator);
  388. menu->addChild(createMenuLabel("Channel 1"));
  389. for (int i = 0; i < streams::kNumChannelModes; i++)
  390. {
  391. auto mode_item = createMenuItem<ChannelModeItem>(
  392. streams::kChannelModeTable[i].label, CHECKMARK(
  393. module->function(0) == streams::kChannelModeTable[i].function &&
  394. module->alternate(0) == streams::kChannelModeTable[i].alternate));
  395. mode_item->module = module;
  396. mode_item->channel = 0;
  397. mode_item->mode = i;
  398. menu->addChild(mode_item);
  399. }
  400. menu->addChild(new MenuSeparator);
  401. menu->addChild(createMenuLabel("Channel 2"));
  402. for (int i = 0; i < streams::kNumChannelModes; i++)
  403. {
  404. auto mode_item = createMenuItem<ChannelModeItem>(
  405. streams::kChannelModeTable[i].label, CHECKMARK(
  406. module->function(1) == streams::kChannelModeTable[i].function &&
  407. module->alternate(1) == streams::kChannelModeTable[i].alternate));
  408. mode_item->module = module;
  409. mode_item->channel = 1;
  410. mode_item->mode = i;
  411. menu->addChild(mode_item);
  412. }
  413. menu->addChild(new MenuSeparator);
  414. menu->addChild(createMenuLabel("Meter"));
  415. for (int i = 0; i < streams::kNumMonitorModes; i++)
  416. {
  417. auto mode_item = createMenuItem<MonitorModeItem>(
  418. streams::kMonitorModeTable[i].label, CHECKMARK(
  419. module->monitor_mode() == streams::kMonitorModeTable[i].mode));
  420. mode_item->module = module;
  421. mode_item->mode = i;
  422. menu->addChild(mode_item);
  423. }
  424. }
  425. };
  426. Model* modelStreams = createModel<Streams, StreamsWidget>("Streams");