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.

240 lines
5.3KB

  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. if (isOut) {
  17. fprintf(stderr, "Midi Out is currently not supported (will be added soon)");
  18. }
  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. /* Note: we could also use an existing interface if one exists */
  43. RtMidiIn m;
  44. std::vector<std::string> names = {};
  45. for (unsigned int i = 0; i < m.getPortCount(); i++) {
  46. names.push_back(m.getPortName(i));
  47. }
  48. return names;
  49. }
  50. void MidiIO::openDevice(std::string deviceName) {
  51. if (this->id > 0 || this->deviceName != "") {
  52. close();
  53. }
  54. MidiInWrapper *mw = midiInMap[deviceName];
  55. if (!mw) {
  56. try {
  57. mw = new MidiInWrapper();
  58. midiInMap[deviceName] = mw;
  59. for (unsigned int i = 0; i < mw->getPortCount(); i++) {
  60. if (deviceName == mw->getPortName(i)) {
  61. mw->openPort(i);
  62. break;
  63. }
  64. }
  65. }
  66. catch (RtMidiError &error) {
  67. fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
  68. this->deviceName = "";
  69. this->id = -1;
  70. return;
  71. }
  72. }
  73. this->deviceName = deviceName;
  74. id = midiInMap[deviceName]->add();
  75. onDeviceChange();
  76. }
  77. void MidiIO::setIgnores(bool ignoreSysex, bool ignoreTime, bool ignoreSense) {
  78. bool sy = true, ti = true, se = true;
  79. midiInMap[deviceName]->ignoresMap[id].midiSysex = ignoreSysex;
  80. midiInMap[deviceName]->ignoresMap[id].midiTime = ignoreTime;
  81. midiInMap[deviceName]->ignoresMap[id].midiSense = ignoreSense;
  82. for (auto kv : midiInMap[deviceName]->ignoresMap) {
  83. sy = sy && kv.second.midiSysex;
  84. ti = ti && kv.second.midiTime;
  85. se = se && kv.second.midiSense;
  86. }
  87. midiInMap[deviceName]->ignoreTypes(se, ti, se);
  88. }
  89. std::string MidiIO::getDeviceName() {
  90. return deviceName;
  91. }
  92. double MidiIO::getMessage(std::vector<unsigned char> *msg) {
  93. MidiMessage next_msg = MidiMessage();
  94. MidiInWrapper *mw = midiInMap[deviceName];
  95. if (!mw) {
  96. fprintf(stderr, "Device not opened!: %s\n", deviceName.c_str());
  97. return 0;
  98. }
  99. next_msg.timeStamp = midiInMap[deviceName]->getMessage(&next_msg.bytes);
  100. if (next_msg.bytes.size() > 0) {
  101. for (auto &kv : mw->idMessagesMap) {
  102. kv.second.push_back(next_msg);
  103. }
  104. }
  105. if (mw->idMessagesMap[id].size() > 0) {
  106. next_msg = mw->idMessagesMap[id].front();
  107. mw->idMessagesMap[id].pop_front();
  108. }
  109. *msg = next_msg.bytes;
  110. return next_msg.timeStamp;
  111. }
  112. bool MidiIO::isPortOpen() {
  113. return id > 0;
  114. }
  115. void MidiIO::close() {
  116. MidiInWrapper *mw = midiInMap[deviceName];
  117. if (!mw || id < 0) {
  118. //fprintf(stderr, "Trying to close already closed device!\n");
  119. return;
  120. }
  121. setIgnores(); // reset ignore types for this instance
  122. mw->erase(id);
  123. if (mw->idMessagesMap.size() == 0) {
  124. mw->closePort();
  125. midiInMap.erase(deviceName);
  126. }
  127. id = -1;
  128. deviceName = "";
  129. }
  130. void MidiItem::onAction() {
  131. midiModule->resetMidi(); // reset Midi values
  132. midiModule->openDevice(text);
  133. }
  134. void MidiChoice::onAction() {
  135. Menu *menu = gScene->createMenu();
  136. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  137. menu->box.size.x = box.size.x;
  138. {
  139. MidiItem *midiItem = new MidiItem();
  140. midiItem->midiModule = midiModule;
  141. midiItem->text = "";
  142. menu->pushChild(midiItem);
  143. }
  144. std::vector<std::string> deviceNames = midiModule->getDevices();
  145. for (unsigned int i = 0; i < deviceNames.size(); i++) {
  146. MidiItem *midiItem = new MidiItem();
  147. midiItem->midiModule = midiModule;
  148. midiItem->text = deviceNames[i];
  149. menu->pushChild(midiItem);
  150. }
  151. }
  152. void MidiChoice::step() {
  153. if (midiModule->getDeviceName() == "") {
  154. text = "No Device";
  155. return;
  156. }
  157. std::string name = midiModule->getDeviceName();
  158. text = ellipsize(name, 15);
  159. }
  160. void ChannelItem::onAction() {
  161. midiModule->resetMidi(); // reset Midi values
  162. midiModule->setChannel(channel);
  163. }
  164. void ChannelChoice::onAction() {
  165. Menu *menu = gScene->createMenu();
  166. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  167. menu->box.size.x = box.size.x;
  168. {
  169. ChannelItem *channelItem = new ChannelItem();
  170. channelItem->midiModule = midiModule;
  171. channelItem->channel = -1;
  172. channelItem->text = "All";
  173. menu->pushChild(channelItem);
  174. }
  175. for (int channel = 0; channel < 16; channel++) {
  176. ChannelItem *channelItem = new ChannelItem();
  177. channelItem->midiModule = midiModule;
  178. channelItem->channel = channel;
  179. channelItem->text = stringf("%d", channel + 1);
  180. menu->pushChild(channelItem);
  181. }
  182. }
  183. void ChannelChoice::step() {
  184. text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
  185. }