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.

409 lines
8.7KB

  1. #include <list>
  2. #include <algorithm>
  3. #include "rtmidi/RtMidi.h"
  4. #include "core.hpp"
  5. #include "MidiIO.hpp"
  6. #include "dsp/digital.hpp"
  7. struct MidiKey {
  8. int pitch = 60;
  9. int at = 0; // aftertouch
  10. int vel = 0; // velocity
  11. bool gate = false;
  12. };
  13. struct QuadMIDIToCVInterface : MidiIO, Module {
  14. enum ParamIds {
  15. RESET_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. NUM_INPUTS
  20. };
  21. enum OutputIds {
  22. PITCH_OUTPUT = 0,
  23. GATE_OUTPUT = 4,
  24. VELOCITY_OUTPUT = 8,
  25. AT_OUTPUT = 12,
  26. NUM_OUTPUTS = 16
  27. };
  28. enum LightIds {
  29. RESET_LIGHT,
  30. NUM_LIGHTS
  31. };
  32. enum Modes {
  33. ROTATE,
  34. RESET,
  35. REASSIGN
  36. };
  37. bool pedal = false;
  38. int mode = REASSIGN;
  39. int getMode() const;
  40. void setMode(int mode);
  41. MidiKey activeKeys[4];
  42. std::list<int> open;
  43. SchmittTrigger resetTrigger;
  44. QuadMIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  45. }
  46. ~QuadMIDIToCVInterface() {
  47. };
  48. void step() override;
  49. void processMidi(std::vector<unsigned char> msg);
  50. json_t *toJson() override {
  51. json_t *rootJ = json_object();
  52. addBaseJson(rootJ);
  53. return rootJ;
  54. }
  55. void fromJson(json_t *rootJ) override {
  56. baseFromJson(rootJ);
  57. }
  58. void reset() override {
  59. resetMidi();
  60. }
  61. void resetMidi() override;
  62. };
  63. void QuadMIDIToCVInterface::resetMidi() {
  64. for (int i = 0; i < 4; i++) {
  65. outputs[GATE_OUTPUT + i].value = 0.0;
  66. activeKeys[i].gate = false;
  67. activeKeys[i].vel = 0;
  68. activeKeys[i].at = 0;
  69. }
  70. open.clear();
  71. pedal = false;
  72. lights[RESET_LIGHT].value = 1.0;
  73. }
  74. void QuadMIDIToCVInterface::step() {
  75. if (isPortOpen()) {
  76. std::vector<unsigned char> message;
  77. int msgsProcessed = 0;
  78. // midiIn->getMessage returns empty vector if there are no messages in the queue
  79. // NOTE: For the quadmidi we will process max 4 midi messages per step to avoid
  80. // problems with parallel input.
  81. getMessage(&message);
  82. while (msgsProcessed < 4 && message.size() > 0) {
  83. processMidi(message);
  84. getMessage(&message);
  85. msgsProcessed++;
  86. }
  87. }
  88. for (int i = 0; i < 4; i++) {
  89. outputs[GATE_OUTPUT + i].value = activeKeys[i].gate ? 10.0 : 0;
  90. outputs[PITCH_OUTPUT + i].value = (activeKeys[i].pitch - 60) / 12.0;
  91. outputs[VELOCITY_OUTPUT + i].value = activeKeys[i].vel / 127.0 * 10.0;
  92. outputs[AT_OUTPUT + i].value = activeKeys[i].at / 127.0 * 10.0;
  93. }
  94. if (resetTrigger.process(params[RESET_PARAM].value)) {
  95. resetMidi();
  96. return;
  97. }
  98. lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light
  99. }
  100. void QuadMIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
  101. int channel = msg[0] & 0xf;
  102. int status = (msg[0] >> 4) & 0xf;
  103. int data1 = msg[1];
  104. int data2 = msg[2];
  105. bool gate;
  106. // Filter channels
  107. if (this->channel >= 0 && this->channel != channel)
  108. return;
  109. switch (status) {
  110. // note off
  111. case 0x8: {
  112. gate = false;
  113. }
  114. break;
  115. case 0x9: // note on
  116. if (data2 > 0) {
  117. gate = true;
  118. } else {
  119. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  120. gate = false;
  121. }
  122. break;
  123. case 0xa: // channel aftertouch
  124. for (int i = 0; i < 4; i++) {
  125. if (activeKeys[i].pitch == data1) {
  126. activeKeys[i].at = data2;
  127. }
  128. }
  129. return;
  130. case 0xb: // cc
  131. if (data1 == 0x40) { // pedal
  132. pedal = (data2 >= 64);
  133. if (!pedal) {
  134. open.clear();
  135. for (int i = 0; i < 4; i++) {
  136. activeKeys[i].gate = false;
  137. open.push_back(i);
  138. }
  139. }
  140. }
  141. return;
  142. default:
  143. return;
  144. }
  145. if (pedal && !gate) {
  146. return;
  147. }
  148. if (!gate) {
  149. for (int i = 0; i < 4; i++) {
  150. if (activeKeys[i].pitch == data1) {
  151. activeKeys[i].gate = false;
  152. activeKeys[i].vel = data2;
  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 < 4; i++) {
  163. open.push_back(i);
  164. }
  165. }
  166. if (!activeKeys[0].gate && !activeKeys[1].gate &&
  167. !activeKeys[2].gate && !activeKeys[3].gate) {
  168. open.sort();
  169. }
  170. switch (mode) {
  171. case RESET:
  172. if (open.size() >= 4) {
  173. for (int i = 0; i < 4; i++) {
  174. activeKeys[i].gate = false;
  175. open.push_back(i);
  176. }
  177. }
  178. break;
  179. case REASSIGN:
  180. open.push_back(open.front());
  181. break;
  182. case ROTATE:
  183. break;
  184. }
  185. int next = open.front();
  186. open.pop_front();
  187. for (int i = 0; i < 4; i++) {
  188. if (activeKeys[i].pitch == data1 && activeKeys[i].gate) {
  189. activeKeys[i].vel = data2;
  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. activeKeys[next].vel = data2;
  199. }
  200. int QuadMIDIToCVInterface::getMode() const {
  201. return mode;
  202. }
  203. void QuadMIDIToCVInterface::setMode(int mode) {
  204. resetMidi();
  205. QuadMIDIToCVInterface::mode = mode;
  206. }
  207. struct ModeItem : MenuItem {
  208. int mode;
  209. QuadMIDIToCVInterface *module;
  210. void onAction(EventAction &e) {
  211. module->setMode(mode);
  212. }
  213. };
  214. struct ModeChoice : ChoiceButton {
  215. QuadMIDIToCVInterface *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. QuadMidiToCVWidget::QuadMidiToCVWidget() {
  234. QuadMIDIToCVInterface *module = new QuadMIDIToCVInterface();
  235. setModule(module);
  236. box.size = Vec(15 * 16, 380);
  237. {
  238. Panel *panel = new LightPanel();
  239. panel->box.size = box.size;
  240. addChild(panel);
  241. }
  242. float margin = 5;
  243. float labelHeight = 15;
  244. float yPos = margin;
  245. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  246. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  247. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  248. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  249. {
  250. Label *label = new Label();
  251. label->box.pos = Vec(box.size.x - margin - 12 * 15, margin);
  252. label->text = "Quad MIDI to CV";
  253. addChild(label);
  254. yPos = labelHeight * 2;
  255. }
  256. addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0,
  257. 0.0));
  258. addChild(createLight<SmallLight<RedLight>>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT));
  259. {
  260. Label *label = new Label();
  261. label->box.pos = Vec(margin, yPos);
  262. label->text = "MIDI Interface";
  263. addChild(label);
  264. yPos += labelHeight + margin;
  265. MidiChoice *midiChoice = new MidiChoice();
  266. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  267. midiChoice->box.pos = Vec(margin, yPos);
  268. midiChoice->box.size.x = box.size.x - 10;
  269. addChild(midiChoice);
  270. yPos += midiChoice->box.size.y + margin;
  271. }
  272. {
  273. Label *label = new Label();
  274. label->box.pos = Vec(margin, yPos);
  275. label->text = "Channel";
  276. addChild(label);
  277. yPos += labelHeight + margin;
  278. ChannelChoice *channelChoice = new ChannelChoice();
  279. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  280. channelChoice->box.pos = Vec(margin, yPos);
  281. channelChoice->box.size.x = box.size.x - 10;
  282. addChild(channelChoice);
  283. yPos += channelChoice->box.size.y + margin;
  284. }
  285. {
  286. Label *label = new Label();
  287. label->box.pos = Vec(margin, yPos);
  288. label->text = "Mode";
  289. addChild(label);
  290. yPos += labelHeight + margin;
  291. ModeChoice *modeChoice = new ModeChoice();
  292. modeChoice->module = module;
  293. modeChoice->box.pos = Vec(margin, yPos);
  294. modeChoice->box.size.x = box.size.x - 10;
  295. addChild(modeChoice);
  296. yPos += modeChoice->box.size.y + margin + 15;
  297. }
  298. {
  299. Label *label = new Label();
  300. label->box.pos = Vec(84, yPos);
  301. label->text = "1";
  302. addChild(label);
  303. }
  304. {
  305. Label *label = new Label();
  306. label->box.pos = Vec(125, yPos);
  307. label->text = "2";
  308. addChild(label);
  309. }
  310. {
  311. Label *label = new Label();
  312. label->box.pos = Vec(164, yPos);
  313. label->text = "3";
  314. addChild(label);
  315. }
  316. {
  317. Label *label = new Label();
  318. label->box.pos = Vec(203, yPos);
  319. label->text = "4";
  320. addChild(label);
  321. }
  322. std::string labels[4] = {"1V/oct", "Gate", "Velocity", "Aftertouch"};
  323. yPos += labelHeight + margin * 2;
  324. for (int i = 0; i < 4; i++) {
  325. Label *label = new Label();
  326. label->box.pos = Vec(margin, yPos);
  327. label->text = labels[i];
  328. addChild(label);
  329. addOutput(createOutput<PJ3410Port>(Vec(2 * (40), yPos - 5), module, i * 4));
  330. addOutput(createOutput<PJ3410Port>(Vec(3 * (40), yPos - 5), module, i * 4 + 1));
  331. addOutput(createOutput<PJ3410Port>(Vec(4 * (40), yPos - 5), module, i * 4 + 2));
  332. addOutput(createOutput<PJ3410Port>(Vec(5 * (40), yPos - 5), module, i * 4 + 3));
  333. yPos += 40;
  334. }
  335. }
  336. void QuadMidiToCVWidget::step() {
  337. ModuleWidget::step();
  338. }