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

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents dfff66c07936
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 PianoKeyboard.h: main class that keeps track of each key (and pedal)
andrewm@0 21 on the keyboard, while also providing hooks for mapping and scheduling
andrewm@0 22 of events. One shared instance of this class is used widely throughout
andrewm@0 23 the program.
andrewm@0 24 */
andrewm@0 25
andrewm@0 26 #ifndef KEYCONTROL_PIANOKEYBOARD_H
andrewm@0 27 #define KEYCONTROL_PIANOKEYBOARD_H
andrewm@0 28
andrewm@0 29 #include <iostream>
andrewm@0 30 #include <fstream>
andrewm@0 31 #include <map>
andrewm@0 32 #include "../Utility/Types.h"
andrewm@0 33 #include "../Utility/Node.h"
andrewm@0 34 #include "PianoKey.h"
andrewm@0 35 #include "PianoPedal.h"
andrewm@0 36 #include "../Display/KeyboardDisplay.h"
andrewm@0 37 #include "../Display/KeyPositionGraphDisplay.h"
andrewm@0 38 #include "Osc.h"
andrewm@0 39 #include "../Utility/Scheduler.h"
andrewm@20 40 #include "../JuceLibraryCode/JuceHeader.h"
andrewm@0 41
andrewm@0 42 #define NUM_KEYS 88
andrewm@0 43 #define NUM_PEDALS 3
andrewm@0 44
andrewm@0 45 enum { // Index of each pedal in the buffers
andrewm@0 46 kPedalDamper = 0,
andrewm@0 47 kPedalSostenuto = 1,
andrewm@0 48 kPedalUnaCorda = 2,
andrewm@0 49 kNumPedals
andrewm@0 50 };
andrewm@0 51
andrewm@0 52 const int kDefaultKeyHistoryLength = 8192;
andrewm@0 53 const int kDefaultPedalHistoryLength = 1024;
andrewm@0 54
andrewm@0 55 class TouchkeyDevice;
andrewm@0 56 class Mapping;
andrewm@0 57 class MidiOutputController;
andrewm@0 58 class MappingFactory;
andrewm@0 59 class MidiKeyboardSegment;
andrewm@0 60 class MappingScheduler;
andrewm@0 61
andrewm@0 62 /*
andrewm@0 63 * PianoKeyboard
andrewm@0 64 *
andrewm@0 65 * Base class that implements all the functionality needed to measure and process
andrewm@0 66 * real-time piano key motion. This class is abstract in that it doesn't define a particular
andrewm@0 67 * source for the key motion data. The data source depends on the hardware used: PianoBar,
andrewm@0 68 * PnoScan II, CEUS, etc.
andrewm@0 69 *
andrewm@0 70 * PianoKeyboard is a source of OSC messages (generated by various key actions). Hence,
andrewm@0 71 * objects can register with it to implement custom behavior for different actions.
andrewm@0 72 */
andrewm@0 73
andrewm@0 74 class PianoKeyboard : public OscMessageSource {
andrewm@0 75 public:
andrewm@0 76 ofstream testLog_;
andrewm@0 77
andrewm@0 78 // ***** Constructors *****
andrewm@0 79
andrewm@0 80 PianoKeyboard();
andrewm@0 81
andrewm@0 82 // ***** Destructor *****
andrewm@0 83
andrewm@0 84 ~PianoKeyboard();
andrewm@0 85
andrewm@0 86 // ***** Control Methods *****
andrewm@0 87 //
andrewm@0 88 // These methods start and stop the device and all associated processing. The implementation
andrewm@0 89 // is specific to the hardware used. Methods return true on success.
andrewm@0 90
andrewm@0 91 //virtual bool start() = 0;
andrewm@0 92 //virtual bool stop() = 0;
andrewm@0 93
andrewm@0 94 bool isInitialized() { return isInitialized_; }
andrewm@0 95 bool isRunning() { return isRunning_; }
andrewm@0 96
andrewm@0 97 void reset();
andrewm@0 98
andrewm@0 99 std::pair<int, int> keyboardGUIRange() { return std::pair<int, int>(lowestMidiNote_, highestMidiNote_); }
andrewm@0 100 void setKeyboardGUIRange(int lowest, int highest);
andrewm@0 101
andrewm@0 102 int numberOfPedals() { return numberOfPedals_; }
andrewm@0 103 void setNumberOfPedals(int number);
andrewm@0 104
andrewm@0 105 // ***** Communication Links and Methods *****
andrewm@0 106
andrewm@0 107 // Set/query the output controller
andrewm@0 108 MidiOutputController* midiOutputController() { return midiOutputController_; }
andrewm@0 109 void setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; }
andrewm@0 110
andrewm@0 111 // Set reference to GUI displays
andrewm@0 112 KeyboardDisplay* gui() { return gui_; }
andrewm@0 113 void setGUI(KeyboardDisplay* gui);
andrewm@0 114 KeyPositionGraphDisplay *graphGUI() { return graphGui_; }
andrewm@0 115 void setGraphGUI(KeyPositionGraphDisplay* newGui) { graphGui_ = newGui; }
andrewm@0 116
andrewm@0 117 // OSC transmitter handles the mechanics of sending messages to one or more targets
andrewm@0 118 void setOscTransmitter(OscTransmitter* trans) { oscTransmitter_ = trans; }
andrewm@0 119
andrewm@0 120 // TouchkeyDevice handles communication with the touch-sensor/piano-scanner hardware
andrewm@0 121 void setTouchkeyDevice(TouchkeyDevice* device) { touchkeyDevice_ = device; }
andrewm@0 122
andrewm@0 123 // Send a named message by OSC (and potentially by MIDI or other means if suitable listeners
andrewm@0 124 // are enabled)
andrewm@0 125 void sendMessage(const char * path, const char * type, ...);
andrewm@0 126
andrewm@0 127 // ***** Scheduling Methods *****
andrewm@0 128
andrewm@0 129 // Add or remove events from the scheduler queue
andrewm@0 130 void scheduleEvent(void *who, Scheduler::action func, timestamp_type timestamp) {
andrewm@0 131 futureEventScheduler_.schedule(who, func, timestamp);
andrewm@0 132 }
andrewm@0 133 void unscheduleEvent(void *who) {
andrewm@0 134 futureEventScheduler_.unschedule(who);
andrewm@0 135 }
andrewm@0 136 void unscheduleEvent(void *who, timestamp_type timestamp) {
andrewm@0 137 futureEventScheduler_.unschedule(who, timestamp);
andrewm@0 138 }
andrewm@0 139
andrewm@0 140 // Return the current timestamp associated with the scheduler
andrewm@0 141 timestamp_type schedulerCurrentTimestamp() { return futureEventScheduler_.currentTimestamp(); }
andrewm@0 142
andrewm@0 143 // ***** Individual Key/Pedal Methods *****
andrewm@0 144
andrewm@0 145 // Access to individual keys and pedals
andrewm@0 146 PianoKey* key(int note) {
andrewm@0 147 //if(note < lowestMidiNote_ || note > highestMidiNote_)
andrewm@0 148 // return 0;
andrewm@0 149 //return keys_[note - lowestMidiNote_];
andrewm@0 150 if(note < 0 || note > 127)
andrewm@0 151 return 0;
andrewm@0 152 return keys_[note];
andrewm@0 153 }
andrewm@0 154 PianoPedal* pedal(int pedal) {
andrewm@0 155 if(pedal < 0 || pedal >= numberOfPedals_)
andrewm@0 156 return 0;
andrewm@0 157 return pedals_[pedal];
andrewm@0 158 }
andrewm@0 159
andrewm@0 160 // Keys and pedals are enabled by default. If one has been disabled, reenable it so it reads data
andrewm@0 161 // and triggers notes, as normal.
andrewm@0 162 void enableKey(int key);
andrewm@0 163 void enablePedal(int pedal);
andrewm@0 164
andrewm@0 165 // Disable a key or pedal from causing any activity to occur.
andrewm@0 166 void disableKey(int key);
andrewm@0 167 void disablePedal(int pedal);
andrewm@0 168
andrewm@0 169 // Leave a key enabled, but terminate any activity it has initiated and return it to the idle state.
andrewm@0 170 // If the key is active because of a hardware problem, this may be a short-term solution at best, requiring
andrewm@0 171 // the key to be disabled until the problem can be properly resolved.
andrewm@0 172 void forceKeyIdle(int key);
andrewm@0 173
andrewm@0 174 // Set the color of an RGB LED for the given key, if relevant hardware is present
andrewm@0 175 void setKeyLEDColorRGB(const int note, const float red, const float green, const float blue);
andrewm@0 176 void setKeyLEDColorHSV(const int note, const float hue, const float saturation, const float value);
andrewm@0 177 void setAllKeyLEDsOff();
andrewm@0 178
andrewm@0 179 // ***** Mapping Methods *****
andrewm@0 180 // Mappings are identified by the MIDI note they affect and by
andrewm@0 181 // their owner object.
andrewm@0 182 void addMapping(int noteNumber, Mapping* mapping); // Add a new mapping to the container
andrewm@0 183 void removeMapping(int noteNumber); // Remove a mapping from the container
andrewm@0 184 Mapping* mapping(int noteNumber); // Look up a mapping with the given note and owner
andrewm@0 185 std::vector<int> activeMappings(); // Return a list of all active note mappings
andrewm@0 186 void clearMappings(); // Remove all mappings
andrewm@0 187
andrewm@0 188 // Managing the mapping factories
andrewm@0 189 MappingFactory *mappingFactory(MidiKeyboardSegment* segment) {
andrewm@0 190 ScopedReadLock sl(mappingFactoriesMutex_);
andrewm@0 191 if(mappingFactories_.count(segment) == 0)
andrewm@0 192 return 0;
andrewm@0 193 return mappingFactories_[segment];
andrewm@0 194 }
andrewm@0 195 void setMappingFactory(MidiKeyboardSegment* segment, MappingFactory *factory) {
andrewm@0 196 ScopedWriteLock sl(mappingFactoriesMutex_);
andrewm@0 197 mappingFactories_[segment] = factory;
andrewm@0 198 }
andrewm@0 199 void removeMappingFactory(MidiKeyboardSegment* segment) {
andrewm@0 200 ScopedWriteLock sl(mappingFactoriesMutex_);
andrewm@0 201 if(mappingFactories_.count(segment) > 0)
andrewm@0 202 mappingFactories_.erase(segment);
andrewm@0 203 }
andrewm@0 204
andrewm@0 205 // Passing data to all mapping factories; these methods are not specific to a particular
andrewm@0 206 // MIDI input segment so we need to check with each factory whether it wants this data.
andrewm@0 207 void tellAllMappingFactoriesTouchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
andrewm@0 208 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 209 Node<key_position>* positionBuffer,
andrewm@0 210 KeyPositionTracker* positionTracker);
andrewm@0 211 void tellAllMappingFactoriesTouchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
andrewm@0 212 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 213 Node<key_position>* positionBuffer,
andrewm@0 214 KeyPositionTracker* positionTracker);
andrewm@0 215 void tellAllMappingFactoriesKeyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
andrewm@0 216 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 217 Node<key_position>* positionBuffer,
andrewm@0 218 KeyPositionTracker* positionTracker);
andrewm@0 219 void tellAllMappingFactoriesKeyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
andrewm@0 220 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 221 Node<key_position>* positionBuffer,
andrewm@0 222 KeyPositionTracker* positionTracker);
andrewm@0 223
andrewm@0 224 MappingScheduler& mappingScheduler() { return *mappingScheduler_; }
andrewm@0 225
andrewm@0 226 // ***** Member Variables *****
andrewm@0 227 public:
andrewm@0 228 // This mutex is grabbed by any thread which is supplying performance
andrewm@0 229 // data (MIDI or touch). By synchronizing access once centrally, we
andrewm@0 230 // can avoid many other lock scenarios in individual objects. The object
andrewm@0 231 // is declared public so it can be used in ScopedLocks.
andrewm@0 232 CriticalSection performanceDataMutex_;
andrewm@0 233
andrewm@0 234 private:
andrewm@0 235 // Individual key and pedal data structures
andrewm@0 236 std::vector<PianoKey*> keys_;
andrewm@0 237 std::vector<PianoPedal*> pedals_;
andrewm@0 238
andrewm@0 239 // Reference to GUI display (if present)
andrewm@0 240 KeyboardDisplay* gui_;
andrewm@0 241 KeyPositionGraphDisplay *graphGui_;
andrewm@0 242
andrewm@0 243 // Reference to the MIDI output controller
andrewm@0 244 MidiOutputController* midiOutputController_;
andrewm@0 245
andrewm@0 246 // Reference to message transmitter class
andrewm@0 247 OscTransmitter* oscTransmitter_;
andrewm@0 248
andrewm@0 249 // Reference to TouchKey hardware controller class
andrewm@0 250 TouchkeyDevice* touchkeyDevice_;
andrewm@0 251
andrewm@0 252 // Keyboard range, expressed in MIDI note numbers
andrewm@0 253 int lowestMidiNote_, highestMidiNote_;
andrewm@0 254 int numberOfPedals_;
andrewm@0 255
andrewm@0 256 bool isInitialized_;
andrewm@0 257 bool isRunning_;
andrewm@0 258 bool isCalibrated_;
andrewm@0 259 bool calibrationInProgress_;
andrewm@0 260
andrewm@0 261 // Mapping objects associated with particular messages
andrewm@0 262 // When a sendMessage() command is received, it checks the message
andrewm@0 263 // against this set of possible listeners to see if it matches the
andrewm@0 264 // path. If so, the handler function is called.
andrewm@0 265 std::multimap<std::string, OscHandler*> messageListeners_;
andrewm@0 266
andrewm@0 267 // This object can be used to schedule events to be executed at future timestamps,
andrewm@0 268 // for example to handle timeouts. This will often be called from within a particular
andrewm@0 269 // key, but we should maintain one central repository for these events.
andrewm@0 270 Scheduler futureEventScheduler_;
andrewm@0 271
andrewm@0 272 // Data related to mappings for active notes
andrewm@0 273 std::map<int, Mapping*> mappings_; // Mappings from key motion to sound
andrewm@0 274
andrewm@0 275 // Collection of mapping factories organised by segment of the keyboard. Different
andrewm@0 276 // segments may have different mappings
andrewm@0 277 std::map<MidiKeyboardSegment*, MappingFactory*> mappingFactories_;
andrewm@0 278 ReadWriteLock mappingFactoriesMutex_;
andrewm@0 279
andrewm@0 280 // Scheduler specifically used for coordinating mappings
andrewm@0 281 MappingScheduler *mappingScheduler_;
andrewm@0 282
andrewm@0 283 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PianoKeyboard)
andrewm@0 284 };
andrewm@0 285
andrewm@0 286 #endif /* KEYCONTROL_PIANOKEYBOARD_H */