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