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.

414 lines
12KB

  1. #ifdef USE_VST2
  2. extern void vst2_get_timing_info (int *_retPlaying, float *_retBPM, float *_retSongPosPPQ);
  3. #endif // USE_VST2
  4. #include "global_pre.hpp"
  5. #include "Core.hpp"
  6. #include "midi.hpp"
  7. #include "dsp/digital.hpp"
  8. #include "dsp/filter.hpp"
  9. #include <algorithm>
  10. #include "global.hpp"
  11. struct MIDIToCVInterface : Module {
  12. enum ParamIds {
  13. NUM_PARAMS
  14. };
  15. enum InputIds {
  16. NUM_INPUTS
  17. };
  18. enum OutputIds {
  19. CV_OUTPUT,
  20. GATE_OUTPUT,
  21. VELOCITY_OUTPUT,
  22. AFTERTOUCH_OUTPUT,
  23. PITCH_OUTPUT,
  24. MOD_OUTPUT,
  25. RETRIGGER_OUTPUT,
  26. CLOCK_1_OUTPUT,
  27. CLOCK_2_OUTPUT,
  28. START_OUTPUT,
  29. STOP_OUTPUT,
  30. CONTINUE_OUTPUT,
  31. NUM_OUTPUTS
  32. };
  33. enum LightIds {
  34. NUM_LIGHTS
  35. };
  36. MidiInputQueue midiInput;
  37. uint8_t mod = 0;
  38. ExponentialFilter modFilter;
  39. uint16_t pitch = 8192;
  40. ExponentialFilter pitchFilter;
  41. PulseGenerator retriggerPulse;
  42. PulseGenerator clockPulses[2];
  43. PulseGenerator startPulse;
  44. PulseGenerator stopPulse;
  45. PulseGenerator continuePulse;
  46. int clock = 0;
  47. int divisions[2];
  48. #ifdef USE_VST2
  49. int b_vst_transport_playing = 0;
  50. float vst_timing_clock_samples = 0.0f;
  51. #endif // USE_VST2
  52. struct NoteData {
  53. uint8_t velocity = 0;
  54. uint8_t aftertouch = 0;
  55. };
  56. NoteData noteData[128];
  57. std::vector<uint8_t> heldNotes;
  58. uint8_t lastNote;
  59. bool pedal;
  60. bool gate;
  61. MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), heldNotes(128) {
  62. onReset();
  63. }
  64. json_t *toJson() override {
  65. json_t *rootJ = json_object();
  66. json_t *divisionsJ = json_array();
  67. for (int i = 0; i < 2; i++) {
  68. json_t *divisionJ = json_integer(divisions[i]);
  69. json_array_append_new(divisionsJ, divisionJ);
  70. }
  71. json_object_set_new(rootJ, "divisions", divisionsJ);
  72. json_object_set_new(rootJ, "midi", midiInput.toJson());
  73. return rootJ;
  74. }
  75. void fromJson(json_t *rootJ) override {
  76. json_t *divisionsJ = json_object_get(rootJ, "divisions");
  77. if (divisionsJ) {
  78. for (int i = 0; i < 2; i++) {
  79. json_t *divisionJ = json_array_get(divisionsJ, i);
  80. if (divisionJ)
  81. divisions[i] = json_integer_value(divisionJ);
  82. }
  83. }
  84. json_t *midiJ = json_object_get(rootJ, "midi");
  85. if (midiJ)
  86. midiInput.fromJson(midiJ);
  87. }
  88. void onReset() override {
  89. heldNotes.clear();
  90. lastNote = 60;
  91. pedal = false;
  92. gate = false;
  93. clock = 0;
  94. divisions[0] = 24;
  95. divisions[1] = 6;
  96. #ifdef USE_VST2
  97. b_vst_transport_playing = 0;
  98. vst_timing_clock_samples = 0.0f;
  99. #endif // USE_VST2
  100. }
  101. void pressNote(uint8_t note) {
  102. // Remove existing similar note
  103. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  104. if (it != heldNotes.end())
  105. heldNotes.erase(it);
  106. // Push note
  107. heldNotes.push_back(note);
  108. lastNote = note;
  109. gate = true;
  110. retriggerPulse.trigger(1e-3);
  111. }
  112. void releaseNote(uint8_t note) {
  113. // Remove the note
  114. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  115. if (it != heldNotes.end())
  116. heldNotes.erase(it);
  117. // Hold note if pedal is pressed
  118. if (pedal)
  119. return;
  120. // Set last note
  121. if (!heldNotes.empty()) {
  122. lastNote = heldNotes[heldNotes.size() - 1];
  123. gate = true;
  124. }
  125. else {
  126. gate = false;
  127. }
  128. }
  129. void pressPedal() {
  130. pedal = true;
  131. }
  132. void releasePedal() {
  133. pedal = false;
  134. releaseNote(255);
  135. }
  136. #ifdef USE_VST2
  137. void handleVSTClock(void) {
  138. // (note) calling this _per sample_ is quite excessive (but should not be a problem)
  139. // (note) the VST host might not update this per sample, though
  140. float songPosPPQ = -1.0f;
  141. float bpm = -1.0f;
  142. int bPlaying = 0;
  143. vst2_get_timing_info(&bPlaying, &bpm, &songPosPPQ);
  144. // printf("xxx songPosPPQ=%f bpm=%f bPlaying=%d\n", songPosPPQ, bpm, bPlaying);
  145. if(b_vst_transport_playing ^ bPlaying)
  146. {
  147. if(bPlaying)
  148. {
  149. startPulse.trigger(1e-3);
  150. clock = 0;
  151. vst_timing_clock_samples = 0.0f;
  152. }
  153. else
  154. {
  155. stopPulse.trigger(1e-3);
  156. // Reset timing
  157. clock = 0;
  158. }
  159. b_vst_transport_playing = bPlaying;
  160. }
  161. if(bPlaying && (bpm > 0.0f))
  162. {
  163. float secondsPerQuarter = 60.0f / bpm;
  164. // 24 clock ticks per quarter note (MIDI timing clock)
  165. float samplesPerTimingClockTick = (engineGetSampleRate() * secondsPerQuarter) / 24.0f;
  166. if (clock % divisions[0] == 0) {
  167. clockPulses[0].trigger(1e-3);
  168. }
  169. if (clock % divisions[1] == 0) {
  170. clockPulses[1].trigger(1e-3);
  171. }
  172. vst_timing_clock_samples += 1.0f;
  173. if(vst_timing_clock_samples >= samplesPerTimingClockTick)
  174. {
  175. vst_timing_clock_samples -= samplesPerTimingClockTick;
  176. // if(++clock < 0) clock = 0 (may be optimized away by a C compiler)
  177. union {
  178. int s;
  179. unsigned int u;
  180. } uclock;
  181. uclock.s = clock;
  182. uclock.u++;
  183. clock = (uclock.s < 0) ? 0 : uclock.s;
  184. }
  185. } // if bPlaying
  186. }
  187. #endif // USE_VST2
  188. void step() override {
  189. MidiMessage msg;
  190. while (midiInput.shift(&msg)) {
  191. processMessage(msg);
  192. }
  193. float deltaTime = engineGetSampleTime();
  194. #ifdef USE_VST2
  195. handleVSTClock();
  196. #endif // USE_VST2
  197. outputs[CV_OUTPUT].value = (lastNote - 60) / 12.f;
  198. outputs[GATE_OUTPUT].value = gate ? 10.f : 0.f;
  199. outputs[VELOCITY_OUTPUT].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f);
  200. outputs[AFTERTOUCH_OUTPUT].value = rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f);
  201. pitchFilter.lambda = 100.f * deltaTime;
  202. outputs[PITCH_OUTPUT].value = pitchFilter.process(rescale(pitch, 0, 16384, -5.f, 5.f));
  203. modFilter.lambda = 100.f * deltaTime;
  204. outputs[MOD_OUTPUT].value = modFilter.process(rescale(mod, 0, 127, 0.f, 10.f));
  205. outputs[RETRIGGER_OUTPUT].value = retriggerPulse.process(deltaTime) ? 10.f : 0.f;
  206. outputs[CLOCK_1_OUTPUT].value = clockPulses[0].process(deltaTime) ? 10.f : 0.f;
  207. outputs[CLOCK_2_OUTPUT].value = clockPulses[1].process(deltaTime) ? 10.f : 0.f;
  208. outputs[START_OUTPUT].value = startPulse.process(deltaTime) ? 10.f : 0.f;
  209. outputs[STOP_OUTPUT].value = stopPulse.process(deltaTime) ? 10.f : 0.f;
  210. outputs[CONTINUE_OUTPUT].value = continuePulse.process(deltaTime) ? 10.f : 0.f;
  211. }
  212. void processMessage(MidiMessage msg) {
  213. // debug("MIDI: %01x %01x %02x %02x", msg.status(), msg.channel(), msg.note(), msg.value());
  214. switch (msg.status()) {
  215. // note off
  216. case 0x8: {
  217. releaseNote(msg.note());
  218. } break;
  219. // note on
  220. case 0x9: {
  221. if (msg.value() > 0) {
  222. noteData[msg.note()].velocity = msg.value();
  223. pressNote(msg.note());
  224. }
  225. else {
  226. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  227. releaseNote(msg.note());
  228. }
  229. } break;
  230. // channel aftertouch
  231. case 0xa: {
  232. uint8_t note = msg.note();
  233. noteData[note].aftertouch = msg.value();
  234. } break;
  235. // cc
  236. case 0xb: {
  237. processCC(msg);
  238. } break;
  239. // pitch wheel
  240. case 0xe: {
  241. pitch = msg.value() * 128 + msg.note();
  242. } break;
  243. case 0xf: {
  244. processSystem(msg);
  245. } break;
  246. default: break;
  247. }
  248. }
  249. void processCC(MidiMessage msg) {
  250. switch (msg.note()) {
  251. // mod
  252. case 0x01: {
  253. mod = msg.value();
  254. } break;
  255. // sustain
  256. case 0x40: {
  257. if (msg.value() >= 64)
  258. pressPedal();
  259. else
  260. releasePedal();
  261. } break;
  262. default: break;
  263. }
  264. }
  265. void processSystem(MidiMessage msg) {
  266. #ifndef USE_VST2
  267. switch (msg.channel()) {
  268. // Timing
  269. case 0x8: {
  270. if (clock % divisions[0] == 0) {
  271. clockPulses[0].trigger(1e-3);
  272. }
  273. if (clock % divisions[1] == 0) {
  274. clockPulses[1].trigger(1e-3);
  275. }
  276. if (++clock >= (24*16*16)) {
  277. // Avoid overflowing the integer
  278. clock = 0;
  279. }
  280. } break;
  281. // Start
  282. case 0xa: {
  283. startPulse.trigger(1e-3);
  284. clock = 0;
  285. } break;
  286. // Continue
  287. case 0xb: {
  288. continuePulse.trigger(1e-3);
  289. } break;
  290. // Stop
  291. case 0xc: {
  292. stopPulse.trigger(1e-3);
  293. // Reset timing
  294. clock = 0;
  295. } break;
  296. default: break;
  297. }
  298. #else
  299. (void)msg;
  300. #endif // USE_VST2
  301. }
  302. };
  303. struct MIDIToCVInterfaceWidget : ModuleWidget {
  304. MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) {
  305. setPanel(SVG::load(assetGlobal("res/Core/MIDIToCVInterface.svg")));
  306. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  307. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  308. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  309. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  310. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::CV_OUTPUT));
  311. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::GATE_OUTPUT));
  312. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::VELOCITY_OUTPUT));
  313. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::AFTERTOUCH_OUTPUT));
  314. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::PITCH_OUTPUT));
  315. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::MOD_OUTPUT));
  316. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::RETRIGGER_OUTPUT));
  317. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_1_OUTPUT));
  318. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_2_OUTPUT));
  319. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::START_OUTPUT));
  320. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::STOP_OUTPUT));
  321. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CONTINUE_OUTPUT));
  322. MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
  323. midiWidget->box.size = mm2px(Vec(33.840, 28));
  324. midiWidget->midiIO = &module->midiInput;
  325. addChild(midiWidget);
  326. }
  327. void appendContextMenu(Menu *menu) override {
  328. MIDIToCVInterface *module = dynamic_cast<MIDIToCVInterface*>(this->module);
  329. struct ClockDivisionItem : MenuItem {
  330. MIDIToCVInterface *module;
  331. int index;
  332. int division;
  333. void onAction(EventAction &e) override {
  334. module->divisions[index] = division;
  335. }
  336. };
  337. struct ClockItem : MenuItem {
  338. MIDIToCVInterface *module;
  339. int index;
  340. Menu *createChildMenu() override {
  341. Menu *menu = new Menu();
  342. std::vector<int> divisions = {24*4, 24*2, 24, 24/2, 24/4, 24/8, 2, 1};
  343. std::vector<std::string> divisionNames = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
  344. for (size_t i = 0; i < divisions.size(); i++) {
  345. ClockDivisionItem *item = MenuItem::create<ClockDivisionItem>(divisionNames[i], CHECKMARK(module->divisions[index] == divisions[i]));
  346. item->module = module;
  347. item->index = index;
  348. item->division = divisions[i];
  349. menu->addChild(item);
  350. }
  351. return menu;
  352. }
  353. };
  354. menu->addChild(construct<MenuLabel>());
  355. for (int i = 0; i < 2; i++) {
  356. ClockItem *item = MenuItem::create<ClockItem>(stringf("CLK %d rate", i + 1));
  357. item->module = module;
  358. item->index = i;
  359. menu->addChild(item);
  360. }
  361. }
  362. };
  363. RACK_PLUGIN_MODEL_INIT(Core, MIDIToCVInterface) {
  364. Model *modelMIDIToCVInterface = Model::create<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG);
  365. return modelMIDIToCVInterface;
  366. }