andrewm@0: /* andrewm@0: TouchKeys: multi-touch musical keyboard control software andrewm@0: Copyright (c) 2013 Andrew McPherson andrewm@0: andrewm@0: This program is free software: you can redistribute it and/or modify andrewm@0: it under the terms of the GNU General Public License as published by andrewm@0: the Free Software Foundation, either version 3 of the License, or andrewm@0: (at your option) any later version. andrewm@0: andrewm@0: This program is distributed in the hope that it will be useful, andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrewm@0: GNU General Public License for more details. andrewm@0: andrewm@0: You should have received a copy of the GNU General Public License andrewm@0: along with this program. If not, see . andrewm@0: andrewm@0: ===================================================================== andrewm@0: andrewm@0: PianoKeyboard.h: main class that keeps track of each key (and pedal) andrewm@0: on the keyboard, while also providing hooks for mapping and scheduling andrewm@0: of events. One shared instance of this class is used widely throughout andrewm@0: the program. andrewm@0: */ andrewm@0: andrewm@0: #ifndef KEYCONTROL_PIANOKEYBOARD_H andrewm@0: #define KEYCONTROL_PIANOKEYBOARD_H andrewm@0: andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include "../Utility/Types.h" andrewm@0: #include "../Utility/Node.h" andrewm@0: #include "PianoKey.h" andrewm@0: #include "PianoPedal.h" andrewm@0: #include "../Display/KeyboardDisplay.h" andrewm@0: #include "../Display/KeyPositionGraphDisplay.h" andrewm@0: #include "Osc.h" andrewm@0: #include "../Utility/Scheduler.h" andrewm@20: #include "../JuceLibraryCode/JuceHeader.h" andrewm@0: andrewm@0: #define NUM_KEYS 88 andrewm@0: #define NUM_PEDALS 3 andrewm@0: andrewm@0: enum { // Index of each pedal in the buffers andrewm@0: kPedalDamper = 0, andrewm@0: kPedalSostenuto = 1, andrewm@0: kPedalUnaCorda = 2, andrewm@0: kNumPedals andrewm@0: }; andrewm@0: andrewm@0: const int kDefaultKeyHistoryLength = 8192; andrewm@0: const int kDefaultPedalHistoryLength = 1024; andrewm@0: andrewm@0: class TouchkeyDevice; andrewm@0: class Mapping; andrewm@0: class MidiOutputController; andrewm@0: class MappingFactory; andrewm@0: class MidiKeyboardSegment; andrewm@0: class MappingScheduler; andrewm@0: andrewm@0: /* andrewm@0: * PianoKeyboard andrewm@0: * andrewm@0: * Base class that implements all the functionality needed to measure and process andrewm@0: * real-time piano key motion. This class is abstract in that it doesn't define a particular andrewm@0: * source for the key motion data. The data source depends on the hardware used: PianoBar, andrewm@0: * PnoScan II, CEUS, etc. andrewm@0: * andrewm@0: * PianoKeyboard is a source of OSC messages (generated by various key actions). Hence, andrewm@0: * objects can register with it to implement custom behavior for different actions. andrewm@0: */ andrewm@0: andrewm@0: class PianoKeyboard : public OscMessageSource { andrewm@0: public: andrewm@0: ofstream testLog_; andrewm@0: andrewm@0: // ***** Constructors ***** andrewm@0: andrewm@0: PianoKeyboard(); andrewm@0: andrewm@0: // ***** Destructor ***** andrewm@0: andrewm@0: ~PianoKeyboard(); andrewm@0: andrewm@0: // ***** Control Methods ***** andrewm@0: // andrewm@0: // These methods start and stop the device and all associated processing. The implementation andrewm@0: // is specific to the hardware used. Methods return true on success. andrewm@0: andrewm@0: //virtual bool start() = 0; andrewm@0: //virtual bool stop() = 0; andrewm@0: andrewm@0: bool isInitialized() { return isInitialized_; } andrewm@0: bool isRunning() { return isRunning_; } andrewm@0: andrewm@0: void reset(); andrewm@0: andrewm@0: std::pair keyboardGUIRange() { return std::pair(lowestMidiNote_, highestMidiNote_); } andrewm@0: void setKeyboardGUIRange(int lowest, int highest); andrewm@0: andrewm@0: int numberOfPedals() { return numberOfPedals_; } andrewm@0: void setNumberOfPedals(int number); andrewm@0: andrewm@0: // ***** Communication Links and Methods ***** andrewm@0: andrewm@0: // Set/query the output controller andrewm@0: MidiOutputController* midiOutputController() { return midiOutputController_; } andrewm@0: void setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; } andrewm@0: andrewm@0: // Set reference to GUI displays andrewm@0: KeyboardDisplay* gui() { return gui_; } andrewm@0: void setGUI(KeyboardDisplay* gui); andrewm@0: KeyPositionGraphDisplay *graphGUI() { return graphGui_; } andrewm@0: void setGraphGUI(KeyPositionGraphDisplay* newGui) { graphGui_ = newGui; } andrewm@0: andrewm@0: // OSC transmitter handles the mechanics of sending messages to one or more targets andrewm@0: void setOscTransmitter(OscTransmitter* trans) { oscTransmitter_ = trans; } andrewm@0: andrewm@0: // TouchkeyDevice handles communication with the touch-sensor/piano-scanner hardware andrewm@0: void setTouchkeyDevice(TouchkeyDevice* device) { touchkeyDevice_ = device; } andrewm@0: andrewm@0: // Send a named message by OSC (and potentially by MIDI or other means if suitable listeners andrewm@0: // are enabled) andrewm@0: void sendMessage(const char * path, const char * type, ...); andrewm@0: andrewm@0: // ***** Scheduling Methods ***** andrewm@0: andrewm@0: // Add or remove events from the scheduler queue andrewm@0: void scheduleEvent(void *who, Scheduler::action func, timestamp_type timestamp) { andrewm@0: futureEventScheduler_.schedule(who, func, timestamp); andrewm@0: } andrewm@0: void unscheduleEvent(void *who) { andrewm@0: futureEventScheduler_.unschedule(who); andrewm@0: } andrewm@0: void unscheduleEvent(void *who, timestamp_type timestamp) { andrewm@0: futureEventScheduler_.unschedule(who, timestamp); andrewm@0: } andrewm@0: andrewm@0: // Return the current timestamp associated with the scheduler andrewm@0: timestamp_type schedulerCurrentTimestamp() { return futureEventScheduler_.currentTimestamp(); } andrewm@0: andrewm@0: // ***** Individual Key/Pedal Methods ***** andrewm@0: andrewm@0: // Access to individual keys and pedals andrewm@0: PianoKey* key(int note) { andrewm@0: //if(note < lowestMidiNote_ || note > highestMidiNote_) andrewm@0: // return 0; andrewm@0: //return keys_[note - lowestMidiNote_]; andrewm@0: if(note < 0 || note > 127) andrewm@0: return 0; andrewm@0: return keys_[note]; andrewm@0: } andrewm@0: PianoPedal* pedal(int pedal) { andrewm@0: if(pedal < 0 || pedal >= numberOfPedals_) andrewm@0: return 0; andrewm@0: return pedals_[pedal]; andrewm@0: } andrewm@0: andrewm@0: // Keys and pedals are enabled by default. If one has been disabled, reenable it so it reads data andrewm@0: // and triggers notes, as normal. andrewm@0: void enableKey(int key); andrewm@0: void enablePedal(int pedal); andrewm@0: andrewm@0: // Disable a key or pedal from causing any activity to occur. andrewm@0: void disableKey(int key); andrewm@0: void disablePedal(int pedal); andrewm@0: andrewm@0: // Leave a key enabled, but terminate any activity it has initiated and return it to the idle state. andrewm@0: // If the key is active because of a hardware problem, this may be a short-term solution at best, requiring andrewm@0: // the key to be disabled until the problem can be properly resolved. andrewm@0: void forceKeyIdle(int key); andrewm@0: andrewm@0: // Set the color of an RGB LED for the given key, if relevant hardware is present andrewm@0: void setKeyLEDColorRGB(const int note, const float red, const float green, const float blue); andrewm@0: void setKeyLEDColorHSV(const int note, const float hue, const float saturation, const float value); andrewm@0: void setAllKeyLEDsOff(); andrewm@0: andrewm@0: // ***** Mapping Methods ***** andrewm@0: // Mappings are identified by the MIDI note they affect and by andrewm@0: // their owner object. andrewm@0: void addMapping(int noteNumber, Mapping* mapping); // Add a new mapping to the container andrewm@0: void removeMapping(int noteNumber); // Remove a mapping from the container andrewm@0: Mapping* mapping(int noteNumber); // Look up a mapping with the given note and owner andrewm@0: std::vector activeMappings(); // Return a list of all active note mappings andrewm@0: void clearMappings(); // Remove all mappings andrewm@0: andrewm@0: // Managing the mapping factories andrewm@0: MappingFactory *mappingFactory(MidiKeyboardSegment* segment) { andrewm@0: ScopedReadLock sl(mappingFactoriesMutex_); andrewm@0: if(mappingFactories_.count(segment) == 0) andrewm@0: return 0; andrewm@0: return mappingFactories_[segment]; andrewm@0: } andrewm@0: void setMappingFactory(MidiKeyboardSegment* segment, MappingFactory *factory) { andrewm@0: ScopedWriteLock sl(mappingFactoriesMutex_); andrewm@0: mappingFactories_[segment] = factory; andrewm@0: } andrewm@0: void removeMappingFactory(MidiKeyboardSegment* segment) { andrewm@0: ScopedWriteLock sl(mappingFactoriesMutex_); andrewm@0: if(mappingFactories_.count(segment) > 0) andrewm@0: mappingFactories_.erase(segment); andrewm@0: } andrewm@0: andrewm@0: // Passing data to all mapping factories; these methods are not specific to a particular andrewm@0: // MIDI input segment so we need to check with each factory whether it wants this data. andrewm@0: void tellAllMappingFactoriesTouchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive, andrewm@0: Node* touchBuffer, andrewm@0: Node* positionBuffer, andrewm@0: KeyPositionTracker* positionTracker); andrewm@0: void tellAllMappingFactoriesTouchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive, andrewm@0: Node* touchBuffer, andrewm@0: Node* positionBuffer, andrewm@0: KeyPositionTracker* positionTracker); andrewm@0: void tellAllMappingFactoriesKeyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn, andrewm@0: Node* touchBuffer, andrewm@0: Node* positionBuffer, andrewm@0: KeyPositionTracker* positionTracker); andrewm@0: void tellAllMappingFactoriesKeyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn, andrewm@0: Node* touchBuffer, andrewm@0: Node* positionBuffer, andrewm@0: KeyPositionTracker* positionTracker); andrewm@0: andrewm@0: MappingScheduler& mappingScheduler() { return *mappingScheduler_; } andrewm@0: andrewm@0: // ***** Member Variables ***** andrewm@0: public: andrewm@0: // This mutex is grabbed by any thread which is supplying performance andrewm@0: // data (MIDI or touch). By synchronizing access once centrally, we andrewm@0: // can avoid many other lock scenarios in individual objects. The object andrewm@0: // is declared public so it can be used in ScopedLocks. andrewm@0: CriticalSection performanceDataMutex_; andrewm@0: andrewm@0: private: andrewm@0: // Individual key and pedal data structures andrewm@0: std::vector keys_; andrewm@0: std::vector pedals_; andrewm@0: andrewm@0: // Reference to GUI display (if present) andrewm@0: KeyboardDisplay* gui_; andrewm@0: KeyPositionGraphDisplay *graphGui_; andrewm@0: andrewm@0: // Reference to the MIDI output controller andrewm@0: MidiOutputController* midiOutputController_; andrewm@0: andrewm@0: // Reference to message transmitter class andrewm@0: OscTransmitter* oscTransmitter_; andrewm@0: andrewm@0: // Reference to TouchKey hardware controller class andrewm@0: TouchkeyDevice* touchkeyDevice_; andrewm@0: andrewm@0: // Keyboard range, expressed in MIDI note numbers andrewm@0: int lowestMidiNote_, highestMidiNote_; andrewm@0: int numberOfPedals_; andrewm@0: andrewm@0: bool isInitialized_; andrewm@0: bool isRunning_; andrewm@0: bool isCalibrated_; andrewm@0: bool calibrationInProgress_; andrewm@0: andrewm@0: // Mapping objects associated with particular messages andrewm@0: // When a sendMessage() command is received, it checks the message andrewm@0: // against this set of possible listeners to see if it matches the andrewm@0: // path. If so, the handler function is called. andrewm@0: std::multimap messageListeners_; andrewm@0: andrewm@0: // This object can be used to schedule events to be executed at future timestamps, andrewm@0: // for example to handle timeouts. This will often be called from within a particular andrewm@0: // key, but we should maintain one central repository for these events. andrewm@0: Scheduler futureEventScheduler_; andrewm@0: andrewm@0: // Data related to mappings for active notes andrewm@0: std::map mappings_; // Mappings from key motion to sound andrewm@0: andrewm@0: // Collection of mapping factories organised by segment of the keyboard. Different andrewm@0: // segments may have different mappings andrewm@0: std::map mappingFactories_; andrewm@0: ReadWriteLock mappingFactoriesMutex_; andrewm@0: andrewm@0: // Scheduler specifically used for coordinating mappings andrewm@0: MappingScheduler *mappingScheduler_; andrewm@0: andrewm@0: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PianoKeyboard) andrewm@0: }; andrewm@0: andrewm@0: #endif /* KEYCONTROL_PIANOKEYBOARD_H */