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.

232 lines
6.7KB

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