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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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 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 "CarlaMathUtils.hpp"
  23. #define MAX_EVENT_DATA_SIZE 4
  24. #define MIN_PREALLOCATED_EVENT_COUNT 100
  25. #define MAX_PREALLOCATED_EVENT_COUNT 1000
  26. struct RawMidiEvent {
  27. uint64_t time;
  28. uint8_t size;
  29. uint8_t data[MAX_EVENT_DATA_SIZE];
  30. RawMidiEvent()
  31. : time(0),
  32. size(0)
  33. {
  34. carla_fill<uint8_t>(data, MAX_EVENT_DATA_SIZE, 0);
  35. }
  36. };
  37. class AbstractMidiPlayer
  38. {
  39. public:
  40. virtual ~AbstractMidiPlayer() {}
  41. virtual void writeMidiEvent(const uint64_t timePosFrame, const RawMidiEvent* const event) = 0;
  42. };
  43. class MidiPattern
  44. {
  45. public:
  46. MidiPattern(AbstractMidiPlayer* const player)
  47. : kPlayer(player),
  48. fStartTime(0),
  49. fDuration(0)
  50. {
  51. CARLA_ASSERT(kPlayer != nullptr);
  52. }
  53. ~MidiPattern()
  54. {
  55. fData.clear();
  56. }
  57. void addControl(const uint64_t time, const uint8_t channel, const uint8_t control, const uint8_t value)
  58. {
  59. RawMidiEvent* ctrlEvent(new RawMidiEvent());
  60. ctrlEvent->time = time;
  61. ctrlEvent->size = 3;
  62. ctrlEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F));
  63. ctrlEvent->data[1] = control;
  64. ctrlEvent->data[2] = value;
  65. append(ctrlEvent);
  66. }
  67. void addChannelPressure(const uint64_t time, const uint8_t channel, const uint8_t pressure)
  68. {
  69. RawMidiEvent* pressureEvent(new RawMidiEvent());
  70. pressureEvent->time = time;
  71. pressureEvent->size = 2;
  72. pressureEvent->data[0] = uint8_t(MIDI_STATUS_CHANNEL_PRESSURE | (channel & 0x0F));
  73. pressureEvent->data[1] = pressure;
  74. append(pressureEvent);
  75. }
  76. void addNote(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration)
  77. {
  78. addNoteOn(time, channel, pitch, velocity);
  79. addNoteOff(time+duration, channel, pitch, velocity);
  80. }
  81. void addNoteOn(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity)
  82. {
  83. RawMidiEvent* noteOnEvent(new RawMidiEvent());
  84. noteOnEvent->time = time;
  85. noteOnEvent->size = 3;
  86. noteOnEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_ON | (channel & 0x0F));
  87. noteOnEvent->data[1] = pitch;
  88. noteOnEvent->data[2] = velocity;
  89. append(noteOnEvent);
  90. }
  91. void addNoteOff(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity = 0)
  92. {
  93. RawMidiEvent* noteOffEvent(new RawMidiEvent());
  94. noteOffEvent->time = time;
  95. noteOffEvent->size = 3;
  96. noteOffEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (channel & 0x0F));
  97. noteOffEvent->data[1] = pitch;
  98. noteOffEvent->data[2] = velocity;
  99. append(noteOffEvent);
  100. }
  101. void addNoteAftertouch(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t pressure)
  102. {
  103. RawMidiEvent* noteAfterEvent(new RawMidiEvent());
  104. noteAfterEvent->time = time;
  105. noteAfterEvent->size = 3;
  106. noteAfterEvent->data[0] = uint8_t(MIDI_STATUS_POLYPHONIC_AFTERTOUCH | (channel & 0x0F));
  107. noteAfterEvent->data[1] = pitch;
  108. noteAfterEvent->data[2] = pressure;
  109. append(noteAfterEvent);
  110. }
  111. void addProgram(const uint64_t time, const uint8_t channel, const uint8_t bank, const uint8_t program)
  112. {
  113. RawMidiEvent* bankEvent(new RawMidiEvent());
  114. bankEvent->time = time;
  115. bankEvent->size = 3;
  116. bankEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F));
  117. bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT;
  118. bankEvent->data[2] = bank;
  119. RawMidiEvent* programEvent(new RawMidiEvent());
  120. programEvent->time = time;
  121. programEvent->size = 2;
  122. programEvent->data[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (channel & 0x0F));
  123. programEvent->data[1] = program;
  124. append(bankEvent);
  125. append(programEvent);
  126. }
  127. void addPitchbend(const uint64_t time, const uint8_t channel, const uint8_t lsb, const uint8_t msb)
  128. {
  129. RawMidiEvent* pressureEvent(new RawMidiEvent());
  130. pressureEvent->time = time;
  131. pressureEvent->size = 3;
  132. pressureEvent->data[0] = uint8_t(MIDI_STATUS_PITCH_WHEEL_CONTROL | (channel & 0x0F));
  133. pressureEvent->data[1] = lsb;
  134. pressureEvent->data[2] = msb;
  135. append(pressureEvent);
  136. }
  137. void addRaw(const uint64_t time, const uint8_t* data, const uint8_t size)
  138. {
  139. RawMidiEvent* rawEvent(new RawMidiEvent());
  140. rawEvent->time = time;
  141. rawEvent->size = size;
  142. carla_copy<uint8_t>(rawEvent->data, data, size);
  143. append(rawEvent);
  144. }
  145. void play(uint64_t timePosFrame, uint32_t frames)
  146. {
  147. if (! fMutex.tryLock())
  148. return;
  149. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  150. {
  151. const RawMidiEvent* const rawMidiEvent(it.getValue());
  152. if (timePosFrame > rawMidiEvent->time)
  153. continue;
  154. if (timePosFrame + frames <= rawMidiEvent->time)
  155. continue;
  156. kPlayer->writeMidiEvent(timePosFrame, rawMidiEvent);
  157. }
  158. fMutex.unlock();
  159. }
  160. void clear()
  161. {
  162. const CarlaMutexLocker sl(fMutex);
  163. fData.clear();
  164. }
  165. private:
  166. AbstractMidiPlayer* const kPlayer;
  167. uint32_t fStartTime; // unused
  168. uint32_t fDuration; // unused
  169. CarlaMutex fMutex;
  170. LinkedList<const RawMidiEvent*> fData;
  171. void append(const RawMidiEvent* const event)
  172. {
  173. if (fData.isEmpty())
  174. {
  175. const CarlaMutexLocker sl(fMutex);
  176. fData.append(event);
  177. return;
  178. }
  179. for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin(); it.valid(); it.next())
  180. {
  181. const RawMidiEvent* const oldEvent(it.getValue());
  182. if (event->time >= oldEvent->time)
  183. continue;
  184. const CarlaMutexLocker sl(fMutex);
  185. fData.insertAt(event, it);
  186. return;
  187. }
  188. const CarlaMutexLocker sl(fMutex);
  189. fData.append(event);
  190. }
  191. };
  192. #endif // MIDI_BASE_HPP_INCLUDED