annotate Source/TouchKeys/KeyPositionTracker.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 KeyPositionTracker.h: parses continuous key position and detects the
andrewm@0 21 state of the key.
andrewm@0 22 */
andrewm@0 23
andrewm@0 24
andrewm@0 25 #ifndef __touchkeys__KeyPositionTracker__
andrewm@0 26 #define __touchkeys__KeyPositionTracker__
andrewm@0 27
andrewm@0 28 #include <set>
andrewm@0 29 #include "../Utility/Node.h"
andrewm@0 30 #include "../Utility/Accumulator.h"
andrewm@0 31 #include "PianoTypes.h"
andrewm@0 32
andrewm@0 33 // Three states of idle detector
andrewm@0 34 enum {
andrewm@0 35 kPositionTrackerStateUnknown = 0,
andrewm@0 36 kPositionTrackerStatePartialPressAwaitingMax,
andrewm@0 37 kPositionTrackerStatePartialPressFoundMax,
andrewm@0 38 kPositionTrackerStatePressInProgress,
andrewm@0 39 kPositionTrackerStateDown,
andrewm@0 40 kPositionTrackerStateReleaseInProgress,
andrewm@0 41 kPositionTrackerStateReleaseFinished
andrewm@0 42 };
andrewm@0 43
andrewm@0 44 // Constants for key state detection
andrewm@0 45 const key_position kPositionTrackerPressPosition = scale_key_position(0.75);
andrewm@0 46 const key_position kPositionTrackerPressHysteresis = scale_key_position(0.05);
andrewm@0 47 const key_position kPositionTrackerMinMaxSpacingThreshold = scale_key_position(0.02);
andrewm@0 48 const key_position kPositionTrackerFirstMaxThreshold = scale_key_position(0.075);
andrewm@0 49 const key_position kPositionTrackerReleaseFinishPosition = scale_key_position(0.2);
andrewm@0 50
andrewm@0 51 // How far back to search at the beginning to find the real start or release of a key press
andrewm@0 52 const int kPositionTrackerSamplesToSearchForStartLocation = 50;
andrewm@0 53 const int kPositionTrackerSamplesToSearchBeyondStartLocation = 20;
andrewm@0 54 const int kPositionTrackerSamplesToSearchForReleaseLocation = 100;
andrewm@0 55 const int kPositionTrackerSamplesToAverageForStartVelocity = 3;
andrewm@0 56 const key_velocity kPositionTrackerStartVelocityThreshold = scale_key_velocity(0.5);
andrewm@0 57 const key_velocity kPositionTrackerStartVelocitySpikeThreshold = scale_key_velocity(2.5);
andrewm@0 58 const key_velocity kPositionTrackerReleaseVelocityThreshold = scale_key_velocity(-0.2);
andrewm@0 59
andrewm@0 60 // Constants for feature calculations. The first one is the approximate location of the escapement
andrewm@0 61 // (empirically measured on one piano, so only approximate), used for velocity calculations
andrewm@0 62 const key_position kPositionTrackerDefaultPositionForPressVelocityCalculation = scale_key_position(0.65);
andrewm@0 63 const key_position kPositionTrackerDefaultPositionForReleaseVelocityCalculation = scale_key_position(0.5);
andrewm@0 64 const key_position kPositionTrackerPositionThresholdForPercussivenessCalculation = scale_key_position(0.4);
andrewm@0 65 const int kPositionTrackerSamplesNeededForPressVelocityAfterEscapement = 1;
andrewm@0 66 const int kPositionTrackerSamplesNeededForReleaseVelocityAfterEscapement = 1;
andrewm@0 67
andrewm@0 68 // KeyPositionTrackerNotification
andrewm@0 69 //
andrewm@0 70 // This class contains information on the notifications sent and stored by
andrewm@0 71 // KeyPositionTracker. Includes state changes and feature available.
andrewm@0 72
andrewm@0 73 class KeyPositionTrackerNotification {
andrewm@0 74 public:
andrewm@0 75 enum {
andrewm@0 76 kNotificationTypeStateChange = 1,
andrewm@0 77 kNotificationTypeFeatureAvailableVelocity,
andrewm@0 78 kNotificationTypeFeatureAvailableReleaseVelocity,
andrewm@0 79 kNotificationTypeFeatureAvailablePercussiveness,
andrewm@0 80 kNotificationTypeNewMinimum,
andrewm@0 81 kNotificationTypeNewMaximum
andrewm@0 82 };
andrewm@0 83
andrewm@0 84 enum {
andrewm@0 85 kFeaturesNone = 0,
andrewm@0 86 kFeaturePressVelocity = 0x0001,
andrewm@0 87 kFeatureReleaseVelocity = 0x0002,
andrewm@0 88 kFeaturePercussiveness = 0x0004
andrewm@0 89 };
andrewm@0 90
andrewm@0 91 int type;
andrewm@0 92 int state;
andrewm@0 93 int features;
andrewm@0 94 };
andrewm@0 95
andrewm@0 96 // KeyPositionTracker
andrewm@0 97 //
andrewm@0 98 // This class implements a state machine for a key that is currently active (not idle),
andrewm@0 99 // using continuous key position data to determine the location and parameters of key
andrewm@0 100 // presses and other events. It includes management of partial press patterns and detection
andrewm@0 101 // of percussiveness as well as velocity features.
andrewm@0 102 //
andrewm@0 103 // This class is triggered by new data points in the key position buffer. Its output is
andrewm@0 104 // a series of state changes which indicate what the key is doing.
andrewm@0 105
andrewm@0 106 class KeyPositionTracker : public Node<KeyPositionTrackerNotification> {
andrewm@0 107 public:
andrewm@0 108 typedef Node<key_position>::size_type key_buffer_index;
andrewm@0 109 //typedef void (*KeyActionFunction)(KeyPositionTracker *object, void *userData);
andrewm@0 110
andrewm@0 111 // Simple class to hold index/position/timestamp triads
andrewm@0 112 class Event {
andrewm@0 113 public:
andrewm@0 114 Event() : index(0), position(missing_value<key_position>::missing()),
andrewm@0 115 timestamp(missing_value<timestamp_type>::missing()) {}
andrewm@0 116
andrewm@0 117 Event(key_buffer_index i, key_position p, timestamp_type t)
andrewm@0 118 : index(i), position(p), timestamp(t) {}
andrewm@0 119
andrewm@0 120 Event(const Event& obj)
andrewm@0 121 : index(obj.index), position(obj.position), timestamp(obj.timestamp) {}
andrewm@0 122
andrewm@0 123 Event& operator=(const Event& obj) {
andrewm@0 124 index = obj.index;
andrewm@0 125 position = obj.position;
andrewm@0 126 timestamp = obj.timestamp;
andrewm@0 127 return *this;
andrewm@0 128 }
andrewm@0 129
andrewm@0 130 key_buffer_index index;
andrewm@0 131 key_position position;
andrewm@0 132 timestamp_type timestamp;
andrewm@0 133 };
andrewm@0 134
andrewm@0 135 // Collection of features related to whether a key is percussively played or not
andrewm@0 136 struct PercussivenessFeatures {
andrewm@0 137 float percussiveness; // Calculated single feature based on everything below
andrewm@0 138 Event velocitySpikeMaximum; // Maximum and minimum points of the initial
andrewm@0 139 Event velocitySpikeMinimum; // velocity spike on a percussive press
andrewm@0 140 timestamp_type timeFromStartToSpike; // How long it took to reach the velocity spike
andrewm@0 141 key_velocity areaPrecedingSpike; // Total sum of velocity values from start to max
andrewm@0 142 key_velocity areaFollowingSpike; // Total sum of velocity values from max to min
andrewm@0 143 };
andrewm@0 144
andrewm@0 145 public:
andrewm@0 146 // ***** Constructors *****
andrewm@0 147
andrewm@0 148 // Default constructor, passing the buffer on which to trigger
andrewm@0 149 KeyPositionTracker(capacity_type capacity, Node<key_position>& keyBuffer);
andrewm@0 150
andrewm@0 151 // Copy constructor
andrewm@0 152 //KeyPositionTracker(KeyPositionTracker const& obj);
andrewm@0 153
andrewm@0 154 // ***** State Access *****
andrewm@0 155
andrewm@0 156 // Whether this object is currently tracking states
andrewm@0 157 bool engaged() {
andrewm@0 158 return engaged_;
andrewm@0 159 }
andrewm@0 160
andrewm@0 161 // Return the current state (unknown if nothing is in the buffer)
andrewm@0 162 int currentState() {
andrewm@0 163 return currentState_;
andrewm@0 164 }
andrewm@0 165
andrewm@0 166 // Information about important recent points
andrewm@0 167 Event currentMax() {
andrewm@0 168 return Event(currentMaxIndex_, currentMaxPosition_, currentMaxTimestamp_);
andrewm@0 169 }
andrewm@0 170 Event currentMin() {
andrewm@0 171 return Event(currentMinIndex_, currentMinPosition_, currentMinTimestamp_);
andrewm@0 172 }
andrewm@0 173 Event pressStart() {
andrewm@0 174 return Event(startIndex_, startPosition_, startTimestamp_);
andrewm@0 175 }
andrewm@0 176 Event pressFinish() {
andrewm@0 177 return Event(pressIndex_, pressPosition_, pressTimestamp_);
andrewm@0 178 }
andrewm@0 179 Event releaseStart() {
andrewm@0 180 return Event(releaseBeginIndex_, releaseBeginPosition_, releaseBeginTimestamp_);
andrewm@0 181 }
andrewm@0 182 Event releaseFinish() {
andrewm@0 183 return Event(releaseEndIndex_, releaseEndPosition_, releaseEndTimestamp_);
andrewm@0 184 }
andrewm@0 185
andrewm@0 186 // ***** Key Press Features *****
andrewm@0 187
andrewm@0 188 // Velocity for onset and release. The values without an argument use the stored
andrewm@0 189 // current escapement point (which is also used for notification of availability).
andrewm@0 190 std::pair<timestamp_type, key_velocity> pressVelocity();
andrewm@0 191 std::pair<timestamp_type, key_velocity> releaseVelocity();
andrewm@0 192
andrewm@0 193 std::pair<timestamp_type, key_velocity> pressVelocity(key_position escapementPosition);
andrewm@0 194 std::pair<timestamp_type, key_velocity> releaseVelocity(key_position returnPosition);
andrewm@0 195
andrewm@0 196 // Set the threshold where we look for press velocity calculations. It
andrewm@0 197 // can be anything up to the press position threshold on the upward side
andrewm@0 198 // and anything down to the final release position on the downward side.
andrewm@0 199 void setPressVelocityEscapementPosition(key_position pos) {
andrewm@0 200 if(pos > kPositionTrackerPressPosition + kPositionTrackerPressHysteresis)
andrewm@0 201 pressVelocityEscapementPosition_ = kPositionTrackerPressPosition + kPositionTrackerPressHysteresis;
andrewm@0 202 else
andrewm@0 203 pressVelocityEscapementPosition_ = pos;
andrewm@0 204 }
andrewm@0 205 void setReleaseVelocityEscapementPosition(key_position pos) {
andrewm@0 206 if(pos < kPositionTrackerReleaseFinishPosition)
andrewm@0 207 releaseVelocityEscapementPosition_ = kPositionTrackerReleaseFinishPosition;
andrewm@0 208 else
andrewm@0 209 releaseVelocityEscapementPosition_ = pos;
andrewm@0 210 }
andrewm@0 211
andrewm@0 212
andrewm@0 213 // Percussiveness (struck vs. pressed keys)
andrewm@0 214 PercussivenessFeatures pressPercussiveness();
andrewm@0 215
andrewm@0 216 // ***** Modifiers *****
andrewm@0 217
andrewm@0 218 // Register for updates from the key positon buffer
andrewm@0 219 void engage();
andrewm@0 220
andrewm@0 221 // Unregister for updates from the key position buffer
andrewm@0 222 void disengage();
andrewm@0 223
andrewm@0 224 // Reset the state back initial values
andrewm@0 225 void reset();
andrewm@0 226
andrewm@0 227 // ***** Evaluator *****
andrewm@0 228
andrewm@0 229 // This method receives triggers whenever a new sample enters the buffer. It updates
andrewm@0 230 // the state depending on the profile of the key position.
andrewm@0 231 void triggerReceived(TriggerSource* who, timestamp_type timestamp);
andrewm@0 232
andrewm@0 233 private:
andrewm@0 234 // ***** Internal Helper Methods *****
andrewm@0 235
andrewm@0 236 // Change the current state
andrewm@0 237 void changeState(int newState, timestamp_type timestamp);
andrewm@0 238
andrewm@0 239 // Insert a new feature notification
andrewm@0 240 void notifyFeature(int notificationType, timestamp_type timestamp);
andrewm@0 241
andrewm@0 242 // Work backwards in the key position buffer to find the start/release of a press
andrewm@0 243 void findKeyPressStart(timestamp_type timestamp);
andrewm@0 244 void findKeyReleaseStart(timestamp_type timestamp);
andrewm@0 245
andrewm@0 246 // Generic method to find the most recent crossing of a given point
andrewm@0 247 key_buffer_index findMostRecentKeyPositionCrossing(key_position threshold, bool greaterThan, int maxDistance);
andrewm@0 248
andrewm@0 249 // Look for the crossing of the release velocity threshold to prepare to send the feature
andrewm@0 250 void prepareReleaseVelocityFeature(KeyPositionTracker::key_buffer_index mostRecentIndex, timestamp_type timestamp);
andrewm@0 251
andrewm@0 252 // ***** Member Variables *****
andrewm@0 253
andrewm@0 254 Node<key_position>& keyBuffer_; // Raw key position data
andrewm@0 255 bool engaged_; // Whether we're actively listening to incoming updates
andrewm@0 256 int currentState_; // Our current state
andrewm@0 257 int currentlyAvailableFeatures_; // Which features can be calculated for the current press
andrewm@0 258
andrewm@0 259 // Position tracking information for significant points (minima and maxima)
andrewm@0 260 key_position startPosition_; // Position of where the key press started
andrewm@0 261 timestamp_type startTimestamp_; // Timestamp of where the key press started
andrewm@0 262 key_buffer_index startIndex_; // Index in the buffer where the start occurred
andrewm@0 263 key_position pressPosition_; // Position of where the key is fully pressed
andrewm@0 264 timestamp_type pressTimestamp_; // Timestamp of where the key is fully pressed
andrewm@0 265 key_buffer_index pressIndex_; // Index in the buffer where the press occurred
andrewm@0 266 key_position releaseBeginPosition_; // Position of where the key release began
andrewm@0 267 timestamp_type releaseBeginTimestamp_; // Timestamp of where the key release began
andrewm@0 268 key_buffer_index releaseBeginIndex_; // Index in the buffer of where the key release began
andrewm@0 269 key_position releaseEndPosition_; // Position of where the key release ended
andrewm@0 270 timestamp_type releaseEndTimestamp_; // Timestamp of where the key release ended
andrewm@0 271 key_buffer_index releaseEndIndex_; // Index in the buffer of where the key release ended
andrewm@0 272 key_position currentMinPosition_, currentMaxPosition_; // Running min and max key position
andrewm@0 273 timestamp_type currentMinTimestamp_, currentMaxTimestamp_; // Times for the above positions
andrewm@0 274 key_buffer_index currentMinIndex_, currentMaxIndex_; // Indices in the buffer for the recent min/max
andrewm@0 275 key_position lastMinMaxPosition_; // Position of the last significant point
andrewm@0 276
andrewm@0 277 // Persistent parameters relating to feature calculation
andrewm@0 278 key_position pressVelocityEscapementPosition_; // Position at which onset velocity is calculated
andrewm@0 279 key_position releaseVelocityEscapementPosition_; // Position at which release velocity is calculate
andrewm@0 280 key_buffer_index pressVelocityAvailableIndex_; // When we can calculate press velocity
andrewm@0 281 key_buffer_index releaseVelocityAvailableIndex_; // When we can calculate release velocity
andrewm@0 282 bool releaseVelocityWaitingForThresholdCross_; // Set to true if we need to look for release escapement cross
andrewm@0 283 key_buffer_index percussivenessAvailableIndex_; // When we can calculate percussiveness features
andrewm@0 284
andrewm@0 285 /*
andrewm@0 286 typedef struct {
andrewm@0 287 int runningSum; // sum of last N points (i.e. mean * N)
andrewm@0 288 int runningSumMaxLength; // the value of N above
andrewm@0 289 int runningSumCurrentLength; // How many values are actually part of the sum right now (transient condition)
andrewm@0 290 int startValuesSum; // sum of the last N start values (to calculate returning quiescent position)
andrewm@0 291 int startValuesSumMaxLength;
andrewm@0 292 int startValuesSumCurrentLength;
andrewm@0 293
andrewm@0 294 int maxVariation; // The maximum deviation from mean of the last group of samples
andrewm@0 295 int flatCounter; // how many successive samples have been "flat" (minimal change)
andrewm@0 296 int currentStartValue; // values and positions of several key points for active keys
andrewm@0 297 int currentStartPosition;
andrewm@0 298 int currentMinValue;
andrewm@0 299 int currentMinPosition;
andrewm@0 300 int currentMaxValue;
andrewm@0 301 int currentMaxPosition;
andrewm@0 302 int lastKeyPointValue; // the value of the last important point {start, max, min}
andrewm@0 303
andrewm@0 304 deque<keyPointHistory> recentKeyPoints; // the minima and maxima since the key started
andrewm@0 305 bool sentPercussiveMidiOn; // HACK: whether we've sent the percussive MIDI event
andrewm@0 306
andrewm@0 307 int pressValue; // the value at the maximum corresponding to the end of the key press motion
andrewm@0 308 int pressPosition; // the location in the buffer of this event (note: not the timestamp)
andrewm@0 309 int releaseValue; // the value the key held right before release
andrewm@0 310 int releasePosition; // the location in the buffer of the release corner
andrewm@0 311 } keyParameters;
andrewm@0 312 */
andrewm@0 313 };
andrewm@0 314
andrewm@0 315
andrewm@0 316 #endif /* defined(__touchkeys__KeyPositionTracker__) */