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.

325 lines
12KB

  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), armed_value_(nullptr),
  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::armMidiLearn(std::string name) {
  50. current_bank_ = -1;
  51. current_folder_ = -1;
  52. current_preset_ = -1;
  53. armed_value_ = &vital::Parameters::getDetails(name);
  54. }
  55. void MidiManager::cancelMidiLearn() {
  56. armed_value_ = nullptr;
  57. }
  58. void MidiManager::clearMidiLearn(const std::string& name) {
  59. for (auto& controls : midi_learn_map_) {
  60. if (controls.second.count(name)) {
  61. midi_learn_map_[controls.first].erase(name);
  62. LoadSave::saveMidiMapConfig(this);
  63. }
  64. }
  65. }
  66. void MidiManager::midiInput(int midi_id, vital::mono_float value) {
  67. if (armed_value_) {
  68. midi_learn_map_[midi_id][armed_value_->name] = armed_value_;
  69. armed_value_ = nullptr;
  70. // TODO: Probably shouldn't write this config on the audio thread.
  71. LoadSave::saveMidiMapConfig(this);
  72. }
  73. if (midi_learn_map_.count(midi_id)) {
  74. for (auto& control : midi_learn_map_[midi_id]) {
  75. const vital::ValueDetails* details = control.second;
  76. vital::mono_float percent = value / kControlMax;
  77. vital::mono_float range = details->max - details->min;
  78. vital::mono_float translated = percent * range + details->min;
  79. if (details->value_scale == vital::ValueDetails::kIndexed)
  80. translated = std::round(translated);
  81. listener_->valueChangedThroughMidi(control.first, translated);
  82. }
  83. }
  84. }
  85. bool MidiManager::isMidiMapped(const std::string& name) const {
  86. for (auto& controls : midi_learn_map_) {
  87. if (controls.second.count(name))
  88. return true;
  89. }
  90. return false;
  91. }
  92. void MidiManager::setSampleRate(double sample_rate) {
  93. midi_collector_.reset(sample_rate);
  94. }
  95. void MidiManager::removeNextBlockOfMessages(MidiBuffer& buffer, int num_samples) {
  96. midi_collector_.removeNextBlockOfMessages(buffer, num_samples);
  97. }
  98. void MidiManager::readMpeMessage(const MidiMessage& message) {
  99. mpe_zone_layout_.processNextMidiEvent(message);
  100. }
  101. void MidiManager::processAllNotesOff(const MidiMessage& midi_message, int sample_position, int channel) {
  102. if (isMpeChannelMasterLowerZone(channel))
  103. engine_->allNotesOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  104. else if (isMpeChannelMasterUpperZone(channel))
  105. engine_->allNotesOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  106. else
  107. engine_->allNotesOff(sample_position, channel);
  108. }
  109. void MidiManager::processAllSoundsOff() {
  110. engine_->allSoundsOff();
  111. }
  112. void MidiManager::processSustain(const MidiMessage& midi_message, int sample_position, int channel) {
  113. bool on = midi_message.isSustainPedalOn();
  114. if (isMpeChannelMasterLowerZone(channel)) {
  115. if (on)
  116. engine_->sustainOnRange(lowerZoneStartChannel(), lowerZoneEndChannel());
  117. else
  118. engine_->sustainOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  119. }
  120. else if (isMpeChannelMasterUpperZone(channel)) {
  121. if (on)
  122. engine_->sustainOnRange(upperZoneStartChannel(), upperZoneEndChannel());
  123. else
  124. engine_->sustainOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  125. }
  126. else {
  127. if (on)
  128. engine_->sustainOn(channel);
  129. else
  130. engine_->sustainOff(sample_position, channel);
  131. }
  132. }
  133. void MidiManager::processSostenuto(const MidiMessage& midi_message, int sample_position, int channel) {
  134. bool on = midi_message.isSostenutoPedalOn();
  135. if (isMpeChannelMasterLowerZone(channel)) {
  136. if (on)
  137. engine_->sostenutoOnRange(lowerZoneStartChannel(), lowerZoneEndChannel());
  138. else
  139. engine_->sostenutoOffRange(sample_position, lowerZoneStartChannel(), lowerZoneEndChannel());
  140. }
  141. else if (isMpeChannelMasterUpperZone(channel)) {
  142. if (on)
  143. engine_->sostenutoOnRange(upperZoneStartChannel(), upperZoneEndChannel());
  144. else
  145. engine_->sostenutoOffRange(sample_position, upperZoneStartChannel(), upperZoneEndChannel());
  146. }
  147. else {
  148. if (on)
  149. engine_->sostenutoOn(channel);
  150. else
  151. engine_->sostenutoOff(sample_position, channel);
  152. }
  153. }
  154. void MidiManager::processPitchBend(const MidiMessage& midi_message, int sample_position, int channel) {
  155. vital::mono_float percent = midi_message.getPitchWheelValue() / kHighResolutionMax;
  156. vital::mono_float value = 2 * percent - 1.0f;
  157. if (isMpeChannelMasterLowerZone(channel)) {
  158. engine_->setZonedPitchWheel(value, lowerMasterChannel(), lowerMasterChannel() + 1);
  159. engine_->setZonedPitchWheel(value, lowerZoneStartChannel(), lowerZoneEndChannel());
  160. listener_->pitchWheelMidiChanged(value);
  161. }
  162. else if (isMpeChannelMasterUpperZone(channel)) {
  163. engine_->setZonedPitchWheel(value, upperMasterChannel(), upperMasterChannel() + 1);
  164. engine_->setZonedPitchWheel(value, upperZoneStartChannel(), upperZoneEndChannel());
  165. listener_->pitchWheelMidiChanged(value);
  166. }
  167. else if (mpe_enabled_)
  168. engine_->setPitchWheel(value, channel);
  169. else {
  170. engine_->setZonedPitchWheel(value, channel, channel);
  171. listener_->pitchWheelMidiChanged(value);
  172. }
  173. }
  174. void MidiManager::processPressure(const MidiMessage& midi_message, int sample_position, int channel) {
  175. vital::mono_float value = toHighResolutionValue(msb_pressure_values_[channel], lsb_pressure_values_[channel]);
  176. if (isMpeChannelMasterLowerZone(channel))
  177. engine_->setChannelRangeAftertouch(lowerZoneStartChannel(), lowerZoneEndChannel(), value, 0);
  178. else if (isMpeChannelMasterUpperZone(channel))
  179. engine_->setChannelRangeAftertouch(upperZoneStartChannel(), upperZoneEndChannel(), value, 0);
  180. else
  181. engine_->setChannelAftertouch(channel, value, sample_position);
  182. }
  183. void MidiManager::processSlide(const MidiMessage& midi_message, int sample_position, int channel) {
  184. vital::mono_float value = toHighResolutionValue(msb_slide_values_[channel], lsb_slide_values_[channel]);
  185. if (isMpeChannelMasterLowerZone(channel))
  186. engine_->setChannelRangeSlide(value, lowerZoneStartChannel(), lowerZoneEndChannel(), 0);
  187. else if (isMpeChannelMasterUpperZone(channel))
  188. engine_->setChannelRangeSlide(value, upperZoneStartChannel(), upperZoneEndChannel(), 0);
  189. else
  190. engine_->setChannelSlide(channel, value, sample_position);
  191. }
  192. force_inline bool MidiManager::isMpeChannelMasterLowerZone(int channel) {
  193. return mpe_enabled_ && mpe_zone_layout_.getLowerZone().isActive() && lowerMasterChannel() == channel;
  194. }
  195. force_inline bool MidiManager::isMpeChannelMasterUpperZone(int channel) {
  196. return mpe_enabled_ && mpe_zone_layout_.getUpperZone().isActive() && upperMasterChannel() == channel;
  197. }
  198. void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample_position) {
  199. if (midi_message.isController())
  200. readMpeMessage(midi_message);
  201. int channel = midi_message.getChannel() - 1;
  202. MidiMainType type = static_cast<MidiMainType>(midi_message.getRawData()[0] & 0xf0);
  203. switch (type) {
  204. case kProgramChange:
  205. return;
  206. case kNoteOn: {
  207. uint8 velocity = midi_message.getVelocity();
  208. if (velocity)
  209. engine_->noteOn(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
  210. else
  211. engine_->noteOff(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
  212. return;
  213. }
  214. case kNoteOff: {
  215. vital::mono_float velocity = midi_message.getVelocity() / kControlMax;
  216. engine_->noteOff(midi_message.getNoteNumber(), velocity, sample_position, channel);
  217. return;
  218. }
  219. case kAftertouch: {
  220. int note = midi_message.getNoteNumber();
  221. vital::mono_float value = midi_message.getAfterTouchValue() / kControlMax;
  222. engine_->setAftertouch(note, value, sample_position, channel);
  223. return;
  224. }
  225. case kChannelPressure: {
  226. msb_pressure_values_[channel] = midi_message.getChannelPressureValue();
  227. processPressure(midi_message, sample_position, channel);
  228. return;
  229. }
  230. case kPitchWheel: {
  231. processPitchBend(midi_message, sample_position, channel);
  232. return;
  233. }
  234. case kController: {
  235. MidiSecondaryType secondary_type = static_cast<MidiSecondaryType>(midi_message.getControllerNumber());
  236. switch (secondary_type) {
  237. case kSlide: {
  238. msb_slide_values_[channel] = midi_message.getControllerValue();
  239. processSlide(midi_message, sample_position, channel);
  240. break;
  241. }
  242. case kLsbPressure: {
  243. lsb_pressure_values_[channel] = midi_message.getControllerValue();
  244. processPressure(midi_message, sample_position, channel);
  245. break;
  246. }
  247. case kLsbSlide: {
  248. lsb_slide_values_[channel] = midi_message.getControllerValue();
  249. processSlide(midi_message, sample_position, channel);
  250. break;
  251. }
  252. case kSustainPedal: {
  253. processSustain(midi_message, sample_position, channel);
  254. break;
  255. }
  256. case kSostenutoPedal: {
  257. processSostenuto(midi_message, sample_position, channel);
  258. break;
  259. }
  260. case kSoftPedalOn: // TODO
  261. break;
  262. case kModWheel: {
  263. vital::mono_float percent = (1.0f * midi_message.getControllerValue()) / kControlMax;
  264. engine_->setModWheel(percent, channel);
  265. listener_->modWheelMidiChanged(percent);
  266. break;
  267. }
  268. case kAllNotesOff:
  269. case kAllControllersOff:
  270. processAllNotesOff(midi_message, sample_position, channel);
  271. return;
  272. case kAllSoundsOff:
  273. processAllSoundsOff();
  274. break;
  275. case kBankSelect:
  276. current_bank_ = midi_message.getControllerValue();
  277. return;
  278. case kFolderSelect:
  279. current_folder_ = midi_message.getControllerValue();
  280. return;
  281. }
  282. midiInput(midi_message.getControllerNumber(), midi_message.getControllerValue());
  283. }
  284. }
  285. }
  286. void MidiManager::handleIncomingMidiMessage(MidiInput* source, const MidiMessage &midi_message) {
  287. midi_collector_.addMessageToQueue(midi_message);
  288. }
  289. void MidiManager::replaceKeyboardMessages(MidiBuffer& buffer, int num_samples) {
  290. keyboard_state_->processNextMidiBuffer(buffer, 0, num_samples, true);
  291. }