andrewm@0: /* andrewm@0: TouchKeys: multi-touch musical keyboard control software andrewm@0: Copyright (c) 2013 Andrew McPherson andrewm@0: andrewm@0: This program is free software: you can redistribute it and/or modify andrewm@0: it under the terms of the GNU General Public License as published by andrewm@0: the Free Software Foundation, either version 3 of the License, or andrewm@0: (at your option) any later version. andrewm@0: andrewm@0: This program is distributed in the hope that it will be useful, andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrewm@0: GNU General Public License for more details. andrewm@0: andrewm@0: You should have received a copy of the GNU General Public License andrewm@0: along with this program. If not, see . andrewm@0: andrewm@0: ===================================================================== andrewm@0: andrewm@0: TimestampSynchronizer.h: handles aligning timestamps between multiple andrewm@0: asynchronous sources, while reducing the jitter that occurs when using andrewm@0: system clock time for every received sample. andrewm@0: */ andrewm@0: andrewm@0: #ifndef TIMESTAMP_SYNCHRONIZER_H andrewm@0: #define TIMESTAMP_SYNCHRONIZER_H andrewm@0: andrewm@0: #include andrewm@0: #include "../JuceLibraryCode/JuceHeader.h" andrewm@0: #include "Types.h" andrewm@0: #include "Node.h" andrewm@0: andrewm@0: const int kTimestampSynchronizerHistoryLength = 100; andrewm@0: andrewm@0: /* TimestampSynchronizer andrewm@0: * andrewm@0: * We often want to deal with multiple independent data streams, roughly andrewm@0: * synchronized in time to one another but operating on separate clocks. andrewm@0: * Each data stream can be assumed to have a regular, constant frame interval andrewm@0: * but the exact duration of the interval might not be known and might drift andrewm@0: * with respect to the system clock. andrewm@0: * andrewm@0: * In this class, the self-reported frame number is compared to the current andrewm@0: * system time. In any multitasking OS, the system time when we receive a andrewm@0: * frame may jitter around, but in the long-term average, we want system clock andrewm@0: * and frame clock to stay in sync. Thus we use the low-pass-filtered difference andrewm@0: * between system clock and frame clock to adjust the reported frame rate, keeping andrewm@0: * the two locked together. andrewm@0: */ andrewm@0: andrewm@0: using namespace std; andrewm@0: andrewm@0: class TimestampSynchronizer { andrewm@0: public: andrewm@0: // Constructor andrewm@0: TimestampSynchronizer(); andrewm@0: andrewm@0: // Clear accumulated timestamps and reinitialize a relationship between clock andrewm@0: // time and output timestamp. andrewm@0: void initialize(double clockTimeMilliseconds, timestamp_type startingTimestamp); andrewm@0: andrewm@0: // Return or set the expected interval between frames andrewm@0: timestamp_type nominalSampleInterval() { return nominalSampleInterval_; } andrewm@0: void setNominalSampleInterval(timestamp_type interval) { andrewm@0: nominalSampleInterval_ = interval; andrewm@0: currentSampleInterval_ = interval; andrewm@0: } andrewm@0: andrewm@0: // Return the current calculated interval between frames andrewm@0: timestamp_type currentSampleInterval() { return currentSampleInterval_; } andrewm@0: andrewm@0: // Return or set the frame modulus (at what number the frame counter wraps andrewm@0: // around to 0, since it can't increase forever). andrewm@0: int frameModulus() { return frameModulus_; } andrewm@0: void setFrameModulus(int modulus) { frameModulus_ = modulus; } andrewm@0: andrewm@0: // Process a new timestamp value and return the value synchronized to the andrewm@0: // system clock andrewm@0: timestamp_type synchronizedTimestamp(int rawFrameNumber); andrewm@0: andrewm@0: private: andrewm@0: // History buffer of clock time vs. frame time. The Node has a data type andrewm@0: // (frame number and frame timestamp, respectively) and a timestamp (clock timestamp); andrewm@0: // in other words, two different times are held within the buffer as well as the frame numbers. andrewm@0: andrewm@0: Node > history_; andrewm@0: andrewm@0: // Expected and currently calculated frame intervals andrewm@0: andrewm@0: timestamp_type nominalSampleInterval_; andrewm@0: timestamp_type currentSampleInterval_; andrewm@0: andrewm@0: // Modulus of frame number, i.e. the number at which the frame counter andrewm@0: // wraps around back to 0. andrewm@0: int frameModulus_; andrewm@0: andrewm@0: // The time we start from (clock and output timestamp) andrewm@0: andrewm@0: double startingClockTimeMilliseconds_; andrewm@0: timestamp_type startingTimestamp_; andrewm@0: andrewm@0: int bufferLengthCounter_; andrewm@0: }; andrewm@0: andrewm@0: #endif /* TIMESTAMP_SYNCHRONIZER_H */