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

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 3580ffe87dc8
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 PianoKey.h: handles the operation of a single key on the keyboard,
andrewm@0 21 including fusing touch and MIDI data. Also has hooks for continuous
andrewm@0 22 key angle.
andrewm@0 23 */
andrewm@0 24
andrewm@0 25
andrewm@0 26 #ifndef KEYCONTROL_PIANOKEY_H
andrewm@0 27 #define KEYCONTROL_PIANOKEY_H
andrewm@0 28
andrewm@0 29 #include <set>
andrewm@0 30 #include <map>
andrewm@0 31 #include <list>
andrewm@0 32 #include "../JuceLibraryCode/JuceHeader.h"
andrewm@0 33 #include "../Utility/Node.h"
andrewm@0 34 #include "PianoTypes.h"
andrewm@0 35 #include "KeyIdleDetector.h"
andrewm@0 36 #include "KeyPositionTracker.h"
andrewm@0 37 #include "KeyTouchFrame.h"
andrewm@0 38 #include "../Utility/Scheduler.h"
andrewm@0 39 #include "../Utility/IIRFilter.h"
andrewm@0 40
andrewm@0 41 const unsigned int kPianoKeyStateBufferLength = 20; // How many previous states to save
andrewm@0 42 const unsigned int kPianoKeyIdleBufferLength = 10; // How many idle/active transitions to save
andrewm@0 43 const unsigned int kPianoKeyPositionTrackerBufferLength = 30; // How many state histories to save
andrewm@0 44 const key_position kPianoKeyDefaultIdleActivityThreshold = scale_key_position(.020);
andrewm@0 45 const key_position kPianoKeyDefaultIdlePositionThreshold = scale_key_position(.05);
andrewm@0 46 const int kPianoKeyDefaultIdleCounter = 20;
andrewm@0 47 const timestamp_diff_type kPianoKeyDefaultTouchTimeoutInterval = microseconds_to_timestamp(0); // was 20000
andrewm@0 48 const timestamp_diff_type kPianoKeyGuiUpdateInterval = microseconds_to_timestamp(15000); // How frequently to update the position display
andrewm@0 49
andrewm@0 50 // Possible key states
andrewm@0 51 enum {
andrewm@0 52 kKeyStateToBeInitialized = -1,
andrewm@0 53 kKeyStateUnknown = 0,
andrewm@0 54 kKeyStateDisabled,
andrewm@0 55 kKeyStateIdle,
andrewm@0 56 kKeyStateActive
andrewm@0 57 };
andrewm@0 58
andrewm@0 59 // Possible touch events
andrewm@0 60 enum {
andrewm@0 61 kTouchEventIdle = 0,
andrewm@0 62 kTouchEventAdd,
andrewm@0 63 kTouchEventRemove
andrewm@0 64 };
andrewm@0 65
andrewm@0 66 typedef int key_state;
andrewm@0 67
andrewm@0 68 class PianoKeyboard;
andrewm@0 69 class MidiKeyboardSegment;
andrewm@0 70
andrewm@0 71 /*
andrewm@0 72 * PianoKey
andrewm@0 73 *
andrewm@0 74 * This class holds the buffer and state information for a single piano key,
andrewm@0 75 * with methods to manage its status.
andrewm@0 76 */
andrewm@0 77
andrewm@0 78 class PianoKey : public TriggerDestination {
andrewm@0 79 private:
andrewm@0 80 // ***** Internal Classes *****
andrewm@0 81
andrewm@0 82 // Data on key touch events: what, when and where
andrewm@0 83 struct KeyTouchEvent {
andrewm@0 84 int type;
andrewm@0 85 timestamp_type timestamp;
andrewm@0 86 KeyTouchFrame frame;
andrewm@0 87 };
andrewm@0 88
andrewm@0 89 public:
andrewm@0 90 // ***** Constructors *****
andrewm@0 91
andrewm@0 92 PianoKey(PianoKeyboard& keyboard, int noteNumber, int bufferLength);
andrewm@0 93 //PianoKey(PianoKey const& obj);
andrewm@0 94
andrewm@0 95 // ***** Destructor *****
andrewm@0 96
andrewm@0 97 ~PianoKey();
andrewm@0 98
andrewm@0 99 // ***** Access Methods *****
andrewm@0 100
andrewm@0 101 Node<key_position>& buffer() { return positionBuffer_; }
andrewm@0 102
andrewm@0 103 // ***** Control Methods *****
andrewm@0 104 //
andrewm@0 105 // Force changes in the key state (e.g. to resolve stuck notes)
andrewm@0 106
andrewm@0 107 // Enable or disable a key from generating events
andrewm@0 108 void disable();
andrewm@0 109 void enable();
andrewm@0 110
andrewm@0 111 // Force the key to the Idle state, provided it is enabled
andrewm@0 112 void forceIdle();
andrewm@0 113
andrewm@0 114 // Clear any previous state, go back to initial state
andrewm@0 115 void reset();
andrewm@0 116
andrewm@0 117 void insertSample(key_position pos, timestamp_type ts);
andrewm@0 118
andrewm@0 119 // ***** Trigger Methods *****
andrewm@0 120 //
andrewm@0 121 // This will be called by positionBuffer_ on each new sample. Examine each sample to see
andrewm@0 122 // whether the key is idle or not.
andrewm@0 123
andrewm@0 124 void triggerReceived(TriggerSource* who, timestamp_type timestamp);
andrewm@0 125
andrewm@0 126 // ***** MIDI Methods *****
andrewm@0 127 //
andrewm@0 128 // If MIDI input is used to control this note, the controller should call these functions
andrewm@0 129
andrewm@0 130 void midiNoteOn(MidiKeyboardSegment *who, int velocity, int channel, timestamp_type timestamp);
andrewm@0 131 void midiNoteOff(MidiKeyboardSegment *who, timestamp_type timestamp);
andrewm@0 132 void midiAftertouch(MidiKeyboardSegment *who, int value, timestamp_type timestamp);
andrewm@0 133
andrewm@0 134 bool midiNoteIsOn() { return midiNoteIsOn_; }
andrewm@0 135 int midiVelocity() { return midiVelocity_; }
andrewm@0 136 int midiChannel() { return midiChannel_; }
andrewm@0 137 void changeMidiChannel(int newChannel) { midiChannel_ = newChannel; }
andrewm@0 138 int midiOutputPort() { return midiOutputPort_; }
andrewm@0 139 void changeMidiOutputPort(int newPort) { midiOutputPort_ = newPort; }
andrewm@0 140
andrewm@0 141 // ***** Touch Methods *****
andrewm@0 142 //
andrewm@0 143 // If touchkeys are used, the controller uses these functions to provide data
andrewm@0 144 // touchInsertFrame() implies touch is active if not already.
andrewm@0 145
andrewm@0 146 void touchInsertFrame(KeyTouchFrame& newFrame, timestamp_type timestamp);
andrewm@0 147 void touchOff(timestamp_type timestamp);
andrewm@0 148 bool touchIsActive() { return touchIsActive_; }
andrewm@0 149 void setTouchSensorsPresent(bool present) { touchSensorsArePresent_ = present; }
andrewm@0 150
andrewm@0 151 // This function is called on a timer when the key receives MIDI data before touch data
andrewm@0 152 // and wants to wait to integrate the two. If the touch data doesn't materialize, this function
andrewm@0 153 // is called by the scheduler.
andrewm@0 154 timestamp_type touchTimedOut();
andrewm@0 155
andrewm@0 156 private:
andrewm@0 157 // ***** MIDI Methods (private) *****
andrewm@0 158
andrewm@0 159 // This method does the real work of midiNoteOn(), and might be called from it directly
andrewm@0 160 // or after a delay.
andrewm@0 161 void midiNoteOnHelper(MidiKeyboardSegment *who);
andrewm@0 162
andrewm@0 163 // ***** Touch Methods (private) *****
andrewm@0 164
andrewm@0 165 std::pair<float, std::list<int> > touchMatchClosestPoints(const float* oldPoints, const float *newPoints, float count,
andrewm@0 166 int oldIndex, std::set<int>& availableNewPoints, float currentTotalDistance);
andrewm@0 167 void touchAdd(const KeyTouchFrame& frame, int index, timestamp_type timestamp);
andrewm@0 168 void touchRemove(const KeyTouchFrame& frame, int idRemoved, int remainingCount, timestamp_type timestamp);
andrewm@0 169 void touchMultiFingerGestures(const KeyTouchFrame& lastFrame, const KeyTouchFrame& newFrame, timestamp_type timestamp);
andrewm@0 170
andrewm@0 171 // ***** State Machine Methods *****
andrewm@0 172 //
andrewm@0 173 // This class maintains a current state that determines its response to the incoming
andrewm@0 174 // key position data.
andrewm@0 175
andrewm@0 176 void changeState(key_state newState);
andrewm@0 177 void changeState(key_state newState, timestamp_type timestamp);
andrewm@0 178
andrewm@0 179 void terminateActivity();
andrewm@0 180
andrewm@0 181 // ***** Member Variables *****
andrewm@0 182
andrewm@0 183 // Reference back to the keyboard which centralizes control
andrewm@0 184 PianoKeyboard& keyboard_;
andrewm@0 185
andrewm@0 186 // Identity of the key (MIDI note number)
andrewm@0 187 int noteNumber_;
andrewm@0 188
andrewm@0 189 // --- Data related to MIDI ---
andrewm@0 190
andrewm@0 191 bool midiNoteIsOn_; // Whether this note is currently active from MIDI
andrewm@0 192 int midiChannel_; // MIDI channel currently associated with this note
andrewm@0 193 int midiOutputPort_; // Which port MIDI output for this note goes to
andrewm@0 194 int midiVelocity_; // Velocity of last MIDI onset
andrewm@0 195 Node<int> midiAftertouch_; // Aftertouch history on this note, if any
andrewm@0 196
andrewm@0 197 // Timestamps for the most recent MIDI note on and note off events
andrewm@0 198 timestamp_type midiOnTimestamp_, midiOffTimestamp_;
andrewm@0 199
andrewm@0 200 // --- Data related to continuous key position ---
andrewm@0 201
andrewm@0 202 Node<key_position> positionBuffer_; // Buffer that holds the key positions
andrewm@0 203 KeyIdleDetector idleDetector_; // Detector for whether the key is still or moving
andrewm@0 204 KeyPositionTracker positionTracker_; // Object to track the various active states of the key
andrewm@0 205 timestamp_type timeOfLastGuiUpdate_; // How long it's been since the last key position GUI call
andrewm@0 206 timestamp_type timeOfLastDebugPrint_; // TESTING
andrewm@0 207
andrewm@0 208 Node<key_state> stateBuffer_; // State history
andrewm@0 209 key_state state_; // Current state of the key (see enum above)
andrewm@0 210 CriticalSection stateMutex_; // Use this to synchronize changes of state
andrewm@0 211
andrewm@0 212 //IIRFilterNode<key_position> testFilter_; // Filter the raw key position data, for testing
andrewm@0 213
andrewm@0 214 // --- Data related to surface touches ---
andrewm@0 215
andrewm@0 216 bool touchSensorsArePresent_; // Whether touch sensitivity exists on this key
andrewm@0 217 bool touchIsActive_; // Whether the user is currently touching the key
andrewm@0 218 Node<KeyTouchFrame> touchBuffer_; // Buffer that holds touchkey frames
andrewm@0 219 std::multimap<int, KeyTouchEvent> touchEvents_; // Mapping from touch number to event
andrewm@0 220 bool touchIsWaiting_; // Whether we're waiting for a touch to occur
andrewm@0 221 MidiKeyboardSegment *touchWaitingSource_; // Who we're waiting from a touch for
andrewm@0 222 timestamp_type touchWaitingTimestamp_; // When the timeout will occur
andrewm@0 223 timestamp_diff_type touchTimeoutInterval_; // How long to wait for a touch before timing out
andrewm@0 224
andrewm@0 225 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PianoKey)
andrewm@0 226 };
andrewm@0 227
andrewm@0 228 #endif /* KEYCONTROL_PIANOKEY_H */