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.

394 lines
8.4KB

  1. #if 0
  2. //============================================================================================================
  3. //!
  4. //! \file MIDI-G1.cpp
  5. //!
  6. //! \brief MIDI-G1 is a six voice cut-down clone of the VCV Rack Core module 'Quad MIDI-to-CV Interface'.
  7. //!
  8. //============================================================================================================
  9. #include <list>
  10. #include <algorithm>
  11. #include "rtmidi/RtMidi.h"
  12. #include "core.hpp"
  13. #include "MidiIO.hpp"
  14. #include "dsp/digital.hpp"
  15. #include "Gratrix.hpp"
  16. namespace rack_plugin_Gratrix {
  17. struct Key {
  18. int pitch = 60;
  19. int retriggerC = 0;
  20. bool gate = false;
  21. };
  22. struct Interface : MidiIO, Module {
  23. enum ParamIds {
  24. RESET_PARAM,
  25. NUM_PARAMS
  26. };
  27. enum InputIds {
  28. NUM_INPUTS
  29. };
  30. enum OutputIds {
  31. PITCH_OUTPUT, // N
  32. GATE_OUTPUT, // N
  33. NUM_OUTPUTS,
  34. OFF_OUTPUTS = PITCH_OUTPUT
  35. };
  36. enum LightIds {
  37. RESET_LIGHT,
  38. NUM_LIGHTS
  39. };
  40. enum Modes {
  41. ROTATE,
  42. RESET,
  43. REASSIGN
  44. };
  45. static constexpr std::size_t omap(std::size_t port, std::size_t bank)
  46. {
  47. return port + bank * NUM_OUTPUTS;
  48. }
  49. bool pedal = false;
  50. int mode = REASSIGN;
  51. int getMode() const;
  52. void setMode(int mode);
  53. Key activeKeys[GTX__N];
  54. std::list<int> open;
  55. SchmittTrigger resetTrigger;
  56. Interface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, GTX__N * (NUM_OUTPUTS - OFF_OUTPUTS) + OFF_OUTPUTS, NUM_LIGHTS) {
  57. }
  58. ~Interface() {
  59. };
  60. void step() override;
  61. void processMidi(std::vector<unsigned char> msg);
  62. json_t *toJson() override {
  63. json_t *rootJ = json_object();
  64. addBaseJson(rootJ);
  65. return rootJ;
  66. }
  67. void fromJson(json_t *rootJ) override {
  68. baseFromJson(rootJ);
  69. }
  70. void reset() override {
  71. resetMidi();
  72. }
  73. void resetMidi() override;
  74. };
  75. void Interface::resetMidi() {
  76. for (int i = 0; i < GTX__N; i++) {
  77. outputs[GATE_OUTPUT + i].value = 0.0;
  78. activeKeys[i].gate = false;
  79. }
  80. open.clear();
  81. pedal = false;
  82. lights[RESET_LIGHT].value = 1.0;
  83. }
  84. void Interface::step() {
  85. if (isPortOpen()) {
  86. std::vector<unsigned char> message;
  87. int msgsProcessed = 0;
  88. // midiIn->getMessage returns empty vector if there are no messages in the queue
  89. // NOTE: For the quadmidi we will process max GTX__N midi messages per step to avoid
  90. // problems with parallel input.
  91. getMessage(&message);
  92. while (msgsProcessed < GTX__N && message.size() > 0) {
  93. processMidi(message);
  94. getMessage(&message);
  95. msgsProcessed++;
  96. }
  97. }
  98. for (int i = 0; i < GTX__N; i++) {
  99. outputs[omap( GATE_OUTPUT, i)].value = activeKeys[i].gate ? 10.0 : 0;
  100. outputs[omap( PITCH_OUTPUT, i)].value = (activeKeys[i].pitch - 60) / 12.0;
  101. }
  102. if (resetTrigger.process(params[RESET_PARAM].value)) {
  103. resetMidi();
  104. return;
  105. }
  106. lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light
  107. }
  108. void Interface::processMidi(std::vector<unsigned char> msg) {
  109. int channel = msg[0] & 0xf;
  110. int status = (msg[0] >> 4) & 0xf;
  111. int data1 = msg[1];
  112. int data2 = msg[2];
  113. bool gate;
  114. // Filter channels
  115. if (this->channel >= 0 && this->channel != channel)
  116. return;
  117. switch (status) {
  118. // note off
  119. case 0x8: {
  120. gate = false;
  121. }
  122. break;
  123. case 0x9: // note on
  124. if (data2 > 0) {
  125. gate = true;
  126. } else {
  127. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  128. gate = false;
  129. }
  130. break;
  131. case 0xb: // cc
  132. if (data1 == 0x40) { // pedal
  133. pedal = (data2 >= 64);
  134. if (!pedal) {
  135. open.clear();
  136. for (int i = 0; i < GTX__N; i++) {
  137. activeKeys[i].gate = false;
  138. open.push_back(i);
  139. }
  140. }
  141. }
  142. return;
  143. default:
  144. return;
  145. }
  146. if (pedal && !gate) {
  147. return;
  148. }
  149. if (!gate) {
  150. for (int i = 0; i < GTX__N; i++) {
  151. if (activeKeys[i].pitch == data1) {
  152. activeKeys[i].gate = false;
  153. if (std::find(open.begin(), open.end(), i) != open.end()) {
  154. open.remove(i);
  155. }
  156. open.push_front(i);
  157. }
  158. }
  159. return;
  160. }
  161. if (open.empty()) {
  162. for (int i = 0; i < GTX__N; i++) {
  163. open.push_back(i);
  164. }
  165. }
  166. if (!activeKeys[0].gate && !activeKeys[1].gate &&
  167. !activeKeys[2].gate && !activeKeys[3].gate &&
  168. !activeKeys[4].gate && !activeKeys[5].gate) {
  169. open.sort();
  170. }
  171. switch (mode) {
  172. case RESET:
  173. if (open.size() == GTX__N ) {
  174. for (int i = 0; i < GTX__N; i++) {
  175. activeKeys[i].gate = false;
  176. open.push_back(i);
  177. }
  178. }
  179. break;
  180. case REASSIGN:
  181. open.push_back(open.front());
  182. break;
  183. case ROTATE:
  184. break;
  185. }
  186. int next = open.front();
  187. open.pop_front();
  188. for (int i = 0; i < GTX__N; i++) {
  189. if (activeKeys[i].pitch == data1 && activeKeys[i].gate) {
  190. if (std::find(open.begin(), open.end(), i) != open.end())
  191. open.remove(i);
  192. open.push_front(i);
  193. activeKeys[i].gate = false;
  194. }
  195. }
  196. activeKeys[next].gate = true;
  197. activeKeys[next].pitch = data1;
  198. }
  199. int Interface::getMode() const {
  200. return mode;
  201. }
  202. void Interface::setMode(int mode) {
  203. resetMidi();
  204. Interface::mode = mode;
  205. }
  206. struct ModeItem : MenuItem {
  207. int mode;
  208. Interface *module;
  209. void onAction(EventAction &e) {
  210. module->setMode(mode);
  211. }
  212. };
  213. //============================================================================================================
  214. struct ModeChoice : ChoiceButton {
  215. Interface *module;
  216. const std::vector<std::string> modeNames = {"Rotate", "Reset", "Reassign"};
  217. void onAction(EventAction &e) {
  218. Menu *menu = gScene->createMenu();
  219. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  220. menu->box.size.x = box.size.x;
  221. for (unsigned long i = 0; i < modeNames.size(); i++) {
  222. ModeItem *modeItem = new ModeItem();
  223. modeItem->mode = i;
  224. modeItem->module = module;
  225. modeItem->text = modeNames[i];
  226. menu->pushChild(modeItem);
  227. }
  228. }
  229. void step() {
  230. text = modeNames[module->getMode()];
  231. }
  232. };
  233. //============================================================================================================
  234. //! \brief Menu for selection the MIDI device.
  235. //!
  236. //! This code derives from MidiIO.{cpp/hpp} MidiChoice altered so the menus fit the MIDI-G1 panel width.
  237. struct MidiChoice2 : MidiChoice
  238. {
  239. void step() override
  240. {
  241. if (midiModule->getDeviceName() == "")
  242. {
  243. text = "No Dev.";
  244. return;
  245. }
  246. std::string name = midiModule->getDeviceName();
  247. text = ellipsize(name, 9);
  248. }
  249. };
  250. //============================================================================================================
  251. Widget::Widget()
  252. {
  253. GTX__WIDGET();
  254. Interface *module = new Interface();
  255. setModule(module);
  256. box.size = Vec(6*15, 380);
  257. #if GTX__SAVE_SVG
  258. {
  259. PanelGen pg(assetPlugin(plugin, "build/res/MIDI-G1.svg"), box.size, "MIDI-G1");
  260. pg.but2(-0.3, 0.4, "RESET");
  261. pg.bus_out(0, 1, "GATE");
  262. pg.bus_out(0, 2, "V/OCT");
  263. }
  264. #endif
  265. {
  266. SVGPanel *panel = new SVGPanel();
  267. panel->box.size = box.size;
  268. panel->setBackground(SVG::load(assetPlugin(plugin, "res/MIDI-G1.svg")));
  269. addChild(panel);
  270. }
  271. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  272. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  273. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  274. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  275. addParam(createParam<LEDButton>( but(fx(-0.3), fy(0.4)), module, Interface::RESET_PARAM, 0.0, 1.0, 0.0));
  276. addChild(createLight<SmallLight<RedLight>>(l_s(fx(-0.3), fy(0.4)), module, Interface::RESET_LIGHT));
  277. {
  278. float margin = 8;
  279. float yPos = 42;
  280. {
  281. MidiChoice2 *midiChoice = new MidiChoice2();
  282. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  283. midiChoice->box.pos = Vec(margin, yPos);
  284. midiChoice->box.size.x = box.size.x - margin * 2;
  285. addChild(midiChoice);
  286. yPos += midiChoice->box.size.y + margin;
  287. }
  288. {
  289. ChannelChoice *channelChoice = new ChannelChoice();
  290. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  291. channelChoice->box.pos = Vec(margin, yPos);
  292. channelChoice->box.size.x = box.size.x - margin * 2;
  293. addChild(channelChoice);
  294. yPos += channelChoice->box.size.y + margin;
  295. }
  296. {
  297. ModeChoice *modeChoice = new ModeChoice();
  298. modeChoice->module = module;
  299. modeChoice->box.pos = Vec(margin, yPos);
  300. modeChoice->box.size.x = box.size.x - margin * 2;
  301. addChild(modeChoice);
  302. }
  303. }
  304. for (std::size_t i=0; i<GTX__N; ++i)
  305. {
  306. addOutput(createOutputGTX<PortOutMed>(Vec(px(0, i), py(2, i)), module, Interface::omap(Interface:: PITCH_OUTPUT, i)));
  307. addOutput(createOutputGTX<PortOutMed>(Vec(px(0, i), py(1, i)), module, Interface::omap(Interface:: GATE_OUTPUT, i)));
  308. }
  309. }
  310. void Widget::step()
  311. {
  312. ModuleWidget::step();
  313. }
  314. } // namespace rack_plugin_Gratrix
  315. #endif