andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 MidiKeyboardSegment.h: handles incoming MIDI data and certain input-output
|
andrewm@0
|
21 mappings for one segment of a keyboard. The keyboard may be divided up into
|
andrewm@0
|
22 any number of segments with different behaviors. An important role of this
|
andrewm@0
|
23 class is to manage the output channel allocation when using one MIDI channel
|
andrewm@0
|
24 per note (for example, to handle polyphonic pitch bend).
|
andrewm@0
|
25 */
|
andrewm@0
|
26
|
andrewm@0
|
27
|
andrewm@0
|
28 #ifndef __TouchKeys__MidiKeyboardSegment__
|
andrewm@0
|
29 #define __TouchKeys__MidiKeyboardSegment__
|
andrewm@0
|
30
|
andrewm@0
|
31 #include <iostream>
|
andrewm@0
|
32 #include <vector>
|
andrewm@0
|
33 #include <map>
|
andrewm@0
|
34 #include <set>
|
andrewm@0
|
35 #include "../JuceLibraryCode/JuceHeader.h"
|
andrewm@0
|
36 #include "PianoKeyboard.h"
|
andrewm@0
|
37 #include "../Mappings/MappingFactorySplitter.h"
|
andrewm@0
|
38
|
andrewm@0
|
39 class OscMidiConverter;
|
andrewm@0
|
40
|
andrewm@0
|
41 // This class handles the processing of MIDI input data for a particular
|
andrewm@0
|
42 // segment of the keyboard. It defines the processing mode and stores certain
|
andrewm@0
|
43 // state information about active notes for this particular part of the keyboard.
|
andrewm@0
|
44 // The MidiInputController class will use one or more of these segments to define
|
andrewm@0
|
45 // keyboard behavior. In the case of a split keyboard arrangement, MIDI channel
|
andrewm@0
|
46 // or note number might determine which segment takes ownership of a particular note.
|
andrewm@0
|
47
|
andrewm@0
|
48 class MidiKeyboardSegment : public OscHandler {
|
andrewm@0
|
49 private:
|
andrewm@0
|
50 static const int kMidiControllerDamperPedal;
|
andrewm@53
|
51 static const int kMidiControllerSostenutoPedal;
|
andrewm@0
|
52 static const int kPedalActiveValue;
|
andrewm@0
|
53
|
andrewm@0
|
54 public:
|
andrewm@0
|
55 // Operating modes for MIDI input on this segment
|
andrewm@0
|
56 enum {
|
andrewm@0
|
57 ModeOff = 0,
|
andrewm@0
|
58 ModePassThrough,
|
andrewm@0
|
59 ModeMonophonic,
|
andrewm@53
|
60 ModePolyphonic,
|
andrewm@53
|
61 ModeMPE
|
andrewm@0
|
62 };
|
andrewm@0
|
63
|
andrewm@0
|
64 // The MIDI Pitch Wheel is not handled by control change like the others,
|
andrewm@0
|
65 // but it is something we will want to map to. Use a special control number
|
andrewm@0
|
66 // to designate mapping OSC to the Pitch Wheel. Use 14 bit values when mapping
|
andrewm@0
|
67 // to this control. Similarly, we might want to map to channel aftertouch value.
|
andrewm@0
|
68 // The mechanics here are identical to 7-bit controllers.
|
andrewm@0
|
69 enum {
|
andrewm@0
|
70 kControlDisabled = -1,
|
andrewm@0
|
71 kControlPitchWheel = 128,
|
andrewm@0
|
72 kControlChannelAftertouch,
|
andrewm@0
|
73 kControlPolyphonicAftertouch,
|
andrewm@0
|
74 kControlMax
|
andrewm@0
|
75 };
|
andrewm@0
|
76
|
andrewm@0
|
77 enum {
|
andrewm@0
|
78 kControlActionPassthrough = 0,
|
andrewm@0
|
79 kControlActionBroadcast,
|
andrewm@0
|
80 kControlActionSendToLatest,
|
andrewm@0
|
81 kControlActionBlock
|
andrewm@0
|
82 };
|
andrewm@0
|
83
|
andrewm@0
|
84 public:
|
andrewm@0
|
85 // Constructor
|
andrewm@0
|
86 MidiKeyboardSegment(PianoKeyboard& keyboard);
|
andrewm@0
|
87
|
andrewm@0
|
88 // Destructor
|
andrewm@0
|
89 ~MidiKeyboardSegment();
|
andrewm@0
|
90
|
andrewm@0
|
91 // Set/query the output controller
|
andrewm@0
|
92 MidiOutputController* midiOutputController() { return midiOutputController_; }
|
andrewm@0
|
93 void setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; }
|
andrewm@0
|
94
|
andrewm@0
|
95 // Check whether this MIDI message is for this segment
|
andrewm@0
|
96 bool respondsToMessage(const MidiMessage& message);
|
andrewm@0
|
97 bool respondsToNote(int noteNumber);
|
andrewm@0
|
98
|
andrewm@0
|
99 // Set which channels we listen to
|
andrewm@0
|
100 void enableChannel(int channelNumber);
|
andrewm@0
|
101 void enableAllChannels();
|
andrewm@0
|
102 void disableChannel(int channelNumber);
|
andrewm@0
|
103 void disableAllChanels();
|
andrewm@0
|
104 void setChannelMask(int channelMask) { channelMask_ = channelMask; }
|
andrewm@0
|
105
|
andrewm@0
|
106 // Set which notes we listen to
|
andrewm@0
|
107 void setNoteRange(int minNote, int maxNote);
|
andrewm@0
|
108 std::pair<int, int> noteRange() { return std::pair<int,int>(noteMin_, noteMax_); }
|
andrewm@0
|
109
|
andrewm@0
|
110 // Set whether or not we use aftertouch, pitchwheel or other controls
|
andrewm@0
|
111 // directly from the keyboard
|
andrewm@0
|
112 bool usesKeyboardChannnelPressure() { return usesKeyboardChannelPressure_; }
|
andrewm@0
|
113 void setUsesKeyboardChannelPressure(bool use) {
|
andrewm@0
|
114 usesKeyboardChannelPressure_ = use;
|
andrewm@0
|
115 // Reset to default if not using
|
andrewm@0
|
116 if(!use)
|
andrewm@0
|
117 controllerValues_[kControlChannelAftertouch] = 0;
|
andrewm@0
|
118 }
|
andrewm@0
|
119
|
andrewm@0
|
120 bool usesKeyboardPitchWheel() { return usesKeyboardPitchWheel_; }
|
andrewm@0
|
121 void setUsesKeyboardPitchWheel(bool use) {
|
andrewm@0
|
122 usesKeyboardPitchWheel_ = use;
|
andrewm@0
|
123 // Reset to default if not using
|
andrewm@0
|
124 if(!use)
|
andrewm@0
|
125 controllerValues_[kControlPitchWheel] = 8192;
|
andrewm@0
|
126 }
|
andrewm@5
|
127
|
andrewm@5
|
128 bool usesKeyboardModWheel() { return usesKeyboardModWheel_; }
|
andrewm@5
|
129 void setUsesKeyboardModWheel(bool use) {
|
andrewm@5
|
130 usesKeyboardModWheel_ = use;
|
andrewm@5
|
131 // Reset to default if not using
|
andrewm@5
|
132 if(!use) {
|
andrewm@5
|
133 controllerValues_[1] = 0;
|
andrewm@5
|
134 }
|
andrewm@5
|
135 }
|
andrewm@5
|
136
|
andrewm@53
|
137 bool usesKeyboardPedals() { return usesKeyboardPedals_; }
|
andrewm@53
|
138 void setUsesKeyboardPedals(bool use) {
|
andrewm@53
|
139 usesKeyboardPedals_ = use;
|
andrewm@53
|
140 // Reset to default if not using
|
andrewm@53
|
141 if(!use) {
|
andrewm@53
|
142 // MIDI CCs 64 to 69 are for pedals
|
andrewm@53
|
143 for(int i = 64; i <= 69; i++)
|
andrewm@53
|
144 controllerValues_[i] = 0;
|
andrewm@53
|
145 }
|
andrewm@53
|
146 }
|
andrewm@0
|
147
|
andrewm@0
|
148 bool usesKeyboardMIDIControllers() { return usesKeyboardMidiControllers_; }
|
andrewm@0
|
149 void setUsesKeyboardMIDIControllers(bool use) {
|
andrewm@0
|
150 usesKeyboardMidiControllers_ = use;
|
andrewm@0
|
151 // Reset to default if not using
|
andrewm@0
|
152 if(!use) {
|
andrewm@5
|
153 for(int i = 2; i < 128; i++)
|
andrewm@0
|
154 controllerValues_[i] = 0;
|
andrewm@0
|
155 }
|
andrewm@0
|
156 }
|
andrewm@0
|
157
|
andrewm@0
|
158 // Get or set the MIDI pitch wheel range in semitones, and optionally send an RPN
|
andrewm@0
|
159 // message announcing its new value.
|
andrewm@0
|
160 float midiPitchWheelRange() { return pitchWheelRange_; }
|
andrewm@0
|
161 void setMidiPitchWheelRange(float semitones, bool send = false);
|
andrewm@0
|
162 void sendMidiPitchWheelRange();
|
andrewm@0
|
163
|
andrewm@0
|
164 // TouchKeys standalone mode generates MIDI note onsets from touch data
|
andrewm@0
|
165 // without needing a MIDI keyboard
|
andrewm@0
|
166 void enableTouchkeyStandaloneMode();
|
andrewm@0
|
167 void disableTouchkeyStandaloneMode();
|
andrewm@0
|
168 bool touchkeyStandaloneModeEnabled() { return touchkeyStandaloneMode_; }
|
andrewm@0
|
169
|
andrewm@0
|
170 // All Notes Off: can be sent by MIDI or controlled programmatically
|
andrewm@0
|
171 void allNotesOff();
|
andrewm@0
|
172
|
andrewm@0
|
173 // Query the value of a controller
|
andrewm@0
|
174 int controllerValue(int index) {
|
andrewm@0
|
175 if(index < 0 || index >= kControlMax)
|
andrewm@0
|
176 return 0;
|
andrewm@0
|
177 return controllerValues_[index];
|
andrewm@0
|
178 }
|
andrewm@0
|
179
|
andrewm@0
|
180 // Reset MIDI controller values to defaults
|
andrewm@0
|
181 void resetControllerValues();
|
andrewm@0
|
182
|
andrewm@0
|
183 // Change or query the operating mode of the controller
|
andrewm@0
|
184 int mode() { return mode_; }
|
andrewm@0
|
185 void setMode(int mode);
|
andrewm@0
|
186 void setModeOff();
|
andrewm@0
|
187 void setModePassThrough();
|
andrewm@0
|
188 void setModeMonophonic();
|
andrewm@0
|
189 void setModePolyphonic();
|
andrewm@53
|
190 void setModeMPE();
|
andrewm@0
|
191
|
andrewm@0
|
192 // Get/set polyphony and voice stealing for polyphonic mode
|
andrewm@0
|
193 int polyphony() { return retransmitMaxPolyphony_; }
|
andrewm@0
|
194 void setPolyphony(int polyphony);
|
andrewm@0
|
195 bool voiceStealingEnabled() { return useVoiceStealing_; }
|
andrewm@0
|
196 void setVoiceStealingEnabled(bool enable) { useVoiceStealing_ = enable; }
|
andrewm@0
|
197
|
andrewm@0
|
198 // Get/set the number of the output port that messages on this segment should go to
|
andrewm@0
|
199 int outputPort() { return outputPortNumber_; }
|
andrewm@0
|
200 void setOutputPort(int port) { outputPortNumber_ = port; }
|
andrewm@0
|
201
|
andrewm@0
|
202 // Set the minimum MIDI channel that should be used for output (0-15)
|
andrewm@0
|
203 int outputChannelLowest() { return outputChannelLowest_; }
|
andrewm@53
|
204 void setOutputChannelLowest(int ch);
|
andrewm@0
|
205
|
andrewm@0
|
206 // Get set the output transposition in semitones, relative to input MIDI notes
|
andrewm@0
|
207 int outputTransposition() { return outputTransposition_; }
|
andrewm@0
|
208 void setOutputTransposition(int trans) { outputTransposition_ = trans; }
|
andrewm@0
|
209
|
andrewm@0
|
210 // Whether the damper pedal is enabled in note channel allocation
|
andrewm@0
|
211 bool damperPedalEnabled() { return damperPedalEnabled_; }
|
andrewm@0
|
212 void setDamperPedalEnabled(bool enable);
|
andrewm@0
|
213
|
andrewm@0
|
214 // MIDI handler routine
|
andrewm@0
|
215 void midiHandlerMethod(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
216
|
andrewm@0
|
217 // OSC method: used to get touch callback data from the keyboard
|
andrewm@0
|
218 bool oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data);
|
andrewm@0
|
219
|
andrewm@49
|
220 // OSC control method: called separately via the MidiInputController to manipulate
|
andrewm@49
|
221 // control parameters of this object
|
andrewm@49
|
222 OscMessage* oscControlMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data);
|
andrewm@49
|
223
|
andrewm@0
|
224 // **** Mapping-related methods *****
|
andrewm@0
|
225
|
andrewm@0
|
226 // OSC-MIDI converters: request and release methods. The acquire method
|
andrewm@0
|
227 // will create a converter if it does not already exist, or return an existing
|
andrewm@0
|
228 // one if it does. The release method will release the object when the
|
andrewm@0
|
229 // acquirer no longer needs it.
|
andrewm@0
|
230 OscMidiConverter* acquireOscMidiConverter(int controlId);
|
andrewm@0
|
231 void releaseOscMidiConverter(int controlId);
|
andrewm@0
|
232
|
andrewm@49
|
233 // *** Mapping methods ***
|
andrewm@49
|
234 // Return the number of mapping factory types available
|
andrewm@49
|
235 static int numberOfMappingFactories();
|
andrewm@49
|
236
|
andrewm@49
|
237 // Return the name of a given mapping factory type
|
andrewm@49
|
238 static String mappingFactoryNameForIndex(int index);
|
andrewm@49
|
239
|
andrewm@49
|
240 // Whether a given mapping is experimental
|
andrewm@49
|
241 static bool mappingIsExperimental(int index);
|
andrewm@49
|
242
|
andrewm@49
|
243 // Create a new mapping factory of the given type, attached to
|
andrewm@49
|
244 // the supplied segment
|
andrewm@49
|
245 MappingFactory* createMappingFactoryForIndex(int index);
|
andrewm@49
|
246
|
andrewm@0
|
247 // Create a new mapping factory for this segment. A pointer should be passed in
|
andrewm@0
|
248 // of a newly-allocated object. It will be released upon removal.
|
andrewm@0
|
249 void addMappingFactory(MappingFactory* factory, bool autoGenerateName = false);
|
andrewm@0
|
250
|
andrewm@0
|
251 // Remove a mapping factory, releasing the associated object.
|
andrewm@0
|
252 void removeMappingFactory(MappingFactory* factory);
|
andrewm@0
|
253
|
andrewm@0
|
254 // Remove all mapping factories, releasing each one
|
andrewm@0
|
255 void removeAllMappingFactories();
|
andrewm@0
|
256
|
andrewm@0
|
257 // Return a list of current mapping factories.
|
andrewm@0
|
258 vector<MappingFactory*> const& mappingFactories();
|
andrewm@41
|
259
|
andrewm@41
|
260 // Return the specific index of this mapping factory
|
andrewm@41
|
261 int indexOfMappingFactory(MappingFactory *factory);
|
andrewm@0
|
262
|
andrewm@0
|
263 // Return a unique identifier of the mapping state, so we know when something has changed
|
andrewm@0
|
264 int mappingFactoryUniqueIdentifier() { return mappingFactoryUniqueIdentifier_; }
|
andrewm@33
|
265
|
andrewm@33
|
266 // **** Preset methods ****
|
andrewm@33
|
267
|
andrewm@33
|
268 // Get an XML element describing current settings (for saving presets)
|
andrewm@33
|
269 XmlElement* getPreset();
|
andrewm@33
|
270
|
andrewm@33
|
271 // Load settings from an XML element
|
andrewm@33
|
272 bool loadPreset(XmlElement const* preset);
|
andrewm@0
|
273
|
andrewm@0
|
274 private:
|
andrewm@0
|
275 // Mode-specific MIDI input handlers
|
andrewm@0
|
276 void modePassThroughHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
277 void modeMonophonicHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
278
|
andrewm@0
|
279 void modePolyphonicHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
280 void modePolyphonicNoteOn(unsigned char note, unsigned char velocity);
|
andrewm@0
|
281 void modePolyphonicNoteOff(unsigned char note, bool forceOff = false);
|
andrewm@53
|
282 void modePolyphonicMPENoteOnCallback(const char *path, const char *types, int numValues, lo_arg **values);
|
andrewm@53
|
283
|
andrewm@53
|
284 void modeMPEHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@53
|
285 void modeMPENoteOn(unsigned char note, unsigned char velocity);
|
andrewm@0
|
286
|
andrewm@0
|
287 // Helper functions for polyphonic mode
|
andrewm@0
|
288 void modePolyphonicSetupHelper();
|
andrewm@0
|
289 int oldestNote();
|
andrewm@0
|
290 int oldestNoteInPedal();
|
andrewm@0
|
291 int newestNote();
|
andrewm@0
|
292
|
andrewm@0
|
293 // Methods for managing controllers
|
andrewm@0
|
294 void handleControlChangeRetransit(int controllerNumber, const MidiMessage& message);
|
andrewm@0
|
295 void setAllControllerActionsTo(int action);
|
andrewm@0
|
296
|
andrewm@0
|
297 // Handle action of the damper pedal: when released, clear out any notes held there
|
andrewm@0
|
298 void damperPedalWentOff();
|
andrewm@0
|
299
|
andrewm@0
|
300 // Send pitch wheel range to a specific channel
|
andrewm@0
|
301 void sendMidiPitchWheelRangeHelper(int channel);
|
andrewm@0
|
302
|
andrewm@0
|
303 // ***** Member Variables *****
|
andrewm@0
|
304
|
andrewm@0
|
305 PianoKeyboard& keyboard_; // Reference to main keyboard data
|
andrewm@0
|
306 MidiOutputController *midiOutputController_; // Destination for MIDI output
|
andrewm@0
|
307 int outputPortNumber_; // Which port to use on the output controller
|
andrewm@0
|
308 vector<MappingFactory*> mappingFactories_; // Collection of mappings for this segment
|
andrewm@0
|
309 MappingFactorySplitter mappingFactorySplitter_; // ...and a splitter class to facilitate communication
|
andrewm@0
|
310 int mappingFactoryUniqueIdentifier_; // Unique ID indicating mapping factory changes
|
andrewm@0
|
311
|
andrewm@0
|
312 int mode_; // Current operating mode of the segment
|
andrewm@0
|
313 unsigned int channelMask_; // Which channels we listen to (1 bit per channel)
|
andrewm@0
|
314 int noteMin_, noteMax_; // Ranges of the notes we respond to
|
andrewm@0
|
315 int outputChannelLowest_; // Lowest (or only) MIDI channel we send to
|
andrewm@0
|
316 int outputTransposition_; // Transposition of notes at output
|
andrewm@0
|
317 bool damperPedalEnabled_; // Whether to handle damper pedal events in allocating channels
|
andrewm@0
|
318 bool touchkeyStandaloneMode_; // Whether we emulate MIDI data from TouchKeys
|
andrewm@0
|
319 bool usesKeyboardChannelPressure_; // Whether this segment passes aftertouch from the keyboard
|
andrewm@0
|
320 bool usesKeyboardPitchWheel_; // Whether this segment passes pitchwheel from the keyboard
|
andrewm@5
|
321 bool usesKeyboardModWheel_; // Whether this segment passes CC 1 (mod wheel) from keyboard
|
andrewm@53
|
322 bool usesKeyboardPedals_; // Whether this segment passes CCs 64-69 (pedals) from the keyboard
|
andrewm@0
|
323 bool usesKeyboardMidiControllers_; // Whether this segment passes other controllers
|
andrewm@0
|
324 float pitchWheelRange_; // Range of MIDI pitch wheel (in semitones)
|
andrewm@0
|
325
|
andrewm@0
|
326 int controllerValues_[kControlMax]; // Values of MIDI controllers from input device
|
andrewm@0
|
327 int controllerActions_[kControlMax]; // What to do with MIDI CCs when they come in
|
andrewm@0
|
328
|
andrewm@0
|
329 // Mapping between input notes and output channels. Depending on the mode of operation,
|
andrewm@0
|
330 // each note may be rebroadcast on its own MIDI channel. Need to keep track of what goes where.
|
andrewm@0
|
331 // key is MIDI note #, value is output channel (0-15)
|
andrewm@0
|
332 map<int, int> retransmitChannelForNote_;
|
andrewm@0
|
333 set<int> retransmitChannelsAvailable_;
|
andrewm@0
|
334 set<int> retransmitNotesHeldInPedal_;
|
andrewm@0
|
335 int retransmitMaxPolyphony_;
|
andrewm@0
|
336 bool useVoiceStealing_;
|
andrewm@0
|
337 timestamp_type noteOnsetTimestamps_[128]; // When each currently active note began, for stealing
|
andrewm@0
|
338
|
andrewm@0
|
339 // OSC-MIDI conversion objects for use with data mapping. These are stored in each
|
andrewm@0
|
340 // keyboard segment and specific mapping factories can request one when needed.
|
andrewm@0
|
341 map<int, OscMidiConverter*> oscMidiConverters_;
|
andrewm@0
|
342 map<int, int> oscMidiConverterReferenceCounts_;
|
andrewm@0
|
343 };
|
andrewm@0
|
344
|
andrewm@0
|
345 #endif /* defined(__TouchKeys__MidiKeyboardSegment__) */
|