annotate Source/Utility/Node.h @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents e314677c49f6
children
rev   line source
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 */