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.

269 lines
9.8KB

  1. /* Copyright 2013-2019 Matt Tytel
  2. *
  3. * vital is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * vital is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with vital. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "midi_manager.h"
  17. #include "sound_engine.h"
  18. #include "synth_types.h"
  19. #include "load_save.h"
  20. #include "synth_base.h"
  21. namespace {
  22. constexpr int kMidiControlBits = 7;
  23. constexpr float kHighResolutionMax = (1 << (2 * kMidiControlBits)) - 1.0f;
  24. constexpr float kControlMax = (1 << kMidiControlBits) - 1.0f;
  25. force_inline vital::mono_float toHighResolutionValue(int msb, int lsb) {
  26. if (lsb < 0)
  27. return msb / kControlMax;
  28. return ((msb << kMidiControlBits) + lsb) / kHighResolutionMax;
  29. }
  30. } // namespace
  31. MidiManager::MidiManager(SynthBase* synth, MidiKeyboardState* keyboard_state,
  32. std::map<std::string, String>* gui_state, Listener* listener) :
  33. synth_(synth), keyboard_state_(keyboard_state), gui_state_(gui_state),
  34. listener_(listener),
  35. msb_pressure_values_(), msb_slide_values_() {
  36. engine_ = synth_->getEngine();
  37. current_bank_ = -1;
  38. current_folder_ = -1;
  39. current_preset_ = -1;
  40. for (int i = 0; i < vital::kNumMidiChannels; ++i) {
  41. lsb_slide_values_[i] = -1;
  42. lsb_pressure_values_[i] = -1;
  43. }
  44. mpe_enabled_ = false;
  45. mpe_zone_layout_.setLowerZone(vital::kNumMidiChannels - 1);
  46. }
  47. MidiManager::~MidiManager() {
  48. }
  49. void MidiManager::readMpeMessage(const MidiMessage& message) {
  50. mpe_zone_layout_.processNextMidiEvent(message);
  51. }
  52. void MidiManager::processAllNotesOff(const MidiMessage& midi_message, int sample_position, int channel) {
  53. if (isMpeChannelMasterLowerZone(channel))
  54. engine_->allNotesOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  55. else if (isMpeChannelMasterUpperZone(channel))
  56. engine_->allNotesOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  57. else
  58. engine_->allNotesOff(sample_position, channel);
  59. }
  60. void MidiManager::processAllSoundsOff() {
  61. engine_->allSoundsOff();
  62. }
  63. void MidiManager::processSustain(const MidiMessage& midi_message, int sample_position, int channel) {
  64. bool on = midi_message.isSustainPedalOn();
  65. if (isMpeChannelMasterLowerZone(channel)) {
  66. if (on)
  67. engine_->sustainOnRange(lowerZoneStartChannel(), lowerZoneEndChannel());
  68. else
  69. engine_->sustainOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  70. }
  71. else if (isMpeChannelMasterUpperZone(channel)) {
  72. if (on)
  73. engine_->sustainOnRange(upperZoneStartChannel(), upperZoneEndChannel());
  74. else
  75. engine_->sustainOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  76. }
  77. else {
  78. if (on)
  79. engine_->sustainOn(channel);
  80. else
  81. engine_->sustainOff(sample_position, channel);
  82. }
  83. }
  84. void MidiManager::processSostenuto(const MidiMessage& midi_message, int sample_position, int channel) {
  85. bool on = midi_message.isSostenutoPedalOn();
  86. if (isMpeChannelMasterLowerZone(channel)) {
  87. if (on)
  88. engine_->sostenutoOnRange(lowerZoneStartChannel(), lowerZoneEndChannel());
  89. else
  90. engine_->sostenutoOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  91. }
  92. else if (isMpeChannelMasterUpperZone(channel)) {
  93. if (on)
  94. engine_->sostenutoOnRange(upperZoneStartChannel(), upperZoneEndChannel());
  95. else
  96. engine_->sostenutoOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  97. }
  98. else {
  99. if (on)
  100. engine_->sostenutoOn(channel);
  101. else
  102. engine_->sostenutoOff(sample_position, channel);
  103. }
  104. }
  105. void MidiManager::processPitchBend(const MidiMessage& midi_message, int sample_position, int channel) {
  106. vital::mono_float percent = midi_message.getPitchWheelValue() / kHighResolutionMax;
  107. vital::mono_float value = 2 * percent - 1.0f;
  108. if (isMpeChannelMasterLowerZone(channel)) {
  109. engine_->setZonedPitchWheel(value, lowerMasterChannel(), lowerMasterChannel() + 1);
  110. engine_->setZonedPitchWheel(value, lowerZoneStartChannel(), lowerZoneEndChannel());
  111. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  112. listener_->pitchWheelMidiChanged(value);
  113. #endif
  114. }
  115. else if (isMpeChannelMasterUpperZone(channel)) {
  116. engine_->setZonedPitchWheel(value, upperMasterChannel(), upperMasterChannel() + 1);
  117. engine_->setZonedPitchWheel(value, upperZoneStartChannel(), upperZoneEndChannel());
  118. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  119. listener_->pitchWheelMidiChanged(value);
  120. #endif
  121. }
  122. else if (mpe_enabled_)
  123. engine_->setPitchWheel(value, channel);
  124. else {
  125. engine_->setZonedPitchWheel(value, channel, channel);
  126. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  127. listener_->pitchWheelMidiChanged(value);
  128. #endif
  129. }
  130. }
  131. void MidiManager::processPressure(const MidiMessage& midi_message, int sample_position, int channel) {
  132. vital::mono_float value = toHighResolutionValue(msb_pressure_values_[channel], lsb_pressure_values_[channel]);
  133. if (isMpeChannelMasterLowerZone(channel))
  134. engine_->setChannelRangeAftertouch(lowerZoneStartChannel(), lowerZoneEndChannel(), value, 0);
  135. else if (isMpeChannelMasterUpperZone(channel))
  136. engine_->setChannelRangeAftertouch(upperZoneStartChannel(), upperZoneEndChannel(), value, 0);
  137. else
  138. engine_->setChannelAftertouch(channel, value, sample_position);
  139. }
  140. void MidiManager::processSlide(const MidiMessage& midi_message, int sample_position, int channel) {
  141. vital::mono_float value = toHighResolutionValue(msb_slide_values_[channel], lsb_slide_values_[channel]);
  142. if (isMpeChannelMasterLowerZone(channel))
  143. engine_->setChannelRangeSlide(value, lowerZoneStartChannel(), lowerZoneEndChannel(), 0);
  144. else if (isMpeChannelMasterUpperZone(channel))
  145. engine_->setChannelRangeSlide(value, upperZoneStartChannel(), upperZoneEndChannel(), 0);
  146. else
  147. engine_->setChannelSlide(channel, value, sample_position);
  148. }
  149. force_inline bool MidiManager::isMpeChannelMasterLowerZone(int channel) {
  150. return mpe_enabled_ && mpe_zone_layout_.getLowerZone().isActive() && lowerMasterChannel() == channel;
  151. }
  152. force_inline bool MidiManager::isMpeChannelMasterUpperZone(int channel) {
  153. return mpe_enabled_ && mpe_zone_layout_.getUpperZone().isActive() && upperMasterChannel() == channel;
  154. }
  155. void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample_position) {
  156. if (midi_message.isController())
  157. readMpeMessage(midi_message);
  158. int channel = midi_message.getChannel() - 1;
  159. MidiMainType type = static_cast<MidiMainType>(midi_message.getRawData()[0] & 0xf0);
  160. switch (type) {
  161. case kProgramChange:
  162. return;
  163. case kNoteOn: {
  164. uint8 velocity = midi_message.getVelocity();
  165. if (velocity)
  166. engine_->noteOn(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
  167. else
  168. engine_->noteOff(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
  169. return;
  170. }
  171. case kNoteOff: {
  172. vital::mono_float velocity = midi_message.getVelocity() / kControlMax;
  173. engine_->noteOff(midi_message.getNoteNumber(), velocity, sample_position, channel);
  174. return;
  175. }
  176. case kAftertouch: {
  177. int note = midi_message.getNoteNumber();
  178. vital::mono_float value = midi_message.getAfterTouchValue() / kControlMax;
  179. engine_->setAftertouch(note, value, sample_position, channel);
  180. return;
  181. }
  182. case kChannelPressure: {
  183. msb_pressure_values_[channel] = midi_message.getChannelPressureValue();
  184. processPressure(midi_message, sample_position, channel);
  185. return;
  186. }
  187. case kPitchWheel: {
  188. processPitchBend(midi_message, sample_position, channel);
  189. return;
  190. }
  191. case kController: {
  192. MidiSecondaryType secondary_type = static_cast<MidiSecondaryType>(midi_message.getControllerNumber());
  193. switch (secondary_type) {
  194. case kSlide: {
  195. msb_slide_values_[channel] = midi_message.getControllerValue();
  196. processSlide(midi_message, sample_position, channel);
  197. break;
  198. }
  199. case kLsbPressure: {
  200. lsb_pressure_values_[channel] = midi_message.getControllerValue();
  201. processPressure(midi_message, sample_position, channel);
  202. break;
  203. }
  204. case kLsbSlide: {
  205. lsb_slide_values_[channel] = midi_message.getControllerValue();
  206. processSlide(midi_message, sample_position, channel);
  207. break;
  208. }
  209. case kSustainPedal: {
  210. processSustain(midi_message, sample_position, channel);
  211. break;
  212. }
  213. case kSostenutoPedal: {
  214. processSostenuto(midi_message, sample_position, channel);
  215. break;
  216. }
  217. case kSoftPedalOn: // TODO
  218. break;
  219. case kModWheel: {
  220. vital::mono_float percent = (1.0f * midi_message.getControllerValue()) / kControlMax;
  221. engine_->setModWheel(percent, channel);
  222. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  223. listener_->modWheelMidiChanged(percent);
  224. #endif
  225. break;
  226. }
  227. case kAllNotesOff:
  228. case kAllControllersOff:
  229. processAllNotesOff(midi_message, sample_position, channel);
  230. return;
  231. case kAllSoundsOff:
  232. processAllSoundsOff();
  233. break;
  234. case kBankSelect:
  235. current_bank_ = midi_message.getControllerValue();
  236. return;
  237. case kFolderSelect:
  238. current_folder_ = midi_message.getControllerValue();
  239. return;
  240. }
  241. }
  242. }
  243. }
  244. void MidiManager::replaceKeyboardMessages(MidiBuffer& buffer, int num_samples) {
  245. keyboard_state_->processNextMidiBuffer(buffer, 0, num_samples, true);
  246. }