Mercurial > hg > touchkeys
view Source/TouchKeys/KeyPositionTracker.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 |
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/>. ===================================================================== KeyPositionTracker.h: parses continuous key position and detects the state of the key. */ #ifndef __touchkeys__KeyPositionTracker__ #define __touchkeys__KeyPositionTracker__ #include <set> #include "../Utility/Node.h" #include "../Utility/Accumulator.h" #include "PianoTypes.h" // Three states of idle detector enum { kPositionTrackerStateUnknown = 0, kPositionTrackerStatePartialPressAwaitingMax, kPositionTrackerStatePartialPressFoundMax, kPositionTrackerStatePressInProgress, kPositionTrackerStateDown, kPositionTrackerStateReleaseInProgress, kPositionTrackerStateReleaseFinished }; // Constants for key state detection const key_position kPositionTrackerPressPosition = scale_key_position(0.75); const key_position kPositionTrackerPressHysteresis = scale_key_position(0.05); const key_position kPositionTrackerMinMaxSpacingThreshold = scale_key_position(0.02); const key_position kPositionTrackerFirstMaxThreshold = scale_key_position(0.075); const key_position kPositionTrackerReleaseFinishPosition = scale_key_position(0.2); // How far back to search at the beginning to find the real start or release of a key press const int kPositionTrackerSamplesToSearchForStartLocation = 50; const int kPositionTrackerSamplesToSearchBeyondStartLocation = 20; const int kPositionTrackerSamplesToSearchForReleaseLocation = 100; const int kPositionTrackerSamplesToAverageForStartVelocity = 3; const key_velocity kPositionTrackerStartVelocityThreshold = scale_key_velocity(0.5); const key_velocity kPositionTrackerStartVelocitySpikeThreshold = scale_key_velocity(2.5); const key_velocity kPositionTrackerReleaseVelocityThreshold = scale_key_velocity(-0.2); // Constants for feature calculations. The first one is the approximate location of the escapement // (empirically measured on one piano, so only approximate), used for velocity calculations const key_position kPositionTrackerDefaultPositionForPressVelocityCalculation = scale_key_position(0.65); const key_position kPositionTrackerDefaultPositionForReleaseVelocityCalculation = scale_key_position(0.5); const key_position kPositionTrackerPositionThresholdForPercussivenessCalculation = scale_key_position(0.4); const int kPositionTrackerSamplesNeededForPressVelocityAfterEscapement = 1; const int kPositionTrackerSamplesNeededForReleaseVelocityAfterEscapement = 1; // KeyPositionTrackerNotification // // This class contains information on the notifications sent and stored by // KeyPositionTracker. Includes state changes and feature available. class KeyPositionTrackerNotification { public: enum { kNotificationTypeStateChange = 1, kNotificationTypeFeatureAvailableVelocity, kNotificationTypeFeatureAvailableReleaseVelocity, kNotificationTypeFeatureAvailablePercussiveness, kNotificationTypeNewMinimum, kNotificationTypeNewMaximum }; enum { kFeaturesNone = 0, kFeaturePressVelocity = 0x0001, kFeatureReleaseVelocity = 0x0002, kFeaturePercussiveness = 0x0004 }; int type; int state; int features; }; // KeyPositionTracker // // This class implements a state machine for a key that is currently active (not idle), // using continuous key position data to determine the location and parameters of key // presses and other events. It includes management of partial press patterns and detection // of percussiveness as well as velocity features. // // This class is triggered by new data points in the key position buffer. Its output is // a series of state changes which indicate what the key is doing. class KeyPositionTracker : public Node<KeyPositionTrackerNotification> { public: typedef Node<key_position>::size_type key_buffer_index; //typedef void (*KeyActionFunction)(KeyPositionTracker *object, void *userData); // Simple class to hold index/position/timestamp triads class Event { public: Event() : index(0), position(missing_value<key_position>::missing()), timestamp(missing_value<timestamp_type>::missing()) {} Event(key_buffer_index i, key_position p, timestamp_type t) : index(i), position(p), timestamp(t) {} Event(const Event& obj) : index(obj.index), position(obj.position), timestamp(obj.timestamp) {} Event& operator=(const Event& obj) { index = obj.index; position = obj.position; timestamp = obj.timestamp; return *this; } key_buffer_index index; key_position position; timestamp_type timestamp; }; // Collection of features related to whether a key is percussively played or not struct PercussivenessFeatures { float percussiveness; // Calculated single feature based on everything below Event velocitySpikeMaximum; // Maximum and minimum points of the initial Event velocitySpikeMinimum; // velocity spike on a percussive press timestamp_type timeFromStartToSpike; // How long it took to reach the velocity spike key_velocity areaPrecedingSpike; // Total sum of velocity values from start to max key_velocity areaFollowingSpike; // Total sum of velocity values from max to min }; public: // ***** Constructors ***** // Default constructor, passing the buffer on which to trigger KeyPositionTracker(capacity_type capacity, Node<key_position>& keyBuffer); // Copy constructor //KeyPositionTracker(KeyPositionTracker const& obj); // ***** State Access ***** // Whether this object is currently tracking states bool engaged() { return engaged_; } // Return the current state (unknown if nothing is in the buffer) int currentState() { return currentState_; } // Information about important recent points Event currentMax() { return Event(currentMaxIndex_, currentMaxPosition_, currentMaxTimestamp_); } Event currentMin() { return Event(currentMinIndex_, currentMinPosition_, currentMinTimestamp_); } Event pressStart() { return Event(startIndex_, startPosition_, startTimestamp_); } Event pressFinish() { return Event(pressIndex_, pressPosition_, pressTimestamp_); } Event releaseStart() { return Event(releaseBeginIndex_, releaseBeginPosition_, releaseBeginTimestamp_); } Event releaseFinish() { return Event(releaseEndIndex_, releaseEndPosition_, releaseEndTimestamp_); } // ***** Key Press Features ***** // Velocity for onset and release. The values without an argument use the stored // current escapement point (which is also used for notification of availability). std::pair<timestamp_type, key_velocity> pressVelocity(); std::pair<timestamp_type, key_velocity> releaseVelocity(); std::pair<timestamp_type, key_velocity> pressVelocity(key_position escapementPosition); std::pair<timestamp_type, key_velocity> releaseVelocity(key_position returnPosition); // Set the threshold where we look for press velocity calculations. It // can be anything up to the press position threshold on the upward side // and anything down to the final release position on the downward side. void setPressVelocityEscapementPosition(key_position pos) { if(pos > kPositionTrackerPressPosition + kPositionTrackerPressHysteresis) pressVelocityEscapementPosition_ = kPositionTrackerPressPosition + kPositionTrackerPressHysteresis; else pressVelocityEscapementPosition_ = pos; } void setReleaseVelocityEscapementPosition(key_position pos) { if(pos < kPositionTrackerReleaseFinishPosition) releaseVelocityEscapementPosition_ = kPositionTrackerReleaseFinishPosition; else releaseVelocityEscapementPosition_ = pos; } // Percussiveness (struck vs. pressed keys) PercussivenessFeatures pressPercussiveness(); // ***** Modifiers ***** // Register for updates from the key positon buffer void engage(); // Unregister for updates from the key position buffer void disengage(); // Reset the state back initial values void reset(); // ***** Evaluator ***** // This method receives triggers whenever a new sample enters the buffer. It updates // the state depending on the profile of the key position. void triggerReceived(TriggerSource* who, timestamp_type timestamp); private: // ***** Internal Helper Methods ***** // Change the current state void changeState(int newState, timestamp_type timestamp); // Insert a new feature notification void notifyFeature(int notificationType, timestamp_type timestamp); // Work backwards in the key position buffer to find the start/release of a press void findKeyPressStart(timestamp_type timestamp); void findKeyReleaseStart(timestamp_type timestamp); // Generic method to find the most recent crossing of a given point key_buffer_index findMostRecentKeyPositionCrossing(key_position threshold, bool greaterThan, int maxDistance); // Look for the crossing of the release velocity threshold to prepare to send the feature void prepareReleaseVelocityFeature(KeyPositionTracker::key_buffer_index mostRecentIndex, timestamp_type timestamp); // ***** Member Variables ***** Node<key_position>& keyBuffer_; // Raw key position data bool engaged_; // Whether we're actively listening to incoming updates int currentState_; // Our current state int currentlyAvailableFeatures_; // Which features can be calculated for the current press // Position tracking information for significant points (minima and maxima) key_position startPosition_; // Position of where the key press started timestamp_type startTimestamp_; // Timestamp of where the key press started key_buffer_index startIndex_; // Index in the buffer where the start occurred key_position pressPosition_; // Position of where the key is fully pressed timestamp_type pressTimestamp_; // Timestamp of where the key is fully pressed key_buffer_index pressIndex_; // Index in the buffer where the press occurred key_position releaseBeginPosition_; // Position of where the key release began timestamp_type releaseBeginTimestamp_; // Timestamp of where the key release began key_buffer_index releaseBeginIndex_; // Index in the buffer of where the key release began key_position releaseEndPosition_; // Position of where the key release ended timestamp_type releaseEndTimestamp_; // Timestamp of where the key release ended key_buffer_index releaseEndIndex_; // Index in the buffer of where the key release ended key_position currentMinPosition_, currentMaxPosition_; // Running min and max key position timestamp_type currentMinTimestamp_, currentMaxTimestamp_; // Times for the above positions key_buffer_index currentMinIndex_, currentMaxIndex_; // Indices in the buffer for the recent min/max key_position lastMinMaxPosition_; // Position of the last significant point // Persistent parameters relating to feature calculation key_position pressVelocityEscapementPosition_; // Position at which onset velocity is calculated key_position releaseVelocityEscapementPosition_; // Position at which release velocity is calculate key_buffer_index pressVelocityAvailableIndex_; // When we can calculate press velocity key_buffer_index releaseVelocityAvailableIndex_; // When we can calculate release velocity bool releaseVelocityWaitingForThresholdCross_; // Set to true if we need to look for release escapement cross key_buffer_index percussivenessAvailableIndex_; // When we can calculate percussiveness features /* typedef struct { int runningSum; // sum of last N points (i.e. mean * N) int runningSumMaxLength; // the value of N above int runningSumCurrentLength; // How many values are actually part of the sum right now (transient condition) int startValuesSum; // sum of the last N start values (to calculate returning quiescent position) int startValuesSumMaxLength; int startValuesSumCurrentLength; int maxVariation; // The maximum deviation from mean of the last group of samples int flatCounter; // how many successive samples have been "flat" (minimal change) int currentStartValue; // values and positions of several key points for active keys int currentStartPosition; int currentMinValue; int currentMinPosition; int currentMaxValue; int currentMaxPosition; int lastKeyPointValue; // the value of the last important point {start, max, min} deque<keyPointHistory> recentKeyPoints; // the minima and maxima since the key started bool sentPercussiveMidiOn; // HACK: whether we've sent the percussive MIDI event int pressValue; // the value at the maximum corresponding to the end of the key press motion int pressPosition; // the location in the buffer of this event (note: not the timestamp) int releaseValue; // the value the key held right before release int releasePosition; // the location in the buffer of the release corner } keyParameters; */ }; #endif /* defined(__touchkeys__KeyPositionTracker__) */