Skip to content

Commit 67a2e1d

Browse files
committed
Finish Harp sync; enabled event translation across streams with hardware timestamps
1 parent 418f4f7 commit 67a2e1d

File tree

6 files changed

+156
-31
lines changed

6 files changed

+156
-31
lines changed

Source/Processors/EventTranslator/EventTranslator.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,6 @@ void EventTranslator::handleTTLEvent (TTLEventPtr event)
266266

267267
int64 firstSampleNumberForBlock = getFirstSampleNumberForBlock (streamId);
268268

269-
if (newSampleNumber < firstSampleNumberForBlock)
270-
newSampleNumber = firstSampleNumberForBlock;
271-
272269
//std::cout << "translated sample number (" << streamId << "): " << newSampleNumber - firstSampleNumberForBlock << std::endl;
273270
//std::cout << std::endl;
274271

@@ -279,4 +276,34 @@ void EventTranslator::handleTTLEvent (TTLEventPtr event)
279276
}
280277
}
281278
}
279+
280+
if (synchronizer.getStatus (eventStreamKey) == SyncStatus::HARDWARE_SYNCED)
281+
{
282+
const bool state = event->getState();
283+
284+
double timestamp = synchronizer.convertSampleNumberToTimestamp (eventStreamKey, sampleNumber);
285+
286+
for (auto stream : getDataStreams())
287+
{
288+
const uint16 streamId = stream->getStreamId();
289+
const String streamKey = stream->getKey();
290+
291+
if (streamKey == eventStreamKey || synchronizer.getStatus (streamKey) != SyncStatus::HARDWARE_SYNCED)
292+
continue; // don't translate events to non-hardware-synced streams
293+
294+
int64 newSampleNumber = synchronizer.convertTimestampToSampleNumber (streamKey, timestamp);
295+
296+
//std::cout << "new sample number (" << streamKey << "): " << newSampleNumber << std::endl;
297+
298+
int64 firstSampleNumberForBlock = getFirstSampleNumberForBlock (streamId);
299+
300+
//std::cout << "translated sample number (" << streamKey << "): " << newSampleNumber - firstSampleNumberForBlock << std::endl;
301+
//std::cout << std::endl;
302+
303+
TTLEventPtr translatedEvent =
304+
settings[streamId]->createEvent (newSampleNumber, timestamp, ttlLine, state);
305+
306+
addEvent (translatedEvent, newSampleNumber - firstSampleNumberForBlock);
307+
}
308+
}
282309
}

Source/Processors/Parameter/ParameterEditor.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,22 @@ void SyncControlButton::paintButton (Graphics& g, bool isMouseOver, bool isButto
911911
}
912912
case SyncStatus::HARP_CLOCK:
913913
{
914-
Colour harpClockColour = Colour (200, 150, 255); // Light purplse
914+
Colour harpClockColour = Colour (200, 150, 255); // Light purple
915+
916+
if (isMouseOver)
917+
{
918+
g.setColour (harpClockColour);
919+
}
920+
else
921+
{
922+
g.setColour (harpClockColour.darker (0.5f));
923+
}
924+
break;
925+
}
926+
927+
case SyncStatus::HARP_DETECTING:
928+
{
929+
Colour harpClockColour = Colour (105, 87, 107); // Purple-gray
915930

916931
if (isMouseOver)
917932
{

Source/Processors/RecordNode/BinaryFormat/BinaryRecording.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
801801
int newSamples = jmax (numSamples, m_batchBufferSamples);
802802
int newChannels = jmax (numChannels, m_batchBufferChannels);
803803

804-
LOGC ("BinaryRecording::writeContinuousDataBatch: Resizing batch buffer to ",
804+
LOGD ("BinaryRecording::writeContinuousDataBatch: Resizing batch buffer to ",
805805
newChannels, " channels x ", newSamples, " samples");
806806

807807
m_batchIntBuffer.malloc (newSamples * newChannels);

Source/Processors/RecordNode/RecordNode.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,7 @@ void RecordNode::process (AudioBuffer<float>& buffer)
11571157
{
11581158
first = getFirstTimestampForBlock (streamId);
11591159
second = first + 1 / stream->getSampleRate();
1160+
synchronizer.setHardwareTimestamp (sampleNumber, first, streamKey);
11601161
}
11611162
// Each per-stream queue has only 1 timestamp stream (index 0)
11621163
dataQueues[streamIndex]->writeSynchronizedTimestamps (

Source/Processors/Synchronizer/Synchronizer.cpp

Lines changed: 102 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void SyncStream::reset (String mainStreamKey)
187187
latestSyncSampleNumber = 0;
188188
latestGlobalSyncTime = 0.0;
189189
latestSyncMillis = -1;
190-
190+
191191
// Reset Harp detection state
192192
harpState = HarpDetectionState::IDLE;
193193
completedBarcodes.clear();
@@ -211,10 +211,21 @@ void SyncStream::reset (String mainStreamKey)
211211
}
212212
else
213213
{
214-
actualSampleRate = -1.0;
215-
globalStartTime = 0.0;
216214
overrideHardwareTimestamps = syncLine > -1; // override hardware timestamps for other streams if sync line is set
217-
isSynchronized = generatesTimestamps && !overrideHardwareTimestamps; // if the stream generates its own timestamps, it is synchronized unless it overrides hardware timestamps
215+
216+
if (generatesTimestamps && ! overrideHardwareTimestamps)
217+
{
218+
// if the stream generates its own timestamps, it is synchronized unless it overrides hardware timestamps
219+
actualSampleRate = expectedSampleRate;
220+
isSynchronized = true;
221+
}
222+
else
223+
{
224+
actualSampleRate = -1.0;
225+
isSynchronized = false;
226+
}
227+
228+
globalStartTime = 0.0;
218229
}
219230
}
220231

@@ -260,6 +271,43 @@ void SyncStream::addEvent (int64 sampleNumber, bool state)
260271
}
261272
}
262273

274+
void SyncStream::setHardwareTimestamp (int64 sampleNumber, double timestamp)
275+
{
276+
277+
if (generatesTimestamps && ! overrideHardwareTimestamps)
278+
{
279+
latestSyncSampleNumber = sampleNumber;
280+
latestGlobalSyncTime = timestamp;
281+
282+
if (! baselineMatchingPulse.complete)
283+
{
284+
baselineMatchingPulse.localSampleNumber = sampleNumber;
285+
baselineMatchingPulse.globalTimestamp = timestamp;
286+
baselineMatchingPulse.complete = true;
287+
288+
actualSampleRate = expectedSampleRate; // safe initial
289+
globalStartTime = timestamp - (double (sampleNumber) / expectedSampleRate);
290+
latestSyncMillis = Time::currentTimeMillis();
291+
return;
292+
}
293+
294+
double dt = timestamp - baselineMatchingPulse.globalTimestamp;
295+
int64 ds = sampleNumber - baselineMatchingPulse.localSampleNumber;
296+
297+
if (dt <= 1e-6 || ds <= 0)
298+
return;
299+
300+
double estimated = double (ds) / dt;
301+
302+
if (std::abs (estimated - expectedSampleRate) / expectedSampleRate < 0.01)
303+
{
304+
actualSampleRate = estimated;
305+
latestSyncMillis = Time::currentTimeMillis();
306+
}
307+
}
308+
309+
}
310+
263311
double SyncStream::getLatestSyncTime()
264312
{
265313
//LOGC ("Getting latest sync time for stream ", streamKey, "...");
@@ -281,16 +329,26 @@ double SyncStream::getLatestSyncTime()
281329

282330
double SyncStream::getSyncAccuracy()
283331
{
332+
333+
if (generatesTimestamps && ! overrideHardwareTimestamps)
334+
return 0.0; // or compute error vs. expectedSampleRate drift
335+
284336
if (pulses.size() > 0)
285337
{
286338

287-
//LOGD ("Sync accuracy for stream ", streamKey);
339+
//if (!isMainStream)
340+
//{
341+
//LOGC ("Sync accuracy for stream ", streamKey);
342+
343+
//LOGC ("latestSyncSampleNumber: ", latestSyncSampleNumber);
344+
//LOGC ("latestGlobalSyncTime: ", latestGlobalSyncTime);
345+
//LOGC ("globalStartTime: ", globalStartTime);
346+
//LOGC ("actualSampleRate: ", actualSampleRate);
347+
//LOGC ("baselineMatchingPulse.globalTimestamp: ", baselineMatchingPulse.globalTimestamp);
348+
//LOGC ("baselineMatchingPulse.localSampleNumber: ", baselineMatchingPulse.localSampleNumber);
349+
//LOGC (" ");
350+
//}
288351

289-
//LOGD ("latestSyncSampleNumber: ", latestSyncSampleNumber);
290-
//LOGD ("latestGlobalSyncTime: ", latestGlobalSyncTime);
291-
//LOGD ("globalStartTime: ", globalStartTime);
292-
//LOGD ("actualSampleRate: ", actualSampleRate);
293-
//LOGD ("baselineMatchingPulse.globalTimestamp: ", baselineMatchingPulse.globalTimestamp);
294352

295353
// NEW CALCULATION:
296354
double estimatedGlobalTime = double(latestSyncSampleNumber - baselineMatchingPulse.localSampleNumber)
@@ -309,8 +367,8 @@ double SyncStream::getSyncAccuracy()
309367

310368
void SyncStream::syncWith (const SyncStream* mainStream)
311369
{
312-
//LOGD ("Synchronizing ", streamKey, " with ", mainStream->streamKey, "...");
313-
//LOGD ("Expected sample rate: ", expectedSampleRate);
370+
//LOGC ("Synchronizing ", streamKey, " with ", mainStream->streamKey, "...");
371+
//LOGC ("Expected sample rate: ", expectedSampleRate);
314372

315373
if (mainStream->pulses.size() < 2 || pulses.size() < 2)
316374
{
@@ -333,20 +391,19 @@ void SyncStream::syncWith (const SyncStream* mainStream)
333391
{
334392
if (comparePulses (pulse, mainPulse)) // putative match
335393
{
336-
if (pulses.size() > localIndex + 3 && mainStream->pulses.size() > index + 3)
394+
if (pulses.size() > localIndex + 2 && mainStream->pulses.size() > index + 2)
337395
{
338-
// previous three pulses also match
396+
// previous two pulses also match
339397
if (comparePulses (pulses[localIndex + 1], mainStream->pulses[index + 1])
340-
&& comparePulses (pulses[localIndex + 2], mainStream->pulses[index + 2])
341-
&& comparePulses (pulses[localIndex + 3], mainStream->pulses[index + 3]))
398+
&& comparePulses (pulses[localIndex + 2], mainStream->pulses[index + 2]))
342399
{
343400
pulse.matchingPulseIndex = index;
344401
pulse.globalTimestamp = mainPulse.localTimestamp;
345402
latestSyncSampleNumber = pulse.localSampleNumber;
346403
latestGlobalSyncTime = pulse.globalTimestamp;
347404
latestSyncMillis = pulse.computerTimeMillis;
348-
//LOGD ("Pulse at ", pulse.localTimestamp, " matches with 4 main pulses at ", index);
349-
//LOGD ("latestSyncSampleNumber: ", latestSyncSampleNumber, ", latestGlobalSyncTime: ", latestGlobalSyncTime);
405+
//LOGC ("Pulse at ", pulse.localTimestamp, " matches with 3 main pulses at ", index);
406+
//LOGC ("latestSyncSampleNumber: ", latestSyncSampleNumber, ", latestGlobalSyncTime: ", latestGlobalSyncTime);
350407

351408

352409
if (baselineMatchingPulse.complete == false)
@@ -456,7 +513,9 @@ void SyncStream::syncWith (const SyncStream* mainStream)
456513

457514
{
458515
globalStartTime = estimatedGlobalStartTime;
516+
459517
isSynchronized = true;
518+
460519
}
461520
}
462521
else
@@ -603,11 +662,15 @@ void SyncStream::attemptBarcodeDecoding()
603662
// decode last barcode
604663
if (harpDecoder.decodeBarcode(completedBarcodes.back(), expectedSampleRate))
605664
{
606-
// LOGD ("Successful decoding...setting isHarpStream to true.");
607-
isHarpStream = true;
608-
isSynchronized = true;
609665

610-
666+
if (!isHarpStream)
667+
{
668+
LOGD ("Successful decoding...setting isHarpStream to true.");
669+
isHarpStream = true;
670+
isSynchronized = false;
671+
672+
}
673+
611674
// Validate timing
612675
if (!validateBarcodeTimestamp(completedBarcodes.back()))
613676
{
@@ -648,12 +711,12 @@ void SyncStream::syncWithHarp()
648711

649712
if (std::abs (estimatedSampleRate - expectedSampleRate) / expectedSampleRate < 0.05)
650713
{
651-
LOGC (streamKey, " total barcodes = ", completedBarcodes.size(), "; estimated sample rate: ", estimatedSampleRate);
714+
//LOGC (streamKey, " total barcodes = ", completedBarcodes.size(), "; estimated sample rate: ", estimatedSampleRate);
652715
actualSampleRate = estimatedSampleRate;
653716

654717
// Calculate global start time
655718
//LOGD ("Estimated global start time: ", double (firstBarcode.encodedTime) - firstBarcode.localStartTimestamp);
656-
globalStartTime = (double (firstBarcode.encodedTime) - firstBarcode.localStartTimestamp) / 1000;
719+
globalStartTime = (double (firstBarcode.encodedTime) - firstBarcode.localStartTimestamp) / 1000; // divide by 1000 so that time appears in seconds in the stream info view
657720
baselineMatchingPulse.globalTimestamp = double (firstBarcode.encodedTime);
658721
baselineMatchingPulse.localSampleNumber = firstBarcode.localStartSample;
659722
latestSyncSampleNumber = lastBarcode.barcodeEvents.front().first;
@@ -666,7 +729,7 @@ void SyncStream::syncWithHarp()
666729
}
667730
else
668731
{
669-
LOGC (streamKey, " estimated sample rate out of range; clearing Harp barcodes.");
732+
LOGD (streamKey, " estimated sample rate out of range; clearing Harp barcodes.");
670733
isSynchronized = false;
671734
completedBarcodes.clear();
672735
}
@@ -796,6 +859,14 @@ void Synchronizer::addEvent (String streamKey,
796859
}
797860
}
798861

862+
863+
void Synchronizer::setHardwareTimestamp (int64 sampleNumber, double timestamp, String streamKey)
864+
{
865+
const ScopedLock sl (synchronizerLock);
866+
streams[streamKey]->setHardwareTimestamp (sampleNumber, timestamp);
867+
}
868+
869+
799870
double Synchronizer::convertSampleNumberToTimestamp (String streamKey, int64 sampleNumber)
800871
{
801872
if (streams[streamKey]->isSynchronized)
@@ -889,7 +960,12 @@ SyncStatus Synchronizer::getStatus (String streamKey)
889960
return SyncStatus::HARDWARE_SYNCED;
890961

891962
if (streams[streamKey]->isHarpStream)
892-
return SyncStatus::HARP_CLOCK;
963+
{
964+
if (isStreamSynced (streamKey))
965+
return SyncStatus::HARP_CLOCK;
966+
else
967+
return SyncStatus::HARP_DETECTING;
968+
}
893969

894970
if (isStreamSynced (streamKey))
895971
return SyncStatus::SYNCED;

Source/Processors/Synchronizer/Synchronizer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ class SyncStream
201201
/** Synchronize this stream with Harp timestamps */
202202
void syncWithHarp ();
203203

204+
/** Sets hardware timestamps (hardware-synced streams only) */
205+
void setHardwareTimestamp (int64 sampleNumber, double timestamp);
206+
204207
/** Compares pulses; returns true if a match is found */
205208
bool comparePulses (const SyncPulse& pulse1, const SyncPulse& pulse2);
206209

@@ -397,6 +400,9 @@ class PLUGIN_API Synchronizer : public HighResolutionTimer
397400
/** Adds an event for a stream ID / line combination */
398401
void addEvent (String streamKey, int ttlLine, int64 sampleNumber, bool state);
399402

403+
/** Updates the synchronizer for a hardware-synced stream */
404+
void setHardwareTimestamp (int64 sampleNumber, double timestamp, String streamKey);
405+
400406
/** Signals start of acquisition */
401407
void startAcquisition();
402408

0 commit comments

Comments
 (0)