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@0
|
51 static const int kPedalActiveValue;
|
andrewm@0
|
52
|
andrewm@0
|
53 public:
|
andrewm@0
|
54 // Operating modes for MIDI input on this segment
|
andrewm@0
|
55 enum {
|
andrewm@0
|
56 ModeOff = 0,
|
andrewm@0
|
57 ModePassThrough,
|
andrewm@0
|
58 ModeMonophonic,
|
andrewm@0
|
59 ModePolyphonic
|
andrewm@0
|
60 };
|
andrewm@0
|
61
|
andrewm@0
|
62 // The MIDI Pitch Wheel is not handled by control change like the others,
|
andrewm@0
|
63 // but it is something we will want to map to. Use a special control number
|
andrewm@0
|
64 // to designate mapping OSC to the Pitch Wheel. Use 14 bit values when mapping
|
andrewm@0
|
65 // to this control. Similarly, we might want to map to channel aftertouch value.
|
andrewm@0
|
66 // The mechanics here are identical to 7-bit controllers.
|
andrewm@0
|
67 enum {
|
andrewm@0
|
68 kControlDisabled = -1,
|
andrewm@0
|
69 kControlPitchWheel = 128,
|
andrewm@0
|
70 kControlChannelAftertouch,
|
andrewm@0
|
71 kControlPolyphonicAftertouch,
|
andrewm@0
|
72 kControlMax
|
andrewm@0
|
73 };
|
andrewm@0
|
74
|
andrewm@0
|
75 enum {
|
andrewm@0
|
76 kControlActionPassthrough = 0,
|
andrewm@0
|
77 kControlActionBroadcast,
|
andrewm@0
|
78 kControlActionSendToLatest,
|
andrewm@0
|
79 kControlActionBlock
|
andrewm@0
|
80 };
|
andrewm@0
|
81
|
andrewm@0
|
82 public:
|
andrewm@0
|
83 // Constructor
|
andrewm@0
|
84 MidiKeyboardSegment(PianoKeyboard& keyboard);
|
andrewm@0
|
85
|
andrewm@0
|
86 // Destructor
|
andrewm@0
|
87 ~MidiKeyboardSegment();
|
andrewm@0
|
88
|
andrewm@0
|
89 // Set/query the output controller
|
andrewm@0
|
90 MidiOutputController* midiOutputController() { return midiOutputController_; }
|
andrewm@0
|
91 void setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; }
|
andrewm@0
|
92
|
andrewm@0
|
93 // Check whether this MIDI message is for this segment
|
andrewm@0
|
94 bool respondsToMessage(const MidiMessage& message);
|
andrewm@0
|
95 bool respondsToNote(int noteNumber);
|
andrewm@0
|
96
|
andrewm@0
|
97 // Set which channels we listen to
|
andrewm@0
|
98 void enableChannel(int channelNumber);
|
andrewm@0
|
99 void enableAllChannels();
|
andrewm@0
|
100 void disableChannel(int channelNumber);
|
andrewm@0
|
101 void disableAllChanels();
|
andrewm@0
|
102 void setChannelMask(int channelMask) { channelMask_ = channelMask; }
|
andrewm@0
|
103
|
andrewm@0
|
104 // Set which notes we listen to
|
andrewm@0
|
105 void setNoteRange(int minNote, int maxNote);
|
andrewm@0
|
106 std::pair<int, int> noteRange() { return std::pair<int,int>(noteMin_, noteMax_); }
|
andrewm@0
|
107
|
andrewm@0
|
108 // Set whether or not we use aftertouch, pitchwheel or other controls
|
andrewm@0
|
109 // directly from the keyboard
|
andrewm@0
|
110 bool usesKeyboardChannnelPressure() { return usesKeyboardChannelPressure_; }
|
andrewm@0
|
111 void setUsesKeyboardChannelPressure(bool use) {
|
andrewm@0
|
112 usesKeyboardChannelPressure_ = use;
|
andrewm@0
|
113 // Reset to default if not using
|
andrewm@0
|
114 if(!use)
|
andrewm@0
|
115 controllerValues_[kControlChannelAftertouch] = 0;
|
andrewm@0
|
116 }
|
andrewm@0
|
117
|
andrewm@0
|
118 bool usesKeyboardPitchWheel() { return usesKeyboardPitchWheel_; }
|
andrewm@0
|
119 void setUsesKeyboardPitchWheel(bool use) {
|
andrewm@0
|
120 usesKeyboardPitchWheel_ = use;
|
andrewm@0
|
121 // Reset to default if not using
|
andrewm@0
|
122 if(!use)
|
andrewm@0
|
123 controllerValues_[kControlPitchWheel] = 8192;
|
andrewm@0
|
124 }
|
andrewm@5
|
125
|
andrewm@5
|
126 bool usesKeyboardModWheel() { return usesKeyboardModWheel_; }
|
andrewm@5
|
127 void setUsesKeyboardModWheel(bool use) {
|
andrewm@5
|
128 usesKeyboardModWheel_ = use;
|
andrewm@5
|
129 // Reset to default if not using
|
andrewm@5
|
130 if(!use) {
|
andrewm@5
|
131 controllerValues_[1] = 0;
|
andrewm@5
|
132 }
|
andrewm@5
|
133 }
|
andrewm@5
|
134
|
andrewm@0
|
135
|
andrewm@0
|
136 bool usesKeyboardMIDIControllers() { return usesKeyboardMidiControllers_; }
|
andrewm@0
|
137 void setUsesKeyboardMIDIControllers(bool use) {
|
andrewm@0
|
138 usesKeyboardMidiControllers_ = use;
|
andrewm@0
|
139 // Reset to default if not using
|
andrewm@0
|
140 if(!use) {
|
andrewm@5
|
141 for(int i = 2; i < 128; i++)
|
andrewm@0
|
142 controllerValues_[i] = 0;
|
andrewm@0
|
143 }
|
andrewm@0
|
144 }
|
andrewm@0
|
145
|
andrewm@0
|
146 // Get or set the MIDI pitch wheel range in semitones, and optionally send an RPN
|
andrewm@0
|
147 // message announcing its new value.
|
andrewm@0
|
148 float midiPitchWheelRange() { return pitchWheelRange_; }
|
andrewm@0
|
149 void setMidiPitchWheelRange(float semitones, bool send = false);
|
andrewm@0
|
150 void sendMidiPitchWheelRange();
|
andrewm@0
|
151
|
andrewm@0
|
152 // TouchKeys standalone mode generates MIDI note onsets from touch data
|
andrewm@0
|
153 // without needing a MIDI keyboard
|
andrewm@0
|
154 void enableTouchkeyStandaloneMode();
|
andrewm@0
|
155 void disableTouchkeyStandaloneMode();
|
andrewm@0
|
156 bool touchkeyStandaloneModeEnabled() { return touchkeyStandaloneMode_; }
|
andrewm@0
|
157
|
andrewm@0
|
158 // All Notes Off: can be sent by MIDI or controlled programmatically
|
andrewm@0
|
159 void allNotesOff();
|
andrewm@0
|
160
|
andrewm@0
|
161 // Query the value of a controller
|
andrewm@0
|
162 int controllerValue(int index) {
|
andrewm@0
|
163 if(index < 0 || index >= kControlMax)
|
andrewm@0
|
164 return 0;
|
andrewm@0
|
165 return controllerValues_[index];
|
andrewm@0
|
166 }
|
andrewm@0
|
167
|
andrewm@0
|
168 // Reset MIDI controller values to defaults
|
andrewm@0
|
169 void resetControllerValues();
|
andrewm@0
|
170
|
andrewm@0
|
171 // Change or query the operating mode of the controller
|
andrewm@0
|
172 int mode() { return mode_; }
|
andrewm@0
|
173 void setMode(int mode);
|
andrewm@0
|
174 void setModeOff();
|
andrewm@0
|
175 void setModePassThrough();
|
andrewm@0
|
176 void setModeMonophonic();
|
andrewm@0
|
177 void setModePolyphonic();
|
andrewm@0
|
178
|
andrewm@0
|
179 // Get/set polyphony and voice stealing for polyphonic mode
|
andrewm@0
|
180 int polyphony() { return retransmitMaxPolyphony_; }
|
andrewm@0
|
181 void setPolyphony(int polyphony);
|
andrewm@0
|
182 bool voiceStealingEnabled() { return useVoiceStealing_; }
|
andrewm@0
|
183 void setVoiceStealingEnabled(bool enable) { useVoiceStealing_ = enable; }
|
andrewm@0
|
184
|
andrewm@0
|
185 // Get/set the number of the output port that messages on this segment should go to
|
andrewm@0
|
186 int outputPort() { return outputPortNumber_; }
|
andrewm@0
|
187 void setOutputPort(int port) { outputPortNumber_ = port; }
|
andrewm@0
|
188
|
andrewm@0
|
189 // Set the minimum MIDI channel that should be used for output (0-15)
|
andrewm@0
|
190 int outputChannelLowest() { return outputChannelLowest_; }
|
andrewm@0
|
191 void setOutputChannelLowest(int ch) { outputChannelLowest_ = ch; }
|
andrewm@0
|
192
|
andrewm@0
|
193 // Get set the output transposition in semitones, relative to input MIDI notes
|
andrewm@0
|
194 int outputTransposition() { return outputTransposition_; }
|
andrewm@0
|
195 void setOutputTransposition(int trans) { outputTransposition_ = trans; }
|
andrewm@0
|
196
|
andrewm@0
|
197 // Whether the damper pedal is enabled in note channel allocation
|
andrewm@0
|
198 bool damperPedalEnabled() { return damperPedalEnabled_; }
|
andrewm@0
|
199 void setDamperPedalEnabled(bool enable);
|
andrewm@0
|
200
|
andrewm@0
|
201 // MIDI handler routine
|
andrewm@0
|
202 void midiHandlerMethod(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
203
|
andrewm@0
|
204 // OSC method: used to get touch callback data from the keyboard
|
andrewm@0
|
205 bool oscHandlerMethod(const char *path, const char *types, int numValues, lo_arg **values, void *data);
|
andrewm@0
|
206
|
andrewm@0
|
207 // **** Mapping-related methods *****
|
andrewm@0
|
208
|
andrewm@0
|
209 // OSC-MIDI converters: request and release methods. The acquire method
|
andrewm@0
|
210 // will create a converter if it does not already exist, or return an existing
|
andrewm@0
|
211 // one if it does. The release method will release the object when the
|
andrewm@0
|
212 // acquirer no longer needs it.
|
andrewm@0
|
213 OscMidiConverter* acquireOscMidiConverter(int controlId);
|
andrewm@0
|
214 void releaseOscMidiConverter(int controlId);
|
andrewm@0
|
215
|
andrewm@0
|
216 // Create a new mapping factory for this segment. A pointer should be passed in
|
andrewm@0
|
217 // of a newly-allocated object. It will be released upon removal.
|
andrewm@0
|
218 void addMappingFactory(MappingFactory* factory, bool autoGenerateName = false);
|
andrewm@0
|
219
|
andrewm@0
|
220 // Remove a mapping factory, releasing the associated object.
|
andrewm@0
|
221 void removeMappingFactory(MappingFactory* factory);
|
andrewm@0
|
222
|
andrewm@0
|
223 // Remove all mapping factories, releasing each one
|
andrewm@0
|
224 void removeAllMappingFactories();
|
andrewm@0
|
225
|
andrewm@0
|
226 // Return a list of current mapping factories.
|
andrewm@0
|
227 vector<MappingFactory*> const& mappingFactories();
|
andrewm@0
|
228
|
andrewm@0
|
229 // Return a unique identifier of the mapping state, so we know when something has changed
|
andrewm@0
|
230 int mappingFactoryUniqueIdentifier() { return mappingFactoryUniqueIdentifier_; }
|
andrewm@0
|
231
|
andrewm@0
|
232 private:
|
andrewm@0
|
233 // Mode-specific MIDI input handlers
|
andrewm@0
|
234 void modePassThroughHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
235 void modeMonophonicHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
236
|
andrewm@0
|
237 void modePolyphonicHandler(MidiInput* source, const MidiMessage& message);
|
andrewm@0
|
238 void modePolyphonicNoteOn(unsigned char note, unsigned char velocity);
|
andrewm@0
|
239 void modePolyphonicNoteOff(unsigned char note, bool forceOff = false);
|
andrewm@0
|
240 void modePolyphonicNoteOnCallback(const char *path, const char *types, int numValues, lo_arg **values);
|
andrewm@0
|
241
|
andrewm@0
|
242 // Helper functions for polyphonic mode
|
andrewm@0
|
243 void modePolyphonicSetupHelper();
|
andrewm@0
|
244 int oldestNote();
|
andrewm@0
|
245 int oldestNoteInPedal();
|
andrewm@0
|
246 int newestNote();
|
andrewm@0
|
247
|
andrewm@0
|
248 // Methods for managing controllers
|
andrewm@0
|
249 void handleControlChangeRetransit(int controllerNumber, const MidiMessage& message);
|
andrewm@0
|
250 void setAllControllerActionsTo(int action);
|
andrewm@0
|
251
|
andrewm@0
|
252 // Handle action of the damper pedal: when released, clear out any notes held there
|
andrewm@0
|
253 void damperPedalWentOff();
|
andrewm@0
|
254
|
andrewm@0
|
255 // Send pitch wheel range to a specific channel
|
andrewm@0
|
256 void sendMidiPitchWheelRangeHelper(int channel);
|
andrewm@0
|
257
|
andrewm@0
|
258 // ***** Member Variables *****
|
andrewm@0
|
259
|
andrewm@0
|
260 PianoKeyboard& keyboard_; // Reference to main keyboard data
|
andrewm@0
|
261 MidiOutputController *midiOutputController_; // Destination for MIDI output
|
andrewm@0
|
262 int outputPortNumber_; // Which port to use on the output controller
|
andrewm@0
|
263 vector<MappingFactory*> mappingFactories_; // Collection of mappings for this segment
|
andrewm@0
|
264 MappingFactorySplitter mappingFactorySplitter_; // ...and a splitter class to facilitate communication
|
andrewm@0
|
265 int mappingFactoryUniqueIdentifier_; // Unique ID indicating mapping factory changes
|
andrewm@0
|
266
|
andrewm@0
|
267 int mode_; // Current operating mode of the segment
|
andrewm@0
|
268 unsigned int channelMask_; // Which channels we listen to (1 bit per channel)
|
andrewm@0
|
269 int noteMin_, noteMax_; // Ranges of the notes we respond to
|
andrewm@0
|
270 int outputChannelLowest_; // Lowest (or only) MIDI channel we send to
|
andrewm@0
|
271 int outputTransposition_; // Transposition of notes at output
|
andrewm@0
|
272 bool damperPedalEnabled_; // Whether to handle damper pedal events in allocating channels
|
andrewm@0
|
273 bool touchkeyStandaloneMode_; // Whether we emulate MIDI data from TouchKeys
|
andrewm@0
|
274 bool usesKeyboardChannelPressure_; // Whether this segment passes aftertouch from the keyboard
|
andrewm@0
|
275 bool usesKeyboardPitchWheel_; // Whether this segment passes pitchwheel from the keyboard
|
andrewm@5
|
276 bool usesKeyboardModWheel_; // Whether this segment passes CC 1 (mod wheel) from keyboard
|
andrewm@0
|
277 bool usesKeyboardMidiControllers_; // Whether this segment passes other controllers
|
andrewm@0
|
278 float pitchWheelRange_; // Range of MIDI pitch wheel (in semitones)
|
andrewm@0
|
279
|
andrewm@0
|
280 int controllerValues_[kControlMax]; // Values of MIDI controllers from input device
|
andrewm@0
|
281 int controllerActions_[kControlMax]; // What to do with MIDI CCs when they come in
|
andrewm@0
|
282
|
andrewm@0
|
283 // Mapping between input notes and output channels. Depending on the mode of operation,
|
andrewm@0
|
284 // each note may be rebroadcast on its own MIDI channel. Need to keep track of what goes where.
|
andrewm@0
|
285 // key is MIDI note #, value is output channel (0-15)
|
andrewm@0
|
286 map<int, int> retransmitChannelForNote_;
|
andrewm@0
|
287 set<int> retransmitChannelsAvailable_;
|
andrewm@0
|
288 set<int> retransmitNotesHeldInPedal_;
|
andrewm@0
|
289 int retransmitMaxPolyphony_;
|
andrewm@0
|
290 bool useVoiceStealing_;
|
andrewm@0
|
291 timestamp_type noteOnsetTimestamps_[128]; // When each currently active note began, for stealing
|
andrewm@0
|
292
|
andrewm@0
|
293 // OSC-MIDI conversion objects for use with data mapping. These are stored in each
|
andrewm@0
|
294 // keyboard segment and specific mapping factories can request one when needed.
|
andrewm@0
|
295 map<int, OscMidiConverter*> oscMidiConverters_;
|
andrewm@0
|
296 map<int, int> oscMidiConverterReferenceCounts_;
|
andrewm@0
|
297 };
|
andrewm@0
|
298
|
andrewm@0
|
299 #endif /* defined(__TouchKeys__MidiKeyboardSegment__) */
|