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.

269 lines
8.2KB

  1. #include "plugin.hpp"
  2. namespace rack {
  3. namespace core {
  4. struct MIDI_CC : Module {
  5. enum ParamIds {
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. NUM_INPUTS
  10. };
  11. enum OutputIds {
  12. ENUMS(CC_OUTPUT, 16),
  13. NUM_OUTPUTS
  14. };
  15. enum LightIds {
  16. NUM_LIGHTS
  17. };
  18. midi::InputQueue midiInput;
  19. /** [cc][channel] */
  20. int8_t ccValues[128][16];
  21. /** When LSB is enabled for CC 0-31, the MSB is stored here until the LSB is received.
  22. [cc][channel]
  23. */
  24. int8_t msbValues[32][16];
  25. int learningId;
  26. int learnedCcs[16];
  27. /** [cell][channel] */
  28. dsp::ExponentialFilter valueFilters[16][16];
  29. bool smooth;
  30. bool mpeMode;
  31. bool lsbMode;
  32. MIDI_CC() {
  33. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  34. for (int i = 0; i < 16; i++)
  35. configOutput(CC_OUTPUT + i, string::f("Cell %d", i + 1));
  36. for (int i = 0; i < 16; i++) {
  37. for (int c = 0; c < 16; c++) {
  38. valueFilters[i][c].setTau(1 / 30.f);
  39. }
  40. }
  41. onReset();
  42. }
  43. void onReset() override {
  44. for (int cc = 0; cc < 128; cc++) {
  45. for (int c = 0; c < 16; c++) {
  46. ccValues[cc][c] = 0;
  47. }
  48. }
  49. for (int cc = 0; cc < 32; cc++) {
  50. for (int c = 0; c < 16; c++) {
  51. msbValues[cc][c] = 0;
  52. }
  53. }
  54. learningId = -1;
  55. for (int i = 0; i < 16; i++) {
  56. learnedCcs[i] = i;
  57. }
  58. midiInput.reset();
  59. smooth = true;
  60. mpeMode = false;
  61. lsbMode = false;
  62. }
  63. void process(const ProcessArgs& args) override {
  64. midi::Message msg;
  65. while (midiInput.tryPop(&msg, args.frame)) {
  66. processMessage(msg);
  67. }
  68. int channels = mpeMode ? 16 : 1;
  69. for (int i = 0; i < 16; i++) {
  70. if (!outputs[CC_OUTPUT + i].isConnected())
  71. continue;
  72. outputs[CC_OUTPUT + i].setChannels(channels);
  73. int cc = learnedCcs[i];
  74. for (int c = 0; c < channels; c++) {
  75. int16_t cellValue = int16_t(ccValues[cc][c]) * 128;
  76. if (lsbMode && cc < 32)
  77. cellValue += ccValues[cc + 32][c];
  78. // Maximum value for 14-bit CC should be MSB=127 LSB=0, not MSB=127 LSB=127, because this is the maximum value that 7-bit controllers can send.
  79. float value = float(cellValue) / (128 * 127);
  80. // Support negative values because the gamepad MIDI driver generates nonstandard 8-bit CC values.
  81. value = clamp(value, -1.f, 1.f);
  82. // Detect behavior from MIDI buttons.
  83. if (smooth && std::fabs(valueFilters[i][c].out - value) < 1.f) {
  84. // Smooth value with filter
  85. valueFilters[i][c].process(args.sampleTime, value);
  86. }
  87. else {
  88. // Jump value
  89. valueFilters[i][c].out = value;
  90. }
  91. outputs[CC_OUTPUT + i].setVoltage(valueFilters[i][c].out * 10.f, c);
  92. }
  93. }
  94. }
  95. void processMessage(const midi::Message& msg) {
  96. switch (msg.getStatus()) {
  97. // cc
  98. case 0xb: {
  99. processCC(msg);
  100. } break;
  101. default: break;
  102. }
  103. }
  104. void processCC(const midi::Message& msg) {
  105. uint8_t c = mpeMode ? msg.getChannel() : 0;
  106. uint8_t cc = msg.getNote();
  107. if (msg.bytes.size() < 2)
  108. return;
  109. // Allow CC to be negative if the 8th bit is set.
  110. // The gamepad driver abuses this, for example.
  111. // Cast uint8_t to int8_t
  112. int8_t value = msg.bytes[2];
  113. // Learn
  114. if (learningId >= 0 && ccValues[cc][c] != value) {
  115. learnedCcs[learningId] = cc;
  116. learningId = -1;
  117. }
  118. if (lsbMode && cc < 32) {
  119. // Don't set MSB yet. Wait for LSB to be received.
  120. msbValues[cc][c] = value;
  121. }
  122. else if (lsbMode && 32 <= cc && cc < 64) {
  123. // Apply MSB when LSB is received
  124. ccValues[cc - 32][c] = msbValues[cc - 32][c];
  125. ccValues[cc][c] = value;
  126. }
  127. else {
  128. ccValues[cc][c] = value;
  129. }
  130. }
  131. json_t* dataToJson() override {
  132. json_t* rootJ = json_object();
  133. json_t* ccsJ = json_array();
  134. for (int i = 0; i < 16; i++) {
  135. json_array_append_new(ccsJ, json_integer(learnedCcs[i]));
  136. }
  137. json_object_set_new(rootJ, "ccs", ccsJ);
  138. // Remember values so users don't have to touch MIDI controller knobs when restarting Rack
  139. json_t* valuesJ = json_array();
  140. for (int i = 0; i < 128; i++) {
  141. // Note: Only save channel 0. Since MPE mode won't be commonly used, it's pointless to save all 16 channels.
  142. json_array_append_new(valuesJ, json_integer(ccValues[i][0]));
  143. }
  144. json_object_set_new(rootJ, "values", valuesJ);
  145. json_object_set_new(rootJ, "midi", midiInput.toJson());
  146. json_object_set_new(rootJ, "smooth", json_boolean(smooth));
  147. json_object_set_new(rootJ, "mpeMode", json_boolean(mpeMode));
  148. json_object_set_new(rootJ, "lsbMode", json_boolean(lsbMode));
  149. return rootJ;
  150. }
  151. void dataFromJson(json_t* rootJ) override {
  152. json_t* ccsJ = json_object_get(rootJ, "ccs");
  153. if (ccsJ) {
  154. for (int i = 0; i < 16; i++) {
  155. json_t* ccJ = json_array_get(ccsJ, i);
  156. if (ccJ)
  157. learnedCcs[i] = json_integer_value(ccJ);
  158. }
  159. }
  160. json_t* valuesJ = json_object_get(rootJ, "values");
  161. if (valuesJ) {
  162. for (int i = 0; i < 128; i++) {
  163. json_t* valueJ = json_array_get(valuesJ, i);
  164. if (valueJ) {
  165. ccValues[i][0] = json_integer_value(valueJ);
  166. }
  167. }
  168. }
  169. json_t* midiJ = json_object_get(rootJ, "midi");
  170. if (midiJ)
  171. midiInput.fromJson(midiJ);
  172. json_t* smoothJ = json_object_get(rootJ, "smooth");
  173. if (smoothJ)
  174. smooth = json_boolean_value(smoothJ);
  175. json_t* mpeModeJ = json_object_get(rootJ, "mpeMode");
  176. if (mpeModeJ)
  177. mpeMode = json_boolean_value(mpeModeJ);
  178. json_t* lsbEnabledJ = json_object_get(rootJ, "lsbMode");
  179. if (lsbEnabledJ)
  180. lsbMode = json_boolean_value(lsbEnabledJ);
  181. }
  182. };
  183. struct MIDI_CCWidget : ModuleWidget {
  184. MIDI_CCWidget(MIDI_CC* module) {
  185. setModule(module);
  186. setPanel(Svg::load(asset::system("res/Core/MIDI-CC.svg")));
  187. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  188. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  189. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  190. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  191. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDI_CC::CC_OUTPUT + 0));
  192. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDI_CC::CC_OUTPUT + 1));
  193. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDI_CC::CC_OUTPUT + 2));
  194. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDI_CC::CC_OUTPUT + 3));
  195. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDI_CC::CC_OUTPUT + 4));
  196. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDI_CC::CC_OUTPUT + 5));
  197. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDI_CC::CC_OUTPUT + 6));
  198. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDI_CC::CC_OUTPUT + 7));
  199. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDI_CC::CC_OUTPUT + 8));
  200. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDI_CC::CC_OUTPUT + 9));
  201. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDI_CC::CC_OUTPUT + 10));
  202. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDI_CC::CC_OUTPUT + 11));
  203. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDI_CC::CC_OUTPUT + 12));
  204. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDI_CC::CC_OUTPUT + 13));
  205. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDI_CC::CC_OUTPUT + 14));
  206. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDI_CC::CC_OUTPUT + 15));
  207. typedef Grid16MidiWidget<CcChoice<MIDI_CC>> TMidiWidget;
  208. TMidiWidget* midiWidget = createWidget<TMidiWidget>(mm2px(Vec(3.399621, 14.837339)));
  209. midiWidget->box.size = mm2px(Vec(44, 54.667));
  210. midiWidget->setMidiPort(module ? &module->midiInput : NULL);
  211. midiWidget->setModule(module);
  212. addChild(midiWidget);
  213. }
  214. void appendContextMenu(Menu* menu) override {
  215. MIDI_CC* module = dynamic_cast<MIDI_CC*>(this->module);
  216. menu->addChild(new MenuSeparator);
  217. menu->addChild(createBoolPtrMenuItem("Smooth CC", &module->smooth));
  218. menu->addChild(createBoolPtrMenuItem("MPE mode", &module->mpeMode));
  219. menu->addChild(createBoolPtrMenuItem("CC 0-31 controls are 14-bit", &module->lsbMode));
  220. }
  221. };
  222. // Use legacy slug for compatibility
  223. Model* modelMIDI_CC = createModel<MIDI_CC, MIDI_CCWidget>("MIDICCToCVInterface");
  224. } // namespace core
  225. } // namespace rack