Skip to content

Commit 39307fd

Browse files
committed
Merge branch 'harp-timestamps-and-recording-optimization' of into lfp-viewer-filter-car`
2 parents 144a2fb + 2ab622b commit 39307fd

9 files changed

Lines changed: 211 additions & 63 deletions

File tree

Source/Processors/FileReader/BinaryFileSource/BinaryFileSource.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ void BinaryFileSource::fillRecordInfo()
306306

307307
void BinaryFileSource::processEventData (EventInfo& eventInfo, int64 start, int64 stop)
308308
{
309+
const ScopedLock sl (m_readLock);
309310
int64 local_start = start % getActiveNumSamples();
310311
int64 local_stop = stop % getActiveNumSamples();
311312

@@ -333,6 +334,7 @@ void BinaryFileSource::processEventData (EventInfo& eventInfo, int64 start, int6
333334

334335
void BinaryFileSource::updateActiveRecord (int index)
335336
{
337+
const ScopedLock sl (m_readLock);
336338
m_dataFile.reset();
337339
m_dataFile = std::make_unique<MemoryMappedFile> (m_dataFileArray[index], MemoryMappedFile::readOnly);
338340
m_samplePos = 0;
@@ -348,11 +350,16 @@ void BinaryFileSource::updateActiveRecord (int index)
348350

349351
void BinaryFileSource::seekTo (int64 sample)
350352
{
353+
const ScopedLock sl (m_readLock);
351354
m_samplePos = sample % getActiveNumSamples();
352355
}
353356

354357
int BinaryFileSource::readData (float* buffer, int nSamples)
355358
{
359+
const ScopedLock sl (m_readLock);
360+
361+
if (m_dataFile == nullptr || buffer == nullptr || nSamples <= 0 || numActiveChannels <= 0)
362+
return 0;
356363
int64 samplesToRead;
357364

358365
if (m_samplePos + nSamples > getActiveNumSamples())
@@ -366,6 +373,9 @@ int BinaryFileSource::readData (float* buffer, int nSamples)
366373

367374
int16* data = static_cast<int16*> (m_dataFile->getData()) + (m_samplePos * numActiveChannels);
368375

376+
if (samplesToRead <= 0)
377+
return 0;
378+
369379
for (int i = 0; i < samplesToRead * numActiveChannels; i++)
370380
{
371381
*(buffer + i) = *(data + i) * bitVolts[i % numActiveChannels];

Source/Processors/FileReader/BinaryFileSource/BinaryFileSource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class BinaryFileSource : public FileSource
6666
void processEventData (EventInfo& info, int64 fromSampleNumber, int64 toSampleNumber) override;
6767

6868
private:
69+
CriticalSection m_readLock;
6970
int numActiveChannels;
7071
Array<float> bitVolts;
7172

Source/Processors/FileReader/FileReader.cpp

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ FileReader::FileReader() : GenericProcessor ("File Reader"),
4646
startSample (0),
4747
stopSample (0),
4848
bufferCacheWindow (0),
49+
needsBufferReset (false),
4950
m_shouldFillBackBuffer (false),
5051
m_bufferSize (1024),
5152
m_sysSampleRate (44100),
@@ -381,6 +382,8 @@ void FileReader::setActiveStream (int index, bool reset)
381382
channelInfo.add (input->getChannelInfo (index, i));
382383

383384
input->seekTo (startSample);
385+
firstProcess = false;
386+
needsBufferReset.set (false);
384387

385388
updateSettings();
386389
CoreServices::updateSignalChain (this);
@@ -421,6 +424,15 @@ bool FileReader::startAcquisition()
421424

422425
checkAudioDevice();
423426

427+
{
428+
const ScopedLock sl(bufferLock);
429+
readAndFillBufferCache (bufferA);
430+
readBuffer = &bufferA;
431+
bufferCacheWindow = 0;
432+
needsBufferReset.set (false);
433+
m_shouldFillBackBuffer.set (true);
434+
}
435+
424436
/* Start asynchronous file reading thread */
425437
startThread();
426438

@@ -449,8 +461,10 @@ int64 FileReader::getCurrentSample()
449461

450462
void FileReader::setCurrentSample(int64 sampleNumber)
451463
{
464+
const bool wasThreadRunning = isThreadRunning();
452465
// Stop background thread before modifying shared state
453-
stopThread(100);
466+
if (wasThreadRunning)
467+
stopThread(100);
454468

455469
const ScopedLock sl(bufferLock);
456470

@@ -460,22 +474,34 @@ void FileReader::setCurrentSample(int64 sampleNumber)
460474
// Reset file position
461475
input->seekTo(sampleNumber);
462476

463-
// Get the current back buffer without switching
464-
HeapBlock<float>* backBuffer = getBackBuffer();
465-
466-
// Fill only the back buffer first
467-
readAndFillBufferCache(*backBuffer);
477+
if (wasThreadRunning)
478+
{
479+
// Get the current back buffer without switching
480+
HeapBlock<float>* backBuffer = getBackBuffer();
481+
readAndFillBufferCache(*backBuffer);
482+
}
468483

469-
// Signal that we want to switch buffers on next process() call
470-
bufferCacheWindow.set(BUFFER_WINDOW_CACHE_SIZE - 1); // Force buffer switch on next process
471-
needsBufferReset.set(true);
484+
if (wasThreadRunning)
485+
{
486+
// Signal that we want to switch buffers on next process() call
487+
bufferCacheWindow.set(BUFFER_WINDOW_CACHE_SIZE - 1); // Force buffer switch on next process
488+
needsBufferReset.set(true);
472489

473-
// The process() thread will handle the buffer switch and trigger
474-
// the background thread to fill the new back buffer
475-
m_shouldFillBackBuffer.set(false);
490+
// The process() thread will handle the buffer switch and trigger
491+
// the background thread to fill the new back buffer
492+
m_shouldFillBackBuffer.set(false);
493+
}
494+
else
495+
{
496+
bufferCacheWindow.set(0);
497+
needsBufferReset.set(false);
498+
readBuffer = &bufferA;
499+
m_shouldFillBackBuffer.set(false);
500+
}
476501

477502
// Restart background thread
478-
startThread();
503+
if (wasThreadRunning)
504+
startThread();
479505
}
480506

481507
void FileReader::setPlaybackStart (int64 startSample)
@@ -626,14 +652,9 @@ void FileReader::updateSettings()
626652
input->seekTo (startSample);
627653
currentSample = startSample;
628654

629-
/* Pre-fills the front buffer with a blocking read */
630-
readAndFillBufferCache (bufferA);
631-
632-
readBuffer = &bufferB;
655+
readBuffer = &bufferA;
633656
bufferCacheWindow = 0;
634657
m_shouldFillBackBuffer.set (false);
635-
if (firstProcess)
636-
switchBuffer();
637658

638659
LOGD ("File Reader finished updating custom settings.");
639660
}
@@ -660,11 +681,9 @@ void FileReader::checkAudioDevice()
660681
input->seekTo (startSample);
661682
currentSample = startSample;
662683

663-
/* Pre-fills the front buffer with a blocking read */
664-
readAndFillBufferCache (bufferA);
665-
666-
readBuffer = &bufferB;
684+
readBuffer = &bufferA;
667685
bufferCacheWindow = 0;
686+
needsBufferReset.set (false);
668687
m_shouldFillBackBuffer.set (false);
669688
}
670689
}
@@ -846,38 +865,44 @@ void FileReader::readAndFillBufferCache (HeapBlock<float>& cacheBuffer)
846865
{
847866
const int samplesNeededPerBuffer = m_samplesPerBuffer.get();
848867
const int samplesNeeded = samplesNeededPerBuffer * BUFFER_WINDOW_CACHE_SIZE;
868+
if (samplesNeeded <= 0 || stopSample <= startSample)
869+
return;
849870

850871
int samplesRead = 0;
851872

852873
// should only loop if reached end of file and resuming from start
853874
while (samplesRead < samplesNeeded)
854875
{
855876
int samplesToRead = samplesNeeded - samplesRead;
877+
int samplesJustRead = 0;
878+
879+
const bool wrapsAtEnd = (currentSample + samplesToRead) > stopSample;
856880

857881
// if reached end of file stream
858-
if ((currentSample + samplesToRead) > stopSample)
882+
if (wrapsAtEnd)
859883
{
860884
samplesToRead = int (stopSample - currentSample);
861885
if (samplesToRead > 0)
862-
input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
886+
samplesJustRead = input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
863887

864888
// reset stream to beginning
865889
input->seekTo (startSample);
866890
currentSample = startSample;
867891
}
868892
else // else read the block needed
869893
{
870-
input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
871-
872-
currentSample += samplesToRead;
894+
samplesJustRead = input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
895+
currentSample += samplesJustRead;
873896
}
874897

875-
samplesRead += samplesToRead;
898+
samplesRead += samplesJustRead;
876899

877900
//LOGD("CURRENT SAMPLE: ", currentSample, " samplesRead: ", samplesRead, " samplesNeeded: ", samplesNeeded);
878-
879901
if (samplesRead < 0)
880902
return;
903+
904+
if (samplesJustRead <= 0 && ! wrapsAtEnd)
905+
return;
881906
}
882907
}
883908

Source/Processors/RecordNode/BinaryFormat/BinaryRecording.cpp

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ void BinaryRecording::openFiles (File rootFolder, int experimentNumber, int reco
9797

9898
if (streamId != lastStreamId)
9999
{
100-
wroteFirstSampleNumber[streamId] = false;
101-
102100
firstChannels.add (channelInfo);
103101
streamIndex++;
104102

@@ -678,12 +676,6 @@ void BinaryRecording::writeContinuousData (int writeChannel,
678676

679677
uint32 streamId = getContinuousChannel (realChannel)->getStreamId();
680678

681-
if (! wroteFirstSampleNumber[streamId])
682-
{
683-
firstSampleNumber[streamId] = baseSampleNumber;
684-
wroteFirstSampleNumber[streamId] = true;
685-
}
686-
687679
/* Generate sequential sample numbers using SIMD-optimized fill */
688680
SIMDConverter::fillSequentialInt64 (reinterpret_cast<int64_t*> (m_sampleNumberBuffer.getData()), baseSampleNumber, size);
689681

@@ -792,6 +784,7 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
792784
int numSamples,
793785
int fileIndex)
794786
{
787+
ignoreUnused (fileIndex);
795788
if (numSamples == 0 || numChannels == 0)
796789
return;
797790

@@ -826,8 +819,12 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
826819
m_batchIntBufferPtrs.resize (numChannels);
827820
}
828821

822+
// Resolve the file index from recorded channel mapping.
823+
// This is robust even when some source streams have zero recorded channels.
824+
const int resolvedFileIndex = m_fileIndexes[writeChannels[0]];
825+
829826
// Get file and validate
830-
if (fileIndex < 0 || fileIndex >= m_continuousFiles.size() || ! m_continuousFiles[fileIndex])
827+
if (resolvedFileIndex < 0 || resolvedFileIndex >= m_continuousFiles.size() || ! m_continuousFiles[resolvedFileIndex])
831828
return;
832829

833830
// Setup scale factors and output buffer pointers for each channel
@@ -849,12 +846,12 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
849846
// Get starting sample position (all channels in a stream have same position)
850847
uint64 startPos = m_samplesWritten[writeChannels[0]];
851848

852-
//LOGD("BinaryRecording::writeContinuousDataBatch: Writing ", numSamples, " samples for ", numChannels, " channels at position ", startPos, " to file index ", fileIndex);
849+
//LOGD("BinaryRecording::writeContinuousDataBatch: Writing ", numSamples, " samples for ", numChannels, " channels at position ", startPos, " to file index ", resolvedFileIndex);
853850

854851
// Try batch interleaving if we have all channels for this file
855852
// The file's channel count is determined by the stream's channel count
856853
// If we have a partial batch, fall back to per-channel writes
857-
bool useBatchWrite = m_continuousFiles[fileIndex] != nullptr && m_continuousFiles[fileIndex]->writeChannelBatch (startPos, m_batchIntBufferPtrs.data(), numChannels, numSamples);
854+
bool useBatchWrite = m_continuousFiles[resolvedFileIndex] != nullptr && m_continuousFiles[resolvedFileIndex]->writeChannelBatch (startPos, m_batchIntBufferPtrs.data(), numChannels, numSamples);
858855

859856
if (! useBatchWrite)
860857
{
@@ -863,7 +860,7 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
863860
{
864861
int writeChannel = writeChannels[i];
865862
int channelIdx = m_channelIndexes[writeChannel];
866-
m_continuousFiles[fileIndex]->writeChannel (
863+
m_continuousFiles[resolvedFileIndex]->writeChannel (
867864
startPos,
868865
channelIdx,
869866
m_batchIntBufferPtrs[i],
@@ -886,20 +883,14 @@ void BinaryRecording::writeContinuousDataBatch (const int* writeChannels,
886883
int64 baseSampleNumber = getLatestSampleNumber (writeChannels[i]);
887884
uint32 streamId = getContinuousChannel (realChannels[i])->getStreamId();
888885

889-
if (! wroteFirstSampleNumber[streamId])
890-
{
891-
firstSampleNumber[streamId] = baseSampleNumber;
892-
wroteFirstSampleNumber[streamId] = true;
893-
}
894-
895886
/* Generate sequential sample numbers using SIMD-optimized fill */
896887
SIMDConverter::fillSequentialInt64 (reinterpret_cast<int64_t*> (m_sampleNumberBuffer.getData()), baseSampleNumber, numSamples);
897888

898-
m_dataTimestampFiles[fileIndex]->writeData (m_sampleNumberBuffer, numSamples * sizeof (int64));
899-
m_dataTimestampFiles[fileIndex]->increaseRecordCount (numSamples);
889+
m_dataTimestampFiles[resolvedFileIndex]->writeData (m_sampleNumberBuffer, numSamples * sizeof (int64));
890+
m_dataTimestampFiles[resolvedFileIndex]->increaseRecordCount (numSamples);
900891

901-
m_dataSyncTimestampFiles[fileIndex]->writeData (timestampBuffer, numSamples * sizeof (double));
902-
m_dataSyncTimestampFiles[fileIndex]->increaseRecordCount (numSamples);
892+
m_dataSyncTimestampFiles[resolvedFileIndex]->writeData (timestampBuffer, numSamples * sizeof (double));
893+
m_dataSyncTimestampFiles[resolvedFileIndex]->increaseRecordCount (numSamples);
903894
break;
904895
}
905896
}
@@ -913,13 +904,7 @@ void BinaryRecording::writeTimestampSyncText (uint64 streamId, int64 sampleNumbe
913904
String syncString = text + ": " + String (sampleNumber);
914905
LOGD (syncString);
915906

916-
int64 fsn = firstSampleNumber[streamId];
917-
918-
if (streamId > 0)
919-
jassert (fsn == sampleNumber);
920-
921907
m_syncTextFile->writeText (syncString + "\r\n", false, false, nullptr);
922-
// Note: flush removed - file will be flushed on close or by OS buffering
923908
}
924909

925910
RecordEngineManager* BinaryRecording::getEngineManager()

Source/Processors/RecordNode/BinaryFormat/BinaryRecording.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,6 @@ class BinaryRecording : public RecordEngine
141141
int m_experimentNum;
142142
Array<int64> m_samplesWritten;
143143

144-
std::map<uint64, int64> firstSampleNumber;
145-
std::map<uint64, bool> wroteFirstSampleNumber;
146-
147144
const int samplesPerBlock { 8192 }; // Larger blocks reduce memory block operations
148145
};
149146
#endif

Source/Processors/RecordNode/BinaryFormat/SIMDConverter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <cstdint>
3333
#include <string>
34+
#include "../../../TestableExport.h"
3435

3536
/**
3637
* SIMD-optimized float-to-int16 conversion utilities.
@@ -47,7 +48,7 @@
4748
* (FloatVectorOperations::copyWithMultiply + AudioDataConverters::convertFloatToInt16LE)
4849
* into a single pass, eliminating the intermediate buffer and improving cache utilization.
4950
*/
50-
class SIMDConverter
51+
class TESTABLE SIMDConverter
5152
{
5253
public:
5354
/**

0 commit comments

Comments
 (0)