annotate Source/TouchKeys/KeyIdleDetector.cpp @ 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 KeyIdleDetector.cpp: uses continuous key position to detect whether a key
andrewm@0 21 is idle or active; active keys will have more detailed tracking applied
andrewm@0 22 to their position, so detecting idle keys saves processing.
andrewm@0 23 */
andrewm@0 24
andrewm@0 25 #include "KeyIdleDetector.h"
andrewm@0 26
andrewm@0 27 // Default constructor
andrewm@0 28 KeyIdleDetector::KeyIdleDetector(capacity_type capacity, Node<key_position>& keyBuffer, key_position positionThreshold,
andrewm@0 29 key_position activityThreshold, int counterThreshold)
andrewm@0 30 : Node<int>(capacity), keyBuffer_(keyBuffer), accumulator_(kKeyIdleNumSamples+1, keyBuffer),
andrewm@0 31 keyIdleThreshold_(kDefaultKeyIdleThreshold), activityThreshold_(activityThreshold), positionThreshold_(positionThreshold),
andrewm@0 32 numberOfFramesWithoutActivity_(0), noActivityCounterThreshold_(counterThreshold),
andrewm@0 33 idleState_(kIdleDetectorUnknown)
andrewm@0 34 {
andrewm@0 35 // Register to receive messages from the accumulator each time it gets a new sample
andrewm@0 36 //std::cout << "Registering IdleDetector\n";
andrewm@0 37
andrewm@0 38 registerForTrigger(&accumulator_);
andrewm@0 39
andrewm@0 40 // std::cout << "IdleDetector: this_source = " << (TriggerSource*)this << " this_dest = " << (TriggerDestination*)this << " accumulator = " << &accumulator_ << std::endl;
andrewm@0 41 }
andrewm@0 42
andrewm@0 43 // Copy constructor
andrewm@0 44 /*KeyIdleDetector::KeyIdleDetector(KeyIdleDetector const& obj)
andrewm@0 45 : Node<int>(obj), keyBuffer_(obj.keyBuffer_), accumulator_(obj.accumulator_), idleState_(obj.idleState_),
andrewm@0 46 activityThreshold_(obj.activityThreshold_), positionThreshold_(obj.positionThreshold_),
andrewm@0 47 numberOfFramesWithoutActivity_(obj.numberOfFramesWithoutActivity_),
andrewm@0 48 keyIdleThreshold_(obj.keyIdleThreshold_), noActivityCounterThreshold_(obj.noActivityCounterThreshold_) {
andrewm@0 49 registerForTrigger(&accumulator_);
andrewm@0 50 }*/
andrewm@0 51
andrewm@0 52 // Clear current state and reset to unknown idle state.
andrewm@0 53 void KeyIdleDetector::clear() {
andrewm@0 54 Node<int>::clear();
andrewm@0 55 idleState_ = kIdleDetectorUnknown;
andrewm@0 56 numberOfFramesWithoutActivity_ = 0;
andrewm@0 57 }
andrewm@0 58
andrewm@0 59 // Evaluator function. Find the maximum deviation from average of the key motion.
andrewm@0 60
andrewm@0 61 void KeyIdleDetector::triggerReceived(TriggerSource* who, timestamp_type timestamp) {
andrewm@0 62 //std::cout << "KeyIdleDetector::triggerReceived\n";
andrewm@0 63
andrewm@0 64 if(who != &accumulator_)
andrewm@0 65 return;
andrewm@0 66
andrewm@0 67 key_position currentKeyPosition = keyBuffer_.latest();
andrewm@0 68 std::pair<int, key_position> currentAccumulator = accumulator_.latest();
andrewm@0 69
andrewm@0 70 // Check that we have enough samples
andrewm@0 71 if(currentAccumulator.first < kKeyIdleNumSamples)
andrewm@0 72 return;
andrewm@0 73
andrewm@0 74 // Behavior depends on whether we were idle or not before (or in unknown state)
andrewm@0 75 if(idleState_ == kIdleDetectorIdle) {
andrewm@0 76 // If idle right now, don't do anything if the key position is below a threshold
andrewm@0 77 if(currentKeyPosition < keyIdleThreshold_)
andrewm@0 78 return;
andrewm@0 79
andrewm@0 80 // If average is below a second, slightly higher threshold, stay idle
andrewm@0 81 key_position averageValue = currentAccumulator.second / (key_position)currentAccumulator.first;
andrewm@0 82 if(averageValue < keyIdleThreshold_ * 2)
andrewm@0 83 return;
andrewm@0 84
andrewm@0 85 // Go active, notifying any listeners
andrewm@0 86 idleState_ = kIdleDetectorActive;
andrewm@0 87 insert(kIdleDetectorActive, timestamp);
andrewm@0 88 }
andrewm@0 89 else { // Active or unknown
andrewm@0 90 // Rule out any cases that would immediately take the key active
andrewm@0 91 key_position averageValue = currentAccumulator.second / (key_position)currentAccumulator.first;
andrewm@0 92 if(averageValue >= keyIdleThreshold_ * 2) {
andrewm@0 93 numberOfFramesWithoutActivity_ = 0;
andrewm@0 94 return;
andrewm@0 95 }
andrewm@0 96
andrewm@0 97 #if 0
andrewm@0 98 key_position maxDeviation = 0;
andrewm@0 99 size_type endIndex = keyBuffer_.endIndex();
andrewm@0 100 // Find and return the maximum deviation from the average
andrewm@0 101 for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) {
andrewm@0 102 key_position diff = key_abs(keyBuffer_[i] - averageValue);
andrewm@0 103 if(diff > maxDeviation)
andrewm@0 104 maxDeviation = diff;
andrewm@0 105 }
andrewm@0 106 #endif
andrewm@0 107 key_position averageDeviation = 0;
andrewm@0 108 size_type endIndex = keyBuffer_.endIndex();
andrewm@0 109 // Find and return the average deviation from mean
andrewm@0 110 for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) {
andrewm@0 111 averageDeviation += key_abs(keyBuffer_[i] - averageValue);
andrewm@0 112 }
andrewm@0 113 averageDeviation /= kKeyIdleNumSamples;
andrewm@0 114
andrewm@0 115 //std::cout << "averageDeviation = " << averageDeviation << " counter = " << numberOfFramesWithoutActivity_ << std::endl;
andrewm@0 116
andrewm@0 117 if(averageDeviation < activityThreshold_) {
andrewm@0 118 // Key registers as "flat". Check if it has stayed that way for long enough, and with a position close enough
andrewm@0 119 // to resting position, to change the state back to Idle.
andrewm@0 120
andrewm@0 121 numberOfFramesWithoutActivity_++;
andrewm@0 122 if(numberOfFramesWithoutActivity_ >= noActivityCounterThreshold_) {
andrewm@0 123 idleState_ = kIdleDetectorIdle;
andrewm@0 124 insert(kIdleDetectorIdle, timestamp);
andrewm@0 125 }
andrewm@0 126 }
andrewm@0 127 else
andrewm@0 128 numberOfFramesWithoutActivity_ = 0;
andrewm@0 129 }
andrewm@0 130
andrewm@0 131 #if 0 /* Old idle detection */
andrewm@0 132
andrewm@0 133 //std::cout << "KeyIdleDetector::triggerReceived2\n";
andrewm@0 134
andrewm@0 135 std::pair<int, key_position> current = accumulator_.latest();
andrewm@0 136
andrewm@0 137 if(current.first < kKeyIdleNumSamples)
andrewm@0 138 return;
andrewm@0 139
andrewm@0 140 // Find the average value
andrewm@0 141 key_position averageValue = current.second / (key_position)current.first;
andrewm@0 142 key_position maxDeviation = 0;
andrewm@0 143
andrewm@0 144 size_type endIndex = keyBuffer_.endIndex();
andrewm@0 145 // Find and return the maximum deviation from the average
andrewm@0 146 for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) {
andrewm@0 147 key_position diff = key_abs(keyBuffer_[i] - averageValue);
andrewm@0 148 if(diff > maxDeviation)
andrewm@0 149 maxDeviation = diff;
andrewm@0 150 }
andrewm@0 151
andrewm@0 152 // TODO: If we get here, good enough to go to initial activity. But need to search back to determine start point.
andrewm@0 153 // Also need to update current start values (see kblisten code).
andrewm@0 154
andrewm@0 155 // Insert a new sample (and hence send a trigger) whenever the maximum deviation crosses the threshold.
andrewm@0 156
andrewm@0 157 if((maxDeviation >= activityThreshold_ || keyBuffer_.latest() >= positionThreshold_) && idleState_ != kIdleDetectorActive) {
andrewm@0 158 idleState_ = kIdleDetectorActive;
andrewm@0 159 numberOfFramesWithoutActivity_ = 0;
andrewm@0 160 insert(kIdleDetectorActive, timestamp);
andrewm@0 161 //std::cout << "deviation = " << maxDeviation << " average = " << averageValue << std::endl;
andrewm@0 162 }
andrewm@0 163 else if(maxDeviation < activityThreshold_ && idleState_ != kIdleDetectorIdle) {
andrewm@0 164 // Key registers as "flat". Check if it has stayed that way for long enough, and with a position close enough
andrewm@0 165 // to resting position, to change the state back to Idle.
andrewm@0 166
andrewm@0 167 numberOfFramesWithoutActivity_++;
andrewm@0 168 if(numberOfFramesWithoutActivity_ >= noActivityCounterThreshold_ && keyBuffer_.latest() < positionThreshold_) {
andrewm@0 169 idleState_ = kIdleDetectorIdle;
andrewm@0 170 insert(kIdleDetectorIdle, timestamp);
andrewm@0 171 //std::cout << "deviation = " << maxDeviation << " average = " << averageValue << std::endl;
andrewm@0 172 /*Accumulator<key_position,kKeyIdleNumSamples>::iterator it;
andrewm@0 173
andrewm@0 174 for(it = accumulator_.begin(); it != accumulator_.end(); it++) {
andrewm@0 175 std::cout << it->first << " " << it->second << std::endl;
andrewm@0 176 }*/
andrewm@0 177 }
andrewm@0 178 }
andrewm@0 179 #endif
andrewm@0 180 }