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

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 78b9808a2c65
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.cpp: 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 #include "PianoKeyboard.h"
andrewm@0 27 #include "TouchkeyDevice.h"
andrewm@0 28 #include "../Mappings/Mapping.h"
andrewm@46 29 #include "MidiOutputController.h"
andrewm@0 30 #include "../Mappings/MappingFactory.h"
andrewm@0 31 #include "../Mappings/MappingScheduler.h"
andrewm@0 32
andrewm@0 33 // Constructor
andrewm@0 34 PianoKeyboard::PianoKeyboard()
andrewm@0 35 : gui_(0), graphGui_(0), midiOutputController_(0),
andrewm@0 36 oscTransmitter_(0), touchkeyDevice_(0),
andrewm@0 37 lowestMidiNote_(0), highestMidiNote_(0), numberOfPedals_(0),
andrewm@0 38 isInitialized_(false), isRunning_(false), isCalibrated_(false), calibrationInProgress_(false)
andrewm@0 39 {
andrewm@0 40 // Start a thread by which we can schedule future events
andrewm@0 41 futureEventScheduler_.start(0);
andrewm@0 42
andrewm@0 43 // Build the key list
andrewm@0 44 for(int i = 0; i <= 127; i++)
andrewm@0 45 keys_.push_back(new PianoKey(*this, i, kDefaultKeyHistoryLength));
andrewm@0 46
andrewm@0 47 mappingScheduler_ = new MappingScheduler(*this);
andrewm@0 48 mappingScheduler_->start();
andrewm@0 49 }
andrewm@0 50
andrewm@0 51 // Reset all keys and pedals to their default state.
andrewm@0 52 void PianoKeyboard::reset() {
andrewm@0 53 // Clear any history in the source buffers
andrewm@0 54 std::vector<PianoKey*>::iterator itKey;
andrewm@0 55 std::vector<PianoPedal*>::iterator itPed;
andrewm@0 56
andrewm@0 57 for(itKey = keys_.begin(); itKey != keys_.end(); itKey++)
andrewm@0 58 (*itKey)->reset();
andrewm@0 59 for(itPed = pedals_.begin(); itPed != pedals_.end(); itPed++)
andrewm@0 60 (*itPed)->clear();
andrewm@0 61 }
andrewm@0 62
andrewm@0 63 // Provide a pointer to the graphical display class
andrewm@0 64
andrewm@0 65 void PianoKeyboard::setGUI(KeyboardDisplay* gui) {
andrewm@0 66 gui_ = gui;
andrewm@0 67 if(gui_ != 0) {
andrewm@0 68 gui_->setKeyboardRange(lowestMidiNote_, highestMidiNote_);
andrewm@0 69 }
andrewm@0 70 }
andrewm@0 71
andrewm@0 72 // Set the range of the keyboard in terms of MIDI notes. A standard
andrewm@0 73 // 88-key keyboard has a range of 21-108, but other setups may differ.
andrewm@0 74
andrewm@0 75 void PianoKeyboard::setKeyboardGUIRange(int lowest, int highest) {
andrewm@0 76 lowestMidiNote_ = lowest;
andrewm@0 77 highestMidiNote_ = highest;
andrewm@0 78
andrewm@0 79 // Sanity checks: enforce 0-127 range, high >= low
andrewm@0 80 if(lowestMidiNote_ < 0)
andrewm@0 81 lowestMidiNote_ = 0;
andrewm@0 82 if(highestMidiNote_ < 0)
andrewm@0 83 highestMidiNote_ = 0;
andrewm@0 84 if(lowestMidiNote_ > 127)
andrewm@0 85 lowestMidiNote_ = 127;
andrewm@0 86 if(highestMidiNote_ > 127)
andrewm@0 87 highestMidiNote_ = 127;
andrewm@0 88 if(lowestMidiNote_ > highestMidiNote_)
andrewm@0 89 highestMidiNote_ = lowestMidiNote_;
andrewm@0 90
andrewm@0 91 /*
andrewm@0 92 // Free the existing PianoKey objects
andrewm@0 93 for(std::vector<PianoKey*>::iterator it = keys_.begin(); it != keys_.end(); ++it)
andrewm@0 94 delete (*it);
andrewm@0 95 keys_.clear();
andrewm@0 96
andrewm@0 97 // Rebuild the key list
andrewm@0 98 for(int i = lowestMidiNote_; i <= highestMidiNote_; i++)
andrewm@0 99 keys_.push_back(new PianoKey(*this, i, kDefaultKeyHistoryLength));
andrewm@0 100 */
andrewm@0 101
andrewm@0 102 if(gui_ != 0)
andrewm@0 103 gui_->setKeyboardRange(lowestMidiNote_, highestMidiNote_);
andrewm@0 104 }
andrewm@0 105
andrewm@0 106 // Send a message by OSC (and potentially by other means depending on who's listening)
andrewm@0 107
andrewm@0 108 void PianoKeyboard::sendMessage(const char * path, const char * type, ...) {
andrewm@0 109 //ScopedReadLock sl(oscListenerMutex_);
andrewm@0 110
andrewm@0 111 //cout << "sendMessage: " << path << endl;
andrewm@0 112
andrewm@0 113 // Initialize variable argument list for reading
andrewm@0 114 va_list v;
andrewm@0 115 va_start(v, type);
andrewm@0 116
andrewm@0 117 // Make a new OSC message which we will use both internally and externally
andrewm@0 118 lo_message msg = lo_message_new();
andrewm@0 119 lo_message_add_varargs(msg, type, v);
andrewm@0 120 int argc = lo_message_get_argc(msg);
andrewm@0 121 lo_arg **argv = lo_message_get_argv(msg);
andrewm@0 122
andrewm@0 123 // Internal handler lookup first
andrewm@0 124 // Lock the mutex so the list of listeners doesn't change midway through
andrewm@0 125
andrewm@0 126 updateListeners();
andrewm@0 127
andrewm@0 128 oscListenerMutex_.enter();
andrewm@0 129 //oscListenerMutex_.enterRead();
andrewm@0 130
andrewm@0 131 // Now remove the global prefix and compare the rest of the message to the registered handlers.
andrewm@0 132 std::multimap<std::string, OscHandler*>::iterator it;
andrewm@0 133 std::pair<std::multimap<std::string, OscHandler*>::iterator,std::multimap<std::string, OscHandler*>::iterator> ret;
andrewm@0 134 ret = noteListeners_.equal_range((std::string)path);
andrewm@0 135
andrewm@0 136 double timeInHandlers = 0;
andrewm@0 137 int numHandlers = 0; // DEBUG
andrewm@0 138 it = ret.first;
andrewm@0 139 while(it != ret.second) {
andrewm@0 140 OscHandler *object = (*it++).second;
andrewm@0 141
andrewm@0 142 double before = Time::getMillisecondCounterHiRes();
andrewm@0 143 object->oscHandlerMethod(path, type, argc, argv, 0);
andrewm@0 144 timeInHandlers += Time::getMillisecondCounterHiRes() - before;
andrewm@0 145 numHandlers++; // DEBUG
andrewm@0 146 }
andrewm@0 147 //oscListenerMutex_.exitRead();
andrewm@0 148 oscListenerMutex_.exit();
andrewm@0 149
andrewm@0 150 //if(timeInHandlers > 1.0)
andrewm@0 151 // cout << "sendMessage(): timeInHandlers = " << timeInHandlers << " for " << numHandlers << " handlers (msg " << path << ")\n";
andrewm@0 152
andrewm@0 153 // Now send this message to any external OSC sources
andrewm@0 154 if(oscTransmitter_ != 0)
andrewm@0 155 oscTransmitter_->sendMessage(path, type, msg);
andrewm@0 156
andrewm@0 157 lo_message_free(msg);
andrewm@0 158 va_end(v);
andrewm@0 159
andrewm@0 160
andrewm@0 161 }
andrewm@0 162
andrewm@0 163 // Change number of pedals
andrewm@0 164
andrewm@0 165 void PianoKeyboard::setNumberOfPedals(int number) {
andrewm@0 166 numberOfPedals_ = number;
andrewm@0 167 if(numberOfPedals_ < 0)
andrewm@0 168 numberOfPedals_ = 0;
andrewm@0 169 if(numberOfPedals_ > 127)
andrewm@0 170 numberOfPedals_ = 127;
andrewm@0 171
andrewm@0 172 // Free the existing PianoPedal objects
andrewm@0 173 for(std::vector<PianoPedal*>::iterator it = pedals_.begin(); it != pedals_.end(); ++it)
andrewm@0 174 delete (*it);
andrewm@0 175 pedals_.clear();
andrewm@0 176
andrewm@0 177 // Rebuild the list of pedals
andrewm@0 178 for(int i = 0; i < numberOfPedals_; i++)
andrewm@0 179 pedals_.push_back(new PianoPedal(kDefaultPedalHistoryLength));
andrewm@0 180 }
andrewm@0 181
andrewm@0 182 // Set color of RGB LED for a given key. note indicates the MIDI
andrewm@0 183 // note number of the key, and color can be specified in one of two
andrewm@0 184 // formats.
andrewm@0 185 void PianoKeyboard::setKeyLEDColorRGB(const int note, const float red, const float green, const float blue) {
andrewm@0 186 if(touchkeyDevice_ != 0) {
andrewm@0 187 touchkeyDevice_->rgbledSetColor(note, red, green, blue);
andrewm@0 188 }
andrewm@0 189 }
andrewm@0 190
andrewm@0 191 void PianoKeyboard::setKeyLEDColorHSV(const int note, const float hue, const float saturation, const float value) {
andrewm@0 192 if(touchkeyDevice_ != 0) {
andrewm@0 193 touchkeyDevice_->rgbledSetColorHSV(note, hue, saturation, value);
andrewm@0 194 }
andrewm@0 195 }
andrewm@0 196
andrewm@0 197 void PianoKeyboard::setAllKeyLEDsOff() {
andrewm@0 198 if(touchkeyDevice_ != 0) {
andrewm@0 199 touchkeyDevice_->rgbledAllOff();
andrewm@0 200 }
andrewm@0 201 }
andrewm@0 202
andrewm@0 203 // ***** Mapping Methods *****
andrewm@0 204
andrewm@0 205 // Add a new mapping identified by a MIDI note and an owner
andrewm@0 206 void PianoKeyboard::addMapping(int noteNumber, Mapping* mapping) {
andrewm@0 207 removeMapping(noteNumber); // Free any mapping that's already present on this note
andrewm@0 208 mappings_[noteNumber] = mapping;
andrewm@0 209 }
andrewm@0 210
andrewm@0 211 // Remove an existing mapping identified by owner
andrewm@0 212 void PianoKeyboard::removeMapping(int noteNumber) {
andrewm@0 213 if(mappings_.count(noteNumber) == 0)
andrewm@0 214 return;
andrewm@0 215 Mapping* mapping = mappings_[noteNumber];
andrewm@0 216 delete mapping;
andrewm@0 217 mappings_.erase(noteNumber);
andrewm@0 218 }
andrewm@0 219
andrewm@0 220 // Return a specific mapping by owner and note number
andrewm@0 221 Mapping* PianoKeyboard::mapping(int noteNumber) {
andrewm@0 222 if(mappings_.count(noteNumber) == 0)
andrewm@0 223 return 0;
andrewm@0 224 return mappings_[noteNumber];
andrewm@0 225 }
andrewm@0 226
andrewm@0 227 // Return a list of all MIDI notes with active mappings. Some may have more than
andrewm@0 228 // one but we only want the list of active notes
andrewm@0 229 std::vector<int> PianoKeyboard::activeMappings() {
andrewm@0 230 std::vector<int> keys;
andrewm@0 231 std::map<int, Mapping*>::iterator it = mappings_.begin();
andrewm@0 232 while(it != mappings_.end()) {
andrewm@0 233 int nextKey = (it++)->first;
andrewm@0 234 keys.push_back(nextKey);
andrewm@0 235 }
andrewm@0 236 return keys;
andrewm@0 237 }
andrewm@0 238
andrewm@0 239 void PianoKeyboard::clearMappings() {
andrewm@0 240 std::map<int, Mapping*>::iterator it = mappings_.begin();
andrewm@0 241
andrewm@0 242 while(it != mappings_.end()) {
andrewm@0 243 // Delete everybody in the container
andrewm@0 244 Mapping *mapping = it->second;
andrewm@0 245 delete mapping;
andrewm@0 246 }
andrewm@0 247
andrewm@0 248 // Now clear the container
andrewm@0 249 mappings_.clear();
andrewm@0 250 }
andrewm@0 251
andrewm@0 252 // Mapping factory methods: tell each registered factory about these events if it listens to this particular note
andrewm@0 253 void PianoKeyboard::tellAllMappingFactoriesTouchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
andrewm@0 254 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 255 Node<key_position>* positionBuffer,
andrewm@0 256 KeyPositionTracker* positionTracker) {
andrewm@0 257 ScopedReadLock sl(mappingFactoriesMutex_);
andrewm@0 258 std::map<MidiKeyboardSegment*, MappingFactory*>::iterator it;
andrewm@0 259 for(it = mappingFactories_.begin(); it != mappingFactories_.end(); it++) {
andrewm@0 260 if(it->first->respondsToNote(noteNumber))
andrewm@0 261 it->second->touchBegan(noteNumber, midiNoteIsOn, keyMotionActive, touchBuffer, positionBuffer, positionTracker);
andrewm@0 262 }
andrewm@0 263 }
andrewm@0 264
andrewm@0 265 void PianoKeyboard::tellAllMappingFactoriesTouchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
andrewm@0 266 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 267 Node<key_position>* positionBuffer,
andrewm@0 268 KeyPositionTracker* positionTracker) {
andrewm@0 269 ScopedReadLock sl(mappingFactoriesMutex_);
andrewm@0 270 std::map<MidiKeyboardSegment*, MappingFactory*>::iterator it;
andrewm@0 271 for(it = mappingFactories_.begin(); it != mappingFactories_.end(); it++) {
andrewm@0 272 if(it->first->respondsToNote(noteNumber))
andrewm@0 273 it->second->touchEnded(noteNumber, midiNoteIsOn, keyMotionActive, touchBuffer, positionBuffer, positionTracker);
andrewm@0 274 }
andrewm@0 275 }
andrewm@0 276
andrewm@0 277 void PianoKeyboard::tellAllMappingFactoriesKeyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
andrewm@0 278 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 279 Node<key_position>* positionBuffer,
andrewm@0 280 KeyPositionTracker* positionTracker) {
andrewm@0 281 ScopedReadLock sl(mappingFactoriesMutex_);
andrewm@0 282 std::map<MidiKeyboardSegment*, MappingFactory*>::iterator it;
andrewm@0 283 for(it = mappingFactories_.begin(); it != mappingFactories_.end(); it++) {
andrewm@0 284 if(it->first->respondsToNote(noteNumber))
andrewm@0 285 it->second->keyMotionActive(noteNumber, midiNoteIsOn, touchIsOn, touchBuffer, positionBuffer, positionTracker);
andrewm@0 286 }
andrewm@0 287 }
andrewm@0 288
andrewm@0 289 void PianoKeyboard::tellAllMappingFactoriesKeyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
andrewm@0 290 Node<KeyTouchFrame>* touchBuffer,
andrewm@0 291 Node<key_position>* positionBuffer,
andrewm@0 292 KeyPositionTracker* positionTracker) {
andrewm@0 293 ScopedReadLock sl(mappingFactoriesMutex_);
andrewm@0 294 std::map<MidiKeyboardSegment*, MappingFactory*>::iterator it;
andrewm@0 295 for(it = mappingFactories_.begin(); it != mappingFactories_.end(); it++) {
andrewm@0 296 if(it->first->respondsToNote(noteNumber))
andrewm@0 297 it->second->keyMotionIdle(noteNumber, midiNoteIsOn, touchIsOn, touchBuffer, positionBuffer, positionTracker);
andrewm@0 298 }
andrewm@0 299 }
andrewm@0 300
andrewm@0 301 // Destructor
andrewm@0 302
andrewm@0 303 PianoKeyboard::~PianoKeyboard() {
andrewm@0 304 // Remove all mappings
andrewm@0 305 clearMappings();
andrewm@0 306
andrewm@0 307 // Delete any keys and pedals we've allocated
andrewm@0 308 for(std::vector<PianoKey*>::iterator it = keys_.begin(); it != keys_.end(); ++it)
andrewm@0 309 delete (*it);
andrewm@0 310 for(std::vector<PianoPedal*>::iterator it = pedals_.begin(); it != pedals_.end(); ++it)
andrewm@0 311 delete (*it);
andrewm@0 312 mappingScheduler_->stop();
andrewm@0 313 delete mappingScheduler_;
andrewm@46 314 }