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 Node.h: template class which wraps a circular buffer, providing a collection
|
andrewm@0
|
21 of methods for inserting, accessing and iterating over data (including
|
andrewm@0
|
22 interpolation). Also provides trigger functionality to notify listeners when
|
andrewm@0
|
23 new objects are inserted. Unlike conventional containers, indices always increment
|
andrewm@0
|
24 over time, even though only the last N objects will be held in the buffer. The
|
andrewm@0
|
25 beginning index will therefore be greater than 0 in many cases, but the index of
|
andrewm@0
|
26 a particular item will never change, even if the item itself eventually is dropped
|
andrewm@0
|
27 from the buffer.
|
andrewm@0
|
28 */
|
andrewm@0
|
29
|
andrewm@0
|
30 #ifndef KEYCONTROL_NODE_H
|
andrewm@0
|
31 #define KEYCONTROL_NODE_H
|
andrewm@0
|
32
|
andrewm@0
|
33 #include <iostream>
|
andrewm@0
|
34 #include <set>
|
andrewm@20
|
35 #include <cmath>
|
andrewm@20
|
36 #include <stdint.h>
|
andrewm@0
|
37 #include <boost/circular_buffer.hpp>
|
andrewm@0
|
38 #include <boost/lambda/lambda.hpp>
|
andrewm@0
|
39 #include "Types.h"
|
andrewm@0
|
40 #include "Trigger.h"
|
andrewm@20
|
41 #include "../JuceLibraryCode/JuceHeader.h"
|
andrewm@20
|
42
|
andrewm@0
|
43
|
andrewm@0
|
44 template<typename OutputType> class NodeNonInterpolating;
|
andrewm@0
|
45 template<typename OutputType> class Node;
|
andrewm@0
|
46
|
andrewm@0
|
47 /*
|
andrewm@0
|
48 * NodeIterator
|
andrewm@0
|
49 *
|
andrewm@0
|
50 * Abstract class that implements common functionality for all types of Sources and Filters
|
andrewm@0
|
51 * Modeled on (and contains) boost::cb_details::iterator
|
andrewm@0
|
52 * See boost/circular_buffer/details.hpp for more information
|
andrewm@0
|
53 *
|
andrewm@0
|
54 */
|
andrewm@0
|
55
|
andrewm@0
|
56 // Custom iterator type to move through the Node buffer
|
andrewm@0
|
57 template <class OutputType, class Traits, class NonConstTraits>
|
andrewm@0
|
58 struct NodeIterator :
|
andrewm@0
|
59 public boost::iterator<
|
andrewm@0
|
60 std::random_access_iterator_tag,
|
andrewm@0
|
61 typename Traits::value_type,
|
andrewm@0
|
62 typename Traits::difference_type,
|
andrewm@0
|
63 typename Traits::pointer,
|
andrewm@0
|
64 typename Traits::reference>
|
andrewm@0
|
65 {
|
andrewm@0
|
66 typedef boost::iterator<
|
andrewm@0
|
67 std::random_access_iterator_tag,
|
andrewm@0
|
68 typename Traits::value_type,
|
andrewm@0
|
69 typename Traits::difference_type,
|
andrewm@0
|
70 typename Traits::pointer,
|
andrewm@0
|
71 typename Traits::reference> base_iterator;
|
andrewm@0
|
72
|
andrewm@0
|
73 typedef NodeNonInterpolating<OutputType> Buff;
|
andrewm@0
|
74
|
andrewm@0
|
75 typedef NodeIterator<Buff, typename Traits::nonconst_self, NonConstTraits> nonconst_self;
|
andrewm@0
|
76 typedef typename boost::cb_details::iterator<boost::circular_buffer<OutputType>, NonConstTraits> cb_iterator;
|
andrewm@0
|
77
|
andrewm@0
|
78 typedef typename base_iterator::value_type value_type;
|
andrewm@0
|
79 typedef typename base_iterator::pointer pointer;
|
andrewm@0
|
80 typedef typename base_iterator::reference reference;
|
andrewm@0
|
81 typedef typename Traits::size_type size_type;
|
andrewm@0
|
82 typedef typename base_iterator::difference_type difference_type;
|
andrewm@0
|
83
|
andrewm@0
|
84 // ***** Member Variables *****
|
andrewm@0
|
85
|
andrewm@0
|
86 // Pointer to the Node object
|
andrewm@0
|
87 Buff* m_buff;
|
andrewm@0
|
88
|
andrewm@0
|
89 // Base (non-const) iterator to the circular buffer
|
andrewm@0
|
90 cb_iterator m_cb_it;
|
andrewm@0
|
91
|
andrewm@0
|
92 // ***** Constructors *****
|
andrewm@0
|
93
|
andrewm@0
|
94 // Default constructor
|
andrewm@0
|
95 NodeIterator() : m_buff(0), m_cb_it(cb_iterator()) {}
|
andrewm@0
|
96
|
andrewm@0
|
97 // Copy constructor
|
andrewm@0
|
98 NodeIterator(const nonconst_self& it) : m_cb_it(it.m_cb_it) {}
|
andrewm@0
|
99
|
andrewm@0
|
100 // Constructor based on a circular_buffer iterator
|
andrewm@0
|
101 NodeIterator(Buff* cb, const cb_iterator cb_it) : m_buff(cb), m_cb_it(cb_it) {}
|
andrewm@0
|
102
|
andrewm@0
|
103 // ***** Operators *****
|
andrewm@0
|
104 //
|
andrewm@0
|
105 // Modeled on boost::cb_details::iterator (boost/circular_buffer/details.hpp)
|
andrewm@0
|
106
|
andrewm@0
|
107 NodeIterator& operator = (const NodeIterator& it) {
|
andrewm@0
|
108 if (this == &it)
|
andrewm@0
|
109 return *this;
|
andrewm@0
|
110 m_buff = it.m_buff;
|
andrewm@0
|
111 m_cb_it = it.m_cb_it;
|
andrewm@0
|
112 return *this;
|
andrewm@0
|
113 }
|
andrewm@0
|
114
|
andrewm@0
|
115 // Dereferencing operator. We change the behavior here to evaluate missing values as needed.
|
andrewm@0
|
116 // Note that this requires m_cb_it to be a non_const type, even if we are a const iterator.
|
andrewm@0
|
117
|
andrewm@0
|
118 reference operator * () const {
|
andrewm@0
|
119 return *m_cb_it;
|
andrewm@0
|
120 //reference val = *m_cb_it;
|
andrewm@0
|
121 //if(!missing_value<OutputType>::isMissing(val))
|
andrewm@0
|
122 // return val;
|
andrewm@0
|
123 //return (*m_cb_it = m_buff->evaluate(index()));
|
andrewm@0
|
124 }
|
andrewm@0
|
125
|
andrewm@0
|
126 pointer operator -> () const { return &(operator*()); }
|
andrewm@0
|
127
|
andrewm@0
|
128 template <class Traits0, class Traits1>
|
andrewm@0
|
129 difference_type operator - (const NodeIterator<OutputType, Traits0, Traits1>& it) const { return m_cb_it - it.m_cb_it; }
|
andrewm@0
|
130
|
andrewm@0
|
131 NodeIterator& operator ++ () { // ++it
|
andrewm@0
|
132 ++m_cb_it;
|
andrewm@0
|
133 return *this;
|
andrewm@0
|
134 }
|
andrewm@0
|
135 NodeIterator operator ++ (int) { // it++
|
andrewm@0
|
136 NodeIterator<OutputType, Traits, NonConstTraits> tmp = *this;
|
andrewm@0
|
137 ++m_cb_it;
|
andrewm@0
|
138 return tmp;
|
andrewm@0
|
139 }
|
andrewm@0
|
140 NodeIterator& operator -- () { // --it
|
andrewm@0
|
141 --m_cb_it;
|
andrewm@0
|
142 return *this;
|
andrewm@0
|
143 }
|
andrewm@0
|
144 NodeIterator operator -- (int) { // it--
|
andrewm@0
|
145 NodeIterator<OutputType, Traits, NonConstTraits> tmp = *this;
|
andrewm@0
|
146 m_cb_it--;
|
andrewm@0
|
147 return tmp;
|
andrewm@0
|
148 }
|
andrewm@0
|
149 NodeIterator& operator += (difference_type n) { // it += n
|
andrewm@0
|
150 m_cb_it += n;
|
andrewm@0
|
151 return *this;
|
andrewm@0
|
152 }
|
andrewm@0
|
153 NodeIterator& operator -= (difference_type n) { // it -= n
|
andrewm@0
|
154 m_cb_it -= n;
|
andrewm@0
|
155 return *this;
|
andrewm@0
|
156 }
|
andrewm@0
|
157
|
andrewm@0
|
158 NodeIterator operator + (difference_type n) const { return NodeIterator<OutputType, Traits, NonConstTraits>(*this) += n; }
|
andrewm@0
|
159 NodeIterator operator - (difference_type n) const { return NodeIterator<OutputType, Traits, NonConstTraits>(*this) -= n; }
|
andrewm@0
|
160
|
andrewm@0
|
161 reference operator [] (difference_type n) const { return *(*this + n); }
|
andrewm@0
|
162
|
andrewm@0
|
163 // ***** Comparisons *****
|
andrewm@0
|
164 //
|
andrewm@0
|
165 // This iterator class implements some unusual comparison behavior: two iterators are evaluated by their offset within
|
andrewm@0
|
166 // their respective buffers, even if they point to separate buffers. When used on synchronized buffers, this allows
|
andrewm@0
|
167 // us to evaluate which of two iterators points to the earlier event.
|
andrewm@0
|
168
|
andrewm@0
|
169 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
170 bool operator == (const NodeIterator<OutputType0, Traits0, Traits1>& it) const {
|
andrewm@0
|
171 return index() == it.index();
|
andrewm@0
|
172 }
|
andrewm@0
|
173
|
andrewm@0
|
174 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
175 bool operator != (const NodeIterator<OutputType0, Traits0, Traits1>& it) const {
|
andrewm@0
|
176 return index() != it.index();
|
andrewm@0
|
177 }
|
andrewm@0
|
178
|
andrewm@0
|
179 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
180 bool operator < (const NodeIterator<OutputType0, Traits0, Traits1>& it) const {
|
andrewm@0
|
181 return index() < it.index();
|
andrewm@0
|
182 }
|
andrewm@0
|
183
|
andrewm@0
|
184 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
185 bool operator > (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return it < *this; }
|
andrewm@0
|
186
|
andrewm@0
|
187 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
188 bool operator <= (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return !(it < *this); }
|
andrewm@0
|
189
|
andrewm@0
|
190 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
191 bool operator >= (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return !(*this < it); }
|
andrewm@0
|
192
|
andrewm@0
|
193 // ***** Special Methods *****
|
andrewm@0
|
194
|
andrewm@0
|
195 // Return the offset within the buffer for this iterator's current location
|
andrewm@0
|
196 // Can be used with at() or operator[], and can be used to compare relative locations
|
andrewm@0
|
197 // of two iterators, even if they don't refer to the same buffer
|
andrewm@0
|
198
|
andrewm@0
|
199 size_type index() const {
|
andrewm@0
|
200 return (size_type)(*this - m_buff->begin() + m_buff->firstSampleIndex_);
|
andrewm@0
|
201 }
|
andrewm@0
|
202
|
andrewm@0
|
203 // Return the timestamp associated with the sample this iterator points to
|
andrewm@0
|
204
|
andrewm@0
|
205 timestamp_type timestamp() const { return m_buff->timestampAt(index()); }
|
andrewm@0
|
206
|
andrewm@0
|
207 // Tells us whether the index points to a valid place in the buffer.
|
andrewm@0
|
208 // TODO: make sure this is right
|
andrewm@0
|
209 // bool isValid() const { return (index() >= m_buff->beginIndex() && index() <= (m_buff->endIndex() - 1)); }
|
andrewm@0
|
210 };
|
andrewm@0
|
211
|
andrewm@0
|
212 /*
|
andrewm@0
|
213 * NodeReverseIterator
|
andrewm@0
|
214 *
|
andrewm@0
|
215 * Adapter for reverse iterators on Node classes, preserving some of the
|
andrewm@0
|
216 * special behavior the iterators implement.
|
andrewm@0
|
217 */
|
andrewm@0
|
218
|
andrewm@0
|
219 template <typename T>
|
andrewm@0
|
220 struct NodeReverseIterator : public boost::reverse_iterator<T> {
|
andrewm@0
|
221 NodeReverseIterator() {}
|
andrewm@0
|
222 explicit NodeReverseIterator(T baseIt) : boost::reverse_iterator<T>(baseIt) {}
|
andrewm@0
|
223
|
andrewm@0
|
224 typedef typename boost::reverse_iterator<T>::base_type::size_type size_type;
|
andrewm@0
|
225
|
andrewm@0
|
226 size_type index() const { return boost::prior(this->base_reference()).index(); }
|
andrewm@0
|
227 timestamp_type timestamp() const { return boost::prior(this->base_reference()).timestamp(); }
|
andrewm@0
|
228 };
|
andrewm@0
|
229
|
andrewm@0
|
230 /*
|
andrewm@0
|
231 * NodeInterpolatedIterator
|
andrewm@0
|
232 *
|
andrewm@0
|
233 * Extends the iterator concept to fractional indices, using linear interpolation to return
|
andrewm@0
|
234 * values and timestamps. This is always a const iterator class.
|
andrewm@0
|
235 */
|
andrewm@0
|
236
|
andrewm@0
|
237 template<typename OutputType, typename Traits>
|
andrewm@0
|
238 struct NodeInterpolatedIterator :
|
andrewm@0
|
239 public boost::iterator<
|
andrewm@0
|
240 std::random_access_iterator_tag,
|
andrewm@0
|
241 typename Traits::value_type,
|
andrewm@0
|
242 typename Traits::difference_type,
|
andrewm@0
|
243 typename Traits::pointer,
|
andrewm@0
|
244 typename Traits::reference>
|
andrewm@0
|
245 {
|
andrewm@0
|
246 typedef NodeInterpolatedIterator<OutputType,Traits> self_type;
|
andrewm@0
|
247
|
andrewm@0
|
248 typedef boost::iterator<
|
andrewm@0
|
249 std::random_access_iterator_tag,
|
andrewm@0
|
250 typename Traits::value_type,
|
andrewm@0
|
251 typename Traits::difference_type,
|
andrewm@0
|
252 typename Traits::pointer,
|
andrewm@0
|
253 typename Traits::reference> base_iterator;
|
andrewm@0
|
254
|
andrewm@0
|
255 typedef typename Traits::size_type size_type;
|
andrewm@0
|
256 typedef typename base_iterator::value_type value_type;
|
andrewm@0
|
257 typedef typename base_iterator::pointer pointer;
|
andrewm@0
|
258 typedef typename base_iterator::reference reference;
|
andrewm@0
|
259
|
andrewm@0
|
260 // ***** Member Variables *****
|
andrewm@0
|
261
|
andrewm@0
|
262 // Reference to the buffer this iterator indexes
|
andrewm@0
|
263 Node<OutputType>* m_buff;
|
andrewm@0
|
264
|
andrewm@0
|
265 // Index location within the buffer
|
andrewm@0
|
266 double m_index;
|
andrewm@0
|
267
|
andrewm@0
|
268 // Step size for ++ and similar operators
|
andrewm@0
|
269 double m_step;
|
andrewm@0
|
270
|
andrewm@0
|
271 // ***** Constructors *****
|
andrewm@0
|
272
|
andrewm@0
|
273 // Default (empty) constructor
|
andrewm@0
|
274 NodeInterpolatedIterator() : m_buff(0), m_index(0.0), m_step(1.0) {}
|
andrewm@0
|
275
|
andrewm@0
|
276 // Constructor that should be used primarily by the Node class itself
|
andrewm@0
|
277 NodeInterpolatedIterator(Node<OutputType>* buff, double index, double stepSize)
|
andrewm@0
|
278 : m_buff(buff), m_index(index), m_step(stepSize) {}
|
andrewm@0
|
279
|
andrewm@0
|
280 // Copy constructor
|
andrewm@0
|
281 NodeInterpolatedIterator(const self_type& obj) : m_buff(obj.m_buff), m_index(obj.m_index), m_step(obj.m_step) {}
|
andrewm@0
|
282
|
andrewm@0
|
283 // ***** Operators *****
|
andrewm@0
|
284 //
|
andrewm@0
|
285 // Modeled on STL iterators, but using fractional indices. This class should be considered a sort of random access,
|
andrewm@0
|
286 // bidirectional iterator.
|
andrewm@0
|
287
|
andrewm@0
|
288 NodeInterpolatedIterator& operator = (const self_type& it) {
|
andrewm@0
|
289 if (this == &it)
|
andrewm@0
|
290 return *this;
|
andrewm@0
|
291 m_buff = it.m_buff;
|
andrewm@0
|
292 m_index = it.m_index;
|
andrewm@0
|
293 m_step = it.m_step;
|
andrewm@0
|
294 return *this;
|
andrewm@0
|
295 }
|
andrewm@0
|
296
|
andrewm@0
|
297 // Dereferencing operators. Like the non-interpolated version of this iterator, it will calculate the values as needed.
|
andrewm@0
|
298 // This happens within the operator[] method of Node, rather than in this class itself.
|
andrewm@0
|
299
|
andrewm@0
|
300 value_type operator * () const { return m_buff->interpolate(m_index); }
|
andrewm@0
|
301 pointer operator -> () const { return &(operator*()); }
|
andrewm@0
|
302
|
andrewm@0
|
303 // Difference of two iterators (return the difference in indices)
|
andrewm@0
|
304 double operator - (const self_type& it) const { return m_index - it.m_index; }
|
andrewm@0
|
305
|
andrewm@0
|
306 // Increment and decrement are typically integer operators. We define their behavior here to change the index by a predetermined
|
andrewm@0
|
307 // step size, set at construction but changeable.
|
andrewm@0
|
308
|
andrewm@0
|
309 self_type& operator ++ () { // ++it
|
andrewm@0
|
310 m_index += m_step;
|
andrewm@0
|
311 return *this;
|
andrewm@0
|
312 }
|
andrewm@0
|
313 self_type operator ++ (int) { // it++
|
andrewm@0
|
314 self_type tmp = *this;
|
andrewm@0
|
315 m_index += m_step;
|
andrewm@0
|
316 return tmp;
|
andrewm@0
|
317 }
|
andrewm@0
|
318 self_type& operator -- () { // --it
|
andrewm@0
|
319 m_index -= m_step;
|
andrewm@0
|
320 return *this;
|
andrewm@0
|
321 }
|
andrewm@0
|
322 self_type operator -- (int) { // it--
|
andrewm@0
|
323 self_type tmp = *this;
|
andrewm@0
|
324 m_index -= m_step;
|
andrewm@0
|
325 return tmp;
|
andrewm@0
|
326 }
|
andrewm@0
|
327
|
andrewm@0
|
328 // These methods change the iterator location by a fractional amount. Notice that this is NOT scaled by m_step.
|
andrewm@0
|
329 self_type& operator += (double n) { // it += n
|
andrewm@0
|
330 m_index += n;
|
andrewm@0
|
331 return *this;
|
andrewm@0
|
332 }
|
andrewm@0
|
333 self_type& operator -= (double n) { // it -= n
|
andrewm@0
|
334 m_index -= n;
|
andrewm@0
|
335 return *this;
|
andrewm@0
|
336 }
|
andrewm@0
|
337
|
andrewm@0
|
338 self_type operator + (double n) const { return NodeInterpolatedIterator<OutputType,Traits>(*this) += n; }
|
andrewm@0
|
339 self_type operator - (double n) const { return NodeInterpolatedIterator<OutputType,Traits>(*this) -= n; }
|
andrewm@0
|
340
|
andrewm@0
|
341 reference operator [] (double n) const { return *(*this + n); }
|
andrewm@0
|
342
|
andrewm@0
|
343
|
andrewm@0
|
344 // ***** Comparisons *****
|
andrewm@0
|
345 //
|
andrewm@0
|
346 // The comparison behavior is the same as for NodeIterator: even if two iterators point to different buffers,
|
andrewm@0
|
347 // they can be compared on the basis of the indices. Of course, this is only meaningful if the two buffers are synchronized
|
andrewm@0
|
348 // in time.
|
andrewm@0
|
349
|
andrewm@0
|
350 template<class OutputType0, class Traits0>
|
andrewm@0
|
351 bool operator == (const NodeInterpolatedIterator<OutputType0,Traits0>& it) const { return m_index == it.m_index; }
|
andrewm@0
|
352
|
andrewm@0
|
353 template<class OutputType0, class Traits0>
|
andrewm@0
|
354 bool operator != (const NodeInterpolatedIterator<OutputType0, Traits0>& it) const { return m_index != it.m_index; }
|
andrewm@0
|
355
|
andrewm@0
|
356 template<class OutputType0, class Traits0>
|
andrewm@0
|
357 bool operator < (const NodeInterpolatedIterator<OutputType0, Traits0>& it) const { return m_index < it.m_index; }
|
andrewm@0
|
358
|
andrewm@0
|
359 template<class OutputType0, class Traits0>
|
andrewm@0
|
360 bool operator > (const NodeInterpolatedIterator<OutputType0, Traits0>& it) const { return m_index > it.m_index; }
|
andrewm@0
|
361
|
andrewm@0
|
362 template<class OutputType0, class Traits0>
|
andrewm@0
|
363 bool operator <= (const NodeInterpolatedIterator<OutputType0, Traits0>& it) const { return !(it < *this); }
|
andrewm@0
|
364
|
andrewm@0
|
365 template<class OutputType0, class Traits0>
|
andrewm@0
|
366 bool operator >= (const NodeInterpolatedIterator<OutputType0, Traits0>& it) const { return !(*this < it); }
|
andrewm@0
|
367
|
andrewm@0
|
368 // We can also compare interpolated and non-interpolated iterators.
|
andrewm@0
|
369
|
andrewm@0
|
370 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
371 bool operator == (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index == (double)it.index(); }
|
andrewm@0
|
372
|
andrewm@0
|
373 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
374 bool operator != (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index != (double)it.index(); }
|
andrewm@0
|
375
|
andrewm@0
|
376 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
377 bool operator < (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index < (double)it.index(); }
|
andrewm@0
|
378
|
andrewm@0
|
379 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
380 bool operator > (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index > (double)it.index(); }
|
andrewm@0
|
381
|
andrewm@0
|
382 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
383 bool operator <= (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index <= (double)it.index(); }
|
andrewm@0
|
384
|
andrewm@0
|
385 template <class OutputType0, class Traits0, class Traits1>
|
andrewm@0
|
386 bool operator >= (const NodeIterator<OutputType0, Traits0, Traits1>& it) const { return m_index >= (double)it.index(); }
|
andrewm@0
|
387
|
andrewm@0
|
388 // ***** Special Methods *****
|
andrewm@0
|
389
|
andrewm@0
|
390 // Round a fractional index up, down, or to the nearest integer
|
andrewm@0
|
391
|
andrewm@0
|
392 self_type& roundDown() {
|
andrewm@0
|
393 m_index = floor(m_index);
|
andrewm@0
|
394 return *this;
|
andrewm@0
|
395 }
|
andrewm@0
|
396 self_type& roundUp() {
|
andrewm@0
|
397 m_index = ceil(m_index);
|
andrewm@0
|
398 return *this;
|
andrewm@0
|
399 }
|
andrewm@0
|
400 self_type& round() {
|
andrewm@0
|
401 m_index = floor(m_index + 0.5);
|
andrewm@0
|
402 return *this;
|
andrewm@0
|
403 }
|
andrewm@0
|
404
|
andrewm@0
|
405 // Increment the iterator by a difference in time rather than a difference in index. This is less efficient to
|
andrewm@0
|
406 // compute, but can be useful for buffers whose samples are not regularly spaced in time.
|
andrewm@0
|
407
|
andrewm@0
|
408 self_type& incrementTime(timestamp_diff_type ts) {
|
andrewm@0
|
409 if(ts > 0) {
|
andrewm@0
|
410 size_type afterIndex = (size_type)ceil(m_index);
|
andrewm@0
|
411 if(afterIndex >= m_buff->endIndex()) {
|
andrewm@0
|
412 m_index = (double)m_buff->endIndex();
|
andrewm@0
|
413 return *this;
|
andrewm@0
|
414 }
|
andrewm@0
|
415 timestamp_type target = timestamp() + ts;
|
andrewm@0
|
416 timestamp_type after = m_buff->timestampAt(afterIndex);
|
andrewm@0
|
417
|
andrewm@0
|
418 // First of all, find the first integer index with a timestamp greater than our new target time.
|
andrewm@0
|
419 while(after < target) {
|
andrewm@0
|
420 afterIndex++;
|
andrewm@0
|
421 if(afterIndex >= m_buff->endIndex()) {
|
andrewm@0
|
422 m_index = (double)m_buff->endIndex();
|
andrewm@0
|
423 return *this;
|
andrewm@0
|
424 }
|
andrewm@0
|
425 after = m_buff->timestampAt(afterIndex);
|
andrewm@0
|
426 }
|
andrewm@0
|
427
|
andrewm@0
|
428 // Then find the timestamp immediately before that. We'll interpolate between these two to get
|
andrewm@0
|
429 // the adjusted index.
|
andrewm@0
|
430 timestamp_type before = m_buff->timestampAt(afterIndex-1);
|
andrewm@0
|
431 m_index = ((target - before) / (after - before)) + (double)(afterIndex - 1);
|
andrewm@0
|
432 }
|
andrewm@0
|
433 else if(ts < 0) {
|
andrewm@0
|
434 size_type beforeIndex = (size_type)floor(m_index);
|
andrewm@0
|
435 if(beforeIndex < m_buff->beginIndex()) {
|
andrewm@0
|
436 m_index = (double)m_buff->beginIndex()-1.0;
|
andrewm@0
|
437 return *this;
|
andrewm@0
|
438 }
|
andrewm@0
|
439 timestamp_type target = timestamp() + ts;
|
andrewm@0
|
440 timestamp_type before = m_buff->timestampAt(beforeIndex);
|
andrewm@0
|
441
|
andrewm@0
|
442 // Find the first integer index with a timestamp less than our new target time.
|
andrewm@0
|
443 while(before > target) {
|
andrewm@0
|
444 beforeIndex--;
|
andrewm@0
|
445 if(beforeIndex < m_buff->beginIndex()) {
|
andrewm@0
|
446 m_index = (double)m_buff->beginIndex()-1.0;
|
andrewm@0
|
447 return *this;
|
andrewm@0
|
448 }
|
andrewm@0
|
449 before = m_buff->timestampAt(beforeIndex);
|
andrewm@0
|
450 }
|
andrewm@0
|
451
|
andrewm@0
|
452 // Now find the timestamp immediately after that. Interpolated to get the adjusted index.
|
andrewm@0
|
453 timestamp_type after = m_buff->timestampAt(beforeIndex+1);
|
andrewm@0
|
454 m_index = ((target - before)/(after - before)) + (double)beforeIndex;
|
andrewm@0
|
455 }
|
andrewm@0
|
456 // if(ts == 0), do nothing
|
andrewm@0
|
457 return *this;
|
andrewm@0
|
458 }
|
andrewm@0
|
459
|
andrewm@0
|
460
|
andrewm@0
|
461 // Return (or change) the step size
|
andrewm@0
|
462 double& step() { return m_step; }
|
andrewm@0
|
463
|
andrewm@0
|
464 // Return the index within the buffer. The index is an always-increasing sample number (which means that as the
|
andrewm@0
|
465 // buffer fills, an index will continue to point to the same piece of data until that data is overwritten, at which
|
andrewm@0
|
466 // point that index is no longer valid for accessing the buffer at all.)
|
andrewm@0
|
467 double index() const { return m_index; }
|
andrewm@0
|
468
|
andrewm@0
|
469 // Return the timestamp for this particular location in the buffer (using linear interpolation).
|
andrewm@0
|
470 timestamp_type timestamp() const { return m_buff->interpolatedTimestampAt(m_index); }
|
andrewm@0
|
471
|
andrewm@0
|
472 // Tells us whether the index points to a valid place in the buffer.
|
andrewm@0
|
473 bool isValid() const { return (m_index >= m_buff->beginIndex() && m_index <= (m_buff->endIndex() - 1)); }
|
andrewm@0
|
474 };
|
andrewm@0
|
475
|
andrewm@0
|
476 /*
|
andrewm@0
|
477 * NodeBase
|
andrewm@0
|
478 *
|
andrewm@0
|
479 * Abstract class that implements common functionality for all Node data types.
|
andrewm@0
|
480 *
|
andrewm@0
|
481 */
|
andrewm@0
|
482
|
andrewm@0
|
483 class NodeBase : public TriggerSource, public TriggerDestination {
|
andrewm@0
|
484 public:
|
andrewm@0
|
485 typedef uint32_t size_type;
|
andrewm@0
|
486
|
andrewm@0
|
487 // ***** Constructors *****
|
andrewm@0
|
488
|
andrewm@0
|
489 // Default constructor
|
andrewm@0
|
490 NodeBase() {}
|
andrewm@0
|
491
|
andrewm@0
|
492 // Copy constructor: can't copy the mutexes
|
andrewm@0
|
493 NodeBase(NodeBase const& obj) {}
|
andrewm@0
|
494
|
andrewm@0
|
495 NodeBase& operator= (NodeBase const& obj) {
|
andrewm@0
|
496 //listeners_ = obj.listeners_;
|
andrewm@0
|
497 return *this;
|
andrewm@0
|
498 }
|
andrewm@0
|
499
|
andrewm@0
|
500 // ***** Destructor *****
|
andrewm@0
|
501
|
andrewm@0
|
502 virtual ~NodeBase() {
|
andrewm@0
|
503 //clearTriggerSources();
|
andrewm@0
|
504 }
|
andrewm@0
|
505
|
andrewm@0
|
506 // ***** Modifiers *****
|
andrewm@0
|
507
|
andrewm@0
|
508 virtual void clear() = 0;
|
andrewm@0
|
509 //virtual void insertMissing(timestamp_type timestamp) = 0;
|
andrewm@0
|
510
|
andrewm@0
|
511 // ***** Listener Methods *****
|
andrewm@0
|
512 //
|
andrewm@0
|
513 // We need to keep the buffers of all connected units in sync. That means any Source or Filter needs to know what its output
|
andrewm@0
|
514 // connects to. That functionality is accomplished by "listeners": any time a new sample is added to the buffer, the listeners
|
andrewm@0
|
515 // are notified with a corresponding timestamp. The latter is to ensure that a buffer does not respond to notifications from multiple
|
andrewm@0
|
516 // inputs for the same data point.
|
andrewm@0
|
517
|
andrewm@0
|
518 /*void registerListener(NodeBase* listener) {
|
andrewm@0
|
519 if(listener != 0) {
|
andrewm@0
|
520 listenerAccessMutex_.lock();
|
andrewm@0
|
521 listeners_.insert(listener);
|
andrewm@0
|
522 listenerAccessMutex_.unlock();
|
andrewm@0
|
523 }
|
andrewm@0
|
524 }
|
andrewm@0
|
525 void unregisterListener(NodeBase* listener) {
|
andrewm@0
|
526 listenerAccessMutex_.lock();
|
andrewm@0
|
527 listeners_.erase(listener);
|
andrewm@0
|
528 listenerAccessMutex_.unlock();
|
andrewm@0
|
529 }
|
andrewm@0
|
530 void clearListeners() {
|
andrewm@0
|
531 listenerAccessMutex_.lock();
|
andrewm@0
|
532 listeners_.clear();
|
andrewm@0
|
533 listenerAccessMutex_.unlock();
|
andrewm@0
|
534 }
|
andrewm@0
|
535 protected:
|
andrewm@0
|
536 // Tell all our listeners about a new data point with the given timestamp
|
andrewm@0
|
537 void notifyListenersOfInsert(timestamp_type timestamp) {
|
andrewm@0
|
538 std::set<NodeBase*>::iterator it;
|
andrewm@0
|
539
|
andrewm@0
|
540 listenerAccessMutex_.lock();
|
andrewm@0
|
541 for(it = listeners_.begin(); it != listeners_.end(); it++)
|
andrewm@0
|
542 (*it)->insertMissing(timestamp);
|
andrewm@0
|
543 listenerAccessMutex_.unlock();
|
andrewm@0
|
544 }
|
andrewm@0
|
545 // Tell all our listeners that the buffer has cleared
|
andrewm@0
|
546 void notifyListenersOfClear() {
|
andrewm@0
|
547 std::set<NodeBase*>::iterator it;
|
andrewm@0
|
548
|
andrewm@0
|
549 listenerAccessMutex_.lock();
|
andrewm@0
|
550 for(it = listeners_.begin(); it != listeners_.end(); it++)
|
andrewm@0
|
551 (*it)->clear();
|
andrewm@0
|
552 listenerAccessMutex_.unlock();
|
andrewm@0
|
553 }*/
|
andrewm@0
|
554
|
andrewm@0
|
555 public:
|
andrewm@0
|
556 // ***** Tree-Parsing Methods *****
|
andrewm@0
|
557 //
|
andrewm@0
|
558 // Sometimes we want to find out properties of the initial data source, regardless of what filters it's passed through. These virtual
|
andrewm@0
|
559 // methods should be implemented differently by Source and Filter classes.
|
andrewm@0
|
560
|
andrewm@0
|
561 //virtual SourceBase& source() = 0; // Return the source at the top of the tree for this unit
|
andrewm@0
|
562
|
andrewm@0
|
563 // ***** Mutex Methods *****
|
andrewm@0
|
564 //
|
andrewm@0
|
565 // These methods should be used to acquire a lock whenever a process wants to read values from the buffer. This would, for example,
|
andrewm@0
|
566 // allow iteration over the contents of the buffer without worrying that the contents will change in the course of the iteration.
|
andrewm@0
|
567
|
andrewm@0
|
568 void lock_mutex() { bufferAccessMutex_.enter(); }
|
andrewm@0
|
569 bool try_lock_mutex() { return bufferAccessMutex_.tryEnter(); }
|
andrewm@0
|
570 void unlock_mutex() { bufferAccessMutex_.exit(); }
|
andrewm@0
|
571
|
andrewm@0
|
572 // ***** Circular Buffer (STL) Methods *****
|
andrewm@0
|
573 //
|
andrewm@0
|
574 // Certain STL methods (and related queries) that do not depend on the specific data type of the buffer should
|
andrewm@0
|
575 // be available to any object with a NodeBase reference.
|
andrewm@0
|
576
|
andrewm@0
|
577 virtual size_type size() = 0; // Size: how many elements are currently in the buffer
|
andrewm@0
|
578 virtual bool empty() = 0;
|
andrewm@0
|
579 virtual bool full() = 0;
|
andrewm@0
|
580 virtual size_type reserve() = 0; // Reserve: how many elements are left before the buffer is full
|
andrewm@0
|
581 virtual size_type capacity() const = 0; // Capacity: how many elements could be in the buffer
|
andrewm@0
|
582
|
andrewm@0
|
583 virtual size_type beginIndex() = 0; // Index of the first sample we still have in the buffer
|
andrewm@0
|
584 virtual size_type endIndex() = 0; // Index just past the end of the buffer
|
andrewm@0
|
585
|
andrewm@0
|
586 // ***** Timestamp Methods *****
|
andrewm@0
|
587 //
|
andrewm@0
|
588 // Every sample is tagged with a timestamp. We don't necessarily need to know the type of the sample to retrieve its
|
andrewm@0
|
589 // associated timestamp. However, we DO need to know the type in order to return an iterator.
|
andrewm@0
|
590
|
andrewm@0
|
591 virtual timestamp_type timestampAt(size_type index) = 0;
|
andrewm@0
|
592 virtual timestamp_type latestTimestamp() = 0;
|
andrewm@0
|
593 virtual timestamp_type earliestTimestamp() = 0;
|
andrewm@0
|
594
|
andrewm@0
|
595 virtual size_type indexNearestTo(timestamp_type t) = 0;
|
andrewm@0
|
596 virtual size_type indexNearestBefore(timestamp_type t) = 0;
|
andrewm@0
|
597 virtual size_type indexNearestAfter(timestamp_type t) = 0;
|
andrewm@0
|
598
|
andrewm@0
|
599 // ***** Member Variables *****
|
andrewm@0
|
600 protected:
|
andrewm@0
|
601 // A collection of the units that are listening for updates on this unit.
|
andrewm@0
|
602 //std::set<NodeBase*> listeners_;
|
andrewm@0
|
603
|
andrewm@0
|
604 // This mutex protects access to the underlying buffer. It is locked every time a sample is written to the buffer,
|
andrewm@0
|
605 // and external systems reading values from the buffer should also acquire at least a shared lock.
|
andrewm@0
|
606 CriticalSection bufferAccessMutex_;
|
andrewm@0
|
607
|
andrewm@0
|
608 // This mutex protects the list of listeners. It prevents a listener from being added or removed while a notification
|
andrewm@0
|
609 // is in progress.
|
andrewm@0
|
610 //boost::mutex listenerAccessMutex_;
|
andrewm@0
|
611
|
andrewm@0
|
612 // Keep an internal registry of who we've asked to send us triggers. It's important to keep
|
andrewm@0
|
613 // a list of these so that when this object is destroyed, all triggers are automatically unregistered.
|
andrewm@0
|
614 //std::set<NodeBase*> triggerSources_;
|
andrewm@0
|
615
|
andrewm@0
|
616 // This list tracks the destinations we are *sending* triggers to (as opposed to the sources we're receiving from above)
|
andrewm@0
|
617 //std::set<NodeBase*> triggerDestinations_;
|
andrewm@0
|
618 };
|
andrewm@0
|
619
|
andrewm@0
|
620 /*
|
andrewm@0
|
621 * NodeNonInterpolating
|
andrewm@0
|
622 *
|
andrewm@0
|
623 * This class handles all functionality for a Node of a specific data type EXCEPT:
|
andrewm@0
|
624 * -- Interpolating accessors (for data types that support interpolation, use the more common Node subclass)
|
andrewm@0
|
625 * -- triggerReceived() which should be implemented by a specific subclass.
|
andrewm@0
|
626 */
|
andrewm@0
|
627
|
andrewm@0
|
628 template<typename OutputType>
|
andrewm@0
|
629 class NodeNonInterpolating : public NodeBase {
|
andrewm@0
|
630 public:
|
andrewm@0
|
631 // Useful type shorthands. See <boost/circular_buffer.hpp> for details.
|
andrewm@47
|
632 typedef typename boost::container::allocator_traits<std::allocator<OutputType> > Alloc;
|
andrewm@47
|
633
|
andrewm@0
|
634 typedef typename boost::circular_buffer<OutputType,Alloc>::value_type value_type;
|
andrewm@0
|
635 typedef typename boost::circular_buffer<OutputType,Alloc>::pointer pointer;
|
andrewm@0
|
636 typedef typename boost::circular_buffer<OutputType,Alloc>::const_pointer const_pointer;
|
andrewm@0
|
637 typedef typename boost::circular_buffer<OutputType,Alloc>::reference reference;
|
andrewm@0
|
638 typedef typename boost::circular_buffer<OutputType,Alloc>::const_reference const_reference;
|
andrewm@0
|
639 typedef typename boost::circular_buffer<OutputType,Alloc>::difference_type difference_type;
|
andrewm@0
|
640 //typedef typename boost::circular_buffer<OutputType,Alloc>::size_type size_type;
|
andrewm@0
|
641 typedef typename boost::circular_buffer<OutputType,Alloc>::capacity_type capacity_type;
|
andrewm@0
|
642 typedef typename boost::circular_buffer<OutputType,Alloc>::array_range array_range;
|
andrewm@0
|
643 typedef typename boost::circular_buffer<OutputType,Alloc>::const_array_range const_array_range;
|
andrewm@20
|
644 typedef typename boost::circular_buffer<OutputType,Alloc>::param_value_type return_value_type;
|
andrewm@0
|
645
|
andrewm@0
|
646 // We only support const iterators. (Modifying data in the buffer is restricted to only a few specialized instances.)
|
andrewm@0
|
647
|
andrewm@0
|
648 typedef NodeIterator<OutputType, boost::cb_details::const_traits<Alloc>,
|
andrewm@0
|
649 boost::cb_details::nonconst_traits<Alloc> > const_iterator;
|
andrewm@0
|
650 typedef const_iterator iterator;
|
andrewm@0
|
651 typedef NodeReverseIterator<const_iterator> const_reverse_iterator;
|
andrewm@0
|
652 typedef const_reverse_iterator reverse_iterator;
|
andrewm@0
|
653
|
andrewm@0
|
654 template<class O, class T, class NCT> friend struct NodeIterator;
|
andrewm@0
|
655
|
andrewm@0
|
656 // ***** Constructors *****
|
andrewm@0
|
657
|
andrewm@0
|
658 //Node() : buffer_(0), insertMissingLastTimestamp_(0), numSamples_(0), firstSampleIndex_(0) {}
|
andrewm@0
|
659
|
andrewm@0
|
660 // Recommended constructor: specify the capacity in samples
|
andrewm@0
|
661 explicit NodeNonInterpolating(capacity_type capacity) : insertMissingLastTimestamp_(0), buffer_(0), numSamples_(0), firstSampleIndex_(0) {
|
andrewm@0
|
662 buffer_ = new boost::circular_buffer<OutputType>(capacity);
|
andrewm@0
|
663 timestamps_ = new boost::circular_buffer<timestamp_type>(capacity);
|
andrewm@0
|
664 }
|
andrewm@0
|
665
|
andrewm@0
|
666 // Copy constructor
|
andrewm@0
|
667 NodeNonInterpolating(const NodeNonInterpolating<OutputType>& obj) : numSamples_(obj.numSamples_), firstSampleIndex_(obj.firstSampleIndex_) {
|
andrewm@0
|
668 if(obj.buffer_ != 0)
|
andrewm@0
|
669 this->buffer_ = new boost::circular_buffer<OutputType>(*obj.buffer_);
|
andrewm@0
|
670 else
|
andrewm@0
|
671 this->buffer_ = 0;
|
andrewm@0
|
672 if(obj.timestamps_ != 0)
|
andrewm@0
|
673 timestamps_ = new boost::circular_buffer<timestamp_type>(*obj.timestamps_);
|
andrewm@0
|
674 else
|
andrewm@0
|
675 this->timestamps_ = 0;
|
andrewm@0
|
676 }
|
andrewm@0
|
677
|
andrewm@0
|
678 // ***** Destructor *****
|
andrewm@0
|
679
|
andrewm@0
|
680 virtual ~NodeNonInterpolating() {
|
andrewm@0
|
681 delete buffer_;
|
andrewm@0
|
682 delete timestamps_;
|
andrewm@0
|
683 }
|
andrewm@0
|
684
|
andrewm@0
|
685 // ***** Circular Buffer (STL) Methods *****
|
andrewm@0
|
686 //
|
andrewm@0
|
687 // In general we support const methods accessing the contents of the buffer, but only in limited cases can the buffer
|
andrewm@0
|
688 // contents be modified. Source objects allow inserting objects into the buffer, but Filters require the buffer to contain
|
andrewm@0
|
689 // either the result of the evaluator function or a "missing" value.
|
andrewm@0
|
690
|
andrewm@0
|
691 // ***** Accessors *****
|
andrewm@0
|
692
|
andrewm@0
|
693 const_iterator begin() { return const_iterator(this, buffer_->begin()); }
|
andrewm@0
|
694 const_iterator end() { return const_iterator(this, buffer_->end()); }
|
andrewm@0
|
695 const_reverse_iterator rbegin() { return const_reverse_iterator(end()); }
|
andrewm@0
|
696 const_reverse_iterator rend() { return const_reverse_iterator(begin()); }
|
andrewm@0
|
697
|
andrewm@0
|
698 const_iterator iteratorAtIndex(size_type index) { return const_iterator(this, buffer_->begin()) + (index - firstSampleIndex_); }
|
andrewm@0
|
699 const_reverse_iterator riteratorAtIndex(size_type index) { return const_reverse_iterator(iteratorAtIndex(index+1)); }
|
andrewm@0
|
700
|
andrewm@0
|
701 return_value_type operator [] (size_type index) { return (*(this->buffer_))[index-this->firstSampleIndex_]; }
|
andrewm@0
|
702 return_value_type at(size_type index) { return this->buffer_->at(index-this->firstSampleIndex_); }
|
andrewm@0
|
703 return_value_type front() { return this->buffer_->front(); }
|
andrewm@0
|
704 return_value_type back() { return this->buffer_->back(); }
|
andrewm@0
|
705
|
andrewm@0
|
706 // Two more convenience methods to avoid confusion about what front and back mean!
|
andrewm@0
|
707 return_value_type earliest() { return this->buffer_->front(); }
|
andrewm@0
|
708 return_value_type latest() { return this->buffer_->back(); }
|
andrewm@0
|
709
|
andrewm@0
|
710 // In the following methods, check whether the value is missing and calculate it as necessary
|
andrewm@0
|
711 // These methods return a value_type (i.e. not a reference, can't be used to modify the buffer.)
|
andrewm@0
|
712 // However, they internally make use of modifying calls in order to update "missing" values.
|
andrewm@0
|
713
|
andrewm@0
|
714 /*return_value_type operator [] (size_type index) {
|
andrewm@0
|
715 return_value_type val = (*buffer_)[index-firstSampleIndex_];
|
andrewm@0
|
716 if(!missing_value<OutputType>::isMissing(val))
|
andrewm@0
|
717 return val;
|
andrewm@0
|
718 return ((*buffer_)[index-firstSampleIndex_] = evaluate(index));
|
andrewm@0
|
719 }
|
andrewm@0
|
720 return_value_type at(size_type index) {
|
andrewm@0
|
721 return_value_type val = buffer_->at(index-firstSampleIndex_);
|
andrewm@0
|
722 if(!missing_value<OutputType>::isMissing(val))
|
andrewm@0
|
723 return val;
|
andrewm@0
|
724 return (buffer_->at(index-firstSampleIndex_) = evaluate(index));
|
andrewm@0
|
725 }
|
andrewm@0
|
726 return_value_type front() {
|
andrewm@0
|
727 return_value_type val = buffer_->front();
|
andrewm@0
|
728 if(!missing_value<OutputType>::isMissing(val))
|
andrewm@0
|
729 return val;
|
andrewm@0
|
730 return (buffer_->front() = evaluate(firstSampleIndex_));
|
andrewm@0
|
731 }
|
andrewm@0
|
732 return_value_type back() {
|
andrewm@0
|
733 return_value_type val = buffer_->back();
|
andrewm@0
|
734 if(!missing_value<OutputType>::isMissing(val) && buffer_->size() > 0)
|
andrewm@0
|
735 return val;
|
andrewm@0
|
736 return (buffer_->back() = evaluate(buffer_->size() - 1 + firstSampleIndex_));
|
andrewm@0
|
737 }*/
|
andrewm@0
|
738
|
andrewm@0
|
739 size_type size() { return buffer_->size(); } // Size: how many elements are currently in the buffer
|
andrewm@0
|
740 bool empty() { return buffer_->empty(); }
|
andrewm@0
|
741 bool full() { return buffer_->full(); }
|
andrewm@0
|
742 size_type reserve() { return buffer_->reserve(); } // Reserve: how many elements are left before the buffer is full
|
andrewm@0
|
743 size_type capacity() const { return buffer_->capacity(); } // Capacity: how many elements could be in the buffer
|
andrewm@0
|
744
|
andrewm@0
|
745 size_type beginIndex() { return firstSampleIndex_; } // Index of the first sample we still have in the buffer
|
andrewm@0
|
746 size_type endIndex() { return firstSampleIndex_ + buffer_->size(); } // Index just past the end of the buffer
|
andrewm@0
|
747
|
andrewm@0
|
748 // ***** Modifiers *****
|
andrewm@0
|
749
|
andrewm@0
|
750 // Clear all stored samples and timestamps
|
andrewm@0
|
751 void clear() {
|
andrewm@0
|
752 bufferAccessMutex_.enter();
|
andrewm@0
|
753 timestamps_->clear();
|
andrewm@0
|
754 buffer_->clear();
|
andrewm@0
|
755 numSamples_ = firstSampleIndex_ = 0;
|
andrewm@0
|
756 bufferAccessMutex_.exit();
|
andrewm@0
|
757
|
andrewm@0
|
758 //notifyListenersOfClear();
|
andrewm@0
|
759 }
|
andrewm@0
|
760
|
andrewm@0
|
761 // Insert a new item into the buffer
|
andrewm@0
|
762 void insert(const OutputType& item, timestamp_type timestamp) {
|
andrewm@0
|
763 this->bufferAccessMutex_.enter();
|
andrewm@0
|
764 if(this->buffer_->full())
|
andrewm@0
|
765 this->firstSampleIndex_++;
|
andrewm@0
|
766 this->timestamps_->push_back(timestamp);
|
andrewm@0
|
767 this->buffer_->push_back(item);
|
andrewm@0
|
768 this->numSamples_++;
|
andrewm@0
|
769 this->bufferAccessMutex_.exit();
|
andrewm@0
|
770
|
andrewm@0
|
771 // Notify anyone who's listening for a trigger
|
andrewm@0
|
772 this->sendTrigger(timestamp);
|
andrewm@0
|
773 }
|
andrewm@0
|
774
|
andrewm@0
|
775 // Insert a "missing" item into the buffer. This is really for the Filter subclasses, but we should provide an implementation
|
andrewm@0
|
776 /*void insertMissing(timestamp_type timestamp) {
|
andrewm@0
|
777 if(timestamp == insertMissingLastTimestamp_)
|
andrewm@0
|
778 return;
|
andrewm@0
|
779 this->bufferAccessMutex_.lock();
|
andrewm@0
|
780 if(this->buffer_->full())
|
andrewm@0
|
781 this->firstSampleIndex_++;
|
andrewm@0
|
782 this->timestamps_->push_back(timestamp);
|
andrewm@0
|
783 this->buffer_->push_back(missing_value<OutputType>::missing());
|
andrewm@0
|
784 this->numSamples_++;
|
andrewm@0
|
785 this->bufferAccessMutex_.unlock();
|
andrewm@0
|
786
|
andrewm@0
|
787 this->insertMissingLastTimestamp_ = timestamp;
|
andrewm@0
|
788 this->notifyListenersOfInsert(timestamp);
|
andrewm@0
|
789 } */
|
andrewm@0
|
790
|
andrewm@0
|
791 protected:
|
andrewm@0
|
792 // Subclasses are allowed to change the values stored in their buffers. Give this a different
|
andrewm@0
|
793 // name to avoid confusion with the behavior of [] and at(), which call evaluate() if the sample
|
andrewm@0
|
794 // is missing.
|
andrewm@0
|
795
|
andrewm@0
|
796 reference rawValueAt(size_type index) { return (*buffer_)[index-firstSampleIndex_]; }
|
andrewm@0
|
797
|
andrewm@0
|
798 public:
|
andrewm@0
|
799 // ***** Timestamp Methods *****
|
andrewm@0
|
800 //
|
andrewm@0
|
801 // Every sample is tagged with a timestamp. The Filter classes will look up the chain to find the timestamp associated
|
andrewm@0
|
802 // with the Source of any particular sample. We also support methods to return an iterator to a piece of data most closely
|
andrewm@0
|
803 // matching a given timestamp.
|
andrewm@0
|
804
|
andrewm@0
|
805 timestamp_type timestampAt(size_type index) { return timestamps_->at(index-this->firstSampleIndex_); }
|
andrewm@0
|
806 timestamp_type latestTimestamp() { return timestamps_->back(); }
|
andrewm@0
|
807 timestamp_type earliestTimestamp() { return timestamps_->front(); }
|
andrewm@0
|
808
|
andrewm@0
|
809 size_type indexNearestBefore(timestamp_type t) {
|
andrewm@0
|
810 boost::circular_buffer<timestamp_type>::iterator it = std::find_if(timestamps_->begin(), timestamps_->end(), t < boost::lambda::_1);
|
andrewm@0
|
811 if(it == timestamps_->end())
|
andrewm@0
|
812 return timestamps_->size()-1+this->firstSampleIndex_;
|
andrewm@0
|
813 if(it - timestamps_->begin() == 0)
|
andrewm@0
|
814 return this->firstSampleIndex_;
|
andrewm@0
|
815 return (size_type)((--it) - timestamps_->begin()) + this->firstSampleIndex_;
|
andrewm@0
|
816 }
|
andrewm@0
|
817 size_type indexNearestAfter(timestamp_type t) {
|
andrewm@0
|
818 boost::circular_buffer<timestamp_type>::iterator it = std::find_if(timestamps_->begin(), timestamps_->end(), t < boost::lambda::_1);
|
andrewm@0
|
819 return std::min<size_type>((it - timestamps_->begin()), timestamps_->size()-1) + this->firstSampleIndex_;
|
andrewm@0
|
820 }
|
andrewm@0
|
821 size_type indexNearestTo(timestamp_type t) {
|
andrewm@0
|
822 boost::circular_buffer<timestamp_type>::iterator it = std::find_if(timestamps_->begin(), timestamps_->end(), t < boost::lambda::_1);
|
andrewm@0
|
823 if(it == timestamps_->end())
|
andrewm@0
|
824 return timestamps_->size()-1+this->firstSampleIndex_;
|
andrewm@0
|
825 if(it - timestamps_->begin() == 0)
|
andrewm@0
|
826 return this->firstSampleIndex_;
|
andrewm@0
|
827 timestamp_diff_type after = *it - t; // Calculate the distance between the desired timestamp and the before/after values,
|
andrewm@0
|
828 timestamp_diff_type before = t - *(it-1); // then return whichever index gets closer to the target.
|
andrewm@0
|
829 if(after < before)
|
andrewm@0
|
830 return (size_type)(it - timestamps_->begin()) + this->firstSampleIndex_;
|
andrewm@0
|
831 return (size_type)((--it) - timestamps_->begin()) + this->firstSampleIndex_;
|
andrewm@0
|
832 }
|
andrewm@0
|
833
|
andrewm@0
|
834 const_iterator nearestTo(timestamp_type t) { return begin() + (difference_type)indexNearestTo(t); }
|
andrewm@0
|
835 const_iterator nearestBefore(timestamp_type t) { return begin() + (difference_type)indexNearestBefore(t); }
|
andrewm@0
|
836 const_iterator nearestAfter(timestamp_type t) { return begin() + (difference_type)indexNearestAfter(t); }
|
andrewm@0
|
837
|
andrewm@0
|
838 const_reverse_iterator rnearestTo(timestamp_type t) { return rend() - (difference_type)indexNearestTo(t); }
|
andrewm@0
|
839 const_reverse_iterator rnearestBefore(timestamp_type t) { return rend() - (difference_type)indexNearestBefore(t); }
|
andrewm@0
|
840 const_reverse_iterator rnearestAfter(timestamp_type t) { return rend() - (difference_type)indexNearestAfter(t); }
|
andrewm@0
|
841
|
andrewm@0
|
842 private:
|
andrewm@0
|
843 // Calculate the actual value of one sample. Behavior of this method will be different for Source and Filter types.
|
andrewm@0
|
844 // virtual OutputType evaluate(size_type index) = 0;
|
andrewm@0
|
845
|
andrewm@0
|
846 timestamp_type insertMissingLastTimestamp_; // The last timestamp that came from insertMissing(), so we can avoid duplication
|
andrewm@0
|
847
|
andrewm@0
|
848 protected:
|
andrewm@0
|
849 boost::circular_buffer<OutputType> *buffer_; // Internal buffer to store or cache values
|
andrewm@0
|
850 boost::circular_buffer<timestamp_type> *timestamps_; // Internal buffer to hold timestamps for each value
|
andrewm@0
|
851
|
andrewm@0
|
852 size_type numSamples_; // How many samples total we've stored in this buffer
|
andrewm@0
|
853 size_type firstSampleIndex_; // Index of the first sample that still remains in the buffer
|
andrewm@0
|
854 };
|
andrewm@0
|
855
|
andrewm@0
|
856 /*
|
andrewm@0
|
857 * Node
|
andrewm@0
|
858 *
|
andrewm@0
|
859 * This class handles a node of a specific data type, including the ability to interpolate between samples. This is the recommended
|
andrewm@0
|
860 * class to use for numeric data types and others for which linear interpolation makes sense.
|
andrewm@0
|
861 */
|
andrewm@0
|
862
|
andrewm@0
|
863 template<typename OutputType>
|
andrewm@0
|
864 class Node : public NodeNonInterpolating<OutputType> {
|
andrewm@0
|
865 public:
|
andrewm@0
|
866 typedef typename std::allocator<OutputType> Alloc;
|
andrewm@0
|
867 typedef NodeInterpolatedIterator<OutputType, boost::cb_details::const_traits<Alloc> > interpolated_iterator;
|
andrewm@0
|
868
|
andrewm@0
|
869 typedef typename NodeNonInterpolating<OutputType>::return_value_type return_value_type;
|
andrewm@0
|
870 typedef typename NodeNonInterpolating<OutputType>::capacity_type capacity_type;
|
andrewm@0
|
871 typedef typename NodeNonInterpolating<OutputType>::size_type size_type;
|
andrewm@0
|
872
|
andrewm@0
|
873 // ***** Constructors *****
|
andrewm@0
|
874 //
|
andrewm@0
|
875 // Use the same constructors as the non-interpolating version.
|
andrewm@0
|
876
|
andrewm@0
|
877 explicit Node(capacity_type capacity) : NodeNonInterpolating<OutputType>(capacity) {}
|
andrewm@0
|
878 Node(Node<OutputType> const& obj) : NodeNonInterpolating<OutputType>(obj) {}
|
andrewm@0
|
879
|
andrewm@0
|
880 // ***** Interpolating Accessors *****
|
andrewm@0
|
881 //
|
andrewm@0
|
882 // These overloaded methods allow querying a location between two samples, using linear
|
andrewm@0
|
883 // interpolation to generate the value.
|
andrewm@0
|
884
|
andrewm@0
|
885 return_value_type interpolate(double index) {
|
andrewm@0
|
886 size_type before = floor(index); // Find the sample before the interpolated location
|
andrewm@0
|
887 double frac = index - (double)before; // Find the fractional remainder component
|
andrewm@0
|
888 OutputType val1 = this->buffer_->at(before-this->firstSampleIndex_);
|
andrewm@0
|
889 if(before == this->endIndex()-1)
|
andrewm@0
|
890 return val1;
|
andrewm@0
|
891 OutputType val2 = this->buffer_->at(before+1-this->firstSampleIndex_);
|
andrewm@0
|
892 //if(missing_value<OutputType>::isMissing(val1)) // Make sure both values have been calculated
|
andrewm@0
|
893 // val1 = (buffer_->at(before-firstSampleIndex_) = evaluate(before));
|
andrewm@0
|
894 //if(missing_value<OutputType>::isMissing(val2))
|
andrewm@0
|
895 // val2 = (buffer_->at(before+1-firstSampleIndex_) = evaluate(before+1));
|
andrewm@0
|
896 return val1*(1.0-frac) + val2*frac; // Return the interpolated value
|
andrewm@0
|
897 }
|
andrewm@0
|
898
|
andrewm@0
|
899 interpolated_iterator interpolatedBegin(double step = 1.0) { return interpolated_iterator(this, (double)this->beginIndex(), step); }
|
andrewm@0
|
900 interpolated_iterator interpolatedEnd(double step = 1.0) { return interpolated_iterator(this, (double)this->endIndex(), step); }
|
andrewm@0
|
901 interpolated_iterator interpolatedIteratorAtIndex(double index, double step = 1.0) { return interpolated_iterator(this, index, step); }
|
andrewm@0
|
902
|
andrewm@0
|
903 // ***** Interpolating Timestamp Methods *****
|
andrewm@0
|
904 //
|
andrewm@0
|
905 // Using linear interpolation, find the exact relationship between a timestamp and an index in the buffer.
|
andrewm@0
|
906 // Designed to be used in conjunction with the interpolate() method for buffer access.
|
andrewm@0
|
907
|
andrewm@0
|
908 // Fractional index --> timestamp
|
andrewm@0
|
909 timestamp_type interpolatedTimestampAt(double index) {
|
andrewm@0
|
910 size_type before = floor(index);
|
andrewm@0
|
911 double frac = index - (double)before;
|
andrewm@0
|
912 timestamp_type ts1 = timestampAt(before);
|
andrewm@0
|
913 if(before == this->endIndex()-1)
|
andrewm@0
|
914 return ts1;
|
andrewm@0
|
915 timestamp_type ts2 = timestampAt(before+1);
|
andrewm@0
|
916 return ts1*(1.0-frac)+ts2*frac;
|
andrewm@0
|
917 }
|
andrewm@0
|
918
|
andrewm@0
|
919 // Timestamp --> fractional index
|
andrewm@0
|
920 double interpolatedIndexForTimestamp(timestamp_type timestamp) {
|
andrewm@0
|
921 size_type before = this->indexNearestBefore(timestamp);
|
andrewm@0
|
922 if(before >= this->timestamps_->size() - 1 + this->firstSampleIndex_) // If it's at the end of the buffer, return the last available timestamp
|
andrewm@0
|
923 return (double)before;
|
andrewm@0
|
924 timestamp_type beforeTimestamp = this->timestampAt(before); // Get the timestamp immediately before
|
andrewm@0
|
925 if(beforeTimestamp >= timestamp) // If it comes after the requested timestamp, we're at the beginning of the buffer
|
andrewm@0
|
926 return (double)before;
|
andrewm@0
|
927 timestamp_type afterTimestamp = this->timestampAt(before+1);
|
andrewm@0
|
928 double frac = (timestamp - beforeTimestamp)/(afterTimestamp-beforeTimestamp);
|
andrewm@0
|
929 return (double)before + frac;
|
andrewm@0
|
930 }
|
andrewm@0
|
931 };
|
andrewm@0
|
932
|
andrewm@0
|
933 #endif /* KEYCONTROL_NODE_H */ |