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 } |