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.

407 lines
8.5KB

  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();
  49. void processMidi(std::vector<unsigned char> msg);
  50. json_t *toJson() {
  51. json_t *rootJ = json_object();
  52. addBaseJson(rootJ);
  53. return rootJ;
  54. }
  55. void fromJson(json_t *rootJ) {
  56. baseFromJson(rootJ);
  57. }
  58. void reset() {
  59. resetMidi();
  60. }
  61. void resetMidi();
  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 (pedal) {
  162. for (int i = 0; i < 4 ; i++) {
  163. if (activeKeys[i].pitch == data1 && activeKeys[i].gate) {
  164. activeKeys[i].vel = data2;
  165. return;
  166. }
  167. }
  168. }
  169. if (open.empty()) {
  170. for (int i = 0; i < 4; i++) {
  171. open.push_back(i);
  172. }
  173. }
  174. if (!activeKeys[0].gate && !activeKeys[1].gate &&
  175. !activeKeys[2].gate && !activeKeys[3].gate) {
  176. open.sort();
  177. }
  178. switch (mode) {
  179. case RESET:
  180. if (open.size() == 4 ) {
  181. for (int i = 0; i < 4; i++) {
  182. activeKeys[i].gate = false;
  183. open.push_back(i);
  184. }
  185. }
  186. break;
  187. case REASSIGN:
  188. open.push_back(open.front());
  189. break;
  190. case ROTATE:
  191. break;
  192. }
  193. activeKeys[open.front()].gate = true;
  194. activeKeys[open.front()].pitch = data1;
  195. activeKeys[open.front()].vel = data2;
  196. open.pop_front();
  197. return;
  198. }
  199. int QuadMIDIToCVInterface::getMode() const {
  200. return mode;
  201. }
  202. void QuadMIDIToCVInterface::setMode(int mode) {
  203. resetMidi();
  204. QuadMIDIToCVInterface::mode = mode;
  205. }
  206. struct ModeItem : MenuItem {
  207. int mode;
  208. QuadMIDIToCVInterface *module;
  209. void onAction() {
  210. module->setMode(mode);
  211. }
  212. };
  213. struct ModeChoice : ChoiceButton {
  214. QuadMIDIToCVInterface *module;
  215. const std::vector<std::string> modeNames = {"ROTATE", "RESET", "REASSIGN"};
  216. void onAction() {
  217. Menu *menu = gScene->createMenu();
  218. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  219. menu->box.size.x = box.size.x;
  220. for (unsigned long i = 0; i < modeNames.size(); i++) {
  221. ModeItem *modeItem = new ModeItem();
  222. modeItem->mode = i;
  223. modeItem->module = module;
  224. modeItem->text = modeNames[i];
  225. menu->pushChild(modeItem);
  226. }
  227. }
  228. void step() {
  229. text = modeNames[module->getMode()];
  230. }
  231. };
  232. QuadMidiToCVWidget::QuadMidiToCVWidget() {
  233. QuadMIDIToCVInterface *module = new QuadMIDIToCVInterface();
  234. setModule(module);
  235. box.size = Vec(15 * 16, 380);
  236. {
  237. Panel *panel = new LightPanel();
  238. panel->box.size = box.size;
  239. addChild(panel);
  240. }
  241. float margin = 5;
  242. float labelHeight = 15;
  243. float yPos = margin;
  244. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  245. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  246. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  247. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  248. {
  249. Label *label = new Label();
  250. label->box.pos = Vec(box.size.x - margin - 12 * 15, margin);
  251. label->text = "Quad MIDI to CV";
  252. addChild(label);
  253. yPos = labelHeight * 2;
  254. }
  255. addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0,
  256. 0.0));
  257. addChild(createLight<SmallLight<RedLight>>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT));
  258. {
  259. Label *label = new Label();
  260. label->box.pos = Vec(margin, yPos);
  261. label->text = "MIDI Interface";
  262. addChild(label);
  263. yPos += labelHeight + margin;
  264. MidiChoice *midiChoice = new MidiChoice();
  265. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  266. midiChoice->box.pos = Vec(margin, yPos);
  267. midiChoice->box.size.x = box.size.x - 10;
  268. addChild(midiChoice);
  269. yPos += midiChoice->box.size.y + margin;
  270. }
  271. {
  272. Label *label = new Label();
  273. label->box.pos = Vec(margin, yPos);
  274. label->text = "Channel";
  275. addChild(label);
  276. yPos += labelHeight + margin;
  277. ChannelChoice *channelChoice = new ChannelChoice();
  278. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  279. channelChoice->box.pos = Vec(margin, yPos);
  280. channelChoice->box.size.x = box.size.x - 10;
  281. addChild(channelChoice);
  282. yPos += channelChoice->box.size.y + margin;
  283. }
  284. {
  285. Label *label = new Label();
  286. label->box.pos = Vec(margin, yPos);
  287. label->text = "Mode";
  288. addChild(label);
  289. yPos += labelHeight + margin;
  290. ModeChoice *modeChoice = new ModeChoice();
  291. modeChoice->module = module;
  292. modeChoice->box.pos = Vec(margin, yPos);
  293. modeChoice->box.size.x = box.size.x - 10;
  294. addChild(modeChoice);
  295. yPos += modeChoice->box.size.y + margin + 15;
  296. }
  297. {
  298. Label *label = new Label();
  299. label->box.pos = Vec(84, yPos);
  300. label->text = "1";
  301. addChild(label);
  302. }
  303. {
  304. Label *label = new Label();
  305. label->box.pos = Vec(125, yPos);
  306. label->text = "2";
  307. addChild(label);
  308. }
  309. {
  310. Label *label = new Label();
  311. label->box.pos = Vec(164, yPos);
  312. label->text = "3";
  313. addChild(label);
  314. }
  315. {
  316. Label *label = new Label();
  317. label->box.pos = Vec(203, yPos);
  318. label->text = "4";
  319. addChild(label);
  320. }
  321. std::string labels[4] = {"1V/oct", "Gate", "Velocity", "Aftertouch"};
  322. yPos += labelHeight + margin * 2;
  323. for (int i = 0; i < 4; i++) {
  324. Label *label = new Label();
  325. label->box.pos = Vec(margin, yPos);
  326. label->text = labels[i];
  327. addChild(label);
  328. addOutput(createOutput<PJ3410Port>(Vec(2 * (40), yPos - 5), module, i * 4));
  329. addOutput(createOutput<PJ3410Port>(Vec(3 * (40), yPos - 5), module, i * 4 + 1));
  330. addOutput(createOutput<PJ3410Port>(Vec(4 * (40), yPos - 5), module, i * 4 + 2));
  331. addOutput(createOutput<PJ3410Port>(Vec(5 * (40), yPos - 5), module, i * 4 + 3));
  332. yPos += 40;
  333. }
  334. }
  335. void QuadMidiToCVWidget::step() {
  336. ModuleWidget::step();
  337. }