Audio plugin host https://kx.studio/carla
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.

midi-base.hpp 14KB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #ifndef MIDI_BASE_HPP_INCLUDED
  18. #define MIDI_BASE_HPP_INCLUDED
  19. #include "CarlaMIDI.h"
  20. #include "CarlaMutex.hpp"
  21. #include "LinkedList.hpp"
  22. #include "CarlaJuceUtils.hpp"
  23. #include "CarlaMathUtils.hpp"
  24. // -----------------------------------------------------------------------
  25. #define MAX_EVENT_DATA_SIZE 4
  26. #define MIN_PREALLOCATED_EVENT_COUNT 100
  27. #define MAX_PREALLOCATED_EVENT_COUNT 1000
  28. // -----------------------------------------------------------------------
  29. struct RawMidiEvent {
  30. uint64_t time;
  31. uint8_t size;
  32. uint8_t data[MAX_EVENT_DATA_SIZE];
  33. };
  34. // -----------------------------------------------------------------------
  35. class AbstractMidiPlayer
  36. {
  37. public:
  38. virtual ~AbstractMidiPlayer() {}
  39. virtual void writeMidiEvent(const uint8_t port, const long double timePosFrame, const RawMidiEvent* const event) = 0;
  40. };
  41. // -----------------------------------------------------------------------
  42. class MidiPattern
  43. {
  44. public:
  45. MidiPattern(AbstractMidiPlayer* const player) noexcept
  46. : kPlayer(player),
  47. fMidiPort(0),
  48. fStartTime(0),
  49. fMutex(),
  50. fData(),
  51. leakDetector_MidiPattern()
  52. {
  53. CARLA_SAFE_ASSERT(kPlayer != nullptr);
  54. }
  55. ~MidiPattern() noexcept
  56. {
  57. clear();
  58. }
  59. // -------------------------------------------------------------------
  60. // add data, time always counts from 0
  61. void addControl(const uint64_t time, const uint8_t channel, const uint8_t control, const uint8_t value)
  62. {
  63. RawMidiEvent* const ctrlEvent(new RawMidiEvent());
  64. ctrlEvent->time = time;
  65. ctrlEvent->size = 3;
  66. ctrlEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
  67. ctrlEvent->data[1] = control;
  68. ctrlEvent->data[2] = value;
  69. appendSorted(ctrlEvent);
  70. }
  71. void addChannelPressure(const uint64_t time, const uint8_t channel, const uint8_t pressure)
  72. {
  73. RawMidiEvent* const pressureEvent(new RawMidiEvent());
  74. pressureEvent->time = time;
  75. pressureEvent->size = 2;
  76. pressureEvent->data[0] = uint8_t(MIDI_STATUS_CHANNEL_PRESSURE | (channel & MIDI_CHANNEL_BIT));
  77. pressureEvent->data[1] = pressure;
  78. appendSorted(pressureEvent);
  79. }
  80. void addNote(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration)
  81. {
  82. addNoteOn(time, channel, pitch, velocity);
  83. addNoteOff(time+duration, channel, pitch, velocity);
  84. }
  85. void addNoteOn(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity)
  86. {
  87. RawMidiEvent* const noteOnEvent(new RawMidiEvent());
  88. noteOnEvent->time = time;
  89. noteOnEvent->size = 3;
  90. noteOnEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_ON | (channel & MIDI_CHANNEL_BIT));
  91. noteOnEvent->data[1] = pitch;
  92. noteOnEvent->data[2] = velocity;
  93. appendSorted(noteOnEvent);
  94. }
  95. void addNoteOff(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity = 0)
  96. {
  97. RawMidiEvent* const noteOffEvent(new RawMidiEvent());
  98. noteOffEvent->time = time;
  99. noteOffEvent->size = 3;
  100. noteOffEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (channel & MIDI_CHANNEL_BIT));
  101. noteOffEvent->data[1] = pitch;
  102. noteOffEvent->data[2] = velocity;
  103. appendSorted(noteOffEvent);
  104. }
  105. void addNoteAftertouch(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t pressure)
  106. {
  107. RawMidiEvent* const noteAfterEvent(new RawMidiEvent());
  108. noteAfterEvent->time = time;
  109. noteAfterEvent->size = 3;
  110. noteAfterEvent->data[0] = uint8_t(MIDI_STATUS_POLYPHONIC_AFTERTOUCH | (channel & MIDI_CHANNEL_BIT));
  111. noteAfterEvent->data[1] = pitch;
  112. noteAfterEvent->data[2] = pressure;
  113. appendSorted(noteAfterEvent);
  114. }
  115. void addProgram(const uint64_t time, const uint8_t channel, const uint8_t bank, const uint8_t program)
  116. {
  117. RawMidiEvent* const bankEvent(new RawMidiEvent());
  118. bankEvent->time = time;
  119. bankEvent->size = 3;
  120. bankEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
  121. bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT;
  122. bankEvent->data[2] = bank;
  123. RawMidiEvent* const programEvent(new RawMidiEvent());
  124. programEvent->time = time;
  125. programEvent->size = 2;
  126. programEvent->data[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT));
  127. programEvent->data[1] = program;
  128. appendSorted(bankEvent);
  129. appendSorted(programEvent);
  130. }
  131. void addPitchbend(const uint64_t time, const uint8_t channel, const uint8_t lsb, const uint8_t msb)
  132. {
  133. RawMidiEvent* const pressureEvent(new RawMidiEvent());
  134. pressureEvent->time = time;
  135. pressureEvent->size = 3;
  136. pressureEvent->data[0] = uint8_t(MIDI_STATUS_PITCH_WHEEL_CONTROL | (channel & MIDI_CHANNEL_BIT));
  137. pressureEvent->data[1] = lsb;
  138. pressureEvent->data[2] = msb;
  139. appendSorted(pressureEvent);
  140. }
  141. void addRaw(const uint64_t time, const uint8_t* const data, const uint8_t size)
  142. {
  143. RawMidiEvent* const rawEvent(new RawMidiEvent());
  144. rawEvent->time = time;
  145. rawEvent->size = size;
  146. carla_copy<uint8_t>(rawEvent->data, data, size);
  147. appendSorted(rawEvent);
  148. }
  149. // -------------------------------------------------------------------
  150. // remove data
  151. void removeRaw(const uint64_t time, const uint8_t* const data, const uint8_t size)
  152. {
  153. const CarlaMutexLocker sl(fMutex);
  154. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  155. {
  156. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  157. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  158. if (rawMidiEvent->time != time)
  159. continue;
  160. if (rawMidiEvent->size != size)
  161. continue;
  162. if (std::memcmp(rawMidiEvent->data, data, size) != 0)
  163. continue;
  164. delete rawMidiEvent;
  165. fData.remove(it);
  166. return;
  167. }
  168. carla_stderr("MidiPattern::removeRaw(" P_INT64 ", %p, %i) - unable to find event to remove", time, data, size);
  169. }
  170. // -------------------------------------------------------------------
  171. // clear
  172. void clear() noexcept
  173. {
  174. const CarlaMutexLocker sl(fMutex);
  175. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  176. delete it.getValue(nullptr);
  177. fData.clear();
  178. }
  179. // -------------------------------------------------------------------
  180. // play on time
  181. void play(const uint64_t timePosFrame, const uint32_t frames)
  182. {
  183. play(static_cast<long double>(timePosFrame), static_cast<double>(frames));
  184. }
  185. void play(long double timePosFrame, const double frames)
  186. {
  187. if (! fMutex.tryLock())
  188. return;
  189. if (fStartTime != 0)
  190. timePosFrame += static_cast<long double>(fStartTime);
  191. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  192. {
  193. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  194. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  195. if (timePosFrame > rawMidiEvent->time)
  196. continue;
  197. if (timePosFrame + frames <= rawMidiEvent->time)
  198. continue;
  199. kPlayer->writeMidiEvent(fMidiPort, static_cast<long double>(rawMidiEvent->time)-timePosFrame, rawMidiEvent);
  200. }
  201. fMutex.unlock();
  202. }
  203. // -------------------------------------------------------------------
  204. // configure
  205. void setMidiPort(const uint8_t port) noexcept
  206. {
  207. fMidiPort = port;
  208. }
  209. void setStartTime(const uint64_t time) noexcept
  210. {
  211. fStartTime = time;
  212. }
  213. // -------------------------------------------------------------------
  214. // special
  215. const CarlaMutex& getLock() const noexcept
  216. {
  217. return fMutex;
  218. }
  219. LinkedList<const RawMidiEvent*>::Itenerator iteneratorBegin() const noexcept
  220. {
  221. return fData.begin();
  222. }
  223. // -------------------------------------------------------------------
  224. // state
  225. char* getState() const
  226. {
  227. static const std::size_t maxTimeSize = 20; // std::strlen("18446744073709551615");
  228. static const std::size_t maxDataSize = 4 + 4*MAX_EVENT_DATA_SIZE; // std::strlen("0xFF:127:127:127");
  229. static const std::size_t maxMsgSize = maxTimeSize + 3 /* sep + size + sep */ + maxDataSize + 1 /* newline */;
  230. const CarlaMutexLocker sl(fMutex);
  231. if (fData.count() == 0)
  232. return nullptr;
  233. char* const data((char*)std::calloc(1, fData.count()*maxMsgSize));
  234. CARLA_SAFE_ASSERT_RETURN(data != nullptr, nullptr);
  235. char* dataWrtn = data;
  236. int wrtn;
  237. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  238. {
  239. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  240. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  241. wrtn = std::snprintf(dataWrtn, maxTimeSize+4, P_INT64 ":%i:", rawMidiEvent->time, rawMidiEvent->size);
  242. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  243. dataWrtn += wrtn;
  244. wrtn = std::snprintf(dataWrtn, 5, "0x%02X", rawMidiEvent->data[0]);
  245. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  246. dataWrtn += wrtn;
  247. for (uint8_t i=1, size=rawMidiEvent->size; i<size; ++i)
  248. {
  249. wrtn = std::snprintf(dataWrtn, 5, ":%03u", rawMidiEvent->data[i]);
  250. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  251. dataWrtn += wrtn;
  252. }
  253. *dataWrtn++ = '\n';
  254. }
  255. *dataWrtn = '\0';
  256. return data;
  257. }
  258. void setState(const char* const data)
  259. {
  260. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  261. const char* dataRead = data;
  262. const char* needle;
  263. RawMidiEvent midiEvent;
  264. char tmpBuf[24];
  265. ssize_t tmpSize;
  266. clear();
  267. const CarlaMutexLocker sl(fMutex);
  268. for (; *dataRead != '\0';)
  269. {
  270. // get time
  271. needle = std::strchr(dataRead, ':');
  272. if (needle == nullptr)
  273. return;
  274. carla_zeroStruct(midiEvent);
  275. tmpSize = needle - dataRead;
  276. CARLA_SAFE_ASSERT_RETURN(tmpSize > 0 && tmpSize < 24,);
  277. std::strncpy(tmpBuf, dataRead, static_cast<size_t>(tmpSize));
  278. tmpBuf[tmpSize] = '\0';
  279. dataRead += tmpSize+1;
  280. const long long time = std::atoll(tmpBuf);
  281. CARLA_SAFE_ASSERT_RETURN(time >= 0,);
  282. midiEvent.time = static_cast<uint64_t>(time);
  283. // get size
  284. needle = std::strchr(dataRead, ':');
  285. CARLA_SAFE_ASSERT_RETURN(needle != nullptr,);
  286. tmpSize = needle - dataRead;
  287. CARLA_SAFE_ASSERT_RETURN(tmpSize > 0 && tmpSize < 24,);
  288. std::strncpy(tmpBuf, dataRead, static_cast<size_t>(tmpSize));
  289. tmpBuf[tmpSize] = '\0';
  290. dataRead += tmpSize+1;
  291. const int size = std::atoi(tmpBuf);
  292. CARLA_SAFE_ASSERT_RETURN(size > 0 && size <= MAX_EVENT_DATA_SIZE,);
  293. midiEvent.size = static_cast<uint8_t>(size);
  294. // get events
  295. for (int i=0; i<size; ++i)
  296. {
  297. CARLA_SAFE_ASSERT_RETURN(dataRead-data >= 4,);
  298. tmpSize = i==0 ? 4 : 3;
  299. std::strncpy(tmpBuf, dataRead, static_cast<size_t>(tmpSize));
  300. tmpBuf[tmpSize] = '\0';
  301. dataRead += tmpSize+1;
  302. long mdata;
  303. if (i == 0)
  304. {
  305. mdata = std::strtol(tmpBuf, nullptr, 16);
  306. CARLA_SAFE_ASSERT_RETURN(mdata >= 0x80 && mdata <= 0xFF,);
  307. }
  308. else
  309. {
  310. mdata = std::atoi(tmpBuf);
  311. CARLA_SAFE_ASSERT_RETURN(mdata >= 0 && mdata < MAX_MIDI_VALUE,);
  312. }
  313. midiEvent.data[i] = static_cast<uint8_t>(mdata);
  314. }
  315. for (int i=size; i<MAX_EVENT_DATA_SIZE; ++i)
  316. midiEvent.data[i] = 0;
  317. RawMidiEvent* const event(new RawMidiEvent());
  318. carla_copyStruct(*event, midiEvent);
  319. fData.append(event);
  320. }
  321. }
  322. // -------------------------------------------------------------------
  323. private:
  324. AbstractMidiPlayer* const kPlayer;
  325. uint8_t fMidiPort;
  326. uint64_t fStartTime;
  327. CarlaMutex fMutex;
  328. LinkedList<const RawMidiEvent*> fData;
  329. void appendSorted(const RawMidiEvent* const event)
  330. {
  331. const CarlaMutexLocker sl(fMutex);
  332. if (fData.isEmpty())
  333. {
  334. fData.append(event);
  335. return;
  336. }
  337. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  338. {
  339. const RawMidiEvent* const oldEvent(it.getValue(nullptr));
  340. CARLA_SAFE_ASSERT_CONTINUE(oldEvent != nullptr);
  341. if (event->time >= oldEvent->time)
  342. continue;
  343. fData.insertAt(event, it);
  344. return;
  345. }
  346. fData.append(event);
  347. }
  348. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiPattern)
  349. };
  350. // -----------------------------------------------------------------------
  351. #endif // MIDI_BASE_HPP_INCLUDED