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.

431 lines
16KB

  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, "Channel 1 shape", "%", 0, 100);
  105. configParam(CH1_MOD_PARAM, 0.f, 1.f, 0.5f, "Channel 1 mod", "%", 0, 100);
  106. configParam(CH1_LEVEL_MOD_PARAM, 0.f, 1.f, 0.5f, "Channel 1 level mod", "%", 0, 100);
  107. configParam(CH2_SHAPE_PARAM, 0.f, 1.f, 0.5f, "Channel 2 shape", "%", 0, 100);
  108. configParam(CH2_MOD_PARAM, 0.f, 1.f, 0.5f, "Channel 2 mod", "%", 0, 100);
  109. configParam(CH2_LEVEL_MOD_PARAM, 0.f, 1.f, 0.5f, "Channel 2 level mod", "%", 0, 100);
  110. configParam(CH1_RESPONSE_PARAM, 0.f, 1.f, 0.5f, "Channel 1 response", "%", 0, 100);
  111. configParam(CH2_RESPONSE_PARAM, 0.f, 1.f, 0.5f, "Channel 2 response", "%", 0, 100);
  112. configButton(CH1_FUNCTION_BUTTON_PARAM, "Channel 1 function");
  113. configButton(CH2_FUNCTION_BUTTON_PARAM, "Channel 2 function");
  114. configButton(METERING_BUTTON_PARAM, "Meter");
  115. configInput(CH1_EXCITE_INPUT, "Channel 1 excite");
  116. configInput(CH1_SIGNAL_INPUT, "Channel 1");
  117. configInput(CH1_LEVEL_INPUT, "Channel 1 level");
  118. configInput(CH2_EXCITE_INPUT, "Channel 2 excite");
  119. configInput(CH2_SIGNAL_INPUT, "Channel 2");
  120. configInput(CH2_LEVEL_INPUT, "Channel 2 level");
  121. configOutput(CH1_SIGNAL_OUTPUT, "Channel 1");
  122. configOutput(CH2_SIGNAL_OUTPUT, "Channel 2");
  123. configBypass(CH1_SIGNAL_INPUT, CH1_SIGNAL_OUTPUT);
  124. configBypass(CH2_SIGNAL_INPUT, CH2_SIGNAL_OUTPUT);
  125. onReset();
  126. }
  127. void onReset() override {
  128. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  129. engines[c].Reset();
  130. }
  131. prevNumChannels = 1;
  132. onSampleRateChange();
  133. }
  134. void onSampleRateChange() override {
  135. float sampleRate = APP->engine->getSampleRate();
  136. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  137. engines[c].SetSampleRate(sampleRate);
  138. }
  139. }
  140. json_t* dataToJson() override {
  141. streams::UiSettings settings = engines[0].ui_settings();
  142. json_t* rootJ = json_object();
  143. json_object_set_new(rootJ, "function1", json_integer(settings.function[0]));
  144. json_object_set_new(rootJ, "function2", json_integer(settings.function[1]));
  145. json_object_set_new(rootJ, "alternate1", json_integer(settings.alternate[0]));
  146. json_object_set_new(rootJ, "alternate2", json_integer(settings.alternate[1]));
  147. json_object_set_new(rootJ, "monitorMode", json_integer(settings.monitor_mode));
  148. json_object_set_new(rootJ, "linked", json_integer(settings.linked));
  149. return rootJ;
  150. }
  151. void dataFromJson(json_t* rootJ) override {
  152. json_t* function1J = json_object_get(rootJ, "function1");
  153. json_t* function2J = json_object_get(rootJ, "function2");
  154. json_t* alternate1J = json_object_get(rootJ, "alternate1");
  155. json_t* alternate2J = json_object_get(rootJ, "alternate2");
  156. json_t* monitorModeJ = json_object_get(rootJ, "monitorMode");
  157. json_t* linkedJ = json_object_get(rootJ, "linked");
  158. streams::UiSettings settings = {};
  159. if (function1J)
  160. settings.function[0] = json_integer_value(function1J);
  161. if (function2J)
  162. settings.function[1] = json_integer_value(function2J);
  163. if (alternate1J)
  164. settings.alternate[0] = json_integer_value(alternate1J);
  165. if (alternate2J)
  166. settings.alternate[1] = json_integer_value(alternate2J);
  167. if (monitorModeJ)
  168. settings.monitor_mode = json_integer_value(monitorModeJ);
  169. if (linkedJ)
  170. settings.linked = json_integer_value(linkedJ);
  171. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  172. engines[c].ApplySettings(settings);
  173. }
  174. }
  175. void onRandomize() override {
  176. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  177. engines[c].Randomize();
  178. }
  179. }
  180. void setLinked(bool linked) {
  181. streams::UiSettings settings = engines[0].ui_settings();
  182. settings.linked = linked;
  183. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  184. engines[c].ApplySettings(settings);
  185. }
  186. }
  187. int getChannelMode(int channel) {
  188. streams::UiSettings settings = engines[0].ui_settings();
  189. // Search channel mode index in table
  190. for (int i = 0; i < streams::kNumChannelModes; i++) {
  191. if (settings.function[channel] == streams::kChannelModeTable[i].function
  192. && settings.alternate[channel] == streams::kChannelModeTable[i].alternate)
  193. return i;
  194. }
  195. return -1;
  196. }
  197. void setChannelMode(int channel, int mode_id) {
  198. streams::UiSettings settings = engines[0].ui_settings();
  199. settings.function[channel] = streams::kChannelModeTable[mode_id].function;
  200. settings.alternate[channel] = streams::kChannelModeTable[mode_id].alternate;
  201. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  202. engines[c].ApplySettings(settings);
  203. }
  204. }
  205. void setMonitorMode(int mode_id) {
  206. streams::UiSettings settings = engines[0].ui_settings();
  207. settings.monitor_mode = streams::kMonitorModeTable[mode_id].mode;
  208. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  209. engines[c].ApplySettings(settings);
  210. }
  211. }
  212. int function(int channel) {
  213. return engines[0].ui_settings().function[channel];
  214. }
  215. int alternate(int channel) {
  216. return engines[0].ui_settings().alternate[channel];
  217. }
  218. bool linked() {
  219. return engines[0].ui_settings().linked;
  220. }
  221. int monitorMode() {
  222. return engines[0].ui_settings().monitor_mode;
  223. }
  224. void process(const ProcessArgs& args) override {
  225. int numChannels = std::max(inputs[CH1_SIGNAL_INPUT].getChannels(), inputs[CH2_SIGNAL_INPUT].getChannels());
  226. numChannels = std::max(numChannels, 1);
  227. if (numChannels > prevNumChannels) {
  228. for (int c = prevNumChannels; c < numChannels; c++) {
  229. engines[c].SyncUI(engines[0]);
  230. }
  231. }
  232. prevNumChannels = numChannels;
  233. // Reuse the same frame object for multiple engines because the params
  234. // aren't touched.
  235. streams::StreamsEngine::Frame frame;
  236. frame.ch1.shape_knob = params[CH1_SHAPE_PARAM] .getValue();
  237. frame.ch1.mod_knob = params[CH1_MOD_PARAM] .getValue();
  238. frame.ch1.level_mod_knob = params[CH1_LEVEL_MOD_PARAM].getValue();
  239. frame.ch1.response_knob = params[CH1_RESPONSE_PARAM] .getValue();
  240. frame.ch2.shape_knob = params[CH2_SHAPE_PARAM] .getValue();
  241. frame.ch2.mod_knob = params[CH2_MOD_PARAM] .getValue();
  242. frame.ch2.level_mod_knob = params[CH2_LEVEL_MOD_PARAM].getValue();
  243. frame.ch2.response_knob = params[CH2_RESPONSE_PARAM] .getValue();
  244. frame.ch1.signal_in_connected = inputs[CH1_SIGNAL_INPUT].isConnected();
  245. frame.ch1.level_cv_connected = inputs[CH1_LEVEL_INPUT] .isConnected();
  246. frame.ch2.signal_in_connected = inputs[CH2_SIGNAL_INPUT].isConnected();
  247. frame.ch2.level_cv_connected = inputs[CH2_LEVEL_INPUT] .isConnected();
  248. frame.ch1.function_button = params[CH1_FUNCTION_BUTTON_PARAM].getValue();
  249. frame.ch2.function_button = params[CH2_FUNCTION_BUTTON_PARAM].getValue();
  250. frame.metering_button = params[METERING_BUTTON_PARAM].getValue();
  251. bool lights_updated = false;
  252. for (int c = 0; c < numChannels; c++) {
  253. frame.ch1.excite_in = inputs[CH1_EXCITE_INPUT].getPolyVoltage(c);
  254. frame.ch1.signal_in = inputs[CH1_SIGNAL_INPUT].getPolyVoltage(c);
  255. frame.ch1.level_cv = inputs[CH1_LEVEL_INPUT] .getPolyVoltage(c);
  256. frame.ch2.excite_in = inputs[CH2_EXCITE_INPUT].getPolyVoltage(c);
  257. frame.ch2.signal_in = inputs[CH2_SIGNAL_INPUT].getPolyVoltage(c);
  258. frame.ch2.level_cv = inputs[CH2_LEVEL_INPUT] .getPolyVoltage(c);
  259. engines[c].Process(frame);
  260. outputs[CH1_SIGNAL_OUTPUT].setVoltage(frame.ch1.signal_out, c);
  261. outputs[CH2_SIGNAL_OUTPUT].setVoltage(frame.ch2.signal_out, c);
  262. if (frame.lights_updated) {
  263. brightnesses[CH1_LIGHT_1_G][c] = frame.ch1.led_green[0];
  264. brightnesses[CH1_LIGHT_2_G][c] = frame.ch1.led_green[1];
  265. brightnesses[CH1_LIGHT_3_G][c] = frame.ch1.led_green[2];
  266. brightnesses[CH1_LIGHT_4_G][c] = frame.ch1.led_green[3];
  267. brightnesses[CH1_LIGHT_1_R][c] = frame.ch1.led_red[0];
  268. brightnesses[CH1_LIGHT_2_R][c] = frame.ch1.led_red[1];
  269. brightnesses[CH1_LIGHT_3_R][c] = frame.ch1.led_red[2];
  270. brightnesses[CH1_LIGHT_4_R][c] = frame.ch1.led_red[3];
  271. brightnesses[CH2_LIGHT_1_G][c] = frame.ch2.led_green[0];
  272. brightnesses[CH2_LIGHT_2_G][c] = frame.ch2.led_green[1];
  273. brightnesses[CH2_LIGHT_3_G][c] = frame.ch2.led_green[2];
  274. brightnesses[CH2_LIGHT_4_G][c] = frame.ch2.led_green[3];
  275. brightnesses[CH2_LIGHT_1_R][c] = frame.ch2.led_red[0];
  276. brightnesses[CH2_LIGHT_2_R][c] = frame.ch2.led_red[1];
  277. brightnesses[CH2_LIGHT_3_R][c] = frame.ch2.led_red[2];
  278. brightnesses[CH2_LIGHT_4_R][c] = frame.ch2.led_red[3];
  279. }
  280. lights_updated |= frame.lights_updated;
  281. }
  282. outputs[CH1_SIGNAL_OUTPUT].setChannels(numChannels);
  283. outputs[CH2_SIGNAL_OUTPUT].setChannels(numChannels);
  284. if (lights_updated) {
  285. // Drive lights according to maximum brightness across engines
  286. for (int i = 0; i < NUM_LIGHTS; i++) {
  287. float brightness = 0.f;
  288. for (int c = 0; c < numChannels; c++) {
  289. brightness = std::max(brightnesses[i][c], brightness);
  290. }
  291. lights[i].setBrightness(brightness);
  292. }
  293. }
  294. }
  295. };
  296. struct StreamsWidget : ModuleWidget {
  297. StreamsWidget(Streams* module) {
  298. setModule(module);
  299. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Streams.svg")));
  300. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  301. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  302. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  303. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  304. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 107.695)), module, Streams::CH1_SHAPE_PARAM));
  305. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 84.196)), module, Streams::CH1_MOD_PARAM));
  306. addParam(createParamCentered<Rogan1PSRed> (mm2px(Vec(11.065, 128.75 - 60.706)), module, Streams::CH1_LEVEL_MOD_PARAM));
  307. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 107.695)), module, Streams::CH2_SHAPE_PARAM));
  308. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 84.196)), module, Streams::CH2_MOD_PARAM));
  309. addParam(createParamCentered<Rogan1PSGreen>(mm2px(Vec(49.785, 128.75 - 60.706)), module, Streams::CH2_LEVEL_MOD_PARAM));
  310. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 68.006)), module, Streams::CH1_RESPONSE_PARAM));
  311. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 53.406)), module, Streams::CH2_RESPONSE_PARAM));
  312. addParam(createParamCentered<TL1105>(mm2px(Vec(24.715, 128.75 - 113.726)), module, Streams::CH1_FUNCTION_BUTTON_PARAM));
  313. addParam(createParamCentered<TL1105>(mm2px(Vec(36.135, 128.75 - 113.726)), module, Streams::CH2_FUNCTION_BUTTON_PARAM));
  314. addParam(createParamCentered<TL1105>(mm2px(Vec(30.425, 128.75 - 81.976)), module, Streams::METERING_BUTTON_PARAM));
  315. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 32.136)), module, Streams::CH1_EXCITE_INPUT));
  316. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 32.136)), module, Streams::CH1_SIGNAL_INPUT));
  317. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 17.526)), module, Streams::CH1_LEVEL_INPUT));
  318. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 32.136)), module, Streams::CH2_EXCITE_INPUT));
  319. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 32.136)), module, Streams::CH2_SIGNAL_INPUT));
  320. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 17.526)), module, Streams::CH2_LEVEL_INPUT));
  321. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 17.526)), module, Streams::CH1_SIGNAL_OUTPUT));
  322. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 17.526)), module, Streams::CH2_SIGNAL_OUTPUT));
  323. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 106.746)), module, Streams::CH1_LIGHT_1_G));
  324. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 101.026)), module, Streams::CH1_LIGHT_2_G));
  325. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 95.305)), module, Streams::CH1_LIGHT_3_G));
  326. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 89.585)), module, Streams::CH1_LIGHT_4_G));
  327. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 106.746)), module, Streams::CH2_LIGHT_1_G));
  328. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 101.026)), module, Streams::CH2_LIGHT_2_G));
  329. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 95.305)), module, Streams::CH2_LIGHT_3_G));
  330. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 89.585)), module, Streams::CH2_LIGHT_4_G));
  331. }
  332. void appendContextMenu(Menu* menu) override {
  333. Streams* module = dynamic_cast<Streams*>(this->module);
  334. menu->addChild(new MenuSeparator);
  335. menu->addChild(createBoolMenuItem("Link channels", "",
  336. [=]() {return module->linked();},
  337. [=](bool val) {module->setLinked(val);}
  338. ));
  339. std::vector<std::string> modeLabels;
  340. for (int i = 0; i < streams::kNumChannelModes; i++) {
  341. modeLabels.push_back(streams::kChannelModeTable[i].label);
  342. }
  343. for (int c = 0; c < 2; c++) {
  344. menu->addChild(createIndexSubmenuItem(string::f("Channel %d mode", c + 1), modeLabels,
  345. [=]() {return module->getChannelMode(c);},
  346. [=](int index) {module->setChannelMode(c, index);}
  347. ));
  348. }
  349. std::vector<std::string> meterLabels;
  350. for (int i = 0; i < streams::kNumMonitorModes; i++) {
  351. meterLabels.push_back(streams::kMonitorModeTable[i].label);
  352. }
  353. menu->addChild(createIndexSubmenuItem("Meter", meterLabels,
  354. [=]() {return module->monitorMode();},
  355. [=](int index) {module->setMonitorMode(index);}
  356. ));
  357. }
  358. };
  359. Model* modelStreams = createModel<Streams, StreamsWidget>("Streams");