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.

328 lines
7.5KB

  1. #if 0
  2. #include <list>
  3. #include <algorithm>
  4. #include "core.hpp"
  5. #include "MidiIO.hpp"
  6. struct CCValue {
  7. int val = 0; // Controller value
  8. TransitionSmoother tSmooth;
  9. int num = 0; // Controller number
  10. bool numInited = false; // Num inited by config file
  11. bool numSelected = false; // Text field selected for midi learn
  12. bool changed = false; // Value has been changed by midi message (only if it is in sync!)
  13. int sync = 0; // Output value sync (implies diff)
  14. bool syncFirst = true; // First value after sync was reset
  15. void resetSync() {
  16. sync = 0;
  17. syncFirst = true;
  18. }
  19. };
  20. struct MIDICCToCVInterface : MidiIO, Module {
  21. enum ParamIds {
  22. NUM_PARAMS
  23. };
  24. enum InputIds {
  25. NUM_INPUTS
  26. };
  27. enum OutputIds {
  28. NUM_OUTPUTS = 16
  29. };
  30. enum LightIds {
  31. NUM_LIGHTS = 16
  32. };
  33. CCValue cc[NUM_OUTPUTS];
  34. MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  35. for (int i = 0; i < NUM_OUTPUTS; i++) {
  36. cc[i].num = i;
  37. }
  38. }
  39. ~MIDICCToCVInterface() {}
  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(cc[i].num));
  48. if (outputs[i].active) {
  49. json_object_set_new(rootJ, ("ccVal" + std::to_string(i)).c_str(), json_integer(cc[i].val));
  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. cc[i].num = json_integer_value(ccNumJ);
  60. cc[i].numInited = true;
  61. }
  62. json_t *ccValJ = json_object_get(rootJ, ("ccVal" + std::to_string(i)).c_str());
  63. if (ccValJ) {
  64. cc[i].val = json_integer_value(ccValJ);
  65. cc[i].tSmooth.set((cc[i].val / 127.0 * 10.0), (cc[i].val / 127.0 * 10.0));
  66. cc[i].resetSync();
  67. }
  68. }
  69. }
  70. void onReset() override {
  71. resetMidi();
  72. }
  73. };
  74. void MIDICCToCVInterface::step() {
  75. if (isPortOpen()) {
  76. std::vector<unsigned char> message;
  77. // midiIn->getMessage returns empty vector if there are no messages in the queue
  78. getMessage(&message);
  79. if (message.size() > 0) {
  80. processMidi(message);
  81. }
  82. }
  83. for (int i = 0; i < NUM_OUTPUTS; i++) {
  84. lights[i].setBrightness(cc[i].sync / 127.0);
  85. if (cc[i].changed) {
  86. cc[i].tSmooth.set(outputs[i].value, (cc[i].val / 127.0 * 10.0), int(engineGetSampleRate() / 32));
  87. cc[i].changed = false;
  88. }
  89. outputs[i].value = cc[i].tSmooth.next();
  90. }
  91. }
  92. void MIDICCToCVInterface::resetMidi() {
  93. for (int i = 0; i < NUM_OUTPUTS; i++) {
  94. cc[i].val = 0;
  95. cc[i].resetSync();
  96. cc[i].tSmooth.set(0, 0);
  97. }
  98. };
  99. void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) {
  100. int channel = msg[0] & 0xf;
  101. int status = (msg[0] >> 4) & 0xf;
  102. int data1 = msg[1];
  103. int data2 = msg[2];
  104. //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2);
  105. // Filter channels
  106. if (this->channel >= 0 && this->channel != channel)
  107. return;
  108. if (status == 0xb) {
  109. for (int i = 0; i < NUM_OUTPUTS; i++) {
  110. if (cc[i].numSelected) {
  111. cc[i].resetSync();
  112. cc[i].num = data1;
  113. }
  114. if (data1 == cc[i].num) {
  115. /* If the first value we received after sync was reset is +/- 1 of
  116. * the output value the values are in sync*/
  117. if (cc[i].syncFirst) {
  118. cc[i].syncFirst = false;
  119. if (data2 < cc[i].val + 2 && data2 > cc[i].val - 2) {
  120. cc[i].sync = 0;
  121. }
  122. else {
  123. cc[i].sync = absi(data2 - cc[i].val);
  124. }
  125. return;
  126. }
  127. if (cc[i].sync == 0) {
  128. cc[i].val = data2;
  129. cc[i].changed = true;
  130. }
  131. else {
  132. cc[i].sync = absi(data2 - cc[i].val);
  133. }
  134. }
  135. }
  136. }
  137. }
  138. struct CCTextField : TextField {
  139. void onTextChange() override;
  140. void draw(NVGcontext *vg) override;
  141. void onMouseDown(EventMouseDown &e) override;
  142. void onMouseUp(EventMouseUp &e) override;
  143. void onMouseLeave(EventMouseLeave &e) override;
  144. int outNum;
  145. MIDICCToCVInterface *module;
  146. };
  147. void CCTextField::draw(NVGcontext *vg) {
  148. /* This is necessary, since the save
  149. * file is loaded after constructing the widget*/
  150. if (module->cc[outNum].numInited) {
  151. module->cc[outNum].numInited = false;
  152. text = std::to_string(module->cc[outNum].num);
  153. }
  154. /* If number is selected for midi learn*/
  155. if (module->cc[outNum].numSelected) {
  156. text = std::to_string(module->cc[outNum].num);
  157. }
  158. TextField::draw(vg);
  159. }
  160. void CCTextField::onMouseUp(EventMouseUp &e) {
  161. if (e.button == 1) {
  162. module->cc[outNum].numSelected = false;
  163. e.consumed = true;
  164. }
  165. TextField::onMouseUp(e);
  166. }
  167. void CCTextField::onMouseDown(EventMouseDown &e) {
  168. if (e.button == 1) {
  169. module->cc[outNum].numSelected = true;
  170. e.consumed = true;
  171. }
  172. TextField::onMouseDown(e);
  173. }
  174. void CCTextField::onMouseLeave(EventMouseLeave &e) {
  175. module->cc[outNum].numSelected = false;
  176. e.consumed = true;
  177. }
  178. void CCTextField::onTextChange() {
  179. if (text.size() > 0) {
  180. try {
  181. int num = std::stoi(text);
  182. // Only allow valid cc numbers
  183. if (num < 0 || num > 127 || text.size() > 3) {
  184. text = "";
  185. begin = end = 0;
  186. module->cc[outNum].num = -1;
  187. }
  188. else {
  189. module->cc[outNum].num = num;
  190. module->cc[outNum].resetSync();
  191. }
  192. }
  193. catch (...) {
  194. text = "";
  195. begin = end = 0;
  196. module->cc[outNum].num = -1;
  197. }
  198. };
  199. }
  200. MIDICCToCVWidget::MIDICCToCVWidget() {
  201. MIDICCToCVInterface *module = new MIDICCToCVInterface();
  202. setModule(module);
  203. box.size = Vec(16 * 15, 380);
  204. {
  205. Panel *panel = new LightPanel();
  206. panel->box.size = box.size;
  207. addChild(panel);
  208. }
  209. float margin = 5;
  210. float labelHeight = 15;
  211. float yPos = margin;
  212. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  213. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  214. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  215. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  216. {
  217. Label *label = new Label();
  218. label->box.pos = Vec(box.size.x - margin - 11 * 15, margin);
  219. label->text = "MIDI CC to CV";
  220. addChild(label);
  221. yPos = labelHeight * 2;
  222. }
  223. {
  224. Label *label = new Label();
  225. label->box.pos = Vec(margin, yPos);
  226. label->text = "MIDI Interface";
  227. addChild(label);
  228. MidiChoice *midiChoice = new MidiChoice();
  229. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  230. midiChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  231. midiChoice->box.size.x = (box.size.x / 2.0) - margin;
  232. addChild(midiChoice);
  233. yPos += midiChoice->box.size.y + margin;
  234. }
  235. {
  236. Label *label = new Label();
  237. label->box.pos = Vec(margin, yPos);
  238. label->text = "Channel";
  239. addChild(label);
  240. ChannelChoice *channelChoice = new ChannelChoice();
  241. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  242. channelChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  243. channelChoice->box.size.x = (box.size.x / 2.0) - margin;
  244. addChild(channelChoice);
  245. yPos += channelChoice->box.size.y + margin * 3;
  246. }
  247. for (int i = 0; i < MIDICCToCVInterface::NUM_OUTPUTS; i++) {
  248. CCTextField *ccNumChoice = new CCTextField();
  249. ccNumChoice->module = module;
  250. ccNumChoice->outNum = i;
  251. ccNumChoice->text = std::to_string(module->cc[i].num);
  252. ccNumChoice->box.pos = Vec(11 + (i % 4) * (63), yPos);
  253. ccNumChoice->box.size.x = 29;
  254. addChild(ccNumChoice);
  255. yPos += labelHeight + margin;
  256. addOutput(createOutput<PJ3410Port>(Vec((i % 4) * (63) + 10, yPos + 5), module, i));
  257. addChild(createLight<SmallLight<RedLight>>(Vec((i % 4) * (63) + 32, yPos + 5), module, i));
  258. if ((i + 1) % 4 == 0) {
  259. yPos += 47 + margin;
  260. }
  261. else {
  262. yPos -= labelHeight + margin;
  263. }
  264. }
  265. }
  266. void MIDICCToCVWidget::step() {
  267. ModuleWidget::step();
  268. }
  269. #endif