11namespace tracktion::inline engine
16ReadAheadTimeStretcher::ProcessThread::ProcessThread()
21ReadAheadTimeStretcher::ProcessThread::~ProcessThread()
23 waitingToExitFlag.test_and_set();
28void ReadAheadTimeStretcher::ProcessThread::addInstance (ReadAheadTimeStretcher* instance)
30 breakForAddRemoveInstance.test_and_set();
33 instances.emplace_back (instance);
35 breakForAddRemoveInstance.clear();
38void ReadAheadTimeStretcher::ProcessThread::removeInstance (ReadAheadTimeStretcher* instance)
40 breakForAddRemoveInstance.test_and_set();
43 std::erase_if (instances, [&] (
auto& i) {
return i == instance; });
45 breakForAddRemoveInstance.clear();
55void ReadAheadTimeStretcher::ProcessThread::process()
62 bool anyInstancesProcessed =
false;
68 for (
auto instance : instances)
75 anyInstancesProcessed =
false;
80 if (instance->getEpoch() > currentEpoch)
83 if (instance->processNextBlock (
false))
84 anyInstancesProcessed =
true;
90 if (! anyInstancesProcessed)
98ReadAheadTimeStretcher::ReadAheadTimeStretcher (
int numBlocksToReadAhead_)
99 : numBlocksToReadAhead (numBlocksToReadAhead_)
105 processThread->removeInstance (
this);
109 int numChannelsToUse, TimeStretcher::Mode mode, TimeStretcher::ElastiqueProOptions proOpts,
112 assert (! stretcher.isInitialised() &&
"Can only initialise once");
114 numSamplesPerOutputBlock = samplesPerBlock;
115 numChannels = numChannelsToUse;
117 stretcher.initialise (sourceSampleRate, samplesPerBlock,
118 numChannelsToUse, mode, proOpts,
124 processThread->addInstance (
this);
126 outputFifo.setSize (numChannels, samplesPerBlock * numBlocksToReadAhead);
131 return stretcher.isInitialised();
143 tryToSetNewSpeedAndPitch();
153 if (setSpeed || setPitch)
162 return stretcher.getMaxFramesNeeded();
168 return stretcher.getFramesNeeded();
190 return inputFifo.getFreeSpace();
195 assert (inputFifo.getFreeSpace() >= numSamples);
196 inputFifo.write (inChannels, numSamples);
197 processThread->flagForProcessing (epoch);
205 return outputFifo.getNumReady();
212 if (outputFifo.getNumReady() <= numSamples)
214 [[ maybe_unused ]]
const int numPopped = processNextBlock (
true);
215 assert (numPopped > 0 &&
"Not enough input frames pushed");
218 const int numToRead =
std::min (numSamples, outputFifo.getNumReady());
220 outputFifo.read (destBuffer, 0);
227 AudioScratchBuffer scratchBuffer (numChannels, numSamplesPerOutputBlock);
228 const int numFramesReturned = stretcher.flush ((
float **) scratchBuffer.buffer.getArrayOfWritePointers());
229 assert (numFramesReturned <= scratchBuffer.buffer.getNumSamples());
230 assert (outputFifo.getFreeSpace() >= numFramesReturned);
231 outputFifo.write (scratchBuffer.buffer, 0, numFramesReturned);
235 if (
const int numReady = outputFifo.getNumReady(); numReady > 0)
237 const int numToReturn =
std::min (numReady, numSamplesPerOutputBlock);
239 [[maybe_unused]]
const bool success = outputFifo.read (outputView, 0);
248void ReadAheadTimeStretcher::tryToSetNewSpeedAndPitch()
const
257int ReadAheadTimeStretcher::processNextBlock (
bool block)
259 if (outputFifo.getFreeSpace() < numSamplesPerOutputBlock)
274 if (outputFifo.getNumReady() >= numSamplesPerOutputBlock)
275 return numSamplesPerOutputBlock;
277 if (inputFifo.getNumReady() < stretcher.getFramesNeeded())
280 tryToSetNewSpeedAndPitch();
281 return stretcher.processData (inputFifo, stretcher.getFramesNeeded(), outputFifo);
int getMaxFramesNeeded() const
Returns the maximum number of frames that will ever be returned by getFramesNeeded.
void reset()
Resets the TimeStretcher ready for a new set of audio data, maintains mode, speed and pitch ratios.
bool isInitialised() const
Returns true if this has been fully initialised.
int getFramesNeeded() const
Returns the expected number of frames required to generate some output.
void initialise(double sourceSampleRate, int samplesPerBlock, int numChannels, TimeStretcher::Mode, TimeStretcher::ElastiqueProOptions, bool realtime)
Initialises the TimeStretcher ready to perform timestretching.
int getFreeSpace() const
Returns the number of samples that can be pushed to the input, ready to be processed.
~ReadAheadTimeStretcher()
Destructor.
int popData(float *const *outChannels, int numSamples)
Retrieves some samples from the output buffer.
int flush(float *const *outChannels)
Flushes the end of the stream when input data is exhausted but there is still output data available.
bool setSpeedAndPitch(float speedRatio, float semitones)
Sets the timestretch speed ratio and semitones pitch shift.
int getNumReady() const
Returns the number of samples ready in the output buffer which can be retrieved without processing th...
int pushData(const float *const *inChannels, int numSamples)
Pushes a number of samples to the instance ready to be time-stretched.
bool requiresMoreFrames() const
Returns true if more frames are required to be pushed in order for a pop operation to succeed.
int getFramesRecomended() const
Returns the number of frames that should be pushed to ensure the background thread has ample to work ...
int getNumSamples() const noexcept
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))