annotate Source/TouchKeys/MidiKeyboardSegment.h @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents ff5d65c69e73
children
rev   line source
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__) */