Mercurial > hg > touchkeys
view Source/Mappings/TouchkeyBaseMappingFactory.h @ 20:dfff66c07936
Lots of minor changes to support building on Visual Studio. A few MSVC-specific #ifdefs to eliminate things Visual Studio doesn't like. This version now compiles on Windows (provided liblo, Juce and pthread are present) but the TouchKeys device support is not yet enabled. Also, the code now needs to be re-checked on Mac and Linux.
author | Andrew McPherson <andrewm@eecs.qmul.ac.uk> |
---|---|
date | Sun, 09 Feb 2014 18:40:51 +0000 |
parents | 3580ffe87dc8 |
children | e8965409903e |
line wrap: on
line source
/* TouchKeys: multi-touch musical keyboard control software Copyright (c) 2013 Andrew McPherson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ===================================================================== TouchkeyBaseMappingFactory.h: base factory class specifically for TouchKeys mappings. It provides a collection of useful methods for creating and destroying individual mappings on touch/MIDI onset and release, as well as parameter adjustment code and OSC to MIDI conversion. This is a template class that must be created with a specific Mapping subclass. */ #ifndef __TouchKeys__TouchkeyBaseMappingFactory__ #define __TouchKeys__TouchkeyBaseMappingFactory__ #include <iostream> #include <map> #include <sstream> #include "MappingFactory.h" #include "../TouchKeys/OscMidiConverter.h" #include "../TouchKeys/MidiOutputController.h" #include "../TouchKeys/MidiKeyboardSegment.h" #include "MappingScheduler.h" #undef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY // Base class for mapping factories that meet the following criteria: // * MIDI and TouchKeys data (no continuous angle) // * Mappings begin when either or touch or MIDI starts and end when both finish // * Each mapping object affects a single note template <class MappingType> class TouchkeyBaseMappingFactory : public MappingFactory { public: // ***** Constructor ***** // Default constructor, containing a reference to the PianoKeyboard class. TouchkeyBaseMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment) : MappingFactory(keyboard), keyboardSegment_(segment), midiConverter_(0), controlName_(""), inputRangeMin_(0.0), inputRangeMax_(1.0), inputRangeCenter_(0.0), outOfRangeBehavior_(OscMidiConverter::kOutOfRangeClip), midiControllerNumber_(-1), bypassed_(false), activeNotes_(0x0FFF) {} // ***** Destructor ***** virtual ~TouchkeyBaseMappingFactory() { removeAllMappings(); if(midiConverter_ != 0 && controlName_ != "") midiConverter_->removeControl(controlName_.c_str()); if(midiControllerNumber_ >= 0) { keyboardSegment_.releaseOscMidiConverter(midiControllerNumber_); } } // ***** Accessors / Modifiers ***** // Return the keyboard segment associated with this factory MidiKeyboardSegment& segment() { return keyboardSegment_; } // Look up a mapping with the given note number virtual MappingType* mapping(int noteNumber) { ScopedLock sl(mappingsMutex_); if(mappings_.count(noteNumber) == 0) return 0; return mappings_[noteNumber]; } // Return a list of all active notes virtual std::vector<int> activeMappings() { ScopedLock sl(mappingsMutex_); std::vector<int> keys; typename std::map<int, MappingType*>::iterator it = mappings_.begin(); while(it != mappings_.end()) { int nextKey = (it++)->first; keys.push_back(nextKey); } return keys; } // Remove all active mappings virtual void removeAllMappings() { ScopedLock sl(mappingsMutex_); typename std::map<int, MappingType*>::iterator it = mappings_.begin(); while(it != mappings_.end()) { // Delete everybody in the container MappingType *mapping = it->second; #ifdef NEW_MAPPING_SCHEDULER mapping->disengage(true); //keyboard_.mappingScheduler().unscheduleAndDelete(mapping); #else mapping->disengage(); delete mapping; #endif it++; } // Now clear the container mappings_.clear(); } // Callback from mapping to say it's finished virtual void mappingFinished(int noteNumber) { ScopedLock sl(mappingsMutex_); removeMapping(noteNumber); } // Suspend messages from a particular note virtual void suspendMapping(int noteNumber) { ScopedLock sl(mappingsMutex_); if(mappings_.count(noteNumber) == 0) return; mappings_[noteNumber]->suspend(); } // Suspend messages from all notes virtual void suspendAllMappings() { ScopedLock sl(mappingsMutex_); typename std::map<int, MappingType*>::iterator it = mappings_.begin(); while(it != mappings_.end()) { //std::cout << "suspending mapping on note " << it->first << std::endl; it->second->suspend(); it++; } } // Resume messages from a particular note virtual void resumeMapping(int noteNumber, bool resend) { ScopedLock sl(mappingsMutex_); if(mappings_.count(noteNumber) == 0) return; //std::cout << "resuming mapping on note " << noteNumber << std::endl; mappings_[noteNumber]->resume(resend); } // Resume messages on all notes virtual void resumeAllMappings(bool resend) { ScopedLock sl(mappingsMutex_); typename std::map<int, MappingType*>::iterator it = mappings_.begin(); while(it != mappings_.end()) { it->second->resume(resend); it++; } } // Whether this mapping is bypassed virtual int bypassed() { return bypassed_ ? kBypassOn : kBypassOff; } // Set whether the mapping is bypassed or not virtual void setBypassed(bool bypass) { bypassed_ = bypass; } // ***** Class-Specific Methods ***** virtual void setMidiParameters(int controller, float inputMinValue, float inputMaxValue, float inputCenterValue, int outputDefaultValue = -1, int outputMinValue = -1, int outputMaxValue = -1, int outputCenterValue = -1, bool use14BitControl = false, int outOfRangeBehavior = OscMidiConverter::kOutOfRangeClip) { if(controller < 0) return; inputRangeMin_ = inputMinValue; inputRangeMax_ = inputMaxValue; inputRangeCenter_ = inputCenterValue; outOfRangeBehavior_ = outOfRangeBehavior; // Remove listener on previous name (if any) //midiConverter_.removeAllControls(); if(midiControllerNumber_ >= 0 && controller != midiControllerNumber_) { keyboardSegment_.releaseOscMidiConverter(midiControllerNumber_); midiConverter_ = keyboardSegment_.acquireOscMidiConverter(controller); } else if(midiControllerNumber_ < 0 || midiConverter_ == 0) { midiConverter_ = keyboardSegment_.acquireOscMidiConverter(controller); } midiControllerNumber_ = controller; midiConverter_->setMidiMessageType(outputDefaultValue, outputMinValue, outputMaxValue, outputCenterValue, use14BitControl); // Add listener for new name if(controlName_ != "") midiConverter_->addControl(controlName_.c_str(), 1, inputRangeMin_, inputRangeMax_, inputRangeCenter_, outOfRangeBehavior_); } virtual string const getName() { return controlName_; } virtual void setName(const string& name) { if(name == "") return; std::stringstream ss; // Remove listener on previous name (if any) if(midiConverter_ != 0 && controlName_ != "") midiConverter_->removeControl(controlName_.c_str()); ss << "/touchkeys/mapping/segment" << (int)keyboardSegment_.outputPort() << "/" << name; controlName_ = ss.str(); // Add listener for new name if(midiConverter_ != 0) midiConverter_->addControl(controlName_.c_str(), 1, inputRangeMin_, inputRangeMax_, inputRangeCenter_, outOfRangeBehavior_); } // Set which keys should have this mapping enable virtual void setActiveNotes(unsigned int notes) { activeNotes_ = notes; } // ***** State Updaters ***** // These are called by PianoKey whenever certain events occur that might // merit the start and stop of a mapping. What is done with them depends on // the particular factory subclass. // Touch becomes active on a key where it wasn't previously virtual void touchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) { ScopedLock sl(mappingsMutex_); // Add a new mapping if one doesn't exist already if(mappings_.count(noteNumber) == 0) { #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY std::cout << "Note " << noteNumber << ": adding mapping (touch)\n"; #endif int moduloNoteNumber = noteNumber % 12; if((activeNotes_ & (1 << moduloNoteNumber)) && !bypassed_) addMapping(noteNumber, touchBuffer, positionBuffer, positionTracker); } } // Touch ends on a key where it wasn't previously virtual void touchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) { ScopedLock sl(mappingsMutex_); // If a mapping exists but the MIDI note is off, remove the mapping if(mappings_.count(noteNumber) != 0 && !midiNoteIsOn) { #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY std::cout << "Note " << noteNumber << ": removing mapping (touch)\n"; #endif if(mappings_[noteNumber]->requestFinish()) removeMapping(noteNumber); } } // MIDI note on for a key virtual void midiNoteOn(int noteNumber, bool touchIsOn, bool keyMotionActive, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) { ScopedLock sl(mappingsMutex_); // Add a new mapping if one doesn't exist already if(mappings_.count(noteNumber) == 0) { #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY std::cout << "Note " << noteNumber << ": adding mapping (MIDI)\n"; #endif int moduloNoteNumber = noteNumber % 12; if((activeNotes_ & (1 << moduloNoteNumber)) && !bypassed_) addMapping(noteNumber, touchBuffer, positionBuffer, positionTracker); } } // MIDI note off for a key virtual void midiNoteOff(int noteNumber, bool touchIsOn, bool keyMotionActive, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) { ScopedLock sl(mappingsMutex_); // If a mapping exists but the touch is off, remove the mapping if(mappings_.count(noteNumber) != 0 && !touchIsOn) { #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY std::cout << "Note " << noteNumber << ": removing mapping (MIDI)\n"; #endif if(mappings_[noteNumber]->requestFinish()) removeMapping(noteNumber); } } // Subclasses of this one won't care about these two methods: // Key goes active from continuous key position virtual void keyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) {} // Key goes idle from continuous key position virtual void keyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) {} // But we do use this one to send out default values: virtual void noteWillBegin(int noteNumber, int midiChannel, int midiVelocity) { if(midiConverter_ == 0) return; midiConverter_->clearLastValues(midiChannel, true); //midiConverter_->sendDefaultValue(midiChannel); } protected: // ***** Protected Methods ***** // This method should be set by the subclass to initialize the parameters of // a new mapping. virtual void initializeMappingParameters(int noteNumber, MappingType *mapping) {} private: // ***** Private Methods ***** // Add a new mapping void addMapping(int noteNumber, Node<KeyTouchFrame>* touchBuffer, Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker) { // TODO: mutex removeMapping(noteNumber); // Free any mapping that's already present on this note MappingType *mapping = new MappingType(keyboard_, this, noteNumber, touchBuffer, positionBuffer, positionTracker); // Set parameters mapping->setName(controlName_); initializeMappingParameters(noteNumber, mapping); // Save the mapping mappings_[noteNumber] = mapping; // Finally, engage the new mapping mapping->engage(); } void removeMapping(int noteNumber) { // TODO: mutex if(mappings_.count(noteNumber) == 0) return; MappingType* mapping = mappings_[noteNumber]; #ifdef NEW_MAPPING_SCHEDULER mapping->disengage(true); //keyboard_.mappingScheduler().unscheduleAndDelete(mapping); #else mapping->disengage(); delete mapping; #endif mappings_.erase(noteNumber); } protected: // State variables MidiKeyboardSegment& keyboardSegment_; // Segment of the keyboard that this mapping addresses OscMidiConverter *midiConverter_; // Object to convert OSC messages to MIDI std::map<int, MappingType*> mappings_; // Collection of active mappings CriticalSection mappingsMutex_; // Mutex protecting mappings from changes std::string controlName_; // Name of the mapping float inputRangeMin_, inputRangeMax_; // Input ranges float inputRangeCenter_; int outOfRangeBehavior_; // What happens to out of range inputs int midiControllerNumber_; // Which controller to use bool bypassed_; // Whether the mapping has been bypassed by UI unsigned int activeNotes_; // Indication of which notes out of the 12 to use }; #endif /* defined(__TouchKeys__TouchkeyBaseMappingFactory__) */