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 */ |