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 Accumulator.h: template class that accumulates (adds) samples coming into
|
andrewm@0
|
21 a given Node.
|
andrewm@0
|
22 */
|
andrewm@0
|
23
|
andrewm@0
|
24 #ifndef KEYCONTROL_ACCUMULATOR_H
|
andrewm@0
|
25 #define KEYCONTROL_ACCUMULATOR_H
|
andrewm@0
|
26
|
andrewm@0
|
27 #include <iostream>
|
andrewm@0
|
28 #include <exception>
|
andrewm@0
|
29 #include "Node.h"
|
andrewm@0
|
30
|
andrewm@0
|
31 /*
|
andrewm@0
|
32 * Accumulator
|
andrewm@0
|
33 *
|
andrewm@0
|
34 * Calculate the running sum of the last N points of a signal. Unlike Integral, it does not
|
andrewm@0
|
35 * take into account the timestamps, and so is signficantly faster but less flexible.
|
andrewm@0
|
36 *
|
andrewm@0
|
37 * The output type of this class is a pair, the first of which indicates how many samples are
|
andrewm@0
|
38 * included in the accumulated result, and the second of which is the result itself. This handles
|
andrewm@0
|
39 * transient startup conditions where all N samples are not yet available.
|
andrewm@0
|
40 *
|
andrewm@0
|
41 */
|
andrewm@0
|
42
|
andrewm@0
|
43 template<typename DataType, int N>
|
andrewm@0
|
44 class Accumulator : public Node<std::pair<int, DataType> > {
|
andrewm@0
|
45 public:
|
andrewm@0
|
46 typedef typename std::pair<int, DataType> return_type;
|
andrewm@0
|
47 typedef typename Node<return_type>::capacity_type capacity_type;
|
andrewm@0
|
48 //typedef typename Node<return_type>::size_type size_type;
|
andrewm@0
|
49
|
andrewm@0
|
50 // ***** Constructors *****
|
andrewm@0
|
51
|
andrewm@0
|
52 Accumulator(capacity_type capacity, Node<DataType>& input) : Node<return_type>(capacity), input_(input), samples_(N+1) {
|
andrewm@0
|
53 if(capacity <= N) // Need to have at least N points in history to accumulate
|
andrewm@0
|
54 throw new std::bad_alloc();
|
andrewm@0
|
55 //std::cout << "Registering Accumulator\n";
|
andrewm@0
|
56 this->registerForTrigger(&input_);
|
andrewm@0
|
57 //std::cout << "Accumulator: this_source = " << (TriggerSource*)this << "this_dest = " << (TriggerDestination*)this << " input_ = " << &input_ << std::endl;
|
andrewm@0
|
58 }
|
andrewm@0
|
59
|
andrewm@0
|
60 // Copy constructor
|
andrewm@0
|
61 Accumulator(Accumulator<DataType,N> const& obj) : Node<return_type>(obj), input_(obj.input_), samples_(obj.samples_) {
|
andrewm@0
|
62 this->registerForTrigger(&input_);
|
andrewm@0
|
63 }
|
andrewm@0
|
64
|
andrewm@0
|
65 // ***** Modifiers *****
|
andrewm@0
|
66 //
|
andrewm@0
|
67 // Override this method to clear the samples_ buffer
|
andrewm@0
|
68
|
andrewm@0
|
69 void clear() {
|
andrewm@0
|
70 Node<std::pair<int, DataType> >::clear();
|
andrewm@0
|
71 samples_.clear();
|
andrewm@0
|
72 }
|
andrewm@0
|
73
|
andrewm@0
|
74 // ***** Evaluator *****
|
andrewm@0
|
75 //
|
andrewm@0
|
76 // This is called when the input gets a new data point. Accumulate its value and store it in our buffer.
|
andrewm@0
|
77 // Storing it will also cause a trigger to be sent to anyone who's listening.
|
andrewm@0
|
78
|
andrewm@0
|
79 void triggerReceived(TriggerSource* who, timestamp_type timestamp) {
|
andrewm@0
|
80 //std::cout << "Accumulator::triggerReceived\n";
|
andrewm@0
|
81
|
andrewm@0
|
82 if(who != &input_)
|
andrewm@0
|
83 return;
|
andrewm@0
|
84
|
andrewm@0
|
85 //std::cout << "Accumulator::triggerReceived2\n";
|
andrewm@0
|
86
|
andrewm@0
|
87 DataType newSample = input_.latest();
|
andrewm@0
|
88 samples_.push_back(newSample);
|
andrewm@0
|
89
|
andrewm@0
|
90 if(this->empty()) {
|
andrewm@0
|
91 this->insert(return_type(1, newSample), timestamp);
|
andrewm@0
|
92 }
|
andrewm@0
|
93 else {
|
andrewm@0
|
94 // Get the last point (both sample count and its accumulated value)
|
andrewm@0
|
95 return_type previousAccum = this->latest();
|
andrewm@0
|
96
|
andrewm@0
|
97 // Add the current sample
|
andrewm@0
|
98 DataType accumulatedValue = newSample + previousAccum.second;
|
andrewm@0
|
99 int numPoints = previousAccum.first;
|
andrewm@0
|
100
|
andrewm@0
|
101 // If necessary, subtract off the oldest sample, which by the size of samples_
|
andrewm@0
|
102 // is guaranteed to be its first point.
|
andrewm@0
|
103 if(samples_.full())
|
andrewm@0
|
104 accumulatedValue -= samples_.front();
|
andrewm@0
|
105 else
|
andrewm@0
|
106 numPoints++;
|
andrewm@0
|
107
|
andrewm@0
|
108 this->insert(return_type(numPoints, accumulatedValue), timestamp);
|
andrewm@0
|
109 }
|
andrewm@0
|
110 }
|
andrewm@0
|
111
|
andrewm@0
|
112 // Reset the integral to a given value at a given sample. All samples
|
andrewm@0
|
113 // after this one are marked "missing" to force a recalculation of the integral next time
|
andrewm@0
|
114 // the value is requested.
|
andrewm@0
|
115
|
andrewm@0
|
116 /*void reset(size_type index) {
|
andrewm@0
|
117 if(index < this->beginIndex() || index >= this->endIndex())
|
andrewm@0
|
118 return;
|
andrewm@0
|
119 this->rawValueAt(index) = std::pair<int, DataType>(0,DataType());
|
andrewm@0
|
120 size_type i = index + 1;
|
andrewm@0
|
121 while(i < this->endIndex())
|
andrewm@0
|
122 this->rawValueAt(index) = missing_value<std::pair<int, DataType> >::missing();
|
andrewm@0
|
123 }*/
|
andrewm@0
|
124
|
andrewm@0
|
125 private:
|
andrewm@0
|
126 Node<DataType>& input_;
|
andrewm@0
|
127
|
andrewm@0
|
128 // Buffer holding the individual samples. We need to be able to drop the last sample out of the
|
andrewm@0
|
129 // accumulated buffer, and including our own sample buffer means we don't need to rely on the
|
andrewm@0
|
130 // length of the input to store old samples.
|
andrewm@0
|
131 boost::circular_buffer<DataType> samples_;
|
andrewm@0
|
132 };
|
andrewm@0
|
133
|
andrewm@0
|
134
|
andrewm@0
|
135 #endif /* KEYCONTROL_ACCUMULATOR_H */ |