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.

446 lines
17KB

  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. struct StreamsChannelMode {
  22. ProcessorFunction function;
  23. bool alternate;
  24. std::string label;
  25. };
  26. static constexpr int kNumChannelModes = 10;
  27. static const StreamsChannelMode kChannelModeTable[kNumChannelModes] = {
  28. {PROCESSOR_FUNCTION_ENVELOPE, false, "Envelope"},
  29. {PROCESSOR_FUNCTION_VACTROL, false, "Vactrol"},
  30. {PROCESSOR_FUNCTION_FOLLOWER, false, "Follower"},
  31. {PROCESSOR_FUNCTION_COMPRESSOR, false, "Compressor"},
  32. {PROCESSOR_FUNCTION_ENVELOPE, true, "AR envelope"},
  33. {PROCESSOR_FUNCTION_VACTROL, true, "Plucked vactrol"},
  34. {PROCESSOR_FUNCTION_FOLLOWER, true, "Cutoff controller"},
  35. {PROCESSOR_FUNCTION_COMPRESSOR, true, "Slow compressor"},
  36. {PROCESSOR_FUNCTION_FILTER_CONTROLLER, true, "Direct VCF controller"},
  37. {PROCESSOR_FUNCTION_LORENZ_GENERATOR, false, "Lorenz generator"},
  38. };
  39. struct StreamsMonitorMode {
  40. MonitorMode mode;
  41. std::string label;
  42. };
  43. static constexpr int kNumMonitorModes = 4;
  44. static const StreamsMonitorMode kMonitorModeTable[kNumMonitorModes] = {
  45. {MONITOR_MODE_EXCITE_IN, "Excite"},
  46. {MONITOR_MODE_VCA_CV, "Level"},
  47. {MONITOR_MODE_AUDIO_IN, "In"},
  48. {MONITOR_MODE_OUTPUT, "Out"},
  49. };
  50. }
  51. struct Streams : Module {
  52. enum ParamIds {
  53. CH1_SHAPE_PARAM,
  54. CH1_MOD_PARAM,
  55. CH1_LEVEL_MOD_PARAM,
  56. CH1_RESPONSE_PARAM,
  57. CH2_SHAPE_PARAM,
  58. CH2_MOD_PARAM,
  59. CH2_LEVEL_MOD_PARAM,
  60. CH2_RESPONSE_PARAM,
  61. CH1_FUNCTION_BUTTON_PARAM,
  62. CH2_FUNCTION_BUTTON_PARAM,
  63. METERING_BUTTON_PARAM,
  64. NUM_PARAMS
  65. };
  66. enum InputIds {
  67. CH1_EXCITE_INPUT,
  68. CH1_SIGNAL_INPUT,
  69. CH1_LEVEL_INPUT,
  70. CH2_EXCITE_INPUT,
  71. CH2_SIGNAL_INPUT,
  72. CH2_LEVEL_INPUT,
  73. NUM_INPUTS
  74. };
  75. enum OutputIds {
  76. CH1_SIGNAL_OUTPUT,
  77. CH2_SIGNAL_OUTPUT,
  78. NUM_OUTPUTS
  79. };
  80. enum LightIds {
  81. CH1_LIGHT_1_G,
  82. CH1_LIGHT_1_R,
  83. CH1_LIGHT_2_G,
  84. CH1_LIGHT_2_R,
  85. CH1_LIGHT_3_G,
  86. CH1_LIGHT_3_R,
  87. CH1_LIGHT_4_G,
  88. CH1_LIGHT_4_R,
  89. CH2_LIGHT_1_G,
  90. CH2_LIGHT_1_R,
  91. CH2_LIGHT_2_G,
  92. CH2_LIGHT_2_R,
  93. CH2_LIGHT_3_G,
  94. CH2_LIGHT_3_R,
  95. CH2_LIGHT_4_G,
  96. CH2_LIGHT_4_R,
  97. NUM_LIGHTS
  98. };
  99. streams::StreamsEngine engines[PORT_MAX_CHANNELS];
  100. int prevNumChannels;
  101. float brightnesses[NUM_LIGHTS][PORT_MAX_CHANNELS];
  102. Streams() {
  103. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  104. configParam(CH1_SHAPE_PARAM, 0.f, 1.f, 0.5f, "Ch 1 shape");
  105. configParam(CH1_MOD_PARAM, 0.f, 1.f, 0.5f, "Ch 1 mod");
  106. configParam(CH1_LEVEL_MOD_PARAM, 0.f, 1.f, 0.5f, "Ch 1 level mod");
  107. configParam(CH2_SHAPE_PARAM, 0.f, 1.f, 0.5f, "Ch 2 shape");
  108. configParam(CH2_MOD_PARAM, 0.f, 1.f, 0.5f, "Ch 2 mod");
  109. configParam(CH2_LEVEL_MOD_PARAM, 0.f, 1.f, 0.5f, "Ch 2 level mod");
  110. configParam(CH1_RESPONSE_PARAM, 0.f, 1.f, 0.5f, "Ch 1 response");
  111. configParam(CH2_RESPONSE_PARAM, 0.f, 1.f, 0.5f, "Ch 2 response");
  112. configParam(CH1_FUNCTION_BUTTON_PARAM, 0.f, 1.f, 0.f, "Ch 1 function");
  113. configParam(CH2_FUNCTION_BUTTON_PARAM, 0.f, 1.f, 0.f, "Ch 2 function");
  114. configParam(METERING_BUTTON_PARAM, 0.f, 1.f, 0.f, "Meter");
  115. onReset();
  116. }
  117. void onReset() override {
  118. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  119. engines[c].Reset();
  120. }
  121. prevNumChannels = 1;
  122. onSampleRateChange();
  123. }
  124. void onSampleRateChange() override {
  125. float sampleRate = APP->engine->getSampleRate();
  126. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  127. engines[c].SetSampleRate(sampleRate);
  128. }
  129. }
  130. json_t* dataToJson() override {
  131. streams::UiSettings settings = engines[0].ui_settings();
  132. json_t* rootJ = json_object();
  133. json_object_set_new(rootJ, "function1", json_integer(settings.function[0]));
  134. json_object_set_new(rootJ, "function2", json_integer(settings.function[1]));
  135. json_object_set_new(rootJ, "alternate1", json_integer(settings.alternate[0]));
  136. json_object_set_new(rootJ, "alternate2", json_integer(settings.alternate[1]));
  137. json_object_set_new(rootJ, "monitorMode", json_integer(settings.monitor_mode));
  138. json_object_set_new(rootJ, "linked", json_integer(settings.linked));
  139. return rootJ;
  140. }
  141. void dataFromJson(json_t* rootJ) override {
  142. json_t* function1J = json_object_get(rootJ, "function1");
  143. json_t* function2J = json_object_get(rootJ, "function2");
  144. json_t* alternate1J = json_object_get(rootJ, "alternate1");
  145. json_t* alternate2J = json_object_get(rootJ, "alternate2");
  146. json_t* monitorModeJ = json_object_get(rootJ, "monitorMode");
  147. json_t* linkedJ = json_object_get(rootJ, "linked");
  148. streams::UiSettings settings = {};
  149. if (function1J)
  150. settings.function[0] = json_integer_value(function1J);
  151. if (function2J)
  152. settings.function[1] = json_integer_value(function2J);
  153. if (alternate1J)
  154. settings.alternate[0] = json_integer_value(alternate1J);
  155. if (alternate2J)
  156. settings.alternate[1] = json_integer_value(alternate2J);
  157. if (monitorModeJ)
  158. settings.monitor_mode = json_integer_value(monitorModeJ);
  159. if (linkedJ)
  160. settings.linked = json_integer_value(linkedJ);
  161. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  162. engines[c].ApplySettings(settings);
  163. }
  164. }
  165. void onRandomize() override {
  166. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  167. engines[c].Randomize();
  168. }
  169. }
  170. void toggleLink() {
  171. streams::UiSettings settings = engines[0].ui_settings();
  172. settings.linked ^= 1;
  173. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  174. engines[c].ApplySettings(settings);
  175. }
  176. }
  177. void setChannelMode(int channel, int mode_id) {
  178. streams::UiSettings settings = engines[0].ui_settings();
  179. settings.function[channel] = streams::kChannelModeTable[mode_id].function;
  180. settings.alternate[channel] = streams::kChannelModeTable[mode_id].alternate;
  181. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  182. engines[c].ApplySettings(settings);
  183. }
  184. }
  185. void setMonitorMode(int mode_id) {
  186. streams::UiSettings settings = engines[0].ui_settings();
  187. settings.monitor_mode = streams::kMonitorModeTable[mode_id].mode;
  188. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  189. engines[c].ApplySettings(settings);
  190. }
  191. }
  192. int function(int channel) {
  193. return engines[0].ui_settings().function[channel];
  194. }
  195. int alternate(int channel) {
  196. return engines[0].ui_settings().alternate[channel];
  197. }
  198. bool linked() {
  199. return engines[0].ui_settings().linked;
  200. }
  201. int monitorMode() {
  202. return engines[0].ui_settings().monitor_mode;
  203. }
  204. void process(const ProcessArgs& args) override {
  205. int numChannels = std::max(inputs[CH1_SIGNAL_INPUT].getChannels(), inputs[CH2_SIGNAL_INPUT].getChannels());
  206. numChannels = std::max(numChannels, 1);
  207. if (numChannels > prevNumChannels) {
  208. for (int c = prevNumChannels; c < numChannels; c++) {
  209. engines[c].SyncUI(engines[0]);
  210. }
  211. }
  212. prevNumChannels = numChannels;
  213. // Reuse the same frame object for multiple engines because the params
  214. // aren't touched.
  215. streams::StreamsEngine::Frame frame;
  216. frame.ch1.shape_knob = params[CH1_SHAPE_PARAM] .getValue();
  217. frame.ch1.mod_knob = params[CH1_MOD_PARAM] .getValue();
  218. frame.ch1.level_mod_knob = params[CH1_LEVEL_MOD_PARAM].getValue();
  219. frame.ch1.response_knob = params[CH1_RESPONSE_PARAM] .getValue();
  220. frame.ch2.shape_knob = params[CH2_SHAPE_PARAM] .getValue();
  221. frame.ch2.mod_knob = params[CH2_MOD_PARAM] .getValue();
  222. frame.ch2.level_mod_knob = params[CH2_LEVEL_MOD_PARAM].getValue();
  223. frame.ch2.response_knob = params[CH2_RESPONSE_PARAM] .getValue();
  224. frame.ch1.signal_in_connected = inputs[CH1_SIGNAL_INPUT].isConnected();
  225. frame.ch1.level_cv_connected = inputs[CH1_LEVEL_INPUT] .isConnected();
  226. frame.ch2.signal_in_connected = inputs[CH2_SIGNAL_INPUT].isConnected();
  227. frame.ch2.level_cv_connected = inputs[CH2_LEVEL_INPUT] .isConnected();
  228. frame.ch1.function_button = params[CH1_FUNCTION_BUTTON_PARAM].getValue();
  229. frame.ch2.function_button = params[CH2_FUNCTION_BUTTON_PARAM].getValue();
  230. frame.metering_button = params[METERING_BUTTON_PARAM].getValue();
  231. bool lights_updated = false;
  232. for (int c = 0; c < numChannels; c++) {
  233. frame.ch1.excite_in = inputs[CH1_EXCITE_INPUT].getPolyVoltage(c);
  234. frame.ch1.signal_in = inputs[CH1_SIGNAL_INPUT].getPolyVoltage(c);
  235. frame.ch1.level_cv = inputs[CH1_LEVEL_INPUT] .getPolyVoltage(c);
  236. frame.ch2.excite_in = inputs[CH2_EXCITE_INPUT].getPolyVoltage(c);
  237. frame.ch2.signal_in = inputs[CH2_SIGNAL_INPUT].getPolyVoltage(c);
  238. frame.ch2.level_cv = inputs[CH2_LEVEL_INPUT] .getPolyVoltage(c);
  239. engines[c].Process(frame);
  240. outputs[CH1_SIGNAL_OUTPUT].setVoltage(frame.ch1.signal_out, c);
  241. outputs[CH2_SIGNAL_OUTPUT].setVoltage(frame.ch2.signal_out, c);
  242. if (frame.lights_updated) {
  243. brightnesses[CH1_LIGHT_1_G][c] = frame.ch1.led_green[0];
  244. brightnesses[CH1_LIGHT_2_G][c] = frame.ch1.led_green[1];
  245. brightnesses[CH1_LIGHT_3_G][c] = frame.ch1.led_green[2];
  246. brightnesses[CH1_LIGHT_4_G][c] = frame.ch1.led_green[3];
  247. brightnesses[CH1_LIGHT_1_R][c] = frame.ch1.led_red[0];
  248. brightnesses[CH1_LIGHT_2_R][c] = frame.ch1.led_red[1];
  249. brightnesses[CH1_LIGHT_3_R][c] = frame.ch1.led_red[2];
  250. brightnesses[CH1_LIGHT_4_R][c] = frame.ch1.led_red[3];
  251. brightnesses[CH2_LIGHT_1_G][c] = frame.ch2.led_green[0];
  252. brightnesses[CH2_LIGHT_2_G][c] = frame.ch2.led_green[1];
  253. brightnesses[CH2_LIGHT_3_G][c] = frame.ch2.led_green[2];
  254. brightnesses[CH2_LIGHT_4_G][c] = frame.ch2.led_green[3];
  255. brightnesses[CH2_LIGHT_1_R][c] = frame.ch2.led_red[0];
  256. brightnesses[CH2_LIGHT_2_R][c] = frame.ch2.led_red[1];
  257. brightnesses[CH2_LIGHT_3_R][c] = frame.ch2.led_red[2];
  258. brightnesses[CH2_LIGHT_4_R][c] = frame.ch2.led_red[3];
  259. }
  260. lights_updated |= frame.lights_updated;
  261. }
  262. outputs[CH1_SIGNAL_OUTPUT].setChannels(numChannels);
  263. outputs[CH2_SIGNAL_OUTPUT].setChannels(numChannels);
  264. if (lights_updated) {
  265. // Drive lights according to maximum brightness across engines
  266. for (int i = 0; i < NUM_LIGHTS; i++) {
  267. float brightness = 0.f;
  268. for (int c = 0; c < numChannels; c++) {
  269. brightness = std::max(brightnesses[i][c], brightness);
  270. }
  271. lights[i].setBrightness(brightness);
  272. }
  273. }
  274. }
  275. };
  276. struct StreamsWidget : ModuleWidget {
  277. StreamsWidget(Streams* module) {
  278. setModule(module);
  279. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Streams.svg")));
  280. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  281. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  282. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  283. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  284. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 107.695)), module, Streams::CH1_SHAPE_PARAM));
  285. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 84.196)), module, Streams::CH1_MOD_PARAM));
  286. addParam(createParamCentered<Rogan1PSRed> (mm2px(Vec(11.065, 128.75 - 60.706)), module, Streams::CH1_LEVEL_MOD_PARAM));
  287. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 107.695)), module, Streams::CH2_SHAPE_PARAM));
  288. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 84.196)), module, Streams::CH2_MOD_PARAM));
  289. addParam(createParamCentered<Rogan1PSGreen>(mm2px(Vec(49.785, 128.75 - 60.706)), module, Streams::CH2_LEVEL_MOD_PARAM));
  290. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 68.006)), module, Streams::CH1_RESPONSE_PARAM));
  291. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 53.406)), module, Streams::CH2_RESPONSE_PARAM));
  292. addParam(createParamCentered<TL1105>(mm2px(Vec(24.715, 128.75 - 113.726)), module, Streams::CH1_FUNCTION_BUTTON_PARAM));
  293. addParam(createParamCentered<TL1105>(mm2px(Vec(36.135, 128.75 - 113.726)), module, Streams::CH2_FUNCTION_BUTTON_PARAM));
  294. addParam(createParamCentered<TL1105>(mm2px(Vec(30.425, 128.75 - 81.976)), module, Streams::METERING_BUTTON_PARAM));
  295. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 32.136)), module, Streams::CH1_EXCITE_INPUT));
  296. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 32.136)), module, Streams::CH1_SIGNAL_INPUT));
  297. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 17.526)), module, Streams::CH1_LEVEL_INPUT));
  298. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 32.136)), module, Streams::CH2_EXCITE_INPUT));
  299. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 32.136)), module, Streams::CH2_SIGNAL_INPUT));
  300. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 17.526)), module, Streams::CH2_LEVEL_INPUT));
  301. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 17.526)), module, Streams::CH1_SIGNAL_OUTPUT));
  302. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 17.526)), module, Streams::CH2_SIGNAL_OUTPUT));
  303. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 106.746)), module, Streams::CH1_LIGHT_1_G));
  304. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 101.026)), module, Streams::CH1_LIGHT_2_G));
  305. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 95.305)), module, Streams::CH1_LIGHT_3_G));
  306. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 89.585)), module, Streams::CH1_LIGHT_4_G));
  307. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 106.746)), module, Streams::CH2_LIGHT_1_G));
  308. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 101.026)), module, Streams::CH2_LIGHT_2_G));
  309. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 95.305)), module, Streams::CH2_LIGHT_3_G));
  310. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 89.585)), module, Streams::CH2_LIGHT_4_G));
  311. }
  312. void appendContextMenu(Menu* menu) override {
  313. Streams* module = dynamic_cast<Streams*>(this->module);
  314. struct LinkItem : MenuItem {
  315. Streams* module;
  316. void onAction(const event::Action& e) override {
  317. module->toggleLink();
  318. }
  319. };
  320. struct ChannelModeItem : MenuItem {
  321. Streams* module;
  322. int channel;
  323. int mode;
  324. void onAction(const event::Action& e) override {
  325. module->setChannelMode(channel, mode);
  326. }
  327. };
  328. struct MonitorModeItem : MenuItem {
  329. Streams* module;
  330. int mode;
  331. void onAction(const event::Action& e) override {
  332. module->setMonitorMode(mode);
  333. }
  334. };
  335. menu->addChild(new MenuSeparator);
  336. LinkItem* linkItem = createMenuItem<LinkItem>(
  337. "Link channels", CHECKMARK(module->linked()));
  338. linkItem->module = module;
  339. menu->addChild(linkItem);
  340. menu->addChild(new MenuSeparator);
  341. menu->addChild(createMenuLabel("Channel 1"));
  342. for (int i = 0; i < streams::kNumChannelModes; i++) {
  343. auto modeItem = createMenuItem<ChannelModeItem>(
  344. streams::kChannelModeTable[i].label, CHECKMARK(
  345. module->function(0) == streams::kChannelModeTable[i].function &&
  346. module->alternate(0) == streams::kChannelModeTable[i].alternate));
  347. modeItem->module = module;
  348. modeItem->channel = 0;
  349. modeItem->mode = i;
  350. menu->addChild(modeItem);
  351. }
  352. menu->addChild(new MenuSeparator);
  353. menu->addChild(createMenuLabel("Channel 2"));
  354. for (int i = 0; i < streams::kNumChannelModes; i++) {
  355. auto modeItem = createMenuItem<ChannelModeItem>(
  356. streams::kChannelModeTable[i].label, CHECKMARK(
  357. module->function(1) == streams::kChannelModeTable[i].function &&
  358. module->alternate(1) == streams::kChannelModeTable[i].alternate));
  359. modeItem->module = module;
  360. modeItem->channel = 1;
  361. modeItem->mode = i;
  362. menu->addChild(modeItem);
  363. }
  364. menu->addChild(new MenuSeparator);
  365. menu->addChild(createMenuLabel("Meter"));
  366. for (int i = 0; i < streams::kNumMonitorModes; i++) {
  367. auto modeItem = createMenuItem<MonitorModeItem>(
  368. streams::kMonitorModeTable[i].label, CHECKMARK(
  369. module->monitorMode() == streams::kMonitorModeTable[i].mode));
  370. modeItem->module = module;
  371. modeItem->mode = i;
  372. menu->addChild(modeItem);
  373. }
  374. }
  375. };
  376. Model* modelStreams = createModel<Streams, StreamsWidget>("Streams");