Vital
Loading...
Searching...
No Matches
midi_manager.cpp
Go to the documentation of this file.
1#include "midi_manager.h"
2#include "sound_engine.h"
3#include "synth_types.h"
4#include "load_save.h"
5#include "synth_base.h"
6
7namespace {
8 constexpr int kMidiControlBits = 7;
9 constexpr float kHighResolutionMax = (1 << (2 * kMidiControlBits)) - 1.0f;
10 constexpr float kControlMax = (1 << kMidiControlBits) - 1.0f;
11
19 force_inline vital::mono_float toHighResolutionValue(int msb, int lsb) {
20 if (lsb < 0)
21 return msb / kControlMax;
22
23 return ((msb << kMidiControlBits) + lsb) / kHighResolutionMax;
24 }
25} // namespace
26
27MidiManager::MidiManager(SynthBase* synth, MidiKeyboardState* keyboard_state,
28 std::map<std::string, String>* gui_state, Listener* listener) :
29 synth_(synth), keyboard_state_(keyboard_state), gui_state_(gui_state),
30 listener_(listener), armed_value_(nullptr),
31 msb_pressure_values_(), msb_slide_values_() {
33 current_bank_ = -1;
34 current_folder_ = -1;
35 current_preset_ = -1;
36
37 for (int i = 0; i < vital::kNumMidiChannels; ++i) {
38 lsb_slide_values_[i] = -1;
40 }
41
42 mpe_enabled_ = false;
44}
45
48
49void MidiManager::armMidiLearn(std::string name) {
50 current_bank_ = -1;
51 current_folder_ = -1;
52 current_preset_ = -1;
54}
55
57 armed_value_ = nullptr;
58}
59
60void MidiManager::clearMidiLearn(const std::string& name) {
61 for (auto& controls : midi_learn_map_) {
62 if (controls.second.count(name)) {
63 midi_learn_map_[controls.first].erase(name);
65 }
66 }
67}
68
69void MidiManager::midiInput(int midi_id, vital::mono_float value) {
70 if (armed_value_) {
71 // Assign the armed parameter to the received MIDI control.
73 armed_value_ = nullptr;
74
75 // TODO: Probably shouldn't write this config on the audio thread.
77 }
78
79 if (midi_learn_map_.count(midi_id)) {
80 for (auto& control : midi_learn_map_[midi_id]) {
81 const vital::ValueDetails* details = control.second;
82 vital::mono_float percent = value / kControlMax;
83 vital::mono_float range = details->max - details->min;
84 vital::mono_float translated = percent * range + details->min;
85
87 translated = std::round(translated);
88 listener_->valueChangedThroughMidi(control.first, translated);
89 }
90 }
91}
92
93bool MidiManager::isMidiMapped(const std::string& name) const {
94 for (auto& controls : midi_learn_map_) {
95 if (controls.second.count(name))
96 return true;
97 }
98 return false;
99}
100
101void MidiManager::setSampleRate(double sample_rate) {
102 midi_collector_.reset(sample_rate);
103}
104
105void MidiManager::removeNextBlockOfMessages(MidiBuffer& buffer, int num_samples) {
106 midi_collector_.removeNextBlockOfMessages(buffer, num_samples);
107}
108
109void MidiManager::readMpeMessage(const MidiMessage& message) {
110 mpe_zone_layout_.processNextMidiEvent(message);
111}
112
113void MidiManager::processAllNotesOff(const MidiMessage& midi_message, int sample_position, int channel) {
114 if (isMpeChannelMasterLowerZone(channel))
116 else if (isMpeChannelMasterUpperZone(channel))
118 else
119 engine_->allNotesOff(sample_position, channel);
120}
121
125
126void MidiManager::processSustain(const MidiMessage& midi_message, int sample_position, int channel) {
127 bool on = midi_message.isSustainPedalOn();
128 if (isMpeChannelMasterLowerZone(channel)) {
129 if (on)
131 else
133 }
134 else if (isMpeChannelMasterUpperZone(channel)) {
135 if (on)
137 else
139 }
140 else {
141 if (on)
142 engine_->sustainOn(channel);
143 else
144 engine_->sustainOff(sample_position, channel);
145 }
146}
147
148void MidiManager::processSostenuto(const MidiMessage& midi_message, int sample_position, int channel) {
149 bool on = midi_message.isSostenutoPedalOn();
150 if (isMpeChannelMasterLowerZone(channel)) {
151 if (on)
153 else
155 }
156 else if (isMpeChannelMasterUpperZone(channel)) {
157 if (on)
159 else
161 }
162 else {
163 if (on)
164 engine_->sostenutoOn(channel);
165 else
166 engine_->sostenutoOff(sample_position, channel);
167 }
168}
169
170void MidiManager::processPitchBend(const MidiMessage& midi_message, int sample_position, int channel) {
171 vital::mono_float percent = midi_message.getPitchWheelValue() / kHighResolutionMax;
172 vital::mono_float value = 2 * percent - 1.0f;
173
174 if (isMpeChannelMasterLowerZone(channel)) {
178 }
179 else if (isMpeChannelMasterUpperZone(channel)) {
183 }
184 else if (mpe_enabled_)
185 engine_->setPitchWheel(value, channel);
186 else {
187 engine_->setZonedPitchWheel(value, channel, channel);
189 }
190}
191
192void MidiManager::processPressure(const MidiMessage& midi_message, int sample_position, int channel) {
193 vital::mono_float value = toHighResolutionValue(msb_pressure_values_[channel], lsb_pressure_values_[channel]);
194 if (isMpeChannelMasterLowerZone(channel))
196 else if (isMpeChannelMasterUpperZone(channel))
198 else
199 engine_->setChannelAftertouch(channel, value, sample_position);
200}
201
202void MidiManager::processSlide(const MidiMessage& midi_message, int sample_position, int channel) {
203 vital::mono_float value = toHighResolutionValue(msb_slide_values_[channel], lsb_slide_values_[channel]);
204 if (isMpeChannelMasterLowerZone(channel))
206 else if (isMpeChannelMasterUpperZone(channel))
208 else
209 engine_->setChannelSlide(channel, value, sample_position);
210}
211
213 return mpe_enabled_ && mpe_zone_layout_.getLowerZone().isActive() && lowerMasterChannel() == channel;
214}
215
217 return mpe_enabled_ && mpe_zone_layout_.getUpperZone().isActive() && upperMasterChannel() == channel;
218}
219
220void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample_position) {
221 if (midi_message.isController())
222 readMpeMessage(midi_message);
223
224 int channel = midi_message.getChannel() - 1;
225 MidiMainType type = static_cast<MidiMainType>(midi_message.getRawData()[0] & 0xf0);
226 switch (type) {
227 case kProgramChange:
228 return;
229 case kNoteOn: {
230 uint8 velocity = midi_message.getVelocity();
231 if (velocity)
232 engine_->noteOn(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
233 else
234 engine_->noteOff(midi_message.getNoteNumber(), velocity / kControlMax, sample_position, channel);
235 return;
236 }
237 case kNoteOff: {
238 vital::mono_float velocity = midi_message.getVelocity() / kControlMax;
239 engine_->noteOff(midi_message.getNoteNumber(), velocity, sample_position, channel);
240 return;
241 }
242 case kAftertouch: {
243 int note = midi_message.getNoteNumber();
244 vital::mono_float value = midi_message.getAfterTouchValue() / kControlMax;
245 engine_->setAftertouch(note, value, sample_position, channel);
246 return;
247 }
248 case kChannelPressure: {
249 msb_pressure_values_[channel] = midi_message.getChannelPressureValue();
250 processPressure(midi_message, sample_position, channel);
251 return;
252 }
253 case kPitchWheel: {
254 processPitchBend(midi_message, sample_position, channel);
255 return;
256 }
257 case kController: {
258 MidiSecondaryType secondary_type = static_cast<MidiSecondaryType>(midi_message.getControllerNumber());
259 switch (secondary_type) {
260 case kSlide: {
261 msb_slide_values_[channel] = midi_message.getControllerValue();
262 processSlide(midi_message, sample_position, channel);
263 break;
264 }
265 case kLsbPressure: {
266 lsb_pressure_values_[channel] = midi_message.getControllerValue();
267 processPressure(midi_message, sample_position, channel);
268 break;
269 }
270 case kLsbSlide: {
271 lsb_slide_values_[channel] = midi_message.getControllerValue();
272 processSlide(midi_message, sample_position, channel);
273 break;
274 }
275 case kSustainPedal: {
276 processSustain(midi_message, sample_position, channel);
277 break;
278 }
279 case kSostenutoPedal: {
280 processSostenuto(midi_message, sample_position, channel);
281 break;
282 }
283 case kSoftPedalOn: // TODO
284 break;
285 case kModWheel: {
286 vital::mono_float percent = (1.0f * midi_message.getControllerValue()) / kControlMax;
287 engine_->setModWheel(percent, channel);
289 break;
290 }
291 case kAllNotesOff:
293 processAllNotesOff(midi_message, sample_position, channel);
294 return;
295 case kAllSoundsOff:
297 break;
298 case kBankSelect:
299 current_bank_ = midi_message.getControllerValue();
300 return;
301 case kFolderSelect:
302 current_folder_ = midi_message.getControllerValue();
303 return;
304 }
305 midiInput(midi_message.getControllerNumber(), midi_message.getControllerValue());
306 }
307 }
308}
309
310void MidiManager::handleIncomingMidiMessage(MidiInput* source, const MidiMessage &midi_message) {
312}
313
314void MidiManager::replaceKeyboardMessages(MidiBuffer& buffer, int num_samples) {
315 keyboard_state_->processNextMidiBuffer(buffer, 0, num_samples, true);
316}
static void saveMidiMapConfig(MidiManager *midi_manager)
Saves MIDI mapping configuration.
Definition load_save.cpp:1358
Definition midi_manager.h:12
An interface for classes that listen to MIDI-driven parameter changes.
Definition midi_manager.h:91
virtual void modWheelMidiChanged(vital::mono_float value)=0
Called when the mod wheel changes.
virtual void valueChangedThroughMidi(const std::string &name, vital::mono_float value)=0
Called when a parameter value changes due to a MIDI control message.
virtual void pitchWheelMidiChanged(vital::mono_float value)=0
Called when the pitch wheel changes.
MidiSecondaryType
Enum representing secondary MIDI controller values (MIDI CC numbers).
Definition midi_manager.h:70
@ kAllNotesOff
Definition midi_manager.h:82
@ kBankSelect
Definition midi_manager.h:71
@ kAllControllersOff
Definition midi_manager.h:81
@ kLsbSlide
Definition midi_manager.h:79
@ kAllSoundsOff
Definition midi_manager.h:80
@ kModWheel
Definition midi_manager.h:72
@ kSostenutoPedal
Definition midi_manager.h:75
@ kSlide
Definition midi_manager.h:77
@ kSustainPedal
Definition midi_manager.h:74
@ kSoftPedalOn
Definition midi_manager.h:76
@ kFolderSelect
Definition midi_manager.h:73
@ kLsbPressure
Definition midi_manager.h:78
const vital::ValueDetails * armed_value_
Parameter armed for MIDI learn.
Definition midi_manager.h:380
int current_bank_
Tracks the current bank for preset selection.
Definition midi_manager.h:376
void setSampleRate(double sample_rate)
Sets the audio sample rate for MIDI message timing.
Definition midi_manager.cpp:101
void readMpeMessage(const MidiMessage &message)
Parses and processes an MPE-related MIDI message.
Definition midi_manager.cpp:109
void midiInput(int control, vital::mono_float value)
Handles a direct MIDI input value and applies MIDI learn if armed.
Definition midi_manager.cpp:69
void processSlide(const MidiMessage &midi_message, int sample_position, int channel)
Processes slide messages (MPE CC events) translating them to appropriate modulation.
Definition midi_manager.cpp:202
bool isMpeChannelMasterLowerZone(int channel)
Checks if the given channel is the MPE master channel for the lower zone.
Definition midi_manager.cpp:212
MidiMessageCollector midi_collector_
Collects incoming MIDI messages.
Definition midi_manager.h:373
MidiMainType
Enum representing main categories of MIDI events (status bytes).
Definition midi_manager.h:57
@ kProgramChange
MIDI Program Change.
Definition midi_manager.h:62
@ kChannelPressure
MIDI Channel Pressure (monophonic aftertouch)
Definition midi_manager.h:63
@ kPitchWheel
MIDI Pitch Wheel event.
Definition midi_manager.h:64
@ kController
MIDI Control Change event.
Definition midi_manager.h:61
@ kNoteOn
MIDI Note On event.
Definition midi_manager.h:59
@ kAftertouch
MIDI Polyphonic Aftertouch.
Definition midi_manager.h:60
@ kNoteOff
MIDI Note Off event.
Definition midi_manager.h:58
Listener * listener_
Listener for MIDI-driven parameter changes and preset loads.
Definition midi_manager.h:375
void processPressure(const MidiMessage &midi_message, int sample_position, int channel)
Processes channel pressure (aftertouch) MIDI messages.
Definition midi_manager.cpp:192
bool isMpeChannelMasterUpperZone(int channel)
Checks if the given channel is the MPE master channel for the upper zone.
Definition midi_manager.cpp:216
int lsb_pressure_values_[vital::kNumMidiChannels]
LSB values for channel pressure.
Definition midi_manager.h:384
void handleIncomingMidiMessage(MidiInput *source, const MidiMessage &midi_message) override
Handles incoming MIDI messages from a MidiInput source (MidiInputCallback interface).
Definition midi_manager.cpp:310
bool isMidiMapped(const std::string &name) const
Checks if a parameter is already mapped to a MIDI control.
Definition midi_manager.cpp:93
void processAllSoundsOff()
Processes an 'All Sounds Off' MIDI message, turning off all voices immediately.
Definition midi_manager.cpp:122
void processAllNotesOff(const MidiMessage &midi_message, int sample_position, int channel)
Processes an 'All Notes Off' MIDI message.
Definition midi_manager.cpp:113
int lsb_slide_values_[vital::kNumMidiChannels]
LSB values for slide control.
Definition midi_manager.h:386
force_inline int upperMasterChannel()
Returns the master channel index (0-based) of the MPE upper zone.
Definition midi_manager.h:303
MidiManager(SynthBase *synth, MidiKeyboardState *keyboard_state, std::map< std::string, String > *gui_state, Listener *listener=nullptr)
Constructs a MidiManager to handle MIDI events for a given SynthBase instance.
Definition midi_manager.cpp:27
int current_preset_
Tracks the current preset within a folder.
Definition midi_manager.h:378
void replaceKeyboardMessages(MidiBuffer &buffer, int num_samples)
Replaces keyboard messages in the given MIDI buffer.
Definition midi_manager.cpp:314
void processSostenuto(const MidiMessage &midi_message, int sample_position, int channel)
Processes a sostenuto pedal MIDI message.
Definition midi_manager.cpp:148
void processPitchBend(const MidiMessage &midi_message, int sample_position, int channel)
Processes a pitch bend MIDI message.
Definition midi_manager.cpp:170
void armMidiLearn(std::string name)
Arms the MIDI learn functionality for a given parameter.
Definition midi_manager.cpp:49
void removeNextBlockOfMessages(MidiBuffer &buffer, int num_samples)
Removes the next block of MIDI messages, filling a MidiBuffer.
Definition midi_manager.cpp:105
int current_folder_
Tracks the current folder for preset selection.
Definition midi_manager.h:377
force_inline int lowerZoneEndChannel()
Returns the end channel index (0-based) of the MPE lower zone.
Definition midi_manager.h:297
int msb_pressure_values_[vital::kNumMidiChannels]
MSB values for channel pressure.
Definition midi_manager.h:383
MPEZoneLayout mpe_zone_layout_
MPE zone layout object.
Definition midi_manager.h:389
midi_map midi_learn_map_
Mapping of MIDI controls to synth parameters.
Definition midi_manager.h:381
force_inline int lowerZoneStartChannel()
Returns the start channel index (0-based) of the MPE lower zone.
Definition midi_manager.h:293
vital::SoundEngine * engine_
The sound engine for handling audio events.
Definition midi_manager.h:371
SynthBase * synth_
Associated synthesizer base instance.
Definition midi_manager.h:370
void processSustain(const MidiMessage &midi_message, int sample_position, int channel)
Processes a sustain pedal MIDI message.
Definition midi_manager.cpp:126
MidiKeyboardState * keyboard_state_
The keyboard state for on-screen keyboard integration.
Definition midi_manager.h:372
bool mpe_enabled_
True if MPE mode is enabled.
Definition midi_manager.h:388
virtual ~MidiManager()
Destructor.
Definition midi_manager.cpp:46
void clearMidiLearn(const std::string &name)
Clears an existing MIDI mapping for a given parameter.
Definition midi_manager.cpp:60
force_inline int upperZoneStartChannel()
Returns the start channel index (0-based) of the MPE upper zone.
Definition midi_manager.h:295
int msb_slide_values_[vital::kNumMidiChannels]
MSB values for slide control.
Definition midi_manager.h:385
void processMidiMessage(const MidiMessage &midi_message, int sample_position=0)
Processes a given MIDI message and forwards it to the synth engine.
Definition midi_manager.cpp:220
force_inline int upperZoneEndChannel()
Returns the end channel index (0-based) of the MPE upper zone.
Definition midi_manager.h:299
void cancelMidiLearn()
Cancels the MIDI learn operation without assigning any mapping.
Definition midi_manager.cpp:56
force_inline int lowerMasterChannel()
Returns the master channel index (0-based) of the MPE lower zone.
Definition midi_manager.h:301
void removeNextBlockOfMessages(MidiBuffer &buffer, int num_samples)
Definition midi_manager.h:23
void reset(int sample_rate)
Definition midi_manager.h:22
void addMessageToQueue(const MidiMessage &midi_message)
Definition midi_manager.h:24
A base class providing foundational functionality for the Vital synthesizer’s engine,...
Definition synth_base.h:42
vital::SoundEngine * getEngine()
Returns a pointer to the SoundEngine used for audio and modulation processing.
Definition synth_base.h:484
static const ValueDetails & getDetails(const std::string &name)
Definition synth_parameters.h:200
void setPitchWheel(mono_float value, int channel)
Sets the pitch wheel value for a specified MIDI channel.
Definition sound_engine.cpp:405
void sustainOnRange(int from_channel, int to_channel)
Engages sustain on a range of MIDI channels.
Definition sound_engine.cpp:601
void sustainOn(int channel)
Engages sustain on a single MIDI channel (holding all pressed notes).
Definition sound_engine.cpp:562
void sostenutoOff(int sample, int channel)
Disengages sostenuto on a single MIDI channel.
Definition sound_engine.cpp:591
void allSoundsOff() override
Stops and resets all currently playing notes and any lingering states in the effect chain.
Definition sound_engine.cpp:322
void setChannelAftertouch(int channel, mono_float value, int sample)
Sets channel-wide aftertouch on a given channel.
Definition sound_engine.cpp:494
void sostenutoOnRange(int from_channel, int to_channel)
Engages sostenuto on a range of MIDI channels.
Definition sound_engine.cpp:622
void allNotesOff(int sample) override
Turns off all notes on all channels at the specified sample index.
Definition sound_engine.cpp:331
void setZonedPitchWheel(mono_float value, int from_channel, int to_channel)
Applies a pitch wheel value to a range of MIDI channels.
Definition sound_engine.cpp:416
void sostenutoOn(int channel)
Engages sostenuto on a single MIDI channel (holding only currently pressed notes).
Definition sound_engine.cpp:581
void setChannelSlide(int channel, mono_float value, int sample)
Sets channel slide (e.g., an expression or glide parameter) on a specific channel.
Definition sound_engine.cpp:517
void sustainOffRange(int sample, int from_channel, int to_channel)
Disengages sustain on a range of MIDI channels.
Definition sound_engine.cpp:612
void setChannelRangeSlide(int from_channel, int to_channel, mono_float value, int sample)
Applies channel slide to all channels in a specified range.
Definition sound_engine.cpp:529
void noteOn(int note, mono_float velocity, int sample, int channel) override
Handles a note-on event, triggering the engine’s voice handling.
Definition sound_engine.cpp:364
void setChannelRangeAftertouch(int from_channel, int to_channel, mono_float value, int sample)
Applies aftertouch to all channels in a specified range.
Definition sound_engine.cpp:506
void sustainOff(int sample, int channel)
Disengages sustain on a single MIDI channel, releasing notes if their keys are up.
Definition sound_engine.cpp:572
void allNotesOffRange(int sample, int from_channel, int to_channel)
Turns off all notes within a range of channels.
Definition sound_engine.cpp:352
void setModWheel(mono_float value, int channel)
Sets the mod wheel value for a given MIDI channel.
Definition sound_engine.cpp:386
void sostenutoOffRange(int sample, int from_channel, int to_channel)
Disengages sostenuto on a range of MIDI channels.
Definition sound_engine.cpp:633
void noteOff(int note, mono_float lift, int sample, int channel) override
Handles a note-off event, releasing the voice allocated to the note.
Definition sound_engine.cpp:376
void setAftertouch(mono_float note, mono_float value, int sample, int channel)
Sets polyphonic aftertouch for a specific note on a given channel.
Definition sound_engine.cpp:483
#define force_inline
Definition common.h:23
constexpr int kNumMidiChannels
MIDI channels available per device.
Definition common.h:57
float mono_float
Definition common.h:33
Holds metadata about a single parameter (control) in the Vital synthesizer.
Definition synth_parameters.h:23
mono_float max
Maximum parameter value.
Definition synth_parameters.h:41
@ kIndexed
Parameter steps through discrete indexed values.
Definition synth_parameters.h:29
mono_float min
Minimum parameter value.
Definition synth_parameters.h:40
std::string name
Unique parameter name/identifier.
Definition synth_parameters.h:38
ValueScale value_scale
The scaling mode of the parameter value.
Definition synth_parameters.h:45