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: Accumulator.h: template class that accumulates (adds) samples coming into andrewm@0: a given Node. andrewm@0: */ andrewm@0: andrewm@0: #ifndef KEYCONTROL_ACCUMULATOR_H andrewm@0: #define KEYCONTROL_ACCUMULATOR_H andrewm@0: andrewm@0: #include andrewm@0: #include andrewm@0: #include "Node.h" andrewm@0: andrewm@0: /* andrewm@0: * Accumulator andrewm@0: * andrewm@0: * Calculate the running sum of the last N points of a signal. Unlike Integral, it does not andrewm@0: * take into account the timestamps, and so is signficantly faster but less flexible. andrewm@0: * andrewm@0: * The output type of this class is a pair, the first of which indicates how many samples are andrewm@0: * included in the accumulated result, and the second of which is the result itself. This handles andrewm@0: * transient startup conditions where all N samples are not yet available. andrewm@0: * andrewm@0: */ andrewm@0: andrewm@0: template andrewm@0: class Accumulator : public Node > { andrewm@0: public: andrewm@0: typedef typename std::pair return_type; andrewm@0: typedef typename Node::capacity_type capacity_type; andrewm@0: //typedef typename Node::size_type size_type; andrewm@0: andrewm@0: // ***** Constructors ***** andrewm@0: andrewm@0: Accumulator(capacity_type capacity, Node& input) : Node(capacity), input_(input), samples_(N+1) { andrewm@0: if(capacity <= N) // Need to have at least N points in history to accumulate andrewm@0: throw new std::bad_alloc(); andrewm@0: //std::cout << "Registering Accumulator\n"; andrewm@0: this->registerForTrigger(&input_); andrewm@0: //std::cout << "Accumulator: this_source = " << (TriggerSource*)this << "this_dest = " << (TriggerDestination*)this << " input_ = " << &input_ << std::endl; andrewm@0: } andrewm@0: andrewm@0: // Copy constructor andrewm@0: Accumulator(Accumulator const& obj) : Node(obj), input_(obj.input_), samples_(obj.samples_) { andrewm@0: this->registerForTrigger(&input_); andrewm@0: } andrewm@0: andrewm@0: // ***** Modifiers ***** andrewm@0: // andrewm@0: // Override this method to clear the samples_ buffer andrewm@0: andrewm@0: void clear() { andrewm@0: Node >::clear(); andrewm@0: samples_.clear(); andrewm@0: } andrewm@0: andrewm@0: // ***** Evaluator ***** andrewm@0: // andrewm@0: // This is called when the input gets a new data point. Accumulate its value and store it in our buffer. andrewm@0: // Storing it will also cause a trigger to be sent to anyone who's listening. andrewm@0: andrewm@0: void triggerReceived(TriggerSource* who, timestamp_type timestamp) { andrewm@0: //std::cout << "Accumulator::triggerReceived\n"; andrewm@0: andrewm@0: if(who != &input_) andrewm@0: return; andrewm@0: andrewm@0: //std::cout << "Accumulator::triggerReceived2\n"; andrewm@0: andrewm@0: DataType newSample = input_.latest(); andrewm@0: samples_.push_back(newSample); andrewm@0: andrewm@0: if(this->empty()) { andrewm@0: this->insert(return_type(1, newSample), timestamp); andrewm@0: } andrewm@0: else { andrewm@0: // Get the last point (both sample count and its accumulated value) andrewm@0: return_type previousAccum = this->latest(); andrewm@0: andrewm@0: // Add the current sample andrewm@0: DataType accumulatedValue = newSample + previousAccum.second; andrewm@0: int numPoints = previousAccum.first; andrewm@0: andrewm@0: // If necessary, subtract off the oldest sample, which by the size of samples_ andrewm@0: // is guaranteed to be its first point. andrewm@0: if(samples_.full()) andrewm@0: accumulatedValue -= samples_.front(); andrewm@0: else andrewm@0: numPoints++; andrewm@0: andrewm@0: this->insert(return_type(numPoints, accumulatedValue), timestamp); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: // Reset the integral to a given value at a given sample. All samples andrewm@0: // after this one are marked "missing" to force a recalculation of the integral next time andrewm@0: // the value is requested. andrewm@0: andrewm@0: /*void reset(size_type index) { andrewm@0: if(index < this->beginIndex() || index >= this->endIndex()) andrewm@0: return; andrewm@0: this->rawValueAt(index) = std::pair(0,DataType()); andrewm@0: size_type i = index + 1; andrewm@0: while(i < this->endIndex()) andrewm@0: this->rawValueAt(index) = missing_value >::missing(); andrewm@0: }*/ andrewm@0: andrewm@0: private: andrewm@0: Node& input_; andrewm@0: andrewm@0: // Buffer holding the individual samples. We need to be able to drop the last sample out of the andrewm@0: // accumulated buffer, and including our own sample buffer means we don't need to rely on the andrewm@0: // length of the input to store old samples. andrewm@0: boost::circular_buffer samples_; andrewm@0: }; andrewm@0: andrewm@0: andrewm@0: #endif /* KEYCONTROL_ACCUMULATOR_H */