11namespace tracktion {
inline namespace engine
17 :
juce::AudioFormatReader (nullptr, sourceReader->getFormatName()),
18 source (sourceReader), thread (timeSliceThread),
19 isFullyBuffering (samplesToBuffer < 0)
33 numBlocksToBuffer = samplesToBuffer > -1 ?
static_cast<size_t> (1 + (samplesToBuffer / samplesPerBlock))
34 : totalNumSlotsRequired;
42 for (
size_t i = 0; i < numBlocksToBuffer; ++i)
45 static_assert (
alignof (BufferedBlock*) >= 8);
62 timeoutMs = timeoutMilliseconds;
67 return isFullyBuffering
68 && numBlocksBuffered == numBlocksToBuffer;
71bool BufferedFileReader::readSamples (
int*
const* destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
75 nextReadPosition = startSampleInFile;
81 bool allSamplesRead =
true;
82 bool hasNotified =
false;
84 while (numSamples > 0)
87 auto ssa = ScopedSlotAccess::fromPosition (*
this, startSampleInFile);
89 if (
auto block = ssa.getBlock())
91 jassert (block->range.contains (startSampleInFile));
94 block->lastUseTime = startTime;
96 auto offset = (
int) (startSampleInFile - block->range.getStart());
97 auto numToDo =
std::min (numSamples, (
int) (block->range.getEnd() - startSampleInFile));
99 for (
int j = 0; j < numDestChannels; ++j)
101 if (
auto dest = (
float*) destSamples[j])
103 dest += startOffsetInDestBuffer;
106 juce::FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
108 juce::FloatVectorOperations::clear (dest, numToDo);
112 startOffsetInDestBuffer += numToDo;
113 startSampleInFile += numToDo;
114 numSamples -= numToDo;
116 allSamplesRead = allSamplesRead && block->allSamplesRead;
129 for (
int j = 0; j < numDestChannels; ++j)
130 if (
auto dest = (
float*) destSamples[j])
131 juce::FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
133 allSamplesRead =
false;
143 return allSamplesRead;
147 : buffer ((
int) reader.numChannels, samplesPerBlock)
154 const int numSamples = (
int) newSampleRange.
getLength();
155 range = newSampleRange;
159 allSamplesRead = reader.
read (&buffer, 0, numSamples, (
int) range.getStart(),
true,
true);
160 assert (slotIndex ==
static_cast<int> (currentSlotIndex));
161 slotIndex =
static_cast<int> (currentSlotIndex);
166BufferedFileReader::ScopedSlotAccess::ScopedSlotAccess (BufferedFileReader& reader_,
size_t slotIndex_)
167 : reader (reader_), slotIndex (slotIndex_)
169 assert (slotIndex < reader.slots.size());
171 reader.markSlotUseState (slotIndex,
true);
172 block = reader.slots[slotIndex];
175BufferedFileReader::ScopedSlotAccess::~ScopedSlotAccess()
178 reader.markSlotUseState (slotIndex,
false);
181BufferedFileReader::ScopedSlotAccess BufferedFileReader::ScopedSlotAccess::fromPosition (BufferedFileReader& reader,
juce::int64 position)
183 return { reader,
static_cast<size_t> (position / (
float) samplesPerBlock) };
186void BufferedFileReader::ScopedSlotAccess::setBlock (BufferedBlock* blockToReferTo)
189 block->slotIndex = -1;
191 block = blockToReferTo;
194 block->slotIndex =
static_cast<int> (slotIndex);
196 reader.slots[slotIndex] = block;
199int BufferedFileReader::useTimeSlice()
203 switch (readNextBufferChunk())
205 case PositionStatus::positionChangedByAudioThread:
break;
206 case PositionStatus::nextChunkScheduled:
return 1;
207 case PositionStatus::blocksFull:
return 5;
208 case PositionStatus::fullyLoaded:
return 100;
213BufferedFileReader::PositionStatus BufferedFileReader::readNextBufferChunk()
216 return PositionStatus::fullyLoaded;
221 const auto currentReadPosition = nextReadPosition.
load();
222 const auto currentSlotIndex = getSlotIndexFromSamplePosition (currentReadPosition);
227 ScopedSlotAccess currentSlot (*
this, currentSlotIndex);
229 if (
auto currentBlockInCurrentSlot = currentSlot.getBlock())
232 assert (currentBlockInCurrentSlot->range == getSlotRange (currentSlotIndex));
235 if (! currentBlockInCurrentSlot->allSamplesRead)
236 nextSlotScheduled = currentSlotIndex;
240 const auto slotToReadIndex = nextSlotScheduled.
load();
241 bool readScheduledSlot =
true;
245 ScopedSlotAccess scheduledSlot (*
this, slotToReadIndex);
248 if (
auto currentBlockInScheduledSlot = scheduledSlot.getBlock())
249 if (currentBlockInScheduledSlot->allSamplesRead)
250 readScheduledSlot =
false;
254 if (readScheduledSlot)
259 BufferedBlock* blockToUse =
nullptr;
261 for (
size_t i = 0; i < blocks.
size(); ++i)
263 auto& block = blocks[i];
266 if (useTime > oldestTime)
269 blockToUse = block.get();
270 oldestTime = useTime;
275 if (curentBlockSlotIndex < 0)
279 assert (blockToUse !=
nullptr);
281 ScopedSlotAccess desiredSlot (*
this, slotToReadIndex);
283 if (blockToUseSlotIndex < 0)
286 desiredSlot.setBlock (blockToUse);
291 ScopedSlotAccess slotWithOldestBlock (*
this,
static_cast<size_t> (blockToUseSlotIndex));
292 assert (blockToUse == slotWithOldestBlock.getBlock());
295 slotWithOldestBlock.setBlock (
nullptr);
296 desiredSlot.setBlock (blockToUse);
301 blockToUse->update (*source, slotSampleRange, slotToReadIndex);
304 if (blockToUseSlotIndex < 0 && blockToUse->allSamplesRead)
331 if (nextReadPosition == currentReadPosition)
334 const auto startSlot =
std::max (
size_t (0), getSlotIndexFromSamplePosition (currentReadPosition));
335 nextSlotScheduled = (startSlot + 1) % slots.
size();
337 return PositionStatus::nextChunkScheduled;
341 return PositionStatus::positionChangedByAudioThread;
344size_t BufferedFileReader::getSlotIndexFromSamplePosition (
juce::int64 samplePos)
346 return static_cast<size_t> (samplePos / (
float) samplesPerBlock);
354 return { slotStartSamplePos, slotEndSamplePos };
357void BufferedFileReader::markSlotUseState (
size_t slotIndex,
bool isInUse)
360 auto& slotInUseState = slotsInUse[slotIndex];
361 auto expected = ! isInUse;
365 if (slotInUseState.compare_exchange_weak (expected, isInUse))
368 expected = ! isInUse;
constexpr ValueType getEnd() const noexcept
constexpr ValueType getLength() const noexcept
static void JUCE_CALLTYPE yield()
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
void moveToFrontOfQueue(TimeSliceClient *clientToMove)
static uint32 getMillisecondCounter() noexcept
~BufferedFileReader() override
Destructor.
bool isFullyBuffered() const
Returns true if this has been initialised to buffer the whole file once that is complete,...
void setReadTimeout(int timeoutMilliseconds) noexcept
Sets a number of milliseconds that the reader can block for in its readSamples() method before giving...
BufferedFileReader(juce::AudioFormatReader *sourceReader, juce::TimeSliceThread &timeSliceThread, int samplesToBuffer)
Creates a reader.