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.

241 lines
5.4KB

  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. static RtMidiIn *m = new RtMidiIn();
  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][0] = ignoreSysex;
  80. midiInMap[deviceName]->ignoresMap[id][1] = ignoreTime;
  81. midiInMap[deviceName]->ignoresMap[id][2] = ignoreSense;
  82. for (auto kv : midiInMap[deviceName]->ignoresMap) {
  83. sy = sy && kv.second[0];
  84. ti = ti && kv.second[1];
  85. se = se && kv.second[2];
  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. std::vector<unsigned char> next_msg;
  94. MidiInWrapper *mw = midiInMap[deviceName];
  95. if (!mw) {
  96. fprintf(stderr, "Device not opened!: %s\n", deviceName.c_str());
  97. return 0;
  98. }
  99. double stamp = midiInMap[deviceName]->getMessage(&next_msg);
  100. if (next_msg.size() > 0) {
  101. for (auto kv : mw->idMessagesMap) {
  102. mw->idMessagesMap[kv.first].push_back(next_msg);
  103. mw->idStampsMap[kv.first].push_back(stamp);
  104. }
  105. }
  106. if (mw->idMessagesMap[id].size() <= 0) {
  107. *msg = next_msg;
  108. return stamp;
  109. }
  110. *msg = mw->idMessagesMap[id].front();
  111. stamp = mw->idStampsMap[id].front();
  112. mw->idMessagesMap[id].pop_front();
  113. return stamp;
  114. }
  115. bool MidiIO::isPortOpen() {
  116. return id > 0;
  117. }
  118. void MidiIO::close() {
  119. MidiInWrapper *mw = midiInMap[deviceName];
  120. if (!mw || id < 0) {
  121. //fprintf(stderr, "Trying to close already closed device!\n");
  122. return;
  123. }
  124. setIgnores(); // reset ignore types for this instance
  125. mw->erase(id);
  126. if (mw->subscribers == 0) {
  127. mw->closePort();
  128. midiInMap.erase(deviceName);
  129. }
  130. id = -1;
  131. deviceName = "";
  132. }
  133. void MidiItem::onAction() {
  134. midiModule->resetMidi(); // reset Midi values
  135. midiModule->openDevice(text);
  136. }
  137. void MidiChoice::onAction() {
  138. Menu *menu = gScene->createMenu();
  139. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  140. menu->box.size.x = box.size.x;
  141. {
  142. MidiItem *midiItem = new MidiItem();
  143. midiItem->midiModule = midiModule;
  144. midiItem->text = "";
  145. menu->pushChild(midiItem);
  146. }
  147. std::vector<std::string> deviceNames = midiModule->getDevices();
  148. for (unsigned int i = 0; i < deviceNames.size(); i++) {
  149. MidiItem *midiItem = new MidiItem();
  150. midiItem->midiModule = midiModule;
  151. midiItem->text = deviceNames[i];
  152. menu->pushChild(midiItem);
  153. }
  154. }
  155. void MidiChoice::step() {
  156. if (midiModule->getDeviceName() == "") {
  157. text = "No Device";
  158. return;
  159. }
  160. std::string name = midiModule->getDeviceName();
  161. text = ellipsize(name, 15);
  162. }
  163. void ChannelItem::onAction() {
  164. midiModule->resetMidi(); // reset Midi values
  165. midiModule->setChannel(channel);
  166. }
  167. void ChannelChoice::onAction() {
  168. Menu *menu = gScene->createMenu();
  169. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  170. menu->box.size.x = box.size.x;
  171. {
  172. ChannelItem *channelItem = new ChannelItem();
  173. channelItem->midiModule = midiModule;
  174. channelItem->channel = -1;
  175. channelItem->text = "All";
  176. menu->pushChild(channelItem);
  177. }
  178. for (int channel = 0; channel < 16; channel++) {
  179. ChannelItem *channelItem = new ChannelItem();
  180. channelItem->midiModule = midiModule;
  181. channelItem->channel = channel;
  182. channelItem->text = stringf("%d", channel + 1);
  183. menu->pushChild(channelItem);
  184. }
  185. }
  186. void ChannelChoice::step() {
  187. text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
  188. }