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.

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