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.

262 lines
5.7KB

  1. #include <list>
  2. #include <algorithm>
  3. #include "rtmidi/RtMidi.h"
  4. #include "core.hpp"
  5. #include "MidiIO.hpp"
  6. using namespace rack;
  7. /**
  8. * MidiIO implements the shared functionality of all midi modules, namely:
  9. * + Channel Selection (including helper for storing json)
  10. * + Interface Selection (including helper for storing json)
  11. * + rtMidi initialisation (input or output)
  12. */
  13. MidiIO::MidiIO(bool isOut) {
  14. channel = -1;
  15. this->isOut = isOut;
  16. // TODO
  17. // Support MIDI out
  18. assert(!isOut);
  19. };
  20. void MidiIO::setChannel(int channel) {
  21. this->channel = channel;
  22. }
  23. std::unordered_map<std::string, MidiInWrapper *> MidiIO::midiInMap = {};
  24. json_t *MidiIO::addBaseJson(json_t *rootJ) {
  25. if (deviceName != "") {
  26. json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str()));
  27. json_object_set_new(rootJ, "channel", json_integer(channel));
  28. }
  29. return rootJ;
  30. }
  31. void MidiIO::baseFromJson(json_t *rootJ) {
  32. json_t *portNameJ = json_object_get(rootJ, "interfaceName");
  33. if (portNameJ) {
  34. openDevice(json_string_value(portNameJ));
  35. }
  36. json_t *channelJ = json_object_get(rootJ, "channel");
  37. if (channelJ) {
  38. setChannel(json_integer_value(channelJ));
  39. }
  40. }
  41. std::vector<std::string> MidiIO::getDevices() {
  42. std::vector<std::string> names = {};
  43. if (isOut) {
  44. // TODO
  45. return names;
  46. }
  47. RtMidiIn *m;
  48. try {
  49. m = new RtMidiIn();
  50. } catch (RtMidiError &error) {
  51. log(WARN, "Failed to create RtMidiIn: %s", error.getMessage().c_str());
  52. return names;
  53. }
  54. for (unsigned int i = 0; i < m->getPortCount(); i++) {
  55. names.push_back(m->getPortName(i));
  56. }
  57. if (!isPortOpen())
  58. delete (m);
  59. return names;
  60. }
  61. void MidiIO::openDevice(std::string deviceName) {
  62. if (this->id > 0 || this->deviceName != "") {
  63. close();
  64. }
  65. MidiInWrapper *mw = midiInMap[deviceName];
  66. if (!mw) {
  67. try {
  68. mw = new MidiInWrapper();
  69. midiInMap[deviceName] = mw;
  70. for (unsigned int i = 0; i < mw->getPortCount(); i++) {
  71. if (deviceName == mw->getPortName(i)) {
  72. mw->openPort(i);
  73. break;
  74. }
  75. }
  76. if (!mw->isPortOpen()) {
  77. log(WARN, "Failed to create RtMidiIn: No such device %s", deviceName.c_str());
  78. this->deviceName = "";
  79. this->id = -1;
  80. return;
  81. }
  82. }
  83. catch (RtMidiError &error) {
  84. log(WARN, "Failed to create RtMidiIn: %s", error.getMessage().c_str());
  85. this->deviceName = "";
  86. this->id = -1;
  87. return;
  88. }
  89. }
  90. this->deviceName = deviceName;
  91. id = midiInMap[deviceName]->add();
  92. onDeviceChange();
  93. }
  94. void MidiIO::setIgnores(bool ignoreSysex, bool ignoreTime, bool ignoreSense) {
  95. bool sy = true, ti = true, se = true;
  96. midiInMap[deviceName]->ignoresMap[id].midiSysex = ignoreSysex;
  97. midiInMap[deviceName]->ignoresMap[id].midiTime = ignoreTime;
  98. midiInMap[deviceName]->ignoresMap[id].midiSense = ignoreSense;
  99. for (auto kv : midiInMap[deviceName]->ignoresMap) {
  100. sy = sy && kv.second.midiSysex;
  101. ti = ti && kv.second.midiTime;
  102. se = se && kv.second.midiSense;
  103. }
  104. midiInMap[deviceName]->ignoreTypes(se, ti, se);
  105. }
  106. std::string MidiIO::getDeviceName() {
  107. return deviceName;
  108. }
  109. double MidiIO::getMessage(std::vector<unsigned char> *msg) {
  110. MidiMessage next_msg = MidiMessage();
  111. MidiInWrapper *mw = midiInMap[deviceName];
  112. if (!mw) {
  113. log(WARN, "Device not opened!: %s", deviceName.c_str());
  114. return 0;
  115. }
  116. next_msg.timeStamp = mw->getMessage(&next_msg.bytes);
  117. if (next_msg.bytes.size() > 0) {
  118. for (auto &kv : mw->idMessagesMap) {
  119. kv.second.push_back(next_msg);
  120. }
  121. }
  122. if (mw->idMessagesMap[id].size() > 0) {
  123. next_msg = mw->idMessagesMap[id].front();
  124. mw->idMessagesMap[id].pop_front();
  125. }
  126. *msg = next_msg.bytes;
  127. return next_msg.timeStamp;
  128. }
  129. bool MidiIO::isPortOpen() {
  130. return id > 0;
  131. }
  132. void MidiIO::close() {
  133. MidiInWrapper *mw = midiInMap[deviceName];
  134. if (!mw || id < 0) {
  135. //log(WARN, "Trying to close already closed device!");
  136. return;
  137. }
  138. setIgnores(); // reset ignore types for this instance
  139. mw->erase(id);
  140. if (mw->idMessagesMap.size() == 0) {
  141. mw->closePort();
  142. midiInMap.erase(deviceName);
  143. delete (mw);
  144. }
  145. id = -1;
  146. deviceName = "";
  147. }
  148. void MidiItem::onAction(EventAction &e) {
  149. midiModule->resetMidi(); // reset Midi values
  150. midiModule->openDevice(text);
  151. }
  152. void MidiChoice::onAction(EventAction &e) {
  153. Menu *menu = gScene->createMenu();
  154. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  155. menu->box.size.x = box.size.x;
  156. {
  157. MidiItem *midiItem = new MidiItem();
  158. midiItem->midiModule = midiModule;
  159. midiItem->text = "";
  160. menu->pushChild(midiItem);
  161. }
  162. std::vector<std::string> deviceNames = midiModule->getDevices();
  163. for (unsigned int i = 0; i < deviceNames.size(); i++) {
  164. MidiItem *midiItem = new MidiItem();
  165. midiItem->midiModule = midiModule;
  166. midiItem->text = deviceNames[i];
  167. menu->pushChild(midiItem);
  168. }
  169. }
  170. void MidiChoice::step() {
  171. if (midiModule->getDeviceName() == "") {
  172. text = "No Device";
  173. return;
  174. }
  175. std::string name = midiModule->getDeviceName();
  176. text = ellipsize(name, 15);
  177. }
  178. void ChannelItem::onAction(EventAction &e) {
  179. midiModule->resetMidi(); // reset Midi values
  180. midiModule->setChannel(channel);
  181. }
  182. void ChannelChoice::onAction(EventAction &e) {
  183. Menu *menu = gScene->createMenu();
  184. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  185. menu->box.size.x = box.size.x;
  186. {
  187. ChannelItem *channelItem = new ChannelItem();
  188. channelItem->midiModule = midiModule;
  189. channelItem->channel = -1;
  190. channelItem->text = "All";
  191. menu->pushChild(channelItem);
  192. }
  193. for (int channel = 0; channel < 16; channel++) {
  194. ChannelItem *channelItem = new ChannelItem();
  195. channelItem->midiModule = midiModule;
  196. channelItem->channel = channel;
  197. channelItem->text = stringf("%d", channel + 1);
  198. menu->pushChild(channelItem);
  199. }
  200. }
  201. void ChannelChoice::step() {
  202. text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
  203. }