annotate Source/TouchKeys/MidiKeyboardSegment.h @ 16:61e3c9df4674

Fix bug where TouchKeys standalone mode turns off when mode is changed.
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Mon, 25 Nov 2013 21:36:02 +0000
parents 0bcbe28a25a2
children e8965409903e
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@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__) */