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.

395 lines
11KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2013 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 GPL.txt file
  16. */
  17. #include "CarlaNative.hpp"
  18. #include "CarlaMIDI.h"
  19. #include "CarlaMutex.hpp"
  20. #include "RtList.hpp"
  21. #define MAX_EVENT_DATA_SIZE 4
  22. #define MIN_PREALLOCATED_EVENT_COUNT 100
  23. #define MAX_PREALLOCATED_EVENT_COUNT 1000
  24. struct RawMidiEvent {
  25. uint8_t data[MAX_EVENT_DATA_SIZE];
  26. size_t size;
  27. uint32_t time;
  28. RawMidiEvent()
  29. : data{0},
  30. size(0),
  31. time(0) {}
  32. };
  33. class MidiPattern
  34. {
  35. public:
  36. MidiPattern(const HostDescriptor* const host)
  37. : kHost(host),
  38. fStartTime(0),
  39. fDuration(0)
  40. {
  41. CARLA_ASSERT(host != nullptr);
  42. // TEST SONG (unsorted to test RtList API)
  43. uint32_t m = 44;
  44. addControl(0*m, 0, 7, 99);
  45. addControl(0*m, 0, 10, 63);
  46. addProgram(0*m, 0, 0, 0);
  47. // 6912 On ch=1 n=60 v=90
  48. // 7237 Off ch=1 n=60 v=90
  49. // 7296 On ch=1 n=62 v=90
  50. // 7621 Off ch=1 n=62 v=90
  51. // 7680 On ch=1 n=64 v=90
  52. // 8005 Off ch=1 n=64 v=90
  53. addNote(6912*m, 0, 60, 90, 325*m);
  54. addNote(7680*m, 0, 64, 90, 325*m);
  55. addNote(7296*m, 0, 62, 90, 325*m);
  56. // 1152 On ch=1 n=62 v=90
  57. // 1477 Off ch=1 n=62 v=90
  58. // 1536 On ch=1 n=64 v=90
  59. // 1861 Off ch=1 n=64 v=90
  60. // 1920 On ch=1 n=64 v=90
  61. // 2245 Off ch=1 n=64 v=90
  62. addNote(1152*m, 0, 62, 90, 325*m);
  63. addNote(1920*m, 0, 64, 90, 325*m);
  64. addNote(1536*m, 0, 64, 90, 325*m);
  65. // 3840 On ch=1 n=62 v=90
  66. // 4491 Off ch=1 n=62 v=90
  67. // 4608 On ch=1 n=64 v=90
  68. // 4933 Off ch=1 n=64 v=90
  69. // 4992 On ch=1 n=67 v=90
  70. // 5317 Off ch=1 n=67 v=90
  71. addNote(3840*m, 0, 62, 90, 650*m);
  72. addNote(4992*m, 0, 67, 90, 325*m);
  73. addNote(4608*m, 0, 64, 90, 325*m);
  74. // 0 On ch=1 n=64 v=90
  75. // 325 Off ch=1 n=64 v=90
  76. // 384 On ch=1 n=62 v=90
  77. // 709 Off ch=1 n=62 v=90
  78. // 768 On ch=1 n=60 v=90
  79. //1093 Off ch=1 n=60 v=90
  80. addNote( 0*m, 0, 64, 90, 325*m);
  81. addNote(768*m, 0, 60, 90, 325*m);
  82. addNote(384*m, 0, 62, 90, 325*m);
  83. // 10752 On ch=1 n=60 v=90
  84. // 12056 Off ch=1 n=60 v=90
  85. addNote(10752*m, 0, 60, 90, 650*m);
  86. // 5376 On ch=1 n=67 v=90
  87. // 6027 Off ch=1 n=67 v=90
  88. // 6144 On ch=1 n=64 v=90
  89. // 6469 Off ch=1 n=64 v=90
  90. // 6528 On ch=1 n=62 v=90
  91. // 6853 Off ch=1 n=62 v=90
  92. addNote(5376*m, 0, 67, 90, 650*m);
  93. addNote(6144*m, 0, 64, 90, 325*m);
  94. addNote(6528*m, 0, 62, 90, 325*m);
  95. // 8064 On ch=1 n=64 v=90
  96. // 8389 Off ch=1 n=64 v=90
  97. // 8448 On ch=1 n=64 v=90
  98. // 9099 Off ch=1 n=64 v=90
  99. // 9216 On ch=1 n=62 v=90
  100. // 9541 Off ch=1 n=62 v=90
  101. addNote(8064*m, 0, 64, 90, 325*m);
  102. addNote(8448*m, 0, 64, 90, 650*m);
  103. addNote(9216*m, 0, 62, 90, 325*m);
  104. // 9600 On ch=1 n=62 v=90
  105. // 9925 Off ch=1 n=62 v=90
  106. // 9984 On ch=1 n=64 v=90
  107. // 10309 Off ch=1 n=64 v=90
  108. // 10368 On ch=1 n=62 v=90
  109. // 10693 Off ch=1 n=62 v=90
  110. addNote(9600*m, 0, 62, 90, 325*m);
  111. addNote(9984*m, 0, 64, 90, 325*m);
  112. addNote(10368*m, 0, 62, 90, 325*m);
  113. // 2304 On ch=1 n=64 v=90
  114. // 2955 Off ch=1 n=64 v=90
  115. // 3072 On ch=1 n=62 v=90
  116. // 3397 Off ch=1 n=62 v=90
  117. // 3456 On ch=1 n=62 v=90
  118. // 3781 Off ch=1 n=62 v=90
  119. addNote(2304*m, 0, 64, 90, 650*m);
  120. addNote(3072*m, 0, 62, 90, 325*m);
  121. addNote(3456*m, 0, 62, 90, 325*m);
  122. }
  123. ~MidiPattern()
  124. {
  125. fData.clear();
  126. }
  127. void addControl(const uint32_t time, const uint8_t channel, const uint8_t control, const uint8_t value)
  128. {
  129. RawMidiEvent* ctrlEvent(new RawMidiEvent());
  130. ctrlEvent->data[0] = MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F);
  131. ctrlEvent->data[1] = control;
  132. ctrlEvent->data[2] = value;
  133. ctrlEvent->size = 3;
  134. ctrlEvent->time = time;
  135. append(ctrlEvent);
  136. }
  137. void addProgram(const uint32_t time, const uint8_t channel, const uint8_t bank, const uint8_t program)
  138. {
  139. RawMidiEvent* bankEvent(new RawMidiEvent());
  140. bankEvent->data[0] = MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F);
  141. bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT;
  142. bankEvent->data[2] = bank;
  143. bankEvent->size = 3;
  144. bankEvent->time = time;
  145. RawMidiEvent* programEvent(new RawMidiEvent());
  146. programEvent->data[0] = MIDI_STATUS_PROGRAM_CHANGE | (channel & 0x0F);
  147. programEvent->data[1] = program;
  148. programEvent->size = 2;
  149. programEvent->time = time;
  150. append(bankEvent);
  151. append(programEvent);
  152. }
  153. void addNote(const uint32_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration)
  154. {
  155. RawMidiEvent* noteOnEvent(new RawMidiEvent());
  156. noteOnEvent->data[0] = MIDI_STATUS_NOTE_ON | (channel & 0x0F);
  157. noteOnEvent->data[1] = pitch;
  158. noteOnEvent->data[2] = velocity;
  159. noteOnEvent->size = 3;
  160. noteOnEvent->time = time;
  161. RawMidiEvent* noteOffEvent(new RawMidiEvent());
  162. noteOffEvent->data[0] = MIDI_STATUS_NOTE_OFF | (channel & 0x0F);
  163. noteOffEvent->data[1] = pitch;
  164. noteOffEvent->data[2] = velocity;
  165. noteOffEvent->size = 3;
  166. noteOffEvent->time = time+duration;
  167. append(noteOnEvent);
  168. append(noteOffEvent);
  169. }
  170. void play(uint32_t timePosFrame, uint32_t frames)
  171. {
  172. if (! fMutex.tryLock())
  173. return;
  174. MidiEvent midiEvent;
  175. for (auto it = fData.begin(); it.valid(); it.next())
  176. {
  177. const RawMidiEvent* const rawMidiEvent(*it);
  178. if (timePosFrame > rawMidiEvent->time)
  179. continue;
  180. if (timePosFrame + frames <= rawMidiEvent->time)
  181. continue;
  182. midiEvent.port = 0;
  183. midiEvent.time = rawMidiEvent->time-timePosFrame;
  184. midiEvent.data[0] = rawMidiEvent->data[0];
  185. midiEvent.data[1] = rawMidiEvent->data[1];
  186. midiEvent.data[2] = rawMidiEvent->data[2];
  187. midiEvent.data[3] = rawMidiEvent->data[3];
  188. midiEvent.size = rawMidiEvent->size;
  189. writeMidiEvent(&midiEvent);
  190. }
  191. fMutex.unlock();
  192. }
  193. private:
  194. const HostDescriptor* const kHost;
  195. uint32_t fStartTime;
  196. uint32_t fDuration;
  197. CarlaMutex fMutex;
  198. NonRtList<const RawMidiEvent*> fData;
  199. void append(const RawMidiEvent* const event)
  200. {
  201. if (fData.isEmpty())
  202. {
  203. const CarlaMutex::ScopedLocker sl(&fMutex);
  204. fData.append(event);
  205. return;
  206. }
  207. for (auto it = fData.begin(); it.valid(); it.next())
  208. {
  209. const RawMidiEvent* const oldEvent(*it);
  210. if (event->time >= oldEvent->time)
  211. continue;
  212. const CarlaMutex::ScopedLocker sl(&fMutex);
  213. fData.insertAt(event, it);
  214. return;
  215. }
  216. const CarlaMutex::ScopedLocker sl(&fMutex);
  217. fData.append(event);
  218. }
  219. void writeMidiEvent(const MidiEvent* const event)
  220. {
  221. kHost->write_midi_event(kHost->handle, event);
  222. }
  223. };
  224. class MidiSequencerPlugin : public PluginDescriptorClass
  225. {
  226. public:
  227. MidiSequencerPlugin(const HostDescriptor* const host)
  228. : PluginDescriptorClass(host),
  229. fWantInEvents(false),
  230. fMidiOut(host)
  231. {
  232. }
  233. ~MidiSequencerPlugin()
  234. {
  235. }
  236. protected:
  237. // -------------------------------------------------------------------
  238. // Plugin process calls
  239. void activate()
  240. {
  241. }
  242. void deactivate()
  243. {
  244. }
  245. void process(float**, float**, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents)
  246. {
  247. const TimeInfo* const timePos = getTimeInfo();
  248. if (fWantInEvents)
  249. {
  250. RawMidiEvent rawMidiEvent;
  251. for (uint32_t i=0; i < midiEventCount; i++)
  252. {
  253. const MidiEvent* const midiEvent = &midiEvents[i];
  254. rawMidiEvent.data[0] = midiEvent->data[0];
  255. rawMidiEvent.data[1] = midiEvent->data[1];
  256. rawMidiEvent.data[2] = midiEvent->data[2];
  257. rawMidiEvent.data[3] = midiEvent->data[3];
  258. rawMidiEvent.size = midiEvent->size;
  259. rawMidiEvent.time = timePos->frame + midiEvent->time;
  260. fInEvents.appendRT(rawMidiEvent);
  261. }
  262. if (fInEvents.mutex.tryLock())
  263. {
  264. fInEvents.splice();
  265. fInEvents.mutex.unlock();
  266. }
  267. }
  268. if (timePos->playing)
  269. fMidiOut.play(timePos->frame, frames);
  270. }
  271. private:
  272. bool fWantInEvents;
  273. struct InRtEvents {
  274. CarlaMutex mutex;
  275. RtList<RawMidiEvent>::Pool dataPool;
  276. RtList<RawMidiEvent> data;
  277. RtList<RawMidiEvent> dataPendingRT;
  278. InRtEvents()
  279. : dataPool(MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT),
  280. data(&dataPool),
  281. dataPendingRT(&dataPool) {}
  282. ~InRtEvents()
  283. {
  284. clear();
  285. }
  286. void appendRT(const RawMidiEvent& event)
  287. {
  288. dataPendingRT.append(event);
  289. }
  290. void clear()
  291. {
  292. data.clear();
  293. dataPendingRT.clear();
  294. }
  295. void splice()
  296. {
  297. dataPendingRT.spliceAppend(data, true);
  298. }
  299. } fInEvents;
  300. MidiPattern fMidiOut;
  301. PluginDescriptorClassEND(MidiSequencerPlugin)
  302. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiSequencerPlugin)
  303. };
  304. // -----------------------------------------------------------------------
  305. static const PluginDescriptor midiSequencerDesc = {
  306. /* category */ PLUGIN_CATEGORY_UTILITY,
  307. /* hints */ PLUGIN_IS_RTSAFE/*|PLUGIN_HAS_GUI*/,
  308. /* audioIns */ 0,
  309. /* audioOuts */ 0,
  310. /* midiIns */ 1,
  311. /* midiOuts */ 1,
  312. /* paramIns */ 0,
  313. /* paramOuts */ 0,
  314. /* name */ "MIDI Sequencer",
  315. /* label */ "midiSequencer",
  316. /* maker */ "falkTX",
  317. /* copyright */ "GNU GPL v2+",
  318. PluginDescriptorFILL(MidiSequencerPlugin)
  319. };
  320. // -----------------------------------------------------------------------
  321. void carla_register_native_plugin_midiSequencer()
  322. {
  323. carla_register_native_plugin(&midiSequencerDesc);
  324. }
  325. // -----------------------------------------------------------------------