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