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.

414 lines
9.2KB

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