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.

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