andrewm@0: /* andrewm@0: TouchKeys: multi-touch musical keyboard control software andrewm@0: Copyright (c) 2013 Andrew McPherson andrewm@0: andrewm@0: This program is free software: you can redistribute it and/or modify andrewm@0: it under the terms of the GNU General Public License as published by andrewm@0: the Free Software Foundation, either version 3 of the License, or andrewm@0: (at your option) any later version. andrewm@0: andrewm@0: This program is distributed in the hope that it will be useful, andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrewm@0: GNU General Public License for more details. andrewm@0: andrewm@0: You should have received a copy of the GNU General Public License andrewm@0: along with this program. If not, see . andrewm@0: andrewm@0: ===================================================================== andrewm@0: andrewm@0: KeyIdleDetector.cpp: uses continuous key position to detect whether a key andrewm@0: is idle or active; active keys will have more detailed tracking applied andrewm@0: to their position, so detecting idle keys saves processing. andrewm@0: */ andrewm@0: andrewm@0: #include "KeyIdleDetector.h" andrewm@0: andrewm@0: // Default constructor andrewm@0: KeyIdleDetector::KeyIdleDetector(capacity_type capacity, Node& keyBuffer, key_position positionThreshold, andrewm@0: key_position activityThreshold, int counterThreshold) andrewm@0: : Node(capacity), keyBuffer_(keyBuffer), accumulator_(kKeyIdleNumSamples+1, keyBuffer), andrewm@0: keyIdleThreshold_(kDefaultKeyIdleThreshold), activityThreshold_(activityThreshold), positionThreshold_(positionThreshold), andrewm@0: numberOfFramesWithoutActivity_(0), noActivityCounterThreshold_(counterThreshold), andrewm@0: idleState_(kIdleDetectorUnknown) andrewm@0: { andrewm@0: // Register to receive messages from the accumulator each time it gets a new sample andrewm@0: //std::cout << "Registering IdleDetector\n"; andrewm@0: andrewm@0: registerForTrigger(&accumulator_); andrewm@0: andrewm@0: // std::cout << "IdleDetector: this_source = " << (TriggerSource*)this << " this_dest = " << (TriggerDestination*)this << " accumulator = " << &accumulator_ << std::endl; andrewm@0: } andrewm@0: andrewm@0: // Copy constructor andrewm@0: /*KeyIdleDetector::KeyIdleDetector(KeyIdleDetector const& obj) andrewm@0: : Node(obj), keyBuffer_(obj.keyBuffer_), accumulator_(obj.accumulator_), idleState_(obj.idleState_), andrewm@0: activityThreshold_(obj.activityThreshold_), positionThreshold_(obj.positionThreshold_), andrewm@0: numberOfFramesWithoutActivity_(obj.numberOfFramesWithoutActivity_), andrewm@0: keyIdleThreshold_(obj.keyIdleThreshold_), noActivityCounterThreshold_(obj.noActivityCounterThreshold_) { andrewm@0: registerForTrigger(&accumulator_); andrewm@0: }*/ andrewm@0: andrewm@0: // Clear current state and reset to unknown idle state. andrewm@0: void KeyIdleDetector::clear() { andrewm@0: Node::clear(); andrewm@0: idleState_ = kIdleDetectorUnknown; andrewm@0: numberOfFramesWithoutActivity_ = 0; andrewm@0: } andrewm@0: andrewm@0: // Evaluator function. Find the maximum deviation from average of the key motion. andrewm@0: andrewm@0: void KeyIdleDetector::triggerReceived(TriggerSource* who, timestamp_type timestamp) { andrewm@0: //std::cout << "KeyIdleDetector::triggerReceived\n"; andrewm@0: andrewm@0: if(who != &accumulator_) andrewm@0: return; andrewm@0: andrewm@0: key_position currentKeyPosition = keyBuffer_.latest(); andrewm@0: std::pair currentAccumulator = accumulator_.latest(); andrewm@0: andrewm@0: // Check that we have enough samples andrewm@0: if(currentAccumulator.first < kKeyIdleNumSamples) andrewm@0: return; andrewm@0: andrewm@0: // Behavior depends on whether we were idle or not before (or in unknown state) andrewm@0: if(idleState_ == kIdleDetectorIdle) { andrewm@0: // If idle right now, don't do anything if the key position is below a threshold andrewm@0: if(currentKeyPosition < keyIdleThreshold_) andrewm@0: return; andrewm@0: andrewm@0: // If average is below a second, slightly higher threshold, stay idle andrewm@0: key_position averageValue = currentAccumulator.second / (key_position)currentAccumulator.first; andrewm@0: if(averageValue < keyIdleThreshold_ * 2) andrewm@0: return; andrewm@0: andrewm@0: // Go active, notifying any listeners andrewm@0: idleState_ = kIdleDetectorActive; andrewm@0: insert(kIdleDetectorActive, timestamp); andrewm@0: } andrewm@0: else { // Active or unknown andrewm@0: // Rule out any cases that would immediately take the key active andrewm@0: key_position averageValue = currentAccumulator.second / (key_position)currentAccumulator.first; andrewm@0: if(averageValue >= keyIdleThreshold_ * 2) { andrewm@0: numberOfFramesWithoutActivity_ = 0; andrewm@0: return; andrewm@0: } andrewm@0: andrewm@0: #if 0 andrewm@0: key_position maxDeviation = 0; andrewm@0: size_type endIndex = keyBuffer_.endIndex(); andrewm@0: // Find and return the maximum deviation from the average andrewm@0: for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) { andrewm@0: key_position diff = key_abs(keyBuffer_[i] - averageValue); andrewm@0: if(diff > maxDeviation) andrewm@0: maxDeviation = diff; andrewm@0: } andrewm@0: #endif andrewm@0: key_position averageDeviation = 0; andrewm@0: size_type endIndex = keyBuffer_.endIndex(); andrewm@0: // Find and return the average deviation from mean andrewm@0: for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) { andrewm@0: averageDeviation += key_abs(keyBuffer_[i] - averageValue); andrewm@0: } andrewm@0: averageDeviation /= kKeyIdleNumSamples; andrewm@0: andrewm@0: //std::cout << "averageDeviation = " << averageDeviation << " counter = " << numberOfFramesWithoutActivity_ << std::endl; andrewm@0: andrewm@0: if(averageDeviation < activityThreshold_) { andrewm@0: // Key registers as "flat". Check if it has stayed that way for long enough, and with a position close enough andrewm@0: // to resting position, to change the state back to Idle. andrewm@0: andrewm@0: numberOfFramesWithoutActivity_++; andrewm@0: if(numberOfFramesWithoutActivity_ >= noActivityCounterThreshold_) { andrewm@0: idleState_ = kIdleDetectorIdle; andrewm@0: insert(kIdleDetectorIdle, timestamp); andrewm@0: } andrewm@0: } andrewm@0: else andrewm@0: numberOfFramesWithoutActivity_ = 0; andrewm@0: } andrewm@0: andrewm@0: #if 0 /* Old idle detection */ andrewm@0: andrewm@0: //std::cout << "KeyIdleDetector::triggerReceived2\n"; andrewm@0: andrewm@0: std::pair current = accumulator_.latest(); andrewm@0: andrewm@0: if(current.first < kKeyIdleNumSamples) andrewm@0: return; andrewm@0: andrewm@0: // Find the average value andrewm@0: key_position averageValue = current.second / (key_position)current.first; andrewm@0: key_position maxDeviation = 0; andrewm@0: andrewm@0: size_type endIndex = keyBuffer_.endIndex(); andrewm@0: // Find and return the maximum deviation from the average andrewm@0: for(int i = endIndex - kKeyIdleNumSamples; i < endIndex; i++) { andrewm@0: key_position diff = key_abs(keyBuffer_[i] - averageValue); andrewm@0: if(diff > maxDeviation) andrewm@0: maxDeviation = diff; andrewm@0: } andrewm@0: andrewm@0: // TODO: If we get here, good enough to go to initial activity. But need to search back to determine start point. andrewm@0: // Also need to update current start values (see kblisten code). andrewm@0: andrewm@0: // Insert a new sample (and hence send a trigger) whenever the maximum deviation crosses the threshold. andrewm@0: andrewm@0: if((maxDeviation >= activityThreshold_ || keyBuffer_.latest() >= positionThreshold_) && idleState_ != kIdleDetectorActive) { andrewm@0: idleState_ = kIdleDetectorActive; andrewm@0: numberOfFramesWithoutActivity_ = 0; andrewm@0: insert(kIdleDetectorActive, timestamp); andrewm@0: //std::cout << "deviation = " << maxDeviation << " average = " << averageValue << std::endl; andrewm@0: } andrewm@0: else if(maxDeviation < activityThreshold_ && idleState_ != kIdleDetectorIdle) { andrewm@0: // Key registers as "flat". Check if it has stayed that way for long enough, and with a position close enough andrewm@0: // to resting position, to change the state back to Idle. andrewm@0: andrewm@0: numberOfFramesWithoutActivity_++; andrewm@0: if(numberOfFramesWithoutActivity_ >= noActivityCounterThreshold_ && keyBuffer_.latest() < positionThreshold_) { andrewm@0: idleState_ = kIdleDetectorIdle; andrewm@0: insert(kIdleDetectorIdle, timestamp); andrewm@0: //std::cout << "deviation = " << maxDeviation << " average = " << averageValue << std::endl; andrewm@0: /*Accumulator::iterator it; andrewm@0: andrewm@0: for(it = accumulator_.begin(); it != accumulator_.end(); it++) { andrewm@0: std::cout << it->first << " " << it->second << std::endl; andrewm@0: }*/ andrewm@0: } andrewm@0: } andrewm@0: #endif andrewm@0: }