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.

373 lines
10KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include <algorithm>
  4. struct QuadMIDIToCVInterface : Module {
  5. enum ParamIds {
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. NUM_INPUTS
  10. };
  11. enum OutputIds {
  12. ENUMS(CV_OUTPUT, 4),
  13. ENUMS(GATE_OUTPUT, 4),
  14. ENUMS(VELOCITY_OUTPUT, 4),
  15. ENUMS(AFTERTOUCH_OUTPUT, 4),
  16. NUM_OUTPUTS
  17. };
  18. enum LightIds {
  19. NUM_LIGHTS
  20. };
  21. midi::InputQueue midiInput;
  22. enum PolyMode {
  23. ROTATE_MODE,
  24. REUSE_MODE,
  25. RESET_MODE,
  26. REASSIGN_MODE,
  27. UNISON_MODE,
  28. NUM_MODES
  29. };
  30. PolyMode polyMode = RESET_MODE;
  31. struct NoteData {
  32. uint8_t velocity = 0;
  33. uint8_t aftertouch = 0;
  34. };
  35. NoteData noteData[128];
  36. // cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stolen notes (after the 4th one).
  37. std::vector<uint8_t> cachedNotes;
  38. uint8_t notes[4];
  39. bool gates[4];
  40. // gates set to TRUE by pedal and current gate. FALSE by pedal.
  41. bool pedalgates[4];
  42. bool pedal;
  43. int rotateIndex;
  44. int stealIndex;
  45. QuadMIDIToCVInterface() {
  46. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  47. cachedNotes.resize(128, 0);
  48. onReset();
  49. }
  50. json_t *dataToJson() override {
  51. json_t *rootJ = json_object();
  52. json_object_set_new(rootJ, "midi", midiInput.toJson());
  53. json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
  54. return rootJ;
  55. }
  56. void dataFromJson(json_t *rootJ) override {
  57. json_t *midiJ = json_object_get(rootJ, "midi");
  58. if (midiJ)
  59. midiInput.fromJson(midiJ);
  60. json_t *polyModeJ = json_object_get(rootJ, "polyMode");
  61. if (polyModeJ)
  62. polyMode = (PolyMode) json_integer_value(polyModeJ);
  63. }
  64. void onReset() override {
  65. for (int i = 0; i < 4; i++) {
  66. notes[i] = 60;
  67. gates[i] = false;
  68. pedalgates[i] = false;
  69. }
  70. pedal = false;
  71. rotateIndex = -1;
  72. cachedNotes.clear();
  73. midiInput.reset();
  74. }
  75. int getPolyIndex(int nowIndex) {
  76. for (int i = 0; i < 4; i++) {
  77. nowIndex++;
  78. if (nowIndex > 3)
  79. nowIndex = 0;
  80. if (!(gates[nowIndex] || pedalgates[nowIndex])) {
  81. stealIndex = nowIndex;
  82. return nowIndex;
  83. }
  84. }
  85. // All taken = steal (stealIndex always rotates)
  86. stealIndex++;
  87. if (stealIndex > 3)
  88. stealIndex = 0;
  89. if ((polyMode < REASSIGN_MODE) && (gates[stealIndex]))
  90. cachedNotes.push_back(notes[stealIndex]);
  91. return stealIndex;
  92. }
  93. void pressNote(uint8_t note) {
  94. // Set notes and gates
  95. switch (polyMode) {
  96. case ROTATE_MODE: {
  97. rotateIndex = getPolyIndex(rotateIndex);
  98. } break;
  99. case REUSE_MODE: {
  100. bool reuse = false;
  101. for (int i = 0; i < 4; i++) {
  102. if (notes[i] == note) {
  103. rotateIndex = i;
  104. reuse = true;
  105. break;
  106. }
  107. }
  108. if (!reuse)
  109. rotateIndex = getPolyIndex(rotateIndex);
  110. } break;
  111. case RESET_MODE: {
  112. rotateIndex = getPolyIndex(-1);
  113. } break;
  114. case REASSIGN_MODE: {
  115. cachedNotes.push_back(note);
  116. rotateIndex = getPolyIndex(-1);
  117. } break;
  118. case UNISON_MODE: {
  119. cachedNotes.push_back(note);
  120. for (int i = 0; i < 4; i++) {
  121. notes[i] = note;
  122. gates[i] = true;
  123. pedalgates[i] = pedal;
  124. // reTrigger[i].trigger(1e-3);
  125. }
  126. return;
  127. } break;
  128. default: break;
  129. }
  130. // Set notes and gates
  131. // if (gates[rotateIndex] || pedalgates[rotateIndex])
  132. // reTrigger[rotateIndex].trigger(1e-3);
  133. notes[rotateIndex] = note;
  134. gates[rotateIndex] = true;
  135. pedalgates[rotateIndex] = pedal;
  136. }
  137. void releaseNote(uint8_t note) {
  138. // Remove the note
  139. auto it = std::find(cachedNotes.begin(), cachedNotes.end(), note);
  140. if (it != cachedNotes.end())
  141. cachedNotes.erase(it);
  142. switch (polyMode) {
  143. case REASSIGN_MODE: {
  144. for (int i = 0; i < 4; i++) {
  145. if (i < (int) cachedNotes.size()) {
  146. if (!pedalgates[i])
  147. notes[i] = cachedNotes[i];
  148. pedalgates[i] = pedal;
  149. }
  150. else {
  151. gates[i] = false;
  152. }
  153. }
  154. } break;
  155. case UNISON_MODE: {
  156. if (!cachedNotes.empty()) {
  157. uint8_t backnote = cachedNotes.back();
  158. for (int i = 0; i < 4; i++) {
  159. notes[i] = backnote;
  160. gates[i] = true;
  161. }
  162. }
  163. else {
  164. for (int i = 0; i < 4; i++) {
  165. gates[i] = false;
  166. }
  167. }
  168. } break;
  169. // default ROTATE_MODE REUSE_MODE RESET_MODE
  170. default: {
  171. for (int i = 0; i < 4; i++) {
  172. if (notes[i] == note) {
  173. if (pedalgates[i]) {
  174. gates[i] = false;
  175. }
  176. else if (!cachedNotes.empty()) {
  177. notes[i] = cachedNotes.back();
  178. cachedNotes.pop_back();
  179. }
  180. else {
  181. gates[i] = false;
  182. }
  183. }
  184. }
  185. } break;
  186. }
  187. }
  188. void pressPedal() {
  189. pedal = true;
  190. for (int i = 0; i < 4; i++) {
  191. pedalgates[i] = gates[i];
  192. }
  193. }
  194. void releasePedal() {
  195. pedal = false;
  196. // When pedal is off, recover notes for pressed keys (if any) after they were already being "cycled" out by pedal-sustained notes.
  197. for (int i = 0; i < 4; i++) {
  198. pedalgates[i] = false;
  199. if (!cachedNotes.empty()) {
  200. if (polyMode < REASSIGN_MODE) {
  201. notes[i] = cachedNotes.back();
  202. cachedNotes.pop_back();
  203. gates[i] = true;
  204. }
  205. }
  206. }
  207. if (polyMode == REASSIGN_MODE) {
  208. for (int i = 0; i < 4; i++) {
  209. if (i < (int) cachedNotes.size()) {
  210. notes[i] = cachedNotes[i];
  211. gates[i] = true;
  212. }
  213. else {
  214. gates[i] = false;
  215. }
  216. }
  217. }
  218. }
  219. void step() override {
  220. midi::Message msg;
  221. while (midiInput.shift(&msg)) {
  222. processMessage(msg);
  223. }
  224. for (int i = 0; i < 4; i++) {
  225. uint8_t lastNote = notes[i];
  226. uint8_t lastGate = (gates[i] || pedalgates[i]);
  227. outputs[CV_OUTPUT + i].setVoltage((lastNote - 60) / 12.f);
  228. outputs[GATE_OUTPUT + i].setVoltage(lastGate ? 10.f : 0.f);
  229. outputs[VELOCITY_OUTPUT + i].setVoltage(rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f));
  230. outputs[AFTERTOUCH_OUTPUT + i].setVoltage(rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f));
  231. }
  232. }
  233. void processMessage(midi::Message msg) {
  234. switch (msg.getStatus()) {
  235. // note off
  236. case 0x8: {
  237. releaseNote(msg.getNote());
  238. } break;
  239. // note on
  240. case 0x9: {
  241. if (msg.getValue() > 0) {
  242. noteData[msg.getNote()].velocity = msg.getValue();
  243. pressNote(msg.getNote());
  244. }
  245. else {
  246. releaseNote(msg.getNote());
  247. }
  248. } break;
  249. // channel aftertouch
  250. case 0xa: {
  251. noteData[msg.getNote()].aftertouch = msg.getValue();
  252. } break;
  253. // cc
  254. case 0xb: {
  255. processCC(msg);
  256. } break;
  257. default: break;
  258. }
  259. }
  260. void processCC(midi::Message msg) {
  261. switch (msg.getNote()) {
  262. // sustain
  263. case 0x40: {
  264. if (msg.getValue() >= 64)
  265. pressPedal();
  266. else
  267. releasePedal();
  268. } break;
  269. default: break;
  270. }
  271. }
  272. };
  273. struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
  274. QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) {
  275. setModule(module);
  276. setPanel(SVG::load(asset::system("res/Core/QuadMIDIToCVInterface.svg")));
  277. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  278. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  279. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  280. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  281. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), module, QuadMIDIToCVInterface::CV_OUTPUT + 0));
  282. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 0));
  283. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0));
  284. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0));
  285. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), module, QuadMIDIToCVInterface::CV_OUTPUT + 1));
  286. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 1));
  287. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1));
  288. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1));
  289. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), module, QuadMIDIToCVInterface::CV_OUTPUT + 2));
  290. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 2));
  291. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2));
  292. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2));
  293. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), module, QuadMIDIToCVInterface::CV_OUTPUT + 3));
  294. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), module, QuadMIDIToCVInterface::GATE_OUTPUT + 3));
  295. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3));
  296. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3));
  297. MidiWidget *midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.4009969, 14.837336)));
  298. midiWidget->box.size = mm2px(Vec(44, 28));
  299. if (module)
  300. midiWidget->midiIO = &module->midiInput;
  301. addChild(midiWidget);
  302. }
  303. void appendContextMenu(Menu *menu) override {
  304. QuadMIDIToCVInterface *module = dynamic_cast<QuadMIDIToCVInterface*>(this->module);
  305. struct PolyphonyItem : MenuItem {
  306. QuadMIDIToCVInterface *module;
  307. QuadMIDIToCVInterface::PolyMode polyMode;
  308. void onAction(const event::Action &e) override {
  309. module->polyMode = polyMode;
  310. module->onReset();
  311. }
  312. };
  313. menu->addChild(new MenuEntry);
  314. menu->addChild(createMenuLabel("Polyphony mode"));
  315. auto addPolyphonyItem = [&](QuadMIDIToCVInterface::PolyMode polyMode, std::string name) {
  316. PolyphonyItem *item = new PolyphonyItem;
  317. item->text = name;
  318. item->rightText = CHECKMARK(module->polyMode == polyMode);
  319. item->module = module;
  320. item->polyMode = polyMode;
  321. menu->addChild(item);
  322. };
  323. addPolyphonyItem(QuadMIDIToCVInterface::RESET_MODE, "Reset");
  324. addPolyphonyItem(QuadMIDIToCVInterface::ROTATE_MODE, "Rotate");
  325. addPolyphonyItem(QuadMIDIToCVInterface::REUSE_MODE, "Reuse");
  326. addPolyphonyItem(QuadMIDIToCVInterface::REASSIGN_MODE, "Reassign");
  327. addPolyphonyItem(QuadMIDIToCVInterface::UNISON_MODE, "Unison");
  328. }
  329. };
  330. Model *modelQuadMIDIToCVInterface = createModel<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("QuadMIDIToCVInterface");