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 */