28 editPosition (editTime),
29 loopSection (loop.getStart() * speed, loop.getEnd() * speed),
31 originalSpeedRatio (speed),
34 channelsToUse (channels)
42 info.numberOfChannels =
juce::jlimit (1, channelsToUse.
size(), audioFile.getNumChannels());
45 void visitNodes (
const VisitorFn& v)
override
58 outputSampleRate = info.sampleRate;
59 updateFileSampleRate();
63 bool isReadyToRender()
override
67 if (audioFile.isNull())
70 if (reader ==
nullptr)
74 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
87 callRenderAdding (rc);
92 invokeSplitRender (rc, *
this);
95 void prepareForNextBlock (
const AudioRenderContext& rc)
override
100 if (
const auto localReader = reader)
101 localReader->setReadPosition (
static_cast<SampleCount
> (editTimeToFileSample (rc.getEditTime().editRange1.getStart()) + 0.5) - 5);
104 void renderSection (
const AudioRenderContext& rc, legacy::EditTimeRange editTime)
107 const auto localReader (reader);
111 if (rc.destBuffer ==
nullptr
112 || rc.bufferNumSamples == 0
113 || localReader ==
nullptr
114 || editTime.getStart() >= editPosition.getEnd())
117 SCOPED_REALTIME_CHECK
119 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
122 auto fileStart = editTimeToFileSample (editTime.getStart());
123 auto fileEnd = editTimeToFileSample (editTime.getEnd());
126 auto fileReadStart =
static_cast<SampleCount
> (
std::ceil (fileStart)) - preRead;
127 auto fileReadEnd =
static_cast<SampleCount
> (
std::ceil (fileEnd));
128 auto numSamplesToRead = (
int) (fileReadEnd - fileReadStart);
130 AudioScratchBuffer scratchBuffer (
juce::jmin (2, rc.destBuffer->getNumChannels()), numSamplesToRead + 2);
133 localReader->setReadPosition (fileReadStart);
135 int lastSampleFadeLength = 0;
138 SCOPED_REALTIME_CHECK
140 if (localReader->readSamples (numSamplesToRead + 2, scratchBuffer.buffer, scratchBufferChannels, 0,
142 rc.isRendering ? 5000 : 3))
144 if (rc.isFirstBlockOfLoop() || ! rc.isContiguousWithPreviousBlock())
145 lastSampleFadeLength = rc.playhead.isUserDragging() ? 40 : 10;
149 lastSampleFadeLength = 40;
150 scratchBuffer.buffer.clear();
156 if (rc.destBuffer->getNumChannels() > 1)
161 if (rc.playhead.isUserDragging())
168 const double ratio = (fileEnd - fileStart) / (
double) rc.bufferNumSamples;
169 const double subSamplePos = fileStart - (
int) fileStart;
171 for (
int channel = rc.destBuffer->getNumChannels(); --channel >= 0;)
173 const int srcChan =
juce::jmin (channel, scratchBuffer.buffer.getNumChannels() - 1);
174 const float*
const src = scratchBuffer.buffer.getReadPointer (srcChan);
176 const float gain = gains[channel & 1];
181 li.processAdding (1.0, src, dest, preRead - 1, gain);
182 li.processAdding (subSamplePos, src + preRead - 1, dest, 1, gain);
187 for (
int channel = rc.destBuffer->getNumChannels(); --channel >= 0;)
189 const int srcChan =
juce::jmin (channel, scratchBuffer.buffer.getNumChannels() - 1);
190 const float*
const src = scratchBuffer.buffer.getReadPointer (srcChan, preRead);
191 float*
const dest = rc.destBuffer->getWritePointer (channel, rc.bufferStartSample);
193 resampler[channel].processAdding (ratio, src, dest, rc.bufferNumSamples, gains[channel & 1]);
195 if (lastSampleFadeLength > 0)
197 const int fadeSamps =
juce::jmin (lastSampleFadeLength, rc.bufferNumSamples);
199 for (
int i = 0; i < fadeSamps; ++i)
201 const float alpha = i / (
float) fadeSamps;
202 dest[i] = alpha * dest[i] + lastSample[channel] * (1.0f - alpha);
206 lastSample[channel] = dest[rc.bufferNumSamples - 1];
215 legacy::EditTimeRange editPosition, loopSection;
217 double originalSpeedRatio, outputSampleRate = 44100.0;
220 LiveClipLevel clipLevel;
221 double audioFileSampleRate = 0;
223 AudioFileCache::Reader::Ptr reader;
226 float lastSample[8] = {};
228 double editTimeToFileSample (
double editTime)
const noexcept
230 return (editTime - (editPosition.getStart() - offset)) * originalSpeedRatio * audioFileSampleRate;
233 bool updateFileSampleRate()
235 if (reader !=
nullptr)
237 audioFileSampleRate = reader->getSampleRate();
239 if (audioFileSampleRate > 0)
241 if (! loopSection.isEmpty())
242 reader->setLoopRange (SampleRange ((SampleCount) (loopSection.getStart() * audioFileSampleRate),
243 (SampleCount) (loopSection.getEnd() * audioFileSampleRate)));
252 void resetResamplers()
255 resampler[i].reset();
258 lastSample[i] = 0.0f;
275 fadeIn (in), fadeOut (out),
276 fadeInType (fadeInType_),
277 fadeOutType (fadeOutType_)
279 jassert (! (fadeIn.isEmpty() && fadeOut.isEmpty()));
285 if (renderingNeeded (rc))
286 invokeSplitRender (rc, *
this);
288 input->renderOver (rc);
293 if (renderingNeeded (rc))
296 input->renderAdding (rc);
301 const bool intersectsFadeIn = fadeIn.getLength() > 0.0 && editTime.overlaps (fadeIn);
302 const bool intersectsFadeOut = fadeOut.getLength() > 0.0 && editTime.overlaps (fadeOut);
304 if (intersectsFadeIn && intersectsFadeOut)
308 auto sampleRate = numSamples / editTime.getLength();
313 auto fadeInTime = fadeInSection.getLength();
318 renderRampSection (rc2, fadeInSection, fadeIn,
true);
332 renderRampSection (rc2, fadeOut.getIntersectionWith (editTime), fadeOut,
false);
338 else if (intersectsFadeIn)
340 renderRampSection (rc, editTime, fadeIn,
true);
342 else if (intersectsFadeOut)
344 renderRampSection (rc, editTime, fadeOut,
false);
360 if (editTime.isSplit)
361 return fadeIn.overlaps (editTime.editRange1)
362 || fadeIn.overlaps (editTime.editRange2)
363 || fadeOut.overlaps (editTime.editRange1)
364 || fadeOut.overlaps (editTime.editRange2);
366 return fadeIn.overlaps (editTime.editRange1)
367 || fadeOut.overlaps (editTime.editRange1);
375 auto timeBefore = fade.getStart() - editTime.getStart();
377 if (timeBefore > 0.0)
386 startSample += numSamples;
388 input->renderOver (rc2);
392 auto editTimeIntersection = fade.getIntersectionWith (editTime);
394 if (editTimeIntersection.getLength() > 0.0)
396 auto startAlpha = (editTimeIntersection.getStart() - fade.getStart()) / fade.getLength();
397 auto endAlpha = (editTimeIntersection.getEnd() - fade.getStart()) / fade.getLength();
400 auto startProp = rescale (t, startAlpha, rampUp);
401 auto endProp = rescale (t, endAlpha, rampUp);
407 fade.getStart() + fade.getLength() * endProp);
409 auto numSamples =
juce::roundToInt (editTimeIntersection.getLength() * sampleRate);
410 auto streamDiff = rc.
streamTime.getStart() - editTime.getStart();
415 startSample += numSamples;
417 input->renderOver (rc2);
420 auto timeAfter = editTime.getEnd() - fade.getEnd();
432 startSample += numSamples;
434 input->renderOver (rc2);
446 case AudioFadeCurve::convex:
450 case AudioFadeCurve::concave:
454 case AudioFadeCurve::sCurve:
458 case AudioFadeCurve::linear:
int bufferNumSamples
The number of samples to write into the audio buffer.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.
PlayHead & playhead
The playhead provides information about current time, tempo etc at the block being rendered.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
PlayHead::EditTimeWindow getEditTime() const
Returns the section of the edit that needs to be rendered by this block.
legacy::EditTimeRange streamTime
The time window which needs to be rendered into the current block.