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.

418 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, "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 setLinked(bool linked) {
  171. streams::UiSettings settings = engines[0].ui_settings();
  172. settings.linked = linked;
  173. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  174. engines[c].ApplySettings(settings);
  175. }
  176. }
  177. int getChannelMode(int channel) {
  178. streams::UiSettings settings = engines[0].ui_settings();
  179. // Search channel mode index in table
  180. for (int i = 0; i < streams::kNumChannelModes; i++) {
  181. if (settings.function[channel] == streams::kChannelModeTable[i].function
  182. && settings.alternate[channel] == streams::kChannelModeTable[i].alternate)
  183. return i;
  184. }
  185. return -1;
  186. }
  187. void setChannelMode(int channel, int mode_id) {
  188. streams::UiSettings settings = engines[0].ui_settings();
  189. settings.function[channel] = streams::kChannelModeTable[mode_id].function;
  190. settings.alternate[channel] = streams::kChannelModeTable[mode_id].alternate;
  191. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  192. engines[c].ApplySettings(settings);
  193. }
  194. }
  195. void setMonitorMode(int mode_id) {
  196. streams::UiSettings settings = engines[0].ui_settings();
  197. settings.monitor_mode = streams::kMonitorModeTable[mode_id].mode;
  198. for (int c = 0; c < PORT_MAX_CHANNELS; c++) {
  199. engines[c].ApplySettings(settings);
  200. }
  201. }
  202. int function(int channel) {
  203. return engines[0].ui_settings().function[channel];
  204. }
  205. int alternate(int channel) {
  206. return engines[0].ui_settings().alternate[channel];
  207. }
  208. bool linked() {
  209. return engines[0].ui_settings().linked;
  210. }
  211. int monitorMode() {
  212. return engines[0].ui_settings().monitor_mode;
  213. }
  214. void process(const ProcessArgs& args) override {
  215. int numChannels = std::max(inputs[CH1_SIGNAL_INPUT].getChannels(), inputs[CH2_SIGNAL_INPUT].getChannels());
  216. numChannels = std::max(numChannels, 1);
  217. if (numChannels > prevNumChannels) {
  218. for (int c = prevNumChannels; c < numChannels; c++) {
  219. engines[c].SyncUI(engines[0]);
  220. }
  221. }
  222. prevNumChannels = numChannels;
  223. // Reuse the same frame object for multiple engines because the params
  224. // aren't touched.
  225. streams::StreamsEngine::Frame frame;
  226. frame.ch1.shape_knob = params[CH1_SHAPE_PARAM] .getValue();
  227. frame.ch1.mod_knob = params[CH1_MOD_PARAM] .getValue();
  228. frame.ch1.level_mod_knob = params[CH1_LEVEL_MOD_PARAM].getValue();
  229. frame.ch1.response_knob = params[CH1_RESPONSE_PARAM] .getValue();
  230. frame.ch2.shape_knob = params[CH2_SHAPE_PARAM] .getValue();
  231. frame.ch2.mod_knob = params[CH2_MOD_PARAM] .getValue();
  232. frame.ch2.level_mod_knob = params[CH2_LEVEL_MOD_PARAM].getValue();
  233. frame.ch2.response_knob = params[CH2_RESPONSE_PARAM] .getValue();
  234. frame.ch1.signal_in_connected = inputs[CH1_SIGNAL_INPUT].isConnected();
  235. frame.ch1.level_cv_connected = inputs[CH1_LEVEL_INPUT] .isConnected();
  236. frame.ch2.signal_in_connected = inputs[CH2_SIGNAL_INPUT].isConnected();
  237. frame.ch2.level_cv_connected = inputs[CH2_LEVEL_INPUT] .isConnected();
  238. frame.ch1.function_button = params[CH1_FUNCTION_BUTTON_PARAM].getValue();
  239. frame.ch2.function_button = params[CH2_FUNCTION_BUTTON_PARAM].getValue();
  240. frame.metering_button = params[METERING_BUTTON_PARAM].getValue();
  241. bool lights_updated = false;
  242. for (int c = 0; c < numChannels; c++) {
  243. frame.ch1.excite_in = inputs[CH1_EXCITE_INPUT].getPolyVoltage(c);
  244. frame.ch1.signal_in = inputs[CH1_SIGNAL_INPUT].getPolyVoltage(c);
  245. frame.ch1.level_cv = inputs[CH1_LEVEL_INPUT] .getPolyVoltage(c);
  246. frame.ch2.excite_in = inputs[CH2_EXCITE_INPUT].getPolyVoltage(c);
  247. frame.ch2.signal_in = inputs[CH2_SIGNAL_INPUT].getPolyVoltage(c);
  248. frame.ch2.level_cv = inputs[CH2_LEVEL_INPUT] .getPolyVoltage(c);
  249. engines[c].Process(frame);
  250. outputs[CH1_SIGNAL_OUTPUT].setVoltage(frame.ch1.signal_out, c);
  251. outputs[CH2_SIGNAL_OUTPUT].setVoltage(frame.ch2.signal_out, c);
  252. if (frame.lights_updated) {
  253. brightnesses[CH1_LIGHT_1_G][c] = frame.ch1.led_green[0];
  254. brightnesses[CH1_LIGHT_2_G][c] = frame.ch1.led_green[1];
  255. brightnesses[CH1_LIGHT_3_G][c] = frame.ch1.led_green[2];
  256. brightnesses[CH1_LIGHT_4_G][c] = frame.ch1.led_green[3];
  257. brightnesses[CH1_LIGHT_1_R][c] = frame.ch1.led_red[0];
  258. brightnesses[CH1_LIGHT_2_R][c] = frame.ch1.led_red[1];
  259. brightnesses[CH1_LIGHT_3_R][c] = frame.ch1.led_red[2];
  260. brightnesses[CH1_LIGHT_4_R][c] = frame.ch1.led_red[3];
  261. brightnesses[CH2_LIGHT_1_G][c] = frame.ch2.led_green[0];
  262. brightnesses[CH2_LIGHT_2_G][c] = frame.ch2.led_green[1];
  263. brightnesses[CH2_LIGHT_3_G][c] = frame.ch2.led_green[2];
  264. brightnesses[CH2_LIGHT_4_G][c] = frame.ch2.led_green[3];
  265. brightnesses[CH2_LIGHT_1_R][c] = frame.ch2.led_red[0];
  266. brightnesses[CH2_LIGHT_2_R][c] = frame.ch2.led_red[1];
  267. brightnesses[CH2_LIGHT_3_R][c] = frame.ch2.led_red[2];
  268. brightnesses[CH2_LIGHT_4_R][c] = frame.ch2.led_red[3];
  269. }
  270. lights_updated |= frame.lights_updated;
  271. }
  272. outputs[CH1_SIGNAL_OUTPUT].setChannels(numChannels);
  273. outputs[CH2_SIGNAL_OUTPUT].setChannels(numChannels);
  274. if (lights_updated) {
  275. // Drive lights according to maximum brightness across engines
  276. for (int i = 0; i < NUM_LIGHTS; i++) {
  277. float brightness = 0.f;
  278. for (int c = 0; c < numChannels; c++) {
  279. brightness = std::max(brightnesses[i][c], brightness);
  280. }
  281. lights[i].setBrightness(brightness);
  282. }
  283. }
  284. }
  285. };
  286. struct StreamsWidget : ModuleWidget {
  287. StreamsWidget(Streams* module) {
  288. setModule(module);
  289. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Streams.svg")));
  290. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  291. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  292. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  293. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  294. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 107.695)), module, Streams::CH1_SHAPE_PARAM));
  295. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.065, 128.75 - 84.196)), module, Streams::CH1_MOD_PARAM));
  296. addParam(createParamCentered<Rogan1PSRed> (mm2px(Vec(11.065, 128.75 - 60.706)), module, Streams::CH1_LEVEL_MOD_PARAM));
  297. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 107.695)), module, Streams::CH2_SHAPE_PARAM));
  298. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(49.785, 128.75 - 84.196)), module, Streams::CH2_MOD_PARAM));
  299. addParam(createParamCentered<Rogan1PSGreen>(mm2px(Vec(49.785, 128.75 - 60.706)), module, Streams::CH2_LEVEL_MOD_PARAM));
  300. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 68.006)), module, Streams::CH1_RESPONSE_PARAM));
  301. addParam(createParamCentered<Trimpot>(mm2px(Vec(30.425, 128.75 - 53.406)), module, Streams::CH2_RESPONSE_PARAM));
  302. addParam(createParamCentered<TL1105>(mm2px(Vec(24.715, 128.75 - 113.726)), module, Streams::CH1_FUNCTION_BUTTON_PARAM));
  303. addParam(createParamCentered<TL1105>(mm2px(Vec(36.135, 128.75 - 113.726)), module, Streams::CH2_FUNCTION_BUTTON_PARAM));
  304. addParam(createParamCentered<TL1105>(mm2px(Vec(30.425, 128.75 - 81.976)), module, Streams::METERING_BUTTON_PARAM));
  305. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 32.136)), module, Streams::CH1_EXCITE_INPUT));
  306. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 32.136)), module, Streams::CH1_SIGNAL_INPUT));
  307. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.506, 128.75 - 17.526)), module, Streams::CH1_LEVEL_INPUT));
  308. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 32.136)), module, Streams::CH2_EXCITE_INPUT));
  309. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 32.136)), module, Streams::CH2_SIGNAL_INPUT));
  310. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.335, 128.75 - 17.526)), module, Streams::CH2_LEVEL_INPUT));
  311. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(23.116, 128.75 - 17.526)), module, Streams::CH1_SIGNAL_OUTPUT));
  312. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(37.726, 128.75 - 17.526)), module, Streams::CH2_SIGNAL_OUTPUT));
  313. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 106.746)), module, Streams::CH1_LIGHT_1_G));
  314. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 101.026)), module, Streams::CH1_LIGHT_2_G));
  315. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 95.305)), module, Streams::CH1_LIGHT_3_G));
  316. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(24.715, 128.75 - 89.585)), module, Streams::CH1_LIGHT_4_G));
  317. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 106.746)), module, Streams::CH2_LIGHT_1_G));
  318. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 101.026)), module, Streams::CH2_LIGHT_2_G));
  319. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 95.305)), module, Streams::CH2_LIGHT_3_G));
  320. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(36.135, 128.75 - 89.585)), module, Streams::CH2_LIGHT_4_G));
  321. }
  322. void appendContextMenu(Menu* menu) override {
  323. Streams* module = dynamic_cast<Streams*>(this->module);
  324. menu->addChild(new MenuSeparator);
  325. menu->addChild(createBoolMenuItem("Link channels",
  326. [=]() {return module->linked();},
  327. [=](bool val) {module->setLinked(val);}
  328. ));
  329. std::vector<std::string> modeLabels;
  330. for (int i = 0; i < streams::kNumChannelModes; i++) {
  331. modeLabels.push_back(streams::kChannelModeTable[i].label);
  332. }
  333. for (int c = 0; c < 2; c++) {
  334. menu->addChild(createIndexSubmenuItem(string::f("Channel %d mode", c + 1), modeLabels,
  335. [=]() {return module->getChannelMode(c);},
  336. [=](int index) {module->setChannelMode(c, index);}
  337. ));
  338. }
  339. std::vector<std::string> meterLabels;
  340. for (int i = 0; i < streams::kNumMonitorModes; i++) {
  341. meterLabels.push_back(streams::kMonitorModeTable[i].label);
  342. }
  343. menu->addChild(createIndexSubmenuItem("Meter", meterLabels,
  344. [=]() {return module->monitorMode();},
  345. [=](int index) {module->setMonitorMode(index);}
  346. ));
  347. }
  348. };
  349. Model* modelStreams = createModel<Streams, StreamsWidget>("Streams");