Skip to content

Commit 5f30733

Browse files
committed
Add support for per-sample timestamps in DataBuffer and GenericProcessor
1 parent fc6d246 commit 5f30733

8 files changed

Lines changed: 247 additions & 32 deletions

File tree

Source/Processors/DataThreads/DataBuffer.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ int DataBuffer::getNumSamples() const { return abstractFifo.getNumReady(); }
107107

108108
int DataBuffer::readAllFromBuffer (AudioBuffer<float>& data,
109109
int64* blockSampleNumber,
110-
double* blockTimestamp,
110+
double* blockTimestamps,
111111
uint64* eventCodes,
112112
int maxSize,
113113
int dstStartChannel,
@@ -134,15 +134,15 @@ int DataBuffer::readAllFromBuffer (AudioBuffer<float>& data,
134134
blockSize1); // numSamples
135135
}
136136

137-
memcpy (blockSampleNumber, sampleNumberBuffer + startIndex1, 8);
138-
memcpy (blockTimestamp, timestampBuffer + startIndex1, 8);
139-
memcpy (eventCodes, eventCodeBuffer + startIndex1, blockSize1 * 8);
137+
memcpy (blockSampleNumber, sampleNumberBuffer + startIndex1, sizeof (int64));
138+
memcpy (blockTimestamps, timestampBuffer + startIndex1, (size_t) blockSize1 * sizeof (double));
139+
memcpy (eventCodes, eventCodeBuffer + startIndex1, (size_t) blockSize1 * sizeof (uint64));
140140
}
141141
else
142142
{
143143
// std::cout << "NO SAMPLES" << std::endl;
144-
memcpy (blockSampleNumber, &lastSampleNumber, 8);
145-
memcpy (blockTimestamp, &lastTimestamp, 8);
144+
memcpy (blockSampleNumber, &lastSampleNumber, sizeof (int64));
145+
memcpy (blockTimestamps, &lastTimestamp, sizeof (double));
146146
}
147147

148148
if (blockSize2 > 0)
@@ -156,15 +156,16 @@ int DataBuffer::readAllFromBuffer (AudioBuffer<float>& data,
156156
startIndex2, // sourceStartSample
157157
blockSize2); // numSamples
158158
}
159-
memcpy (eventCodes + blockSize1, eventCodeBuffer + startIndex2, blockSize2 * 8);
159+
memcpy (blockTimestamps + blockSize1, timestampBuffer + startIndex2, (size_t) blockSize2 * sizeof (double));
160+
memcpy (eventCodes + blockSize1, eventCodeBuffer + startIndex2, (size_t) blockSize2 * sizeof (uint64));
160161
}
161162

162163
// std::cout << "START SAMPLE FOR READ: " << *blockSampleNumber << std::endl;
163164

164165
if (numItems > 0)
165166
{
166167
lastSampleNumber = *blockSampleNumber;
167-
lastTimestamp = *blockTimestamp;
168+
lastTimestamp = *blockTimestamps;
168169

169170
// std::cout << "Updating last sample number: " << lastSampleNumber << std::endl;
170171
}

Source/Processors/DataThreads/DataBuffer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ class PLUGIN_API DataBuffer
6464
/** Returns the number of samples currently available in the buffer.*/
6565
int getNumSamples() const;
6666

67-
/** Copies as many samples as possible from the DataBuffer to an AudioBuffer.*/
67+
/** Copies as many samples as possible from the DataBuffer to an AudioBuffer.
68+
69+
The first sample number is returned in `sampleNumbers[0]`, while `timestamps`
70+
and `eventCodes` receive one value per copied sample.
71+
*/
6872
int readAllFromBuffer (AudioBuffer<float>& data,
6973
int64* sampleNumbers,
7074
double* timestamps,

Source/Processors/Events/Event.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,36 @@ size_t SystemEvent::fillTimestampAndSamplesData (HeapBlock<char>& data,
218218
return eventSize;
219219
}
220220

221+
size_t SystemEvent::fillTimestampArrayData (HeapBlock<char>& data,
222+
const GenericProcessor* proc,
223+
uint16 streamId,
224+
int64 startSampleForBlock,
225+
const double* timestamps,
226+
uint32 nSamplesInBlock,
227+
int64 processStartTime,
228+
uint16 syncStreamId)
229+
{
230+
const size_t timestampDataSize = (size_t) nSamplesInBlock * sizeof (double);
231+
const size_t eventSize = EVENT_BASE_SIZE + 4 + 8 + timestampDataSize;
232+
const double startTimestampForBlock = nSamplesInBlock > 0 && timestamps != nullptr ? timestamps[0] : -1.0;
233+
234+
data.allocate (eventSize, true);
235+
data[0] = SYSTEM_EVENT;
236+
data[1] = TIMESTAMP_ARRAY;
237+
*reinterpret_cast<uint16*> (data.getData() + 2) = proc->getNodeId();
238+
*reinterpret_cast<uint16*> (data.getData() + 4) = streamId;
239+
*reinterpret_cast<uint16*> (data.getData() + 6) = syncStreamId;
240+
*reinterpret_cast<int64*> (data.getData() + 8) = startSampleForBlock;
241+
*reinterpret_cast<double*> (data.getData() + 16) = startTimestampForBlock;
242+
*reinterpret_cast<uint32*> (data.getData() + EVENT_BASE_SIZE) = nSamplesInBlock;
243+
*reinterpret_cast<int64*> (data.getData() + EVENT_BASE_SIZE + 4) = processStartTime;
244+
245+
if (timestampDataSize > 0 && timestamps != nullptr)
246+
memcpy (data.getData() + EVENT_BASE_SIZE + 12, timestamps, timestampDataSize);
247+
248+
return eventSize;
249+
}
250+
221251
size_t SystemEvent::fillTimestampSyncTextData (
222252
HeapBlock<char>& data,
223253
const GenericProcessor* proc,
@@ -277,15 +307,25 @@ size_t SystemEvent::fillReferenceSampleEvent (HeapBlock<char>& data,
277307

278308
uint32 SystemEvent::getNumSamples (const EventPacket& packet)
279309
{
280-
if (getBaseType (packet) != SYSTEM_EVENT && getSystemEventType (packet) != TIMESTAMP_AND_SAMPLES)
310+
if (getBaseType (packet) != SYSTEM_EVENT)
311+
return 0;
312+
313+
Type type = getSystemEventType (packet);
314+
315+
if (type != TIMESTAMP_AND_SAMPLES && type != TIMESTAMP_ARRAY)
281316
return 0;
282317

283318
return *reinterpret_cast<const uint32*> (packet.getRawData() + EVENT_BASE_SIZE);
284319
}
285320

286321
int64 SystemEvent::getHiResTicks (const EventPacket& packet)
287322
{
288-
if (getBaseType (packet) != SYSTEM_EVENT && getSystemEventType (packet) != TIMESTAMP_AND_SAMPLES)
323+
if (getBaseType (packet) != SYSTEM_EVENT)
324+
return 0;
325+
326+
Type type = getSystemEventType (packet);
327+
328+
if (type != TIMESTAMP_AND_SAMPLES && type != TIMESTAMP_ARRAY)
289329
return 0;
290330

291331
return *reinterpret_cast<const int64*> (packet.getRawData() + EVENT_BASE_SIZE + 4);

Source/Processors/Events/Event.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,10 @@ class PLUGIN_API SystemEvent : public EventBase
220220
TIMESTAMP_SYNC_TEXT = 3,
221221

222222
// Indicates reference sample information for each incoming data buffer
223-
REFERENCE_SAMPLE = 4
223+
REFERENCE_SAMPLE = 4,
224+
225+
// Per-sample timestamps for the current buffer
226+
TIMESTAMP_ARRAY = 5
224227
};
225228

226229
/* Create a TIMESTAMP_AND_SAMPLES event (used by processors that update timestamps) */
@@ -233,6 +236,16 @@ class PLUGIN_API SystemEvent : public EventBase
233236
int64 processStartTime,
234237
uint16 syncStreamId = 0);
235238

239+
/* Create a TIMESTAMP_ARRAY event (used by processors that provide per-sample timestamps) */
240+
static size_t fillTimestampArrayData (HeapBlock<char>& data,
241+
const GenericProcessor* proc,
242+
uint16 streamId,
243+
int64 startSampleForBlock,
244+
const double* timestamps,
245+
uint32 nSamplesInBlock,
246+
int64 processStartTime,
247+
uint16 syncStreamId = 0);
248+
236249
/* Create a TIMESTAMP_SYNC_TEXT event (used by Record Node) */
237250
static size_t fillTimestampSyncTextData (HeapBlock<char>& data,
238251
const GenericProcessor* proc,

Source/Processors/GenericProcessor/GenericProcessor.cpp

Lines changed: 152 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,59 @@
4646

4747
#define MS_FROM_START Time::highResolutionTicksToSeconds (Time::getHighResolutionTicks() - start) * 1000
4848

49+
namespace
50+
{
51+
using TimestampArrayState = std::unordered_map<const GenericProcessor*, std::map<uint16, std::vector<double>>>;
52+
53+
TimestampArrayState& getTimestampArrayState()
54+
{
55+
static TimestampArrayState state;
56+
return state;
57+
}
58+
59+
void clearTimestampArrayState (const GenericProcessor* processor)
60+
{
61+
getTimestampArrayState().erase (processor);
62+
}
63+
64+
void clearTimestampArrayState (const GenericProcessor* processor, uint16 streamId)
65+
{
66+
auto processorIt = getTimestampArrayState().find (processor);
67+
68+
if (processorIt == getTimestampArrayState().end())
69+
return;
70+
71+
processorIt->second.erase (streamId);
72+
73+
if (processorIt->second.empty())
74+
getTimestampArrayState().erase (processorIt);
75+
}
76+
77+
void setTimestampArrayState (const GenericProcessor* processor, uint16 streamId, const double* timestamps, uint32 nSamples)
78+
{
79+
auto& timestampsByStream = getTimestampArrayState()[processor];
80+
auto& streamTimestamps = timestampsByStream[streamId];
81+
streamTimestamps.assign (timestamps, timestamps + nSamples);
82+
}
83+
84+
const std::vector<double>* findTimestampArrayState (const GenericProcessor* processor, uint16 streamId)
85+
{
86+
auto processorIt = getTimestampArrayState().find (processor);
87+
88+
if (processorIt == getTimestampArrayState().end())
89+
return nullptr;
90+
91+
auto streamIt = processorIt->second.find (streamId);
92+
93+
if (streamIt == processorIt->second.end() || streamIt->second.empty())
94+
return nullptr;
95+
96+
return &streamIt->second;
97+
}
98+
99+
constexpr int timestampArrayOffset = EVENT_BASE_SIZE + 12;
100+
} // namespace
101+
49102
LatencyMeter::LatencyMeter (GenericProcessor* processor_)
50103
: processor (processor_),
51104
counter (0)
@@ -141,6 +194,7 @@ GenericProcessor::GenericProcessor (const String& name, bool headlessMode_)
141194

142195
GenericProcessor::~GenericProcessor()
143196
{
197+
clearTimestampArrayState (this);
144198
editor.reset(); // remove parameter editors before parameters
145199

146200
dataStreamParameters.clear (true);
@@ -663,6 +717,7 @@ void GenericProcessor::clearSettings()
663717

664718
ttlEventChannel = nullptr;
665719

720+
clearTimestampArrayState (this);
666721
startTimestampsForBlock.clear();
667722
startSamplesForBlock.clear();
668723
syncStreamIds.clear();
@@ -1256,12 +1311,46 @@ double GenericProcessor::getFirstTimestampForBlock (uint16 streamId) const
12561311
return startTimestampsForBlock.at (streamId);
12571312
}
12581313

1314+
const double* GenericProcessor::getTimestampsForBlock (uint16 streamId) const
1315+
{
1316+
const std::vector<double>* timestamps = findTimestampArrayState (this, streamId);
1317+
1318+
return timestamps != nullptr ? timestamps->data() : nullptr;
1319+
}
1320+
1321+
bool GenericProcessor::getTimestampForSample (uint16 streamId, int64 sampleNumber, double& timestamp) const
1322+
{
1323+
const std::vector<double>* timestamps = findTimestampArrayState (this, streamId);
1324+
1325+
if (timestamps == nullptr)
1326+
return false;
1327+
1328+
auto sampleIt = startSamplesForBlock.find (streamId);
1329+
auto countIt = numSamplesInBlock.find (streamId);
1330+
1331+
if (sampleIt == startSamplesForBlock.end() || countIt == numSamplesInBlock.end())
1332+
return false;
1333+
1334+
if (sampleNumber < sampleIt->second)
1335+
return false;
1336+
1337+
const uint32 sampleOffset = static_cast<uint32> (sampleNumber - sampleIt->second);
1338+
1339+
if (sampleOffset >= countIt->second || sampleOffset >= timestamps->size())
1340+
return false;
1341+
1342+
timestamp = (*timestamps)[sampleOffset];
1343+
return true;
1344+
}
1345+
12591346
void GenericProcessor::setTimestampAndSamples (int64 sampleNumber,
12601347
double timestamp,
12611348
uint32 nSamples,
12621349
uint16 streamId,
12631350
uint16 syncStreamId)
12641351
{
1352+
clearTimestampArrayState (this, streamId);
1353+
12651354
HeapBlock<char> data;
12661355
size_t dataSize = SystemEvent::fillTimestampAndSamplesData (data,
12671356
this,
@@ -1278,9 +1367,41 @@ void GenericProcessor::setTimestampAndSamples (int64 sampleNumber,
12781367
startTimestampsForBlock[streamId] = timestamp;
12791368
startSamplesForBlock[streamId] = sampleNumber;
12801369
syncStreamIds[streamId] = syncStreamId;
1370+
numSamplesInBlock[streamId] = nSamples;
12811371
processStartTimes[streamId] = m_initialProcessTime;
12821372
}
12831373

1374+
void GenericProcessor::setTimestampArrayForBlock (int64 sampleNumber,
1375+
const double* timestamps,
1376+
uint32 nSamples,
1377+
uint16 streamId,
1378+
uint16 syncStreamId)
1379+
{
1380+
clearTimestampArrayState (this, streamId);
1381+
1382+
if (timestamps == nullptr || nSamples == 0)
1383+
return;
1384+
1385+
HeapBlock<char> data;
1386+
size_t dataSize = SystemEvent::fillTimestampArrayData (data,
1387+
this,
1388+
streamId,
1389+
sampleNumber,
1390+
timestamps,
1391+
nSamples,
1392+
m_initialProcessTime,
1393+
syncStreamId);
1394+
1395+
m_currentMidiBuffer->addEvent (data, int (dataSize), 0);
1396+
1397+
startTimestampsForBlock[streamId] = timestamps[0];
1398+
startSamplesForBlock[streamId] = sampleNumber;
1399+
syncStreamIds[streamId] = syncStreamId;
1400+
numSamplesInBlock[streamId] = nSamples;
1401+
processStartTimes[streamId] = m_initialProcessTime;
1402+
setTimestampArrayState (this, streamId, timestamps, nSamples);
1403+
}
1404+
12841405
int GenericProcessor::getGlobalChannelIndex (uint16 streamId, int localIndex) const
12851406
{
12861407
return getDataStream (streamId)->getContinuousChannels()[localIndex]->getGlobalIndex();
@@ -1303,26 +1424,39 @@ int GenericProcessor::processEventBuffer()
13031424
for (const auto meta : *m_currentMidiBuffer)
13041425
{
13051426
const uint8* dataptr = meta.data;
1427+
const Event::Type baseType = static_cast<Event::Type> (*dataptr);
13061428

1307-
if (static_cast<Event::Type> (*dataptr) == Event::Type::SYSTEM_EVENT
1308-
&& static_cast<SystemEvent::Type> (*(dataptr + 1) == SystemEvent::Type::TIMESTAMP_AND_SAMPLES))
1429+
if (baseType == Event::Type::SYSTEM_EVENT)
13091430
{
1310-
uint16 sourceProcessorId = *reinterpret_cast<const uint16*> (dataptr + 2);
1311-
uint16 sourceStreamId = *reinterpret_cast<const uint16*> (dataptr + 4);
1312-
uint16 syncStreamId = *reinterpret_cast<const uint16*> (dataptr + 6);
1313-
1314-
int64 startSample = *reinterpret_cast<const int64*> (dataptr + 8);
1315-
double startTimestamp = *reinterpret_cast<const double*> (dataptr + 16);
1316-
uint32 nSamples = *reinterpret_cast<const uint32*> (dataptr + 24);
1317-
int64 initialTicks = *reinterpret_cast<const int64*> (dataptr + 28);
1318-
1319-
startSamplesForBlock[sourceStreamId] = startSample;
1320-
startTimestampsForBlock[sourceStreamId] = startTimestamp;
1321-
syncStreamIds[sourceStreamId] = syncStreamId;
1322-
numSamplesInBlock[sourceStreamId] = nSamples;
1323-
processStartTimes[sourceStreamId] = initialTicks;
1431+
const SystemEvent::Type systemEventType = static_cast<SystemEvent::Type> (*(dataptr + 1));
1432+
1433+
if (systemEventType == SystemEvent::Type::TIMESTAMP_AND_SAMPLES
1434+
|| systemEventType == SystemEvent::Type::TIMESTAMP_ARRAY)
1435+
{
1436+
uint16 sourceStreamId = *reinterpret_cast<const uint16*> (dataptr + 4);
1437+
uint16 syncStreamId = *reinterpret_cast<const uint16*> (dataptr + 6);
1438+
1439+
int64 startSample = *reinterpret_cast<const int64*> (dataptr + 8);
1440+
double startTimestamp = *reinterpret_cast<const double*> (dataptr + 16);
1441+
uint32 nSamples = *reinterpret_cast<const uint32*> (dataptr + 24);
1442+
int64 initialTicks = *reinterpret_cast<const int64*> (dataptr + 28);
1443+
1444+
clearTimestampArrayState (this, sourceStreamId);
1445+
1446+
startSamplesForBlock[sourceStreamId] = startSample;
1447+
startTimestampsForBlock[sourceStreamId] = startTimestamp;
1448+
syncStreamIds[sourceStreamId] = syncStreamId;
1449+
numSamplesInBlock[sourceStreamId] = nSamples;
1450+
processStartTimes[sourceStreamId] = initialTicks;
1451+
1452+
if (systemEventType == SystemEvent::Type::TIMESTAMP_ARRAY && nSamples > 0)
1453+
{
1454+
const double* timestamps = reinterpret_cast<const double*> (dataptr + timestampArrayOffset);
1455+
setTimestampArrayState (this, sourceStreamId, timestamps, nSamples);
1456+
}
1457+
}
13241458
}
1325-
else if (static_cast<Event::Type> (*dataptr) == Event::Type::PROCESSOR_EVENT
1459+
else if (baseType == Event::Type::PROCESSOR_EVENT
13261460
&& static_cast<EventChannel::Type> (*(dataptr + 1) == EventChannel::Type::TTL))
13271461
{
13281462
uint16 sourceStreamId = *reinterpret_cast<const uint16*> (dataptr + 4);
@@ -1334,7 +1468,7 @@ int GenericProcessor::processEventBuffer()
13341468
getEditor()->setTTLState (sourceStreamId, eventBit, eventState);
13351469
}
13361470
}
1337-
else if (static_cast<Event::Type> (*dataptr) == Event::Type::PROCESSOR_EVENT
1471+
else if (baseType == Event::Type::PROCESSOR_EVENT
13381472
&& static_cast<EventChannel::Type> (*(dataptr + 1) == EventChannel::Type::TEXT))
13391473
{
13401474
TextEventPtr textEvent = TextEvent::deserialize (dataptr, getMessageChannel());

0 commit comments

Comments
 (0)