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.

242 lines
7.1KB

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