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.

442 lines
10KB

  1. #include "plugin.hpp"
  2. static const int MAX_CHANNELS = 128;
  3. struct MIDI_Map : Module {
  4. enum ParamIds {
  5. NUM_PARAMS
  6. };
  7. enum InputIds {
  8. NUM_INPUTS
  9. };
  10. enum OutputIds {
  11. NUM_OUTPUTS
  12. };
  13. enum LightIds {
  14. NUM_LIGHTS
  15. };
  16. midi::InputQueue midiInput;
  17. /** Number of maps */
  18. int mapLen = 0;
  19. /** The mapped CC number of each channel */
  20. int ccs[MAX_CHANNELS];
  21. /** The mapped param handle of each channel */
  22. ParamHandle paramHandles[MAX_CHANNELS];
  23. /** Channel ID of the learning session */
  24. int learningId;
  25. /** Whether the CC has been set during the learning session */
  26. bool learnedCc;
  27. /** Whether the param has been set during the learning session */
  28. bool learnedParam;
  29. /** The value of each CC number */
  30. int8_t values[128];
  31. /** The smoothing processor (normalized between 0 and 1) of each channel */
  32. dsp::ExponentialFilter valueFilters[MAX_CHANNELS];
  33. MIDI_Map() {
  34. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  35. for (int id = 0; id < MAX_CHANNELS; id++) {
  36. APP->engine->addParamHandle(&paramHandles[id]);
  37. }
  38. onReset();
  39. }
  40. ~MIDI_Map() {
  41. for (int id = 0; id < MAX_CHANNELS; id++) {
  42. APP->engine->removeParamHandle(&paramHandles[id]);
  43. }
  44. }
  45. void onReset() override {
  46. learningId = -1;
  47. learnedCc = false;
  48. learnedParam = false;
  49. clearMaps();
  50. mapLen = 1;
  51. for (int i = 0; i < 128; i++) {
  52. values[i] = -1;
  53. }
  54. midiInput.reset();
  55. }
  56. void process(const ProcessArgs &args) override {
  57. midi::Message msg;
  58. while (midiInput.shift(&msg)) {
  59. processMessage(msg);
  60. }
  61. // Step channels
  62. for (int id = 0; id < mapLen; id++) {
  63. int cc = ccs[id];
  64. if (cc < 0)
  65. continue;
  66. // Check if CC value has been set
  67. if (values[cc] < 0)
  68. continue;
  69. // Get Module
  70. Module *module = paramHandles[id].module;
  71. if (!module)
  72. continue;
  73. // Get ParamQuantity
  74. int paramId = paramHandles[id].paramId;
  75. ParamQuantity *paramQuantity = module->paramQuantities[paramId];
  76. if (!paramQuantity)
  77. continue;
  78. if (!paramQuantity->isBounded())
  79. continue;
  80. // Set ParamQuantity
  81. float v = rescale(values[cc], 0, 127, 0.f, 1.f);
  82. v = valueFilters[id].process(args.sampleTime, v);
  83. paramQuantity->setScaledValue(v);
  84. }
  85. }
  86. void processMessage(midi::Message msg) {
  87. switch (msg.getStatus()) {
  88. // cc
  89. case 0xb: {
  90. processCC(msg);
  91. } break;
  92. default: break;
  93. }
  94. }
  95. void processCC(midi::Message msg) {
  96. uint8_t cc = msg.getNote();
  97. int8_t value = msg.getValue();
  98. // Learn
  99. if (0 <= learningId && values[cc] != value) {
  100. ccs[learningId] = cc;
  101. valueFilters[learningId].reset();
  102. learnedCc = true;
  103. commitLearn();
  104. updateMapLen();
  105. }
  106. values[cc] = value;
  107. }
  108. void clearMap(int id) {
  109. learningId = -1;
  110. ccs[id] = -1;
  111. APP->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
  112. valueFilters[id].reset();
  113. updateMapLen();
  114. }
  115. void clearMaps() {
  116. learningId = -1;
  117. for (int id = 0; id < MAX_CHANNELS; id++) {
  118. ccs[id] = -1;
  119. APP->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
  120. valueFilters[id].reset();
  121. }
  122. mapLen = 0;
  123. }
  124. void updateMapLen() {
  125. // Find last nonempty map
  126. int id;
  127. for (id = MAX_CHANNELS - 1; id >= 0; id--) {
  128. if (ccs[id] >= 0 || paramHandles[id].moduleId >= 0)
  129. break;
  130. }
  131. mapLen = id + 1;
  132. // Add an empty "Mapping..." slot
  133. if (mapLen < MAX_CHANNELS)
  134. mapLen++;
  135. }
  136. void commitLearn() {
  137. if (learningId < 0)
  138. return;
  139. if (!learnedCc)
  140. return;
  141. if (!learnedParam)
  142. return;
  143. // Reset learned state
  144. learnedCc = false;
  145. learnedParam = false;
  146. // Find next incomplete map
  147. while (++learningId < MAX_CHANNELS) {
  148. if (ccs[learningId] < 0 || paramHandles[learningId].moduleId < 0)
  149. return;
  150. }
  151. learningId = -1;
  152. }
  153. void enableLearn(int id) {
  154. if (learningId != id) {
  155. learningId = id;
  156. learnedCc = false;
  157. learnedParam = false;
  158. }
  159. }
  160. void disableLearn(int id) {
  161. if (learningId == id) {
  162. learningId = -1;
  163. }
  164. }
  165. void learnParam(int id, int moduleId, int paramId) {
  166. APP->engine->updateParamHandle(&paramHandles[id], moduleId, paramId, true);
  167. learnedParam = true;
  168. commitLearn();
  169. updateMapLen();
  170. }
  171. json_t *dataToJson() override {
  172. json_t *rootJ = json_object();
  173. json_t *mapsJ = json_array();
  174. for (int id = 0; id < mapLen; id++) {
  175. json_t *mapJ = json_object();
  176. json_object_set_new(mapJ, "cc", json_integer(ccs[id]));
  177. json_object_set_new(mapJ, "moduleId", json_integer(paramHandles[id].moduleId));
  178. json_object_set_new(mapJ, "paramId", json_integer(paramHandles[id].paramId));
  179. json_array_append(mapsJ, mapJ);
  180. }
  181. json_object_set_new(rootJ, "maps", mapsJ);
  182. json_object_set_new(rootJ, "midi", midiInput.toJson());
  183. return rootJ;
  184. }
  185. void dataFromJson(json_t *rootJ) override {
  186. clearMaps();
  187. json_t *mapsJ = json_object_get(rootJ, "maps");
  188. if (mapsJ) {
  189. json_t *mapJ;
  190. size_t mapIndex;
  191. json_array_foreach(mapsJ, mapIndex, mapJ) {
  192. json_t *ccJ = json_object_get(mapJ, "cc");
  193. json_t *moduleIdJ = json_object_get(mapJ, "moduleId");
  194. json_t *paramIdJ = json_object_get(mapJ, "paramId");
  195. if (!(ccJ && moduleIdJ && paramIdJ))
  196. continue;
  197. if (mapIndex >= MAX_CHANNELS)
  198. continue;
  199. ccs[mapIndex] = json_integer_value(ccJ);
  200. APP->engine->updateParamHandle(&paramHandles[mapIndex], json_integer_value(moduleIdJ), json_integer_value(paramIdJ), false);
  201. }
  202. }
  203. updateMapLen();
  204. json_t *midiJ = json_object_get(rootJ, "midi");
  205. if (midiJ)
  206. midiInput.fromJson(midiJ);
  207. }
  208. };
  209. struct MIDI_MapChoice : LedDisplayChoice {
  210. MIDI_Map *module;
  211. int id;
  212. int disableLearnFrames = -1;
  213. void setModule(MIDI_Map *module) {
  214. this->module = module;
  215. }
  216. void onButton(const event::Button &e) override {
  217. e.stopPropagating();
  218. if (!module)
  219. return;
  220. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
  221. e.consume(this);
  222. }
  223. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
  224. module->clearMap(id);
  225. e.consume(this);
  226. }
  227. }
  228. void onSelect(const event::Select &e) override {
  229. if (!module)
  230. return;
  231. ScrollWidget *scroll = getAncestorOfType<ScrollWidget>();
  232. scroll->scrollTo(box);
  233. // Reset touchedParam
  234. APP->scene->rack->touchedParam = NULL;
  235. module->enableLearn(id);
  236. }
  237. void onDeselect(const event::Deselect &e) override {
  238. if (!module)
  239. return;
  240. // Check if a ParamWidget was touched
  241. ParamWidget *touchedParam = APP->scene->rack->touchedParam;
  242. if (touchedParam) {
  243. APP->scene->rack->touchedParam = NULL;
  244. int moduleId = touchedParam->paramQuantity->module->id;
  245. int paramId = touchedParam->paramQuantity->paramId;
  246. module->learnParam(id, moduleId, paramId);
  247. }
  248. else {
  249. module->disableLearn(id);
  250. }
  251. }
  252. void step() override {
  253. if (!module)
  254. return;
  255. // Set bgColor and selected state
  256. if (module->learningId == id) {
  257. bgColor = color;
  258. bgColor.a = 0.15;
  259. // HACK
  260. if (APP->event->selectedWidget != this)
  261. APP->event->setSelected(this);
  262. }
  263. else {
  264. bgColor = nvgRGBA(0, 0, 0, 0);
  265. // HACK
  266. if (APP->event->selectedWidget == this)
  267. APP->event->setSelected(NULL);
  268. }
  269. // Set text
  270. text = "";
  271. if (module->ccs[id] >= 0) {
  272. text += string::f("CC%d ", module->ccs[id]);
  273. }
  274. if (module->paramHandles[id].moduleId >= 0) {
  275. text += getParamName();
  276. }
  277. if (module->ccs[id] < 0 && module->paramHandles[id].moduleId < 0) {
  278. if (module->learningId == id) {
  279. text = "Mapping...";
  280. }
  281. else {
  282. text = "Unmapped";
  283. }
  284. }
  285. // Set text color
  286. if ((module->ccs[id] >= 0 && module->paramHandles[id].moduleId >= 0) || module->learningId == id) {
  287. color.a = 1.0;
  288. }
  289. else {
  290. color.a = 0.5;
  291. }
  292. }
  293. std::string getParamName() {
  294. if (!module)
  295. return "";
  296. if (id >= module->mapLen)
  297. return "";
  298. ParamHandle *paramHandle = &module->paramHandles[id];
  299. if (paramHandle->moduleId < 0)
  300. return "";
  301. ModuleWidget *mw = APP->scene->rack->getModule(paramHandle->moduleId);
  302. if (!mw)
  303. return "";
  304. // Get the Module from the ModuleWidget instead of the ParamHandle.
  305. // I think this is more elegant since this method is called in the app world instead of the engine world.
  306. Module *m = mw->module;
  307. if (!m)
  308. return "";
  309. int paramId = paramHandle->paramId;
  310. if (paramId >= (int) m->params.size())
  311. return "";
  312. ParamQuantity *paramQuantity = m->paramQuantities[paramId];
  313. std::string s;
  314. s += mw->model->name;
  315. s += " ";
  316. s += paramQuantity->label;
  317. return s;
  318. }
  319. };
  320. struct MIDI_MapDisplay : MidiWidget {
  321. MIDI_Map *module;
  322. ScrollWidget *scroll;
  323. MIDI_MapChoice *choices[MAX_CHANNELS];
  324. LedDisplaySeparator *separators[MAX_CHANNELS];
  325. void setModule(MIDI_Map *module) {
  326. this->module = module;
  327. scroll = new ScrollWidget;
  328. scroll->box.pos = channelChoice->box.getBottomLeft();
  329. scroll->box.size.x = box.size.x;
  330. scroll->box.size.y = box.size.y - scroll->box.pos.y;
  331. addChild(scroll);
  332. LedDisplaySeparator *separator = createWidget<LedDisplaySeparator>(scroll->box.pos);
  333. separator->box.size.x = box.size.x;
  334. addChild(separator);
  335. separators[0] = separator;
  336. Vec pos;
  337. for (int id = 0; id < MAX_CHANNELS; id++) {
  338. if (id > 0) {
  339. LedDisplaySeparator *separator = createWidget<LedDisplaySeparator>(pos);
  340. separator->box.size.x = box.size.x;
  341. scroll->container->addChild(separator);
  342. separators[id] = separator;
  343. }
  344. MIDI_MapChoice *choice = createWidget<MIDI_MapChoice>(pos);
  345. choice->box.size.x = box.size.x;
  346. choice->id = id;
  347. choice->setModule(module);
  348. scroll->container->addChild(choice);
  349. choices[id] = choice;
  350. pos = choice->box.getBottomLeft();
  351. }
  352. }
  353. void step() override {
  354. if (module) {
  355. int mapLen = module->mapLen;
  356. for (int id = 0; id < MAX_CHANNELS; id++) {
  357. choices[id]->visible = (id < mapLen);
  358. separators[id]->visible = (id < mapLen);
  359. }
  360. }
  361. MidiWidget::step();
  362. }
  363. };
  364. struct MIDI_MapWidget : ModuleWidget {
  365. MIDI_MapWidget(MIDI_Map *module) {
  366. setModule(module);
  367. setPanel(APP->window->loadSvg(asset::system("res/Core/MIDI-Map.svg")));
  368. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  369. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  370. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  371. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  372. MIDI_MapDisplay *midiWidget = createWidget<MIDI_MapDisplay>(mm2px(Vec(3.41891, 14.8373)));
  373. midiWidget->box.size = mm2px(Vec(43.999, 102.664));
  374. midiWidget->setMidiPort(module ? &module->midiInput : NULL);
  375. midiWidget->setModule(module);
  376. addChild(midiWidget);
  377. }
  378. };
  379. Model *modelMIDI_Map = createModel<MIDI_Map, MIDI_MapWidget>("MIDI-Map");