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.

313 lines
6.8KB

  1. #include <list>
  2. #include <algorithm>
  3. #include "rtmidi/RtMidi.h"
  4. #include "core.hpp"
  5. #include "MidiIO.hpp"
  6. /*
  7. * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod weel to
  8. * CV
  9. */
  10. struct MIDICCToCVInterface : MidiIO, Module {
  11. enum ParamIds {
  12. NUM_PARAMS
  13. };
  14. enum InputIds {
  15. NUM_INPUTS
  16. };
  17. enum OutputIds {
  18. NUM_OUTPUTS = 16
  19. };
  20. enum LightIds {
  21. NUM_LIGHTS = 16
  22. };
  23. int cc[NUM_OUTPUTS];
  24. int ccNum[NUM_OUTPUTS];
  25. int ccSync[NUM_OUTPUTS];
  26. bool ccSyncFirst[NUM_OUTPUTS];
  27. bool ccNumInited[NUM_OUTPUTS];
  28. bool onFocus[NUM_OUTPUTS];
  29. MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  30. for (int i = 0; i < NUM_OUTPUTS; i++) {
  31. cc[i] = 0;
  32. ccNum[i] = i;
  33. ccSync[i] = 0;
  34. ccSyncFirst[i] = true;
  35. onFocus[i] = false;
  36. }
  37. }
  38. ~MIDICCToCVInterface() {
  39. }
  40. void step() override;
  41. void processMidi(std::vector<unsigned char> msg);
  42. void resetMidi() override;
  43. json_t *toJson() override {
  44. json_t *rootJ = json_object();
  45. addBaseJson(rootJ);
  46. for (int i = 0; i < NUM_OUTPUTS; i++) {
  47. json_object_set_new(rootJ, ("ccNum" + std::to_string(i)).c_str(), json_integer(ccNum[i]));
  48. if (outputs[i].active) {
  49. json_object_set_new(rootJ, ("ccVal" + std::to_string(i)).c_str(), json_integer(cc[i]));
  50. }
  51. }
  52. return rootJ;
  53. }
  54. void fromJson(json_t *rootJ) override {
  55. baseFromJson(rootJ);
  56. for (int i = 0; i < NUM_OUTPUTS; i++) {
  57. json_t *ccNumJ = json_object_get(rootJ, ("ccNum" + std::to_string(i)).c_str());
  58. if (ccNumJ) {
  59. ccNum[i] = json_integer_value(ccNumJ);
  60. ccNumInited[i] = true;
  61. }
  62. json_t *ccValJ = json_object_get(rootJ, ("ccVal" + std::to_string(i)).c_str());
  63. if (ccValJ) {
  64. cc[i] = json_integer_value(ccValJ);
  65. }
  66. }
  67. }
  68. void reset() override {
  69. resetMidi();
  70. }
  71. };
  72. void MIDICCToCVInterface::step() {
  73. if (isPortOpen()) {
  74. std::vector<unsigned char> message;
  75. // midiIn->getMessage returns empty vector if there are no messages in the queue
  76. getMessage(&message);
  77. while (message.size() > 0) {
  78. processMidi(message);
  79. getMessage(&message);
  80. }
  81. }
  82. for (int i = 0; i < NUM_OUTPUTS; i++) {
  83. lights[i].setBrightness(ccSync[i] / 127.0);
  84. outputs[i].value = cc[i] / 127.0 * 10.0;
  85. }
  86. }
  87. void MIDICCToCVInterface::resetMidi() {
  88. for (int i = 0; i < NUM_OUTPUTS; i++) {
  89. cc[i] = 0;
  90. ccSync[i] = 0;
  91. ccSyncFirst[i] = true;
  92. }
  93. };
  94. void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) {
  95. int channel = msg[0] & 0xf;
  96. int status = (msg[0] >> 4) & 0xf;
  97. int data1 = msg[1];
  98. int data2 = msg[2];
  99. //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2);
  100. // Filter channels
  101. if (this->channel >= 0 && this->channel != channel)
  102. return;
  103. if (status == 0xb) {
  104. for (int i = 0; i < NUM_OUTPUTS; i++) {
  105. if (onFocus[i]) {
  106. ccSync[i] = true;
  107. ccSyncFirst[i] = true;
  108. ccNum[i] = data1;
  109. }
  110. if (data1 == ccNum[i]) {
  111. if (ccSyncFirst[i]) {
  112. ccSyncFirst[i] = false;
  113. if (data2 < cc[i] + 2 && data2 > cc[i] - 2) {
  114. ccSync[i] = 0;
  115. } else {
  116. ccSync[i] = absi(data2 - cc[i]);
  117. }
  118. }
  119. if (ccSync[i] == 0) {
  120. cc[i] = data2;
  121. } else {
  122. ccSync[i] = absi(data2 - cc[i]);
  123. }
  124. }
  125. }
  126. }
  127. }
  128. struct CCTextField : TextField {
  129. void onTextChange() override;
  130. void draw(NVGcontext *vg) override;
  131. void onMouseDown(EventMouseDown &e) override;
  132. void onMouseUp(EventMouseUp &e) override;
  133. void onMouseLeave(EventMouseLeave &e) override;
  134. int num;
  135. MIDICCToCVInterface *module;
  136. };
  137. void CCTextField::draw(NVGcontext *vg) {
  138. /* This is necessary, since the save
  139. * file is loaded after constructing the widget*/
  140. if (module->ccNumInited[num]) {
  141. module->ccNumInited[num] = false;
  142. text = std::to_string(module->ccNum[num]);
  143. }
  144. if (module->onFocus[num]) {
  145. text = std::to_string(module->ccNum[num]);
  146. }
  147. TextField::draw(vg);
  148. }
  149. void CCTextField::onMouseDown(EventMouseDown &e) {
  150. if (e.button == 1) {
  151. module->onFocus[num] = true;
  152. }
  153. }
  154. void CCTextField::onMouseUp(EventMouseUp &e) {
  155. if (e.button == 1) {
  156. module->onFocus[num] = false;
  157. }
  158. }
  159. void CCTextField::onMouseLeave(EventMouseLeave &e) {
  160. module->onFocus[num] = false;
  161. }
  162. void CCTextField::onTextChange() {
  163. int *ccNum = &module->ccNum[num];
  164. if (text.size() > 0) {
  165. try {
  166. *ccNum = std::stoi(text);
  167. // Only allow valid cc numbers
  168. if (*ccNum < 0 || *ccNum > 127 || text.size() > 3) {
  169. text = "";
  170. begin = end = 0;
  171. *ccNum = -1;
  172. return;
  173. }
  174. if (!module->ccNumInited[num] && *ccNum != std::stoi(text)) {
  175. module->ccSync[num] = 0;
  176. module->ccSyncFirst[num] = true;
  177. }
  178. } catch (...) {
  179. text = "";
  180. begin = end = 0;
  181. *ccNum = -1;
  182. }
  183. };
  184. }
  185. MIDICCToCVWidget::MIDICCToCVWidget() {
  186. MIDICCToCVInterface *module = new MIDICCToCVInterface();
  187. setModule(module);
  188. box.size = Vec(16 * 15, 380);
  189. {
  190. Panel *panel = new LightPanel();
  191. panel->box.size = box.size;
  192. addChild(panel);
  193. }
  194. float margin = 5;
  195. float labelHeight = 15;
  196. float yPos = margin;
  197. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  198. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  199. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  200. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  201. {
  202. Label *label = new Label();
  203. label->box.pos = Vec(box.size.x - margin - 11 * 15, margin);
  204. label->text = "MIDI CC to CV";
  205. addChild(label);
  206. yPos = labelHeight * 2;
  207. }
  208. {
  209. Label *label = new Label();
  210. label->box.pos = Vec(margin, yPos);
  211. label->text = "MIDI Interface";
  212. addChild(label);
  213. MidiChoice *midiChoice = new MidiChoice();
  214. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  215. midiChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  216. midiChoice->box.size.x = (box.size.x / 2.0) - margin;
  217. addChild(midiChoice);
  218. yPos += midiChoice->box.size.y + margin;
  219. }
  220. {
  221. Label *label = new Label();
  222. label->box.pos = Vec(margin, yPos);
  223. label->text = "Channel";
  224. addChild(label);
  225. ChannelChoice *channelChoice = new ChannelChoice();
  226. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  227. channelChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  228. channelChoice->box.size.x = (box.size.x / 2.0) - margin;
  229. addChild(channelChoice);
  230. yPos += channelChoice->box.size.y + margin * 3;
  231. }
  232. for (int i = 0; i < MIDICCToCVInterface::NUM_OUTPUTS; i++) {
  233. CCTextField *ccNumChoice = new CCTextField();
  234. ccNumChoice->module = module;
  235. ccNumChoice->num = i;
  236. ccNumChoice->text = std::to_string(module->ccNum[i]);
  237. ccNumChoice->box.pos = Vec(11 + (i % 4) * (63), yPos);
  238. ccNumChoice->box.size.x = 29;
  239. addChild(ccNumChoice);
  240. yPos += labelHeight + margin;
  241. addOutput(createOutput<PJ3410Port>(Vec((i % 4) * (63) + 10, yPos + 5), module, i));
  242. addChild(createLight<SmallLight<RedLight>>(Vec((i % 4) * (63) + 32, yPos + 5), module, i));
  243. if ((i + 1) % 4 == 0) {
  244. yPos += 47 + margin;
  245. } else {
  246. yPos -= labelHeight + margin;
  247. }
  248. }
  249. }
  250. void MIDICCToCVWidget::step() {
  251. ModuleWidget::step();
  252. }