11namespace tracktion {
inline namespace engine
21 : editPosition (editTime),
22 loopSection (loop.getStart() * speed, loop.getEnd() * speed),
24 originalSpeedRatio (speed),
27 channelsToUse (channelSetToUse)
31WaveAudioNode::~WaveAudioNode()
41 audioFile.getNumChannels());
49void WaveAudioNode::visitNodes (
const VisitorFn& v)
54SampleCount WaveAudioNode::editTimeToFileSample (
double editTime)
const noexcept
56 return static_cast<SampleCount
> ((editTime - (editPosition.getStart() - offset))
57 * originalSpeedRatio * audioFileSampleRate + 0.5);
71 outputSampleRate = info.sampleRate;
72 updateFileSampleRate();
76 if (reader !=
nullptr)
77 for (
int i =
std::max (channelsToUse.
size(), reader->getNumChannels()); --i >= 0;)
81bool WaveAudioNode::isReadyToRender()
85 if (audioFile.isNull())
88 if (reader ==
nullptr)
92 if (reader ==
nullptr)
96 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
102bool WaveAudioNode::updateFileSampleRate()
104 if (reader !=
nullptr)
106 audioFileSampleRate = reader->getSampleRate();
108 if (audioFileSampleRate > 0)
110 if (! loopSection.isEmpty())
111 reader->setLoopRange (SampleRange ((SampleCount) (loopSection.getStart() * audioFileSampleRate),
112 (SampleCount) (loopSection.getEnd() * audioFileSampleRate)));
128 callRenderAdding (rc);
133 invokeSplitRender (rc, *
this);
136void WaveAudioNode::renderSection (
const AudioRenderContext& rc, legacy::EditTimeRange editTime)
139 const auto localReader = reader;
143 if (rc.destBuffer ==
nullptr
144 || rc.bufferNumSamples == 0
145 || localReader ==
nullptr
146 || editTime.getStart() >= editPosition.getEnd())
149 SCOPED_REALTIME_CHECK
151 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
154 const auto fileStart = editTimeToFileSample (editTime.getStart());
155 const auto fileEnd = editTimeToFileSample (editTime.getEnd());
156 const auto numFileSamples = (
int) (fileEnd - fileStart);
158 localReader->setReadPosition (fileStart);
160 AudioScratchBuffer fileData (rc.destBufferChannels.size(), numFileSamples + 2);
162 int lastSampleFadeLength = 0;
165 SCOPED_REALTIME_CHECK
167 if (localReader->readSamples (numFileSamples + 2, fileData.buffer, rc.destBufferChannels, 0,
169 rc.isRendering ? 5000 : 3))
171 if (! rc.isContiguousWithPreviousBlock() && ! rc.isFirstBlockOfLoop())
172 lastSampleFadeLength =
std::min (rc.bufferNumSamples, rc.playhead.isUserDragging() ? 40 : 10);
176 lastSampleFadeLength =
std::min (rc.bufferNumSamples, 40);
177 fileData.buffer.clear();
184 if (rc.destBuffer->getNumChannels() == 2)
189 if (rc.playhead.isUserDragging())
195 auto ratio = numFileSamples / (
double) rc.bufferNumSamples;
199 auto numDestChannels =
std::min (rc.destBuffer->getNumChannels(), fileData.buffer.getNumChannels());
200 jassert (numDestChannels <= channelState.size());
202 for (
int channel = 0; channel < numDestChannels; ++channel)
204 if (channel < channelState.size())
206 const auto src = fileData.buffer.getReadPointer (channel);
207 const auto dest = rc.destBuffer->getWritePointer (channel, rc.bufferStartSample);
209 auto& state = *channelState.getUnchecked (channel);
210 state.resampler.processAdding (ratio, src, dest, rc.bufferNumSamples, gains[channel & 1]);
212 if (lastSampleFadeLength > 0)
214 for (
int i = 0; i < lastSampleFadeLength; ++i)
216 auto alpha = i / (
float) lastSampleFadeLength;
217 dest[i] = alpha * dest[i] + state.lastSample * (1.0f - alpha);
221 state.lastSample = dest[rc.bufferNumSamples - 1];
225 rc.destBuffer->clear (channel, rc.bufferStartSample, rc.bufferNumSamples);
231void WaveAudioNode::prepareForNextBlock (
const AudioRenderContext& rc)
233 SCOPED_REALTIME_CHECK
236 if (
auto localReader = reader)
237 localReader->setReadPosition (editTimeToFileSample (rc.getEditTime().editRange1.getStart()));
int size() const noexcept
Reader::Ptr createReader(const AudioFile &)
Creates a Reader to read an AudioFile.
AudioFileManager & getAudioFileManager() const
Returns the AudioFileManager instance.
bool purgeSubNodes(bool keepAudio, bool keepMidi) override
Tells the node to delete any sub-nodes that don't produce the required type of output.
WaveAudioNode(const AudioFile &file, legacy::EditTimeRange editTime, double offset, legacy::EditTimeRange loopSection, LiveClipLevel level, double speedRatio, const juce::AudioChannelSet &channelsToUse)
offset is a time added to the start of the file, e.g.
void releaseAudioNodeResources() override
tells the node that play has stopped, and it can free up anything it no longer needs.
void prepareAudioNodeToPlay(const PlaybackInitialisationInfo &) override
tells the node to initialise itself ready for playing from the given time.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Interpolators::Lagrange LagrangeInterpolator
Holds some really basic properties of a node.
Passed into AudioNodes when they are being initialised, to give them useful contextual information th...
Provides a thread-safe way to share a clip's levels with an audio engine without worrying about the C...
float getGainIncludingMute() const noexcept
Returns the clip's gain if the clip is not muted.
void getLeftAndRightGains(float &left, float &right) const noexcept
Reutrns the left and right gains taking in to account mute and pan values.