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.

391 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 3
  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. writeMidiEvent(&midiEvent);
  188. }
  189. fMutex.unlock();
  190. }
  191. private:
  192. const HostDescriptor* const kHost;
  193. uint32_t fStartTime;
  194. uint32_t fDuration;
  195. CarlaMutex fMutex;
  196. NonRtList<const RawMidiEvent*> fData;
  197. void append(const RawMidiEvent* const event)
  198. {
  199. if (fData.isEmpty())
  200. {
  201. const CarlaMutex::ScopedLocker sl(&fMutex);
  202. fData.append(event);
  203. return;
  204. }
  205. for (auto it = fData.begin(); it.valid(); it.next())
  206. {
  207. const RawMidiEvent* const oldEvent(*it);
  208. if (event->time >= oldEvent->time)
  209. continue;
  210. const CarlaMutex::ScopedLocker sl(&fMutex);
  211. fData.insertAt(event, it);
  212. return;
  213. }
  214. const CarlaMutex::ScopedLocker sl(&fMutex);
  215. fData.append(event);
  216. }
  217. void writeMidiEvent(const MidiEvent* const event)
  218. {
  219. kHost->write_midi_event(kHost->handle, event);
  220. }
  221. };
  222. class MidiSequencerPlugin : public PluginDescriptorClass
  223. {
  224. public:
  225. MidiSequencerPlugin(const HostDescriptor* const host)
  226. : PluginDescriptorClass(host),
  227. fWantInEvents(false),
  228. fMidiOut(host)
  229. {
  230. }
  231. ~MidiSequencerPlugin()
  232. {
  233. }
  234. protected:
  235. // -------------------------------------------------------------------
  236. // Plugin process calls
  237. void activate()
  238. {
  239. }
  240. void deactivate()
  241. {
  242. }
  243. void process(float**, float**, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents)
  244. {
  245. const TimeInfo* const timePos = getTimeInfo();
  246. if (fWantInEvents)
  247. {
  248. RawMidiEvent rawMidiEvent;
  249. for (uint32_t i=0; i < midiEventCount; i++)
  250. {
  251. const MidiEvent* const midiEvent = &midiEvents[i];
  252. rawMidiEvent.data[0] = midiEvent->data[0];
  253. rawMidiEvent.data[1] = midiEvent->data[1];
  254. rawMidiEvent.data[2] = midiEvent->data[2];
  255. rawMidiEvent.time = timePos->frame + midiEvent->time;
  256. fInEvents.appendRT(rawMidiEvent);
  257. }
  258. if (fInEvents.mutex.tryLock())
  259. {
  260. fInEvents.splice();
  261. fInEvents.mutex.unlock();
  262. }
  263. }
  264. if (timePos->playing)
  265. fMidiOut.play(timePos->frame, frames);
  266. }
  267. private:
  268. bool fWantInEvents;
  269. struct InRtEvents {
  270. CarlaMutex mutex;
  271. RtList<RawMidiEvent>::Pool dataPool;
  272. RtList<RawMidiEvent> data;
  273. RtList<RawMidiEvent> dataPendingRT;
  274. InRtEvents()
  275. : dataPool(MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT),
  276. data(&dataPool),
  277. dataPendingRT(&dataPool) {}
  278. ~InRtEvents()
  279. {
  280. clear();
  281. }
  282. void appendRT(const RawMidiEvent& event)
  283. {
  284. dataPendingRT.append(event);
  285. }
  286. void clear()
  287. {
  288. data.clear();
  289. dataPendingRT.clear();
  290. }
  291. void splice()
  292. {
  293. dataPendingRT.spliceAppend(data, true);
  294. }
  295. } fInEvents;
  296. MidiPattern fMidiOut;
  297. PluginDescriptorClassEND(MidiSequencerPlugin)
  298. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiSequencerPlugin)
  299. };
  300. // -----------------------------------------------------------------------
  301. static const PluginDescriptor midiSequencerDesc = {
  302. /* category */ PLUGIN_CATEGORY_UTILITY,
  303. /* hints */ PLUGIN_IS_RTSAFE/*|PLUGIN_HAS_GUI*/,
  304. /* audioIns */ 0,
  305. /* audioOuts */ 0,
  306. /* midiIns */ 1,
  307. /* midiOuts */ 1,
  308. /* paramIns */ 0,
  309. /* paramOuts */ 0,
  310. /* name */ "MIDI Sequencer",
  311. /* label */ "midiSequencer",
  312. /* maker */ "falkTX",
  313. /* copyright */ "GNU GPL v2+",
  314. PluginDescriptorFILL(MidiSequencerPlugin)
  315. };
  316. // -----------------------------------------------------------------------
  317. void carla_register_native_plugin_midiSequencer()
  318. {
  319. carla_register_native_plugin(&midiSequencerDesc);
  320. }
  321. // -----------------------------------------------------------------------