Skip to content

Commit 30a5569

Browse files
committed
Fix event alignment when scrubbing
1 parent e5dff8e commit 30a5569

2 files changed

Lines changed: 61 additions & 52 deletions

File tree

Source/Processors/FileReader/FileReader.cpp

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -414,20 +414,35 @@ int64 FileReader::getCurrentSample()
414414
return currentSample;
415415
}
416416

417-
void FileReader::setCurrentSample (int64 sampleNumber)
417+
void FileReader::setCurrentSample(int64 sampleNumber)
418418
{
419+
// Stop background thread before modifying shared state
420+
stopThread(100);
421+
422+
const ScopedLock sl(bufferLock);
423+
419424
currentSample = sampleNumber;
420-
this->playbackSamplePos = currentSample;
425+
playbackSamplePos.set(sampleNumber);
421426

422-
/* Reset stream to start of playback */
423-
input->seekTo (currentSample);
427+
// Reset file position
428+
input->seekTo(sampleNumber);
424429

425-
/* Pre-fills the front buffer with a blocking read */
426-
readAndFillBufferCache (bufferA);
430+
// Get the current back buffer without switching
431+
HeapBlock<float>* backBuffer = getBackBuffer();
427432

428-
readBuffer = &bufferB;
429-
bufferCacheWindow = 0;
430-
m_shouldFillBackBuffer.set (false);
433+
// Fill only the back buffer first
434+
readAndFillBufferCache(*backBuffer);
435+
436+
// Signal that we want to switch buffers on next process() call
437+
bufferCacheWindow.set(BUFFER_WINDOW_CACHE_SIZE - 1); // Force buffer switch on next process
438+
needsBufferReset.set(true);
439+
440+
// The process() thread will handle the buffer switch and trigger
441+
// the background thread to fill the new back buffer
442+
m_shouldFillBackBuffer.set(false);
443+
444+
// Restart background thread
445+
startThread();
431446
}
432447

433448
void FileReader::setPlaybackStart (int64 startSample)
@@ -661,68 +676,57 @@ String FileReader::handleConfigMessage (const String& msg)
661676

662677
void FileReader::process (AudioBuffer<float>& buffer)
663678
{
664-
bool switchNeeded = false;
679+
const ScopedLock sl(bufferLock);
665680

666-
int samplesNeededPerBuffer = int (float (buffer.getNumSamples()) * (getDefaultSampleRate() / m_sysSampleRate));
681+
if (needsBufferReset.compareAndSetBool(false, true))
682+
{
683+
// Switch to the newly prepared back buffer
684+
switchBuffer();
685+
bufferCacheWindow.set(0);
686+
}
667687

668-
m_samplesPerBuffer.set (samplesNeededPerBuffer);
669-
m_samplesPerBuffer.set (samplesNeededPerBuffer);
670-
// FIXME: needs to account for the fact that the ratio might not be an exact
671-
// integer value
672-
m_samplesPerBuffer.set (samplesNeededPerBuffer);
673-
// FIXME: needs to account for the fact that the ratio might not be an exact
674-
// integer value
688+
int samplesNeededPerBuffer = int(float(buffer.getNumSamples()) * (getDefaultSampleRate() / m_sysSampleRate));
689+
m_samplesPerBuffer.set(samplesNeededPerBuffer);
675690

676-
// if cache window id == 0, we need to read and cache BUFFER_WINDOW_CACHE_SIZE more buffer windows
677-
if (bufferCacheWindow == 0)
691+
// Handle buffer switching
692+
if (bufferCacheWindow.get() == 0)
678693
{
679694
switchBuffer();
680695
}
681696

682-
const float* tempReadBuffer = readBuffer->getData() + (samplesNeededPerBuffer * currentNumChannels * bufferCacheWindow);
697+
// Get current buffer position
698+
const float* tempReadBuffer = readBuffer->getData() +
699+
(samplesNeededPerBuffer * currentNumChannels * bufferCacheWindow.get());
683700

701+
// Copy data to output buffer
684702
for (int ch = 0; ch < currentNumChannels; ++ch)
685703
{
686-
float* writeBuffer = buffer.getWritePointer (ch);
687-
704+
float* writeBuffer = buffer.getWritePointer(ch);
688705
for (int sample = 0; sample < samplesNeededPerBuffer; sample++)
689706
{
690707
*(writeBuffer + sample) = *(tempReadBuffer + (currentNumChannels * sample) + ch);
691708
}
692-
693-
// DEPRECATED:
694-
// offset readBuffer index by current cache window count * buffer window size * num channels
695-
//input->processChannelData (*readBuffer + (samplesNeededPerBuffer * currentNumChannels * bufferCacheWindow),
696-
// buffer.getWritePointer (i, 0),
697-
// i,
698-
// samplesNeededPerBuffer);
699709
}
700710

701-
setTimestampAndSamples (playbackSamplePos, -1.0, samplesNeededPerBuffer, dataStreams[0]->getStreamId()); //TODO: Look at this could be total or playback
702-
703-
int64 start = playbackSamplePos;
704-
705-
playbackSamplePos += samplesNeededPerBuffer;
711+
// Update timestamps and sample positions atomically
712+
int64 start = playbackSamplePos.get();
713+
playbackSamplePos.set(start + samplesNeededPerBuffer);
714+
int64 stop = playbackSamplePos.get();
706715

707-
//LOGD("Total samples acquired: ", playbackSamplePos);
716+
setTimestampAndSamples(start, -1.0, samplesNeededPerBuffer, dataStreams[0]->getStreamId());
708717

709-
int64 stop = playbackSamplePos;
710-
711-
addEventsInRange (start, stop);
712-
713-
if (playbackSamplePos >= stopSample)
718+
// Handle looping
719+
if (playbackSamplePos.get() >= stopSample)
714720
{
715-
playbackSamplePos = startSample + (playbackSamplePos - stopSample);
721+
playbackSamplePos.set(startSample + (playbackSamplePos.get() - stopSample));
716722
}
717723

718-
bufferCacheWindow += 1;
719-
bufferCacheWindow %= BUFFER_WINDOW_CACHE_SIZE;
724+
// Process events for this buffer
725+
addEventsInRange(start, stop);
720726

721-
if (switchNeeded)
722-
{
723-
bufferCacheWindow = 0;
724-
this->stopThread (100);
725-
}
727+
// Update buffer window counter
728+
int newWindow = (bufferCacheWindow.get() + 1) % BUFFER_WINDOW_CACHE_SIZE;
729+
bufferCacheWindow.set(newWindow);
726730
}
727731

728732
void FileReader::addEventsInRange (int64 start, int64 stop)
@@ -761,12 +765,14 @@ int64 FileReader::millisecondsToSamples (unsigned int ms) const
761765

762766
void FileReader::switchBuffer()
763767
{
768+
const ScopedLock sl(bufferLock);
769+
764770
if (readBuffer == &bufferA)
765771
readBuffer = &bufferB;
766772
else
767773
readBuffer = &bufferA;
768774

769-
m_shouldFillBackBuffer.set (true);
775+
m_shouldFillBackBuffer.set(true);
770776
notify();
771777
}
772778

Source/Processors/FileReader/FileReader.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,13 @@ class FileReader : public GenericProcessor,
196196
bool sampleRateWarningShown;
197197

198198
/** Total number of samples played back since most recent start of acquisition */
199-
int64 playbackSamplePos;
200199

201200
float currentSampleRate;
202201
int currentNumChannels;
203202
int64 currentSample;
204203
int64 currentNumTotalSamples;
205204
int64 startSample;
206205
int64 stopSample;
207-
int64 bufferCacheWindow; // the current buffer window to read from readBuffer
208206
Array<RecordedChannelInfo> channelInfo;
209207
bool playbackActive;
210208

@@ -244,6 +242,11 @@ class FileReader : public GenericProcessor,
244242
/** Holds a path to the default file */
245243
File defaultFile;
246244

245+
CriticalSection bufferLock;
246+
Atomic<int64> playbackSamplePos;
247+
Atomic<int> bufferCacheWindow;
248+
Atomic<bool> needsBufferReset;
249+
247250
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileReader);
248251
};
249252

0 commit comments

Comments
 (0)