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 16KB

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2020 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. fReadMutex(),
  50. fWriteMutex(),
  51. fData(),
  52. fTemporary()
  53. {
  54. CARLA_SAFE_ASSERT(kPlayer != nullptr);
  55. carla_zeroStructs(fTemporary, 2);
  56. fTemporary[0].data[0] = MIDI_STATUS_NOTE_OFF;
  57. fTemporary[1].data[0] = MIDI_STATUS_NOTE_ON;
  58. fTemporary[1].data[2] = 100;
  59. fTemporary[0].size = fTemporary[1].size = 3;
  60. }
  61. ~MidiPattern() noexcept
  62. {
  63. clear();
  64. }
  65. // -------------------------------------------------------------------
  66. // add data, time always counts from 0
  67. void addControl(const uint64_t time, const uint8_t channel, const uint8_t control, const uint8_t value)
  68. {
  69. RawMidiEvent* const ctrlEvent(new RawMidiEvent());
  70. ctrlEvent->time = time;
  71. ctrlEvent->size = 3;
  72. ctrlEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
  73. ctrlEvent->data[1] = control;
  74. ctrlEvent->data[2] = value;
  75. appendSorted(ctrlEvent);
  76. }
  77. void addChannelPressure(const uint64_t time, const uint8_t channel, const uint8_t pressure)
  78. {
  79. RawMidiEvent* const pressureEvent(new RawMidiEvent());
  80. pressureEvent->time = time;
  81. pressureEvent->size = 2;
  82. pressureEvent->data[0] = uint8_t(MIDI_STATUS_CHANNEL_PRESSURE | (channel & MIDI_CHANNEL_BIT));
  83. pressureEvent->data[1] = pressure;
  84. appendSorted(pressureEvent);
  85. }
  86. void addNote(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration)
  87. {
  88. addNoteOn(time, channel, pitch, velocity);
  89. addNoteOff(time+duration, channel, pitch, velocity);
  90. }
  91. void addNoteOn(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity)
  92. {
  93. RawMidiEvent* const noteOnEvent(new RawMidiEvent());
  94. noteOnEvent->time = time;
  95. noteOnEvent->size = 3;
  96. noteOnEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_ON | (channel & MIDI_CHANNEL_BIT));
  97. noteOnEvent->data[1] = pitch;
  98. noteOnEvent->data[2] = velocity;
  99. appendSorted(noteOnEvent);
  100. }
  101. void addNoteOff(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity = 0)
  102. {
  103. RawMidiEvent* const noteOffEvent(new RawMidiEvent());
  104. noteOffEvent->time = time;
  105. noteOffEvent->size = 3;
  106. noteOffEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (channel & MIDI_CHANNEL_BIT));
  107. noteOffEvent->data[1] = pitch;
  108. noteOffEvent->data[2] = velocity;
  109. appendSorted(noteOffEvent);
  110. }
  111. void addNoteAftertouch(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t pressure)
  112. {
  113. RawMidiEvent* const noteAfterEvent(new RawMidiEvent());
  114. noteAfterEvent->time = time;
  115. noteAfterEvent->size = 3;
  116. noteAfterEvent->data[0] = uint8_t(MIDI_STATUS_POLYPHONIC_AFTERTOUCH | (channel & MIDI_CHANNEL_BIT));
  117. noteAfterEvent->data[1] = pitch;
  118. noteAfterEvent->data[2] = pressure;
  119. appendSorted(noteAfterEvent);
  120. }
  121. void addProgram(const uint64_t time, const uint8_t channel, const uint8_t bank, const uint8_t program)
  122. {
  123. RawMidiEvent* const bankEvent(new RawMidiEvent());
  124. bankEvent->time = time;
  125. bankEvent->size = 3;
  126. bankEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
  127. bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT;
  128. bankEvent->data[2] = bank;
  129. RawMidiEvent* const programEvent(new RawMidiEvent());
  130. programEvent->time = time;
  131. programEvent->size = 2;
  132. programEvent->data[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT));
  133. programEvent->data[1] = program;
  134. appendSorted(bankEvent);
  135. appendSorted(programEvent);
  136. }
  137. void addPitchbend(const uint64_t time, const uint8_t channel, const uint8_t lsb, const uint8_t msb)
  138. {
  139. RawMidiEvent* const pressureEvent(new RawMidiEvent());
  140. pressureEvent->time = time;
  141. pressureEvent->size = 3;
  142. pressureEvent->data[0] = uint8_t(MIDI_STATUS_PITCH_WHEEL_CONTROL | (channel & MIDI_CHANNEL_BIT));
  143. pressureEvent->data[1] = lsb;
  144. pressureEvent->data[2] = msb;
  145. appendSorted(pressureEvent);
  146. }
  147. void addRaw(const uint64_t time, const uint8_t* const data, const uint8_t size)
  148. {
  149. RawMidiEvent* const rawEvent(new RawMidiEvent());
  150. rawEvent->time = time;
  151. rawEvent->size = size;
  152. carla_copy<uint8_t>(rawEvent->data, data, size);
  153. // Fix zero-velocity note-ons
  154. if (MIDI_IS_STATUS_NOTE_ON(data[0]) && data[2] == 0)
  155. rawEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (data[0] & MIDI_CHANNEL_BIT));
  156. appendSorted(rawEvent);
  157. }
  158. void flagTemporaryNote(const uint8_t note, const bool on)
  159. {
  160. fTemporary[on ? 1 : 0].time = 1;
  161. fTemporary[on ? 1 : 0].data[1] = note;
  162. }
  163. // -------------------------------------------------------------------
  164. // remove data
  165. void removeRaw(const uint64_t time, const uint8_t* const data, const uint8_t size)
  166. {
  167. const CarlaMutexLocker cmlw(fWriteMutex);
  168. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
  169. {
  170. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  171. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  172. if (rawMidiEvent->time != time)
  173. continue;
  174. if (rawMidiEvent->size != size)
  175. continue;
  176. if (std::memcmp(rawMidiEvent->data, data, size) != 0)
  177. continue;
  178. {
  179. const CarlaMutexLocker cmlr(fReadMutex);
  180. fData.remove(it);
  181. }
  182. delete rawMidiEvent;
  183. return;
  184. }
  185. carla_stderr("MidiPattern::removeRaw(" P_INT64 ", %p, %i) - unable to find event to remove", time, data, size);
  186. }
  187. // -------------------------------------------------------------------
  188. // clear
  189. void clear() noexcept
  190. {
  191. const CarlaMutexLocker cmlr(fReadMutex);
  192. const CarlaMutexLocker cmlw(fWriteMutex);
  193. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
  194. delete it.getValue(nullptr);
  195. fData.clear();
  196. }
  197. // -------------------------------------------------------------------
  198. // play on time
  199. bool play(const uint64_t timePosFrame, const uint32_t frames)
  200. {
  201. return play(static_cast<long double>(timePosFrame), static_cast<double>(frames));
  202. }
  203. bool play(long double timePosFrame, const double frames, const double offset = 0.0)
  204. {
  205. long double ldtime;
  206. playTemporary();
  207. const CarlaMutexTryLocker cmtl(fReadMutex);
  208. if (cmtl.wasNotLocked())
  209. return false;
  210. if (fStartTime != 0)
  211. timePosFrame += static_cast<long double>(fStartTime);
  212. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
  213. {
  214. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  215. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  216. ldtime = static_cast<long double>(rawMidiEvent->time);
  217. if (ldtime < timePosFrame)
  218. continue;
  219. if (ldtime > timePosFrame + frames)
  220. break;
  221. if (carla_isEqual(ldtime, timePosFrame + frames))
  222. {
  223. // only allow a few events to pass through in this special case
  224. if (! MIDI_IS_STATUS_NOTE_OFF(rawMidiEvent->data[0]))
  225. continue;
  226. }
  227. kPlayer->writeMidiEvent(fMidiPort, ldtime + offset - timePosFrame, rawMidiEvent);
  228. }
  229. return true;
  230. }
  231. void playTemporary()
  232. {
  233. if (fTemporary[0].time != 0)
  234. {
  235. fTemporary[0].time = 0;
  236. kPlayer->writeMidiEvent(fMidiPort, 0.0, &fTemporary[0]);
  237. }
  238. if (fTemporary[1].time != 0)
  239. {
  240. fTemporary[1].time = 0;
  241. kPlayer->writeMidiEvent(fMidiPort, 0.0, &fTemporary[1]);
  242. }
  243. }
  244. // -------------------------------------------------------------------
  245. // configure
  246. void setMidiPort(const uint8_t port) noexcept
  247. {
  248. fMidiPort = port;
  249. }
  250. void setStartTime(const uint64_t time) noexcept
  251. {
  252. fStartTime = time;
  253. }
  254. // -------------------------------------------------------------------
  255. // special
  256. const CarlaMutex& getWriteMutex() const noexcept
  257. {
  258. return fWriteMutex;
  259. }
  260. LinkedList<const RawMidiEvent*>::Itenerator iteneratorBegin() const noexcept
  261. {
  262. return fData.begin2();
  263. }
  264. // -------------------------------------------------------------------
  265. // state
  266. char* getState() const
  267. {
  268. static const std::size_t maxTimeSize = 20; // std::strlen("18446744073709551615");
  269. static const std::size_t maxDataSize = 4 + 4*MAX_EVENT_DATA_SIZE; // std::strlen("0xFF:127:127:127");
  270. static const std::size_t maxMsgSize = maxTimeSize + 3 /* sep + size + sep */ + maxDataSize + 1 /* newline */;
  271. const CarlaMutexLocker cmlw(fWriteMutex);
  272. char* const data((char*)std::calloc(1, fData.count() * maxMsgSize + 1));
  273. CARLA_SAFE_ASSERT_RETURN(data != nullptr, nullptr);
  274. if (fData.count() == 0)
  275. {
  276. *data = '\0';
  277. return data;
  278. }
  279. char* dataWrtn = data;
  280. int wrtn;
  281. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
  282. {
  283. const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
  284. CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
  285. wrtn = std::snprintf(dataWrtn, maxTimeSize+6, P_UINT64 ":%u:", rawMidiEvent->time, rawMidiEvent->size);
  286. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  287. dataWrtn += wrtn;
  288. wrtn = std::snprintf(dataWrtn, 5, "0x%02X", rawMidiEvent->data[0]);
  289. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  290. dataWrtn += wrtn;
  291. for (uint8_t i=1, size=rawMidiEvent->size; i<size; ++i)
  292. {
  293. wrtn = std::snprintf(dataWrtn, 5, ":%03u", rawMidiEvent->data[i]);
  294. CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
  295. dataWrtn += wrtn;
  296. }
  297. *dataWrtn++ = '\n';
  298. }
  299. *dataWrtn = '\0';
  300. return data;
  301. }
  302. void setState(const char* const data)
  303. {
  304. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  305. const size_t dataLen = std::strlen(data);
  306. const char* dataRead = data;
  307. const char* needle;
  308. RawMidiEvent midiEvent;
  309. char tmpBuf[24];
  310. ssize_t tmpSize;
  311. clear();
  312. const CarlaMutexLocker cmlr(fReadMutex);
  313. const CarlaMutexLocker cmlw(fWriteMutex);
  314. for (size_t dataPos=0; dataPos < dataLen && *dataRead != '\0';)
  315. {
  316. // get time
  317. needle = std::strchr(dataRead, ':');
  318. if (needle == nullptr)
  319. return;
  320. carla_zeroStruct(midiEvent);
  321. tmpSize = needle - dataRead;
  322. CARLA_SAFE_ASSERT_RETURN(tmpSize > 0,);
  323. CARLA_SAFE_ASSERT_RETURN(tmpSize < 24,);
  324. {
  325. const size_t uSize = static_cast<size_t>(tmpSize);
  326. std::strncpy(tmpBuf, dataRead, uSize);
  327. tmpBuf[tmpSize] = '\0';
  328. dataRead += uSize+1U;
  329. dataPos += uSize+1U;
  330. }
  331. const long long time = std::atoll(tmpBuf);
  332. CARLA_SAFE_ASSERT_RETURN(time >= 0,);
  333. midiEvent.time = static_cast<uint64_t>(time);
  334. // get size
  335. needle = std::strchr(dataRead, ':');
  336. CARLA_SAFE_ASSERT_RETURN(needle != nullptr,);
  337. tmpSize = needle - dataRead;
  338. CARLA_SAFE_ASSERT_RETURN(tmpSize > 0 && tmpSize < 24,);
  339. {
  340. const size_t uSize = static_cast<size_t>(tmpSize);
  341. std::strncpy(tmpBuf, dataRead, uSize);
  342. tmpBuf[tmpSize] = '\0';
  343. dataRead += uSize+1U;
  344. dataPos += uSize+1U;
  345. }
  346. const int midiDataSize = std::atoi(tmpBuf);
  347. CARLA_SAFE_ASSERT_RETURN(midiDataSize > 0 && midiDataSize <= MAX_EVENT_DATA_SIZE,);
  348. midiEvent.size = static_cast<uint8_t>(midiDataSize);
  349. // get events
  350. for (int i=0; i<midiDataSize; ++i)
  351. {
  352. CARLA_SAFE_ASSERT_RETURN(dataRead-data >= 4,);
  353. tmpSize = i==0 ? 4 : 3;
  354. const size_t uSize = static_cast<size_t>(tmpSize);
  355. std::strncpy(tmpBuf, dataRead, uSize);
  356. tmpBuf[tmpSize] = '\0';
  357. dataRead += uSize+1U;
  358. dataPos += uSize+1U;
  359. long mdata;
  360. if (i == 0)
  361. {
  362. mdata = std::strtol(tmpBuf, nullptr, 16);
  363. CARLA_SAFE_ASSERT_RETURN(mdata >= 0x80 && mdata <= 0xFF,);
  364. }
  365. else
  366. {
  367. mdata = std::atoi(tmpBuf);
  368. CARLA_SAFE_ASSERT_RETURN(mdata >= 0 && mdata < MAX_MIDI_VALUE,);
  369. }
  370. midiEvent.data[i] = static_cast<uint8_t>(mdata);
  371. }
  372. for (int i=midiDataSize; i<MAX_EVENT_DATA_SIZE; ++i)
  373. midiEvent.data[i] = 0;
  374. RawMidiEvent* const event(new RawMidiEvent());
  375. carla_copyStruct(*event, midiEvent);
  376. fData.append(event);
  377. }
  378. }
  379. // -------------------------------------------------------------------
  380. private:
  381. AbstractMidiPlayer* const kPlayer;
  382. uint8_t fMidiPort;
  383. uint64_t fStartTime;
  384. CarlaMutex fReadMutex;
  385. CarlaMutex fWriteMutex;
  386. LinkedList<const RawMidiEvent*> fData;
  387. RawMidiEvent fTemporary[2];
  388. void appendSorted(const RawMidiEvent* const event)
  389. {
  390. const CarlaMutexLocker cmlw(fWriteMutex);
  391. if (fData.isEmpty())
  392. {
  393. fData.append(event);
  394. return;
  395. }
  396. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
  397. {
  398. const RawMidiEvent* const oldEvent(it.getValue(nullptr));
  399. CARLA_SAFE_ASSERT_CONTINUE(oldEvent != nullptr);
  400. if (event->time >= oldEvent->time)
  401. continue;
  402. fData.insertAt(event, it);
  403. return;
  404. }
  405. fData.append(event);
  406. }
  407. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiPattern)
  408. };
  409. // -----------------------------------------------------------------------
  410. #endif // MIDI_BASE_HPP_INCLUDED