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.

240 lines
6.9KB

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