11#ifndef REPLACE_ELASTIQUE_WITH_DIRECT_MODE
12 #define REPLACE_ELASTIQUE_WITH_DIRECT_MODE 1
15namespace tracktion {
inline namespace engine
22 inline void zeroSamplesOutsideClipRange (choc::buffer::ChannelArrayView<float> buffer,
23 BeatRange editBeatRange,
24 BeatRange clipBeatRange)
26 if (editBeatRange.isEmpty())
29 using choc::buffer::FrameCount;
30 const auto beatsToClearAtStart = clipBeatRange.getStart() - editBeatRange.getStart();
31 const auto beatsToClearAtEnd = editBeatRange.getEnd() - clipBeatRange.getEnd();
33 const auto editBeatRangeLength = editBeatRange.getLength();
34 const auto numFrames = buffer.getNumFrames();
36 if (beatsToClearAtStart > 0_bd)
38 const auto numSamplesToClearAtStart =
std::min (numFrames,
39 static_cast<FrameCount
> (numFrames * (beatsToClearAtStart / editBeatRangeLength) + 0.5));
41 if (numSamplesToClearAtStart > 0)
42 buffer.getStart (numSamplesToClearAtStart).clear();
45 if (beatsToClearAtEnd > 0_bd)
47 const auto numSamplesToClearAtEnd =
std::min (numFrames,
48 static_cast<FrameCount
> (numFrames * (beatsToClearAtEnd / editBeatRangeLength) + 0.5));
50 if (numSamplesToClearAtEnd)
51 buffer.getEnd (numSamplesToClearAtEnd).clear();
57 #if REPLACE_ELASTIQUE_WITH_DIRECT_MODE
68 return isRendering ? m: replaceElastiqueWithDirectMode (m);
81 destChannelSet (destBufferChannels), sourceChannelSet (sourceBufferChannels)
85 choc::buffer::ChannelCount getNumChannels()
override
90 SampleCount getPosition()
override
92 return reader->getReadPosition();
95 void setPosition (SampleCount t)
override
97 reader->setReadPosition (t);
102 setPosition (toSamples (t, getSampleRate()));
105 void setLoopRange (TimeRange loopRange)
107 reader->setLoopRange (toSamples (loopRange, getSampleRate()));
110 void reset()
override
113 double getSampleRate()
override
115 return reader->getSampleRate();
118 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
120 auto buffer = toAudioBuffer (destBuffer);
121 return reader->readSamples ((
int) destBuffer.getNumFrames(),
133 const choc::buffer::ChannelCount numChannels {
static_cast<choc::buffer::ChannelCount
> (destChannelSet.
size()) };
141 : source (std::move (input))
145 choc::buffer::ChannelCount getNumChannels()
override {
return source->getNumChannels(); }
146 SampleCount getPosition()
override {
return source->getPosition(); }
147 void setPosition (SampleCount t)
override { source->setPosition (t); }
148 void setPosition (
TimePosition t)
override { source->setPosition (t); }
149 void reset()
override { source->reset(); }
150 double getSampleRate()
override {
return source->getSampleRate(); }
152 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
154 return source->readSamples (destBuffer);
170 :
SingleInputAudioReader (std::move (input)), loopRange (toSamples (loopRangeToUse, source->getSampleRate()))
174 void setPosition (SampleCount t)
override
176 const auto loopStart = loopRange.
getStart();
177 const auto loopLength = loopRange.
getLength();
182 t = loopStart + (t % loopLength);
187 source->setPosition (t);
192 setPosition (toSamples (t, getSampleRate()));
195 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
197 using choc::buffer::FrameCount;
199 const auto loopStart = loopRange.
getStart();
200 const auto loopLength = loopRange.
getLength();
203 return source->readSamples (destBuffer);
205 const auto numFrames =
static_cast<SampleCount
> (destBuffer.getNumFrames());
207 auto readPos = source->getPosition();
209 if (readPos >= loopStart + loopLength)
210 readPos -= loopLength;
212 int numSamplesToDo = (
int) numFrames;
213 SampleCount startOffsetInDestBuffer = 0;
216 while (numSamplesToDo > 0)
220 const auto numToRead =
std::min ((SampleCount) numSamplesToDo, loopStart + loopLength - readPos);
222 source->setPosition (readPos);
223 auto destSubsection = destBuffer.getFrameRange ({ (FrameCount) startOffsetInDestBuffer, (FrameCount) (startOffsetInDestBuffer + numToRead) });
224 allOk = source->readSamples (destSubsection) && allOk;
226 readPos += numToRead;
228 if (readPos >= loopStart + loopLength)
229 readPos -= loopLength;
231 startOffsetInDestBuffer += numToRead;
232 numSamplesToDo -= (
int) numToRead;
255 virtual void setGains (
float leftGain,
float rightGain) = 0;
263 :
ResamplerReader (std::move (input)), destSampleRate (sampleRateToConvertTo)
265 for (
int i = (
int) source->getNumChannels(); --i >= 0;)
268 resamplers.
back().reset();
272 void setPosition (SampleCount t)
override
274 setPosition (TimePosition::fromSamples (t, destSampleRate));
279 source->setPosition (t);
285 assert (newSpeedRatio > 0);
286 speedRatio = newSpeedRatio;
290 void setGains (
float leftGain,
float rightGain)
override
293 gains[1] = rightGain;
296 double getSampleRate()
override
298 return destSampleRate;
301 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
303 const auto numChannels = destBuffer.getNumChannels();
304 assert (numChannels <= (choc::buffer::ChannelCount) resamplers.
size());
305 assert (destBuffer.getNumChannels() == numChannels);
307 const auto ratio = sampleRatio * speedRatio;
308 const auto numFrames = destBuffer.getNumFrames();
309 const int numSourceFramesToRead =
static_cast<int> ((numFrames * ratio) + 0.5);
312 const bool ok = source->readSamples (fileDataView);
314 const auto resamplerRatio =
static_cast<double> (numSourceFramesToRead) / numFrames;
316 for (choc::buffer::ChannelCount channel = 0; channel < numChannels; ++channel)
318 if (channel < (choc::buffer::ChannelCount) resamplers.
size())
320 const auto src = fileData.buffer.getReadPointer ((
int) channel);
321 const auto dest = destBuffer.getIterator (channel).sample;
323 auto& resampler = resamplers[(
size_t) channel];
324 resampler.processAdding (resamplerRatio, src, dest, (
int) numFrames, gains[channel & 1]);
328 destBuffer.getChannel (channel).
clear();
335 const double destSampleRate;
336 const double sourceSampleRate { source->getSampleRate() };
337 const double sampleRatio { sourceSampleRate / destSampleRate };
338 double speedRatio = 1.0;
340 float gains[2] = { 1.0f, 1.0f };
350 numChannels ((
int) source->getNumChannels()),
351 destSampleRate (sampleRateToConvertTo)
353 const int converterType = [&]
355 switch (resamplingQuality)
361 default:
assert (
false);
return src::SRC_SINC_FASTEST;
366 src_state = src::src_callback_new (srcReadCallback,
367 converterType, numChannels, &error,
this);
373 src_delete (src_state);
376 SampleCount getPosition()
override
378 return getReadPosition();
381 void setPosition (SampleCount t)
override
383 if (std::abs (t - getReadPosition()) <= 1)
386 readPosition = (
double) t;
388 source->setPosition (TimePosition::fromSamples (t, destSampleRate));
390 src_reset (src_state);
395 setPosition (toSamples (t, destSampleRate));
398 double getSampleRate()
override
400 return destSampleRate;
406 assert (newSpeedRatio > 0);
407 speedRatio = newSpeedRatio;
411 void setGains (
float leftGain,
float rightGain)
override
414 gains[1] = rightGain;
417 void reset()
override
421 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
423 const auto numFramesToDo = destBuffer.getNumFrames();
424 choc::buffer::FrameCount startFrame = 0;
425 auto framesRemaining = numFramesToDo;
429 const auto numFramesThisChunk =
std::min (framesRemaining, chunkSize);
431 if (! readChunk (destBuffer.getFrameRange ({ startFrame, startFrame + numFramesThisChunk })))
434 startFrame += numFramesThisChunk;
435 framesRemaining -= numFramesThisChunk;
437 if (startFrame == numFramesToDo)
441 if (gains[0] != 1.0f || gains[1] != 1.0f)
443 choc::buffer::applyGain (destBuffer.getChannel (0), gains[0]);
445 if (destBuffer.getNumChannels() > 1)
446 choc::buffer::applyGain (destBuffer.getChannel (1), gains[1]);
452 static constexpr choc::buffer::FrameCount chunkSize = 256;
453 const int numChannels;
454 const double destSampleRate;
455 const double sourceSampleRate { source->getSampleRate() };
456 const double sampleRatio { sourceSampleRate / destSampleRate };
458 src::SRC_STATE* src_state =
nullptr;
460 choc::buffer::ChannelArrayBuffer<float> scratchBuffer { choc::buffer::createChannelArrayBuffer (numChannels, chunkSize, [] {
return 0.0f; }) };
461 choc::buffer::InterleavedBuffer<float> interleavedInputScratchBuffer { choc::buffer::createInterleavedBuffer (numChannels, chunkSize, [] {
return 0.0f; }) };
462 choc::buffer::InterleavedBuffer<float> interleavedOutputScratchBuffer { interleavedInputScratchBuffer };
464 double speedRatio = 1.0, readPosition = 0.0;
465 float gains[2] = { 1.0f, 1.0f };
466 bool failedToRead =
false;
468 SampleCount getReadPosition()
const
470 return static_cast<SampleCount
> (readPosition + 0.5);
473 static long srcReadCallback (
void* data,
float** destInterleavedSampleData)
475 return static_cast<HighQualityResamplerReader*
> (
data)->srcReadCallback (destInterleavedSampleData);
478 long srcReadCallback (
float** destInterleavedSampleData)
480 const auto numFramesToRead = interleavedInputScratchBuffer.getSize().numFrames;
483 auto scratchView = scratchBuffer.getView();
488 if (failedToRead = ! source->readSamples (scratchView); failedToRead)
492 choc::buffer::copy (interleavedInputScratchBuffer, scratchBuffer);
493 *destInterleavedSampleData = interleavedInputScratchBuffer.getView().data.data;
495 return static_cast<long> (numFramesToRead);
498 bool readChunk (
const choc::buffer::ChannelArrayView<float>& destBuffer)
500 assert (destBuffer.getNumFrames() > 0);
501 assert (destBuffer.getNumFrames() <= chunkSize);
502 assert (numChannels == (
int) destBuffer.getNumChannels());
503 const auto numFramesToDo = destBuffer.getNumFrames();
504 const auto ratio = sampleRatio * speedRatio;
507 const auto numRead = src_callback_read (src_state, 1.0 / ratio,
508 static_cast<long> (numFramesToDo),
509 interleavedOutputScratchBuffer.getView().data.data);
512 ||
static_cast<decltype (numFramesToDo)
> (numRead) != numFramesToDo)
518 choc::buffer::copy (destBuffer, interleavedOutputScratchBuffer.getStart (numFramesToDo));
519 readPosition += numFramesToDo;
535 virtual void setSpeed (
double speedRatio) = 0;
536 virtual void setPitch (
double semitones) = 0;
547 timeStretcher.
initialise (source->getSampleRate(), chunkSize, numChannels,
548 mode, elastiqueProOptions,
true);
552 source->setPosition (getReadPosition());
553 timeStretcher.
reset();
554 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
559 SampleCount getPosition()
override
561 return getReadPosition();
564 void setPosition (SampleCount t)
override
566 if (std::abs (t - getReadPosition()) <= 10)
569 readPosition = (
double) t;
571 source->setPosition (t);
572 timeStretcher.
reset();
573 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
580 setPosition (toSamples (t, getSampleRate()));
583 void reset()
override
587 void setSpeed (
double speedRatio)
override
589 if (playbackSpeedRatio == speedRatio)
592 playbackSpeedRatio = speedRatio;
593 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
596 void setPitch (
double semitones)
override
598 if (semitonesShift == semitones)
601 semitonesShift = semitones;
602 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
605 void setSpeedAndPitch (
double speedRatio,
double semitones)
607 playbackSpeedRatio = speedRatio;
608 semitonesShift = semitones;
610 [[ maybe_unused ]]
const bool ok = timeStretcher.
setSpeedAndPitch ((
float) (1.0 / speedRatio), (
float) semitonesShift);
614 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
616 assert (numChannels == (
int) destBuffer.getNumChannels());
617 const auto numFramesToDo = destBuffer.getNumFrames();
622 if (outputFifo.getNumReady() >=
int (numFramesToDo))
625 outputFifo.read (destAudioBuffer, 0);
638 if (! source->readSamples (scratchView))
641 inputFifo.write (scratchBuffer.
buffer);
645 assert (inputFifo.getNumReady() >= numThisTime);
646 assert (outputFifo.getFreeSpace() >= numThisTime);
647 assert (outputFifo.getFreeSpace() >= chunkSize);
648 timeStretcher.
processData (inputFifo, numThisTime, outputFifo);
651 readPosition += numFramesToDo * playbackSpeedRatio;
656 static constexpr int chunkSize = 256;
657 const int numChannels;
659 AudioFifo inputFifo { numChannels, chunkSize }, outputFifo { numChannels, chunkSize };
660 double playbackSpeedRatio = 1.0, semitonesShift = 0.0, readPosition = 0.0;
662 SampleCount getReadPosition()
const
664 return static_cast<SampleCount
> (readPosition + 0.5);
676 chunkSize (
std::min (blockSize, 1024))
678 timeStretcher.initialise (source->getSampleRate(), chunkSize, numChannels,
679 mode, elastiqueProOptions,
true);
681 source->setPosition (getReadPosition());
682 timeStretcher.reset();
683 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
686 SampleCount getPosition()
override
688 return getReadPosition();
691 void setPosition (SampleCount t)
override
693 if (std::abs (t - getReadPosition()) <= 10)
696 readPosition = (
double) t;
698 source->setPosition (t);
699 timeStretcher.reset();
700 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
705 setPosition (toSamples (t, getSampleRate()));
708 void reset()
override
712 void setSpeed (
double speedRatio)
override
714 if (playbackSpeedRatio == speedRatio)
717 playbackSpeedRatio = speedRatio;
718 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
721 void setPitch (
double semitones)
override
723 if (semitonesShift == semitones)
726 semitonesShift = semitones;
727 setSpeedAndPitch (playbackSpeedRatio, semitonesShift);
730 void setSpeedAndPitch (
double speedRatio,
double semitones)
732 playbackSpeedRatio = speedRatio;
733 semitonesShift = semitones;
735 [[ maybe_unused ]]
const bool ok = timeStretcher.setSpeedAndPitch ((
float) (1.0 / speedRatio), (
float) semitonesShift);
739 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
741 assert (numChannels == (
int) destBuffer.getNumChannels());
742 const auto numFramesToDo =
static_cast<int> (destBuffer.getNumFrames());
746 if (
const auto numToPush = timeStretcher.getFramesRecomended(); numToPush > 0)
747 if (! readSourceAndPushFrames (numToPush))
750 for (
auto numFramesLeft = numFramesToDo; numFramesLeft > 0;)
752 const auto maxNumFramesThisTime =
std::min (chunkSize, numFramesLeft);
757 const int numRead = timeStretcher.popData (destAudioBuffer.getArrayOfWritePointers(), maxNumFramesThisTime);
765 startFrame += numRead;
766 numFramesLeft -= numRead;
769 if (numFramesLeft > 0 && timeStretcher.requiresMoreFrames())
770 if (
const auto numToPush = timeStretcher.getFramesRecomended(); numToPush > 0)
771 if (! readSourceAndPushFrames (numToPush))
775 readPosition += numFramesToDo * playbackSpeedRatio;
780 const int numChannels, chunkSize = 1024;
781 ReadAheadTimeStretcher timeStretcher { 3 };
782 double playbackSpeedRatio = 1.0, semitonesShift = 0.0, readPosition = 0.0;
784 SampleCount getReadPosition()
const
786 return static_cast<SampleCount
> (readPosition + 0.5);
789 bool readSourceAndPushFrames (
int numSourceFrames)
791 if (numSourceFrames <= 0)
799 if (! source->readSamples (scratchView))
809 double stretchRatio = 1.0;
815 return {
time, 1.0 };
820 auto first = map.
front();
821 auto last = map.
back();
823 if (time <= first.warpTime)
825 const auto durationBefore =
time - first.warpTime;
826 return { toPosition (-durationBefore), 1.0 };
828 else if (time > last.warpTime)
830 const auto durationBeyondEnd =
time - last.warpTime;
831 return { last.sourceTime + durationBeyondEnd, 1.0 };
836 auto numMarkers = map.
size();
838 while (index < numMarkers && map[index].warpTime < time)
842 startMarker = map[index - 1];
844 endMarker = map[index];
847 const auto sourceDuration = endMarker.sourceTime - startMarker.sourceTime;
848 const auto warpedDuration = endMarker.warpTime - startMarker.warpTime;
850 TimePosition sourcePosition;
852 if (warpedDuration == 0.0s)
855 const double warpProportion = (
time - startMarker.warpTime) / warpedDuration;
856 sourcePosition = startMarker.sourceTime + (sourceDuration * warpProportion);
857 const double ratio = sourceDuration / warpedDuration;
859 return { sourcePosition, ratio };
871 reader (
static_cast<TimeStretchReader*
> (source.get())), map (std::move (warpMap))
875 SampleCount getPosition()
override
882 setPosition (toSamples (t, getSampleRate()));
885 void setPosition (SampleCount t)
override
887 if (t == readPosition)
891 setSourcePosition (t);
894 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
896 const auto unwarpedStartTime = TimePosition::fromSamples (readPosition, getSampleRate());
897 const auto ratio = warpTime (map, unwarpedStartTime).stretchRatio;
899 reader->setSpeed (ratio);
900 readPosition += (SampleCount) destBuffer.getNumFrames();
902 return reader->readSamples (destBuffer);
908 SampleCount readPosition = 0;
910 void setSourcePosition (SampleCount pos)
912 const auto sampleRate = getSampleRate();
913 const auto sourceTime = TimePosition::fromSamples (pos, sampleRate);
914 const auto warpedTime = warpTime (map, sourceTime).position;
915 const auto warpedSamplePos = toSamples (warpedTime, sampleRate);
916 source->setPosition (warpedSamplePos);
927 const tempo::Sequence& fileTempoSequence)
929 timeStretchSource (timeStretcher),
930 rootPitch (fileTempoSequence.getKeyAt (0s).pitch),
933 assert (timeStretchSource !=
nullptr);
940 timeStretchSource (timeStretcher),
941 numSemitonesShift (numSemitones)
943 assert (timeStretchSource !=
nullptr);
946 void setKey (tempo::Key newKey)
951 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
955 int pitch = key.pitch;
956 int transposeBase = pitch - rootPitch;
958 while (transposeBase > 6) transposeBase -= 12;
959 while (transposeBase < -6) transposeBase += 12;
961 timeStretchSource->setPitch (
static_cast<double> (transposeBase));
965 timeStretchSource->setPitch (
static_cast<double> (numSemitonesShift));
968 return SingleInputAudioReader::readSamples (destBuffer);
972 const int rootPitch = 60;
975 bool syncToKey =
false;
976 float numSemitonesShift = 0.0f;
999 timeStretchSource (timeStretcher)
1001 assert (timeStretcher !=
nullptr);
1004 bool read (TimeRange tr,
1005 choc::buffer::ChannelArrayView<float>& destBuffer,
1008 double playbackSpeedRatio)
1010 const auto numSourceSamples = toSamples (tr.getLength(), getSampleRate());
1012 if (numSourceSamples == 0)
1018 const auto blockSpeedRatio = (tr.getLength() / editDuration) * playbackSpeedRatio;
1020 if (timeStretchSource)
1021 timeStretchSource->setSpeed (blockSpeedRatio);
1025 setPosition (tr.getStart());
1030 return readSamples (destBuffer);
1044 double speedRatioToUse)
1045 : source (std::move (input)),
1046 clipPosition (sourceTimeRange), offset (offsetTime),
1047 speedRatio (speedRatioToUse)
1051 bool read (TimeRange editTimeRange,
1052 choc::buffer::ChannelArrayView<float>& destBuffer,
1055 double playbackSpeedRatio)
1057 const auto clipStartOffset = toDuration (clipPosition.getStart());
1058 TimeRange tr ((editTimeRange.getStart() - clipStartOffset) * speedRatio,
1059 (editTimeRange.getEnd() - clipStartOffset) * speedRatio);
1060 tr = tr + (offset * speedRatio);
1061 const auto readOk = source->read (tr, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1067 using choc::buffer::FrameCount;
1068 const auto timeToClearAtStart = editTimeRange.contains (clipPosition.getStart()) ? clipPosition.getStart() - editTimeRange.getStart() : 0_td;
1069 const auto timeToClearAtEnd = editTimeRange.contains (clipPosition.getEnd()) ? editTimeRange.getEnd() - clipPosition.getEnd() : 0_td;
1071 const auto editTimeRangeLength = editTimeRange.getLength();
1072 const auto numFrames = destBuffer.getNumFrames();
1074 if (timeToClearAtStart > 0_td)
1076 const auto numSamplesToClearAtStart =
static_cast<FrameCount
> (numFrames * (timeToClearAtStart / editTimeRangeLength) + 0.5);
1077 destBuffer.getStart (numSamplesToClearAtStart).clear();
1080 if (timeToClearAtEnd > 0_td)
1082 const auto numSamplesToClearAtEnd =
static_cast<FrameCount
> (numFrames * (timeToClearAtEnd / editTimeRangeLength) + 0.5);
1083 destBuffer.getEnd (numSamplesToClearAtEnd).clear();
1090 choc::buffer::ChannelCount getNumChannels()
override {
return source->getNumChannels(); }
1091 SampleCount getPosition()
override {
return source->getPosition(); }
1092 void setPosition (SampleCount t)
override { source->setPosition (t); }
1093 void setPosition (
TimePosition t)
override { source->setPosition (t); }
1094 void reset()
override { source->reset(); }
1095 double getSampleRate()
override {
return source->getSampleRate(); }
1097 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
1099 assert (
false &&
"Use the other read method that takes a time range");
1100 return source->readSamples (destBuffer);
1104 const TimeRange clipPosition;
1106 const double speedRatio;
1118 BeatRange loopRange_,
1122 : source (std::move (input)),
1123 loopRange (loopRange_), offset (offset_),
1124 dynamicOffset (std::move (dynamicOffset_)),
1125 sourceSequencePosition (sourceSequencePosition_)
1130 bool read (BeatRange br,
1131 choc::buffer::ChannelArrayView<float>& destBuffer,
1134 double playbackSpeedRatio)
1137 const auto beatRangeToRead = br + offset - *dynamicOffset;
1139 return readLoopedBeatRange (beatRangeToRead, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1142 choc::buffer::ChannelCount getNumChannels()
override {
return source->getNumChannels(); }
1143 SampleCount getPosition()
override {
return source->getPosition(); }
1144 void setPosition (SampleCount t)
override { source->setPosition (t); }
1145 void setPosition (
TimePosition t)
override { source->setPosition (t); }
1146 void reset()
override { source->reset(); }
1147 double getSampleRate()
override {
return source->getSampleRate(); }
1149 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
1151 return source->readSamples (destBuffer);
1156 const BeatRange loopRange;
1161 bool readLoopedBeatRange (BeatRange br,
1162 choc::buffer::ChannelArrayView<float>& destBuffer,
1165 double playbackSpeedRatio)
1167 using choc::buffer::FrameCount;
1169 if (loopRange.isEmpty())
1170 return readBeatRange (br, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1172 const auto s = linearPositionToLoopPosition (br.getStart(), loopRange);
1173 const auto e = linearPositionToLoopPosition (br.getEnd(), loopRange);
1177 if (s >= loopRange.getEnd())
1178 return readBeatRange ({ loopRange.getStart(), e }, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1180 if (e <= loopRange.getStart())
1181 return readBeatRange ({ s, loopRange.getEnd() }, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1184 const BeatRange br1 (s, loopRange.getEnd());
1185 const BeatRange br2 (loopRange.getStart(), e);
1186 const auto prop1 = br1.getLength() / br.getLength();
1187 const auto prop2 = 1.0 - prop1;
1189 const auto numFrames = destBuffer.getNumFrames();
1190 const auto numFrames1 =
static_cast<FrameCount
> (
std::llround (numFrames * prop1));
1192 auto buffer1 = destBuffer.getStart (numFrames1);
1193 auto buffer2 = destBuffer.getFrameRange ({ numFrames1, numFrames });
1195 return readBeatRange (br1, buffer1, editDuration * prop1, isContiguous, playbackSpeedRatio)
1196 && readBeatRange (br2, buffer2, editDuration * prop2,
false, playbackSpeedRatio);
1199 return readBeatRange ({ s, e }, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1202 bool readBeatRange (BeatRange br,
1203 choc::buffer::ChannelArrayView<float>& destBuffer,
1206 double playbackSpeedRatio)
1209 sourceSequencePosition.
set (br.getStart());
1210 const auto startTime = (sourceSequencePosition.
getTime());
1211 sourceSequencePosition.
set (br.getEnd());
1212 const auto endTime = (sourceSequencePosition.
getTime());
1214 return source->read ({ startTime, endTime }, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1219 return loopRange.getStart() + BeatDuration::fromBeats (
std::fmod (position.inBeats(), loopRange.getLength().inBeats()));
1229 : source (std::move (input)), clipPosition (clipPosition_),
1230 dynamicOffset (std::move (dynamicOffset_))
1235 bool read (BeatRange editBeatRange,
1236 choc::buffer::ChannelArrayView<float>& destBuffer,
1239 double playbackSpeedRatio)
1241 const auto clipBeatRange = editBeatRange - toDuration (clipPosition.getStart());
1242 const auto readOk = source->read (clipBeatRange, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1244 utils::zeroSamplesOutsideClipRange (destBuffer, editBeatRange, clipPosition + *dynamicOffset);
1249 choc::buffer::ChannelCount getNumChannels()
override {
return source->getNumChannels(); }
1250 SampleCount getPosition()
override {
return source->getPosition(); }
1251 void setPosition (SampleCount t)
override { source->setPosition (t); }
1252 void setPosition (
TimePosition t)
override { source->setPosition (t); }
1253 void reset()
override { source->reset(); }
1254 double getSampleRate()
override {
return source->getSampleRate(); }
1256 bool readSamples (choc::buffer::ChannelArrayView<float>& destBuffer)
override
1259 return source->readSamples (destBuffer);
1264 const BeatRange clipPosition;
1275 : editToClipBeatReader (std::move (beatReader)),
1276 editToClipTimeReader (std::move (timeReader))
1278 assert ((editToClipBeatReader || editToClipTimeReader) &&
"Must supply one valid reader");
1281 bool isBeatBased()
const {
return editToClipBeatReader !=
nullptr; }
1282 bool isTimeBased()
const {
return editToClipTimeReader !=
nullptr; }
1284 choc::buffer::ChannelCount getNumChannels()
const
1286 return editToClipBeatReader ? editToClipBeatReader->getNumChannels()
1287 : editToClipTimeReader->getNumChannels();
1290 bool read (BeatRange editBeatRange,
1291 TimeRange editTimeRange,
1292 choc::buffer::ChannelArrayView<float>& destBuffer,
1294 double playbackSpeedRatio)
1296 const auto editDuration = editTimeRange.getLength();
1298 if (editToClipBeatReader)
1299 return editToClipBeatReader->read (editBeatRange, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1301 if (editToClipTimeReader)
1302 return editToClipTimeReader->read (editTimeRange, destBuffer, editDuration, isContiguous, playbackSpeedRatio);
1319 : reader (std::move (editReader)),
1320 fadeDesc (speedFadeDesc),
1321 tempoPosition (std::move (editTempoPosition))
1323 for (
int i = (
int) getNumChannels(); --i >= 0;)
1326 resamplers.
back().reset();
1330 bool isBeatBased()
const {
return reader->isBeatBased(); }
1331 bool isTimeBased()
const {
return reader->isTimeBased(); }
1333 choc::buffer::ChannelCount getNumChannels()
const
1335 return reader->getNumChannels();
1338 bool read (BeatRange editBeatRange,
1339 TimeRange editTimeRange,
1340 choc::buffer::ChannelArrayView<float>& destBuffer,
1342 double playbackSpeedRatio)
1344 if (! shouldWarp() || editTimeRange.isEmpty())
1345 return reader->read (editBeatRange, editTimeRange, destBuffer, isContiguous, playbackSpeedRatio);
1347 const auto originalDuration = editTimeRange.getLength();
1348 std::tie (editBeatRange, editTimeRange) = warpTimeRanges (editBeatRange, editTimeRange);
1350 const auto editDuration = editTimeRange.getLength();
1351 const auto ratio = editDuration / originalDuration;
1353 const auto numChannels = destBuffer.getNumChannels();
1354 assert (numChannels <= (choc::buffer::ChannelCount) resamplers.
size());
1355 assert (destBuffer.getNumChannels() == numChannels);
1357 const auto numFrames = destBuffer.getNumFrames();
1358 const int numSourceFramesToRead =
static_cast<int> ((numFrames * ratio) + 0.5);
1361 sourceDataView.clear();
1362 const bool ok = reader->read (editBeatRange, editTimeRange, sourceDataView, isContiguous, playbackSpeedRatio);
1364 const auto resamplerRatio =
static_cast<double> (numSourceFramesToRead) / numFrames;
1366 for (choc::buffer::ChannelCount channel = 0; channel < numChannels; ++channel)
1368 if (channel < (choc::buffer::ChannelCount) resamplers.
size())
1371 const auto dest = destBuffer.getIterator (channel).sample;
1373 auto& resampler = resamplers[(
size_t) channel];
1374 resampler.process (resamplerRatio, src, dest, (
int) numFrames);
1378 destBuffer.getChannel (channel).
clear();
1392 bool shouldWarp()
const
1394 return tempoPosition && ! fadeDesc.isEmpty();
1400 tr = { warpTimePosition (tr.getStart()),
1401 warpTimePosition (tr.getEnd()) };
1403 tempoPosition->set (tr.getStart());
1404 const auto startBeat = tempoPosition->getBeats();
1405 tempoPosition->set (tr.getEnd());
1406 const auto endBeat = tempoPosition->getBeats();
1407 br = { startBeat, endBeat };
1414 if (! fadeDesc.inTimeRange.isEmpty()
1415 && fadeDesc.inTimeRange.containsInclusive (tp))
1417 const auto prop = (tp - fadeDesc.inTimeRange.getStart()) / fadeDesc.inTimeRange.getLength();
1418 const auto newProp = rescale (fadeDesc.fadeInType, prop,
true);
1419 return fadeDesc.inTimeRange.getStart() + (fadeDesc.inTimeRange.getLength() * newProp);
1421 else if (! fadeDesc.outTimeRange.isEmpty()
1422 && fadeDesc.outTimeRange.containsInclusive (tp))
1424 const auto prop = (tp - fadeDesc.outTimeRange.getStart()) / fadeDesc.outTimeRange.getLength();
1425 const auto newProp = rescale (fadeDesc.fadeOutType, prop,
false);
1426 return fadeDesc.outTimeRange.getStart() + (fadeDesc.outTimeRange.getLength() * newProp);
1436 case AudioFadeCurve::convex:
1440 case AudioFadeCurve::concave:
1444 case AudioFadeCurve::sCurve:
1448 case AudioFadeCurve::linear:
1450 return rampUp ? (
juce::square (proportion) * 0.5) + 0.5
1464 float lastSample = 0;
1481 editPosition (editTime),
1482 loopSection (
TimePosition::fromSeconds (loop.getStart().inSeconds() * speed),
1483 TimePosition::fromSeconds (loop.getEnd().inSeconds() * speed)),
1485 originalSpeedRatio (speed),
1486 editItemID (itemIDToUse),
1487 isOfflineRender (isRendering),
1490 channelsToUse (channelSetToUse),
1491 destChannels (destChannelsToFill)
1498 props.hasAudio =
true;
1499 props.hasMidi =
false;
1500 props.numberOfChannels = destChannels.
size();
1501 props.nodeID = (
size_t) editItemID.getRawID();
1509 outputSampleRate = info.sampleRate;
1510 editPositionInSamples = tracktion::toSamples ({ editPosition.getStart(), editPosition.getEnd() }, outputSampleRate);
1511 updateFileSampleRate();
1513 const int numChannelsToUse =
std::max (channelsToUse.
size(), reader !=
nullptr ? reader->getNumChannels() : 0);
1514 replaceChannelStateIfPossible (info.nodeGraphToReplace, numChannelsToUse);
1520 if (reader !=
nullptr)
1521 for (
int i = numChannelsToUse; --i >= 0;)
1529 if (! isOfflineRender)
1534 if (audioFile.isNull())
1537 if (reader ==
nullptr)
1541 if (reader ==
nullptr)
1545 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
1553 SCOPED_REALTIME_CHECK
1561int64_t WaveNode::editPositionToFileSample (int64_t timelinePosition)
const noexcept
1564 return editTimeToFileSample (TimePosition::fromSamples (timelinePosition, outputSampleRate));
1569 return (int64_t) ((editTime - toDuration (editPosition.getStart() - offset)).inSeconds()
1570 * originalSpeedRatio * audioFileSampleRate + 0.5);
1573bool WaveNode::updateFileSampleRate()
1575 using namespace tracktion::graph;
1577 if (reader ==
nullptr)
1580 audioFileSampleRate = reader->getSampleRate();
1582 if (audioFileSampleRate <= 0)
1585 if (! loopSection.isEmpty())
1586 reader->setLoopRange ({ tracktion::toSamples (loopSection.getStart(), audioFileSampleRate),
1587 tracktion::toSamples (loopSection.getEnd(), audioFileSampleRate) });
1592void WaveNode::replaceChannelStateIfPossible (
NodeGraph* nodeGraphToReplace,
int numChannelsToUse)
1594 const auto nodeID = (
size_t) editItemID.getRawID();
1597 if (
auto oldWaveNode = findNodeWithIDIfNonZero<WaveNode> (nodeGraphToReplace, nodeID))
1598 replaceChannelStateIfPossible (*oldWaveNode, numChannelsToUse);
1601void WaveNode::replaceChannelStateIfPossible (
WaveNode& other,
int numChannelsToUse)
1603 if (other.editItemID != editItemID)
1606 if (! other.channelState)
1609 if (other.channelState->size() == numChannelsToUse)
1610 channelState = other.channelState;
1617 if (reader ==
nullptr
1618 || sectionEditTime.getEnd() <= editPosition.getStart()
1619 || sectionEditTime.getStart() >= editPosition.getEnd())
1622 SCOPED_REALTIME_CHECK
1624 if (audioFileSampleRate == 0.0 && ! updateFileSampleRate())
1627 const auto fileStart = editTimeToFileSample (sectionEditTime.getStart());
1628 const auto fileEnd = editTimeToFileSample (sectionEditTime.getEnd());
1629 const auto numFileSamples = (
int) (fileEnd - fileStart);
1631 reader->setReadPosition (fileStart);
1633 auto destBuffer = pc.buffers.audio;
1634 auto numFrames = destBuffer.getNumFrames();
1636 auto numChannels = (choc::buffer::ChannelCount) destBufferChannels.size();
1637 assert (pc.buffers.audio.getNumChannels() == numChannels);
1643 if (numFileSamples > 0)
1645 SCOPED_REALTIME_CHECK
1647 if (reader->readSamples (numFileSamples + 2, fileData.buffer, destBufferChannels, 0,
1649 isOfflineRender ? 5000 : 3))
1652 lastSampleFadeLength =
std::min (numFrames, 40u);
1656 lastSampleFadeLength =
std::min (numFrames, 40u);
1657 fileData.buffer.clear();
1662 lastSampleFadeLength =
std::min (numFrames, 40u);
1664 for (choc::buffer::ChannelCount channel = 0; channel < numChannels; ++channel)
1666 if (channel >= (choc::buffer::ChannelCount) channelState->size())
1669 auto& state = *channelState->getUnchecked ((
int) channel);
1671 if (state.lastSample == 0.0)
1674 const auto dest = destBuffer.getIterator (channel).sample;
1676 for (uint32_t i = 0; i < lastSampleFadeLength; ++i)
1678 auto alpha = i / (
float) lastSampleFadeLength;
1679 dest[i] = state.lastSample * (1.0f - alpha);
1683 for (
auto state : *channelState)
1685 state->resampler.reset();
1686 state->lastSample = 0.0;
1692 auto ratio = numFileSamples / (
double) numFrames;
1700 if (numChannels == 2)
1711 jassert (numChannels <= (choc::buffer::ChannelCount) channelState->size());
1713 for (choc::buffer::ChannelCount channel = 0; channel < numChannels; ++channel)
1715 if (channel < (choc::buffer::ChannelCount) channelState->size())
1717 const auto src = fileData.buffer.getReadPointer ((
int) channel);
1718 const auto dest = destBuffer.getIterator (channel).sample;
1720 auto& state = *channelState->getUnchecked ((
int) channel);
1721 state.resampler.processAdding (ratio, src, dest, (
int) numFrames, gains[channel & 1]);
1723 if (lastSampleFadeLength > 0)
1725 for (uint32_t i = 0; i < lastSampleFadeLength; ++i)
1727 auto alpha = i / (
float) lastSampleFadeLength;
1728 dest[i] = alpha * dest[i] + state.lastSample * (1.0f - alpha);
1732 state.lastSample = dest[numFrames - 1];
1736 destBuffer.getChannel (channel).clear();
1743 auto numSamplesToClearAtStart =
std::min (editPositionInSamples.
getStart() - timelineRange.
getStart(), (SampleCount) destBuffer.getNumFrames());
1744 auto numSamplesToClearAtEnd =
std::min (timelineRange.
getEnd() - editPositionInSamples.
getEnd(), (SampleCount) destBuffer.getNumFrames());
1746 if (numSamplesToClearAtStart > 0)
1747 destBuffer.getStart ((choc::buffer::FrameCount) numSamplesToClearAtStart).clear();
1749 if (numSamplesToClearAtEnd > 0)
1750 destBuffer.getEnd ((choc::buffer::FrameCount) numSamplesToClearAtEnd).clear();
1776 editPositionTime (editTime),
1777 loopSectionTime (loop.rescaled (loop.getStart(), speed)),
1780 editItemID (itemIDToUse),
1781 isOfflineRender (isRendering),
1782 resamplingQuality (resamplingQualityToUse),
1784 speedFadeDescription (
std::move (speedDesc)),
1785 editTempoSequence (
std::move (editTempoSeq)),
1786 timeStretcherMode (utils::replaceElastiqueWithDirectModeIfNotRendering (mode, isRendering)),
1787 elastiqueProOptions (options),
1789 channelsToUse (channelSetToUse),
1790 destChannels (destChannelsToFill),
1791 pitchChangeSemitones (pitchChange),
1792 readAhead (readAhead_)
1795 jassert (! audioFile.isNull());
1797 auto removeRoundingError = [] (
auto d) {
return static_cast<float> (d.inSeconds()); };
1798 hash_combine (stateHash, removeRoundingError (editPositionTime.getStart()));
1799 hash_combine (stateHash, removeRoundingError (editPositionTime.getEnd()));
1800 hash_combine (stateHash, removeRoundingError (loopSectionTime.getStart()));
1801 hash_combine (stateHash, removeRoundingError (loopSectionTime.getEnd()));
1802 hash_combine (stateHash, removeRoundingError (offsetTime));
1803 hash_combine (stateHash, speedRatio);
1804 hash_combine (stateHash, editItemID.getRawID());
1805 hash_combine (stateHash, channelsToUse.
size());
1806 hash_combine (stateHash, destChannels.
size());
1807 hash_combine (stateHash, audioFile.getHash());
1808 hash_combine (stateHash, resamplingQualityToUse);
1809 hash_combine (stateHash, speedFadeDescription);
1810 hash_combine (stateHash,
static_cast<int> (timeStretcherMode));
1812 hash_combine (stateHash, pitchChangeSemitones);
1831 tempo::Sequence sourceFileTempoMap,
1838 editPositionBeats (editTime),
1839 loopSectionBeats (loop),
1841 editItemID (itemIDToUse),
1842 isOfflineRender (isRendering),
1843 resamplingQuality (resamplingQualityToUse),
1845 speedFadeDescription (
std::move (speedDesc)),
1846 editTempoSequence (
std::move (editTempoSeq)),
1847 warpMap (
std::move (warp)),
1848 timeStretcherMode (utils::replaceElastiqueWithDirectModeIfNotRendering (mode, isRendering)),
1849 elastiqueProOptions (options),
1851 channelsToUse (channelSetToUse),
1852 destChannels (destChannelsToFill),
1853 pitchChangeSemitones (pitchChange),
1854 readAhead (readAhead_)
1856 syncTempo = syncTempo_;
1857 syncPitch = syncPitch_;
1862 if (chordPitchSequence_)
1869 jassert (! audioFile.isNull());
1871 auto removeRoundingError = [] (
auto d) {
return static_cast<float> (d.inBeats()); };
1872 hash_combine (stateHash, removeRoundingError (editPositionBeats.getStart()));
1873 hash_combine (stateHash, removeRoundingError (editPositionBeats.getEnd()));
1874 hash_combine (stateHash, removeRoundingError (loopSectionBeats.getStart()));
1875 hash_combine (stateHash, removeRoundingError (loopSectionBeats.getEnd()));
1876 hash_combine (stateHash, removeRoundingError (offsetBeats));
1877 hash_combine (stateHash, editItemID.getRawID());
1878 hash_combine (stateHash, channelsToUse.
size());
1879 hash_combine (stateHash, destChannels.
size());
1880 hash_combine (stateHash, audioFile.getHash());
1881 hash_combine (stateHash, resamplingQualityToUse);
1882 hash_combine (stateHash, speedFadeDescription);
1885 hash_combine (stateHash, *warpMap);
1887 hash_combine (stateHash,
static_cast<int> (timeStretcherMode));
1890 hash_combine (stateHash, fileTempoSequence->hash());
1891 hash_combine (stateHash, syncTempo);
1892 hash_combine (stateHash, syncPitch);
1894 if (chordPitchSequence)
1895 hash_combine (stateHash, chordPitchSequence->hash());
1897 hash_combine (stateHash, pitchChangeSemitones);
1906 (*dynamicOffsetBeats) = newOffset;
1907 isFirstBlock =
true;
1914 props.hasAudio =
true;
1915 props.hasMidi =
false;
1916 props.numberOfChannels = destChannels.
size();
1917 props.nodeID = (
size_t) editItemID.getRawID();
1924 outputSampleRate = info.sampleRate;
1925 outputBlockSize = info.blockSize;
1927 replaceStateIfPossible (info.nodeGraphToReplace);
1928 buildAudioReaderGraph();
1934 if (! isOfflineRender)
1939 if (audioFile.isNull())
1942 return buildAudioReaderGraph();
1947 SCOPED_REALTIME_CHECK
1951 processSection (pc);
1955bool WaveNodeRealTime::buildAudioReaderGraph()
1963 if (audioFile.getInfo().needsCachedProxy)
1974 if (! fileCacheReader)
1977 if (fileCacheReader ==
nullptr || fileCacheReader->getSampleRate() == 0.0)
1981 destChannels, channelsToUse);
1988 loopReader =
std::make_unique<WarpReader> (std::move (audioFileCacheReader), std::move (*warpMap), timeStretcherMode, elastiqueProOptions);
1990 if (! loopSectionTime.isEmpty())
1995 audioFileCacheReader->setLoopRange (loopSectionTime);
1996 loopReader = std::move (audioFileCacheReader);
2007 resamplerReader = resamplerAudioReader.
get();
2010 if (! timestretchDisabled)
2012 if (readAhead == ReadAhead::no)
2018 auto timeStretcher = timeStretchReader.
get();
2022 if (syncPitch == SyncPitch::yes)
2024 assert (fileTempoSequence);
2026 pitchAdjustReader = pitchAdjuster.get();
2031 if (timestretchDisabled)
2037 if (pitchChangeSemitones != 0.0f)
2040 pitchAdjustReader = pitchAdjuster.get();
2050 if (syncTempo == SyncTempo::yes || syncPitch == SyncPitch::yes)
2052 assert (fileTempoSequence);
2054 loopSectionBeats, offsetBeats, dynamicOffsetBeats, *fileTempoPosition);
2069 const int numChannelsToUse =
std::max (channelsToUse.
size(), (
int) (editReader->getNumChannels()));
2071 for (
int i = numChannelsToUse; --i >= 0;)
2072 channelState->emplace_back (0.0f);
2077 isFirstBlock = ! isOfflineRender;
2082void WaveNodeRealTime::replaceStateIfPossible (
NodeGraph* nodeGraphToReplace)
2084 if (nodeGraphToReplace ==
nullptr)
2092 if (
auto oldWaveNode = findNodeWithID<WaveNodeRealTime> (*nodeGraphToReplace, (
size_t) editItemID.getRawID()))
2093 replaceStateIfPossible (*oldWaveNode);
2098 if (other.editItemID != editItemID)
2102 channelState = other.channelState;
2104 if (other.stateHash != stateHash)
2107 fileTempoSequence = other.fileTempoSequence;
2108 fileTempoPosition = other.fileTempoPosition;
2109 resamplerReader = other.resamplerReader;
2110 editReader = other.editReader;
2111 pitchAdjustReader = other.pitchAdjustReader;
2112 dynamicOffsetBeats = other.dynamicOffsetBeats;
2121 assert (destChannels.
size() == (
int) pc.buffers.audio.getNumChannels());
2123 if (editReader ==
nullptr)
2126 if (editReader->isTimeBased()
2127 && (sectionEditTime.getEnd() <= editPositionTime.getStart()
2128 || sectionEditTime.getStart() >= editPositionTime.getEnd()))
2131 if (editReader->isBeatBased()
2132 && (sectionEditBeats.getEnd() <= (editPositionBeats.getStart() + *dynamicOffsetBeats)
2133 || sectionEditBeats.getStart() >= (editPositionBeats.getEnd() + *dynamicOffsetBeats)))
2136 auto destBuffer = pc.buffers.audio;
2137 const auto numFrames = destBuffer.getNumFrames();
2138 const auto numChannels = destBuffer.getNumChannels();
2144 if (numChannels == 2)
2155 if (resamplerReader !=
nullptr)
2156 resamplerReader->
setGains (gains[0], gains[1]);
2158 if (pitchAdjustReader !=
nullptr)
2159 pitchAdjustReader->setKey (getKeyToSyncTo (sectionEditTime.getStart()));
2163 uint32_t lastSampleFadeLength = isFirstBlock ?
std::min (numFrames, 10u) : 0;
2164 isFirstBlock =
false;
2166 if (editReader->read (sectionEditBeats, sectionEditTime, pc.buffers.audio, isContiguous,
getPlaybackSpeedRatio()))
2169 lastSampleFadeLength =
std::min (numFrames, 40u);
2173 lastSampleFadeLength =
std::min (numFrames, 40u);
2174 isFirstBlock =
true;
2178 jassert (numChannels <= (choc::buffer::ChannelCount) channelState->size());
2180 for (choc::buffer::ChannelCount channel = 0; channel < numChannels; ++channel)
2182 if (channel < (choc::buffer::ChannelCount) channelState->size())
2184 const auto dest = pc.buffers.audio.getIterator (channel).sample;
2186 auto& lastSample = (*channelState)[(
size_t) channel];
2188 if (lastSampleFadeLength > 0)
2190 for (uint32_t i = 0; i < lastSampleFadeLength; ++i)
2192 auto alpha = i / (
float) lastSampleFadeLength;
2193 dest[i] = alpha * dest[i] + lastSample * (1.0f - alpha);
2197 lastSample = dest[numFrames - 1];
2201 destBuffer.getChannel (channel).clear();
2206tempo::Key WaveNodeRealTime::getKeyToSyncTo (
TimePosition editPosition)
const
2208 if (chordPitchPosition)
2210 chordPitchPosition->set (editPosition);
2211 return chordPitchPosition->getKey();
const Type * getReadPointer(int channelNumber) const noexcept
const Type *const * getArrayOfReadPointers() const noexcept
int size() const noexcept
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
constexpr ValueType getStart() const noexcept
constexpr ValueType getEnd() const noexcept
constexpr ValueType getLength() const noexcept
int hashCode() const noexcept
Base class for audio based readers that can be chained together.
Reader::Ptr createReader(const AudioFile &)
Creates a Reader to read an AudioFile.
An audio scratch buffer that has pooled storage.
juce::AudioBuffer< float > & buffer
The buffer to use.
AudioFileManager & getAudioFileManager() const
Returns the AudioFileManager instance.
BufferedAudioFileManager & getBufferedAudioFileManager()
Returns the BufferedAudioFileManager instance.
void setSpeedRatio(double newSpeedRatio) override
Sets a ratio to increase or decrease playback speed.
void setGains(float leftGain, float rightGain) override
Sets a l/r gain to apply to channels.
void setGains(float leftGain, float rightGain) override
Sets a l/r gain to apply to channels.
void setSpeedRatio(double newSpeedRatio) override
Sets a ratio to increase or decrease playback speed.
virtual void setGains(float leftGain, float rightGain)=0
Sets a l/r gain to apply to channels.
virtual void setSpeedRatio(double newSpeedRatio)=0
Sets a ratio to increase or decrease playback speed.
Handles time/pitch stretching using various supported libraries.
void initialise(double sourceSampleRate, int samplesPerBlock, int numChannels, Mode, ElastiqueProOptions, bool realtime)
Initialises the TimeStretcher ready to perform timestretching.
int getFramesNeeded() const
Returns the expected number of frames required to generate some output.
void reset()
Resets the TimeStretcher ready for a new set of audio data, maintains mode, speed and pitch ratios.
int getMaxFramesNeeded() const
Returns the maximum number of frames that will ever be returned by getFramesNeeded.
int processData(const float *const *inChannels, int numSamples, float *const *outChannels)
Processes some input frames and fills some output frames with the applied speed ratio and pitch shift...
bool setSpeedAndPitch(float speedRatio, float semitones)
Sets the timestretch speed ratio and semitones pitch shift.
Mode
Holds the various algorithms to which can be used (if enabled).
@ elastiqueDirectPro
Elastique Direct Pro good all round (.
@ elastiquePro
Elastique Pro good all round (.
@ disabled
No algorithm enabled.
@ elastiqueDirectEfficient
Elastique Direct lower quality and lower CPU usage.
@ elastiqueDirectMobile
Elastique Direct lower quality and lower CPU usage, optimised for mobile.
@ elastiqueEfficient
Elastique lower quality and lower CPU usage.
@ elastiqueMobile
Elastique lower quality and lower CPU usage, optimised for mobile.
Base class for Nodes that provides information about the current process call.
TimeRange getEditTimeRange() const
Returns the edit time range of the current process block.
double getPlaybackSpeedRatio() const
Returns the playback speed ratio of the current process block.
BeatRange getEditBeatRange() const
Returns the edit beat range of the current process block.
tempo::Key getKey() const
Returns the key of the current process block.
juce::Range< int64_t > getTimelineSampleRange() const
Returns the timeline sample range of the current process block.
double getSampleRate() const
Returns the sample rate of the current process block.
tracktion::graph::PlayHeadState & getPlayHeadState()
Returns the PlayHeadState in use.
tracktion::graph::PlayHead & getPlayHead()
Returns the PlayHead in use.
An Node that plays back a wave file.
bool isReadyToProcess() override
Should return true when this node is ready to be processed.
SyncTempo
Represets whether the file should try and match Edit tempo changes.
graph::NodeProperties getNodeProperties() override
Should return the properties of the node.
void prepareToPlay(const graph::PlaybackInitialisationInfo &) override
Called once before playback begins for each node.
void setDynamicOffsetBeats(BeatDuration) override
Sets an offset to be applied to all times in this node, effectively shifting it forwards or backwards...
SyncPitch
Represets whether the file should try and match Edit pitch changes.
WaveNodeRealTime(const AudioFile &, TimeRange editTime, TimeDuration offset, TimeRange loopSection, LiveClipLevel, double speedRatio, const juce::AudioChannelSet &sourceChannelsToUse, const juce::AudioChannelSet &destChannelsToFill, ProcessState &, EditItemID, bool isOfflineRender, ResamplingQuality=ResamplingQuality::lagrange, SpeedFadeDescription={}, std::optional< tempo::Sequence::Position > editTempoSequence={}, TimeStretcher::Mode=TimeStretcher::Mode::defaultMode, TimeStretcher::ElastiqueProOptions={}, float pitchChangeSemitones=0.0f, ReadAhead=ReadAhead::no)
offset is a time added to the start of the file, e.g.
void process(ProcessContext &) override
Called when the node is to be processed.
ReadAhead
Whether or not to use a background thread to read ahead the time-stretch buffer.
An Node that plays back a wave file.
tracktion::graph::NodeProperties getNodeProperties() override
Should return the properties of the node.
void process(ProcessContext &) override
Called when the node is to be processed.
void prepareToPlay(const tracktion::graph::PlaybackInitialisationInfo &) override
Called once before playback begins for each node.
bool isReadyToProcess() override
Should return true when this node is ready to be processed.
WaveNode(const AudioFile &, TimeRange editTime, TimeDuration offset, TimeRange loopSection, LiveClipLevel, double speedRatio, const juce::AudioChannelSet &sourceChannelsToUse, const juce::AudioChannelSet &destChannelsToFill, ProcessState &, EditItemID, bool isOfflineRender)
offset is a time added to the start of the file, e.g.
Struct to describe a single iteration of a process call.
bool isContiguousWithPreviousBlock() noexcept
Returns true if the play head did not jump and this block is contiguous with the previous block.
bool isUserDragging() const
Returns true if the user is dragging.
T emplace_back(T... args)
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
constexpr NumericType square(NumericType n) noexcept
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
IntegerType negativeAwareModulo(IntegerType dividend, const IntegerType divisor) noexcept
Interpolators::Lagrange LagrangeInterpolator
choc::buffer::BufferView< SampleType, choc::buffer::SeparateChannelLayout > toBufferView(juce::AudioBuffer< SampleType > &buffer)
Converts a juce::AudioBuffer<SampleType> to a choc::buffer::BufferView.
ResamplingQuality
Specifies a number of resampling qualities that can be used.
@ lagrange
Lagrange interpolation.
@ sincBest
Best quality sinc interpolation provided by libsamplerate.
@ sincFast
Fast sinc interpolation provided by libsamplerate.
@ sincMedium
Medium quality sinc interpolation provided by libsamplerate.
juce::AudioBuffer< float > toAudioBuffer(choc::buffer::ChannelArrayView< float > view)
Creates a juce::AudioBuffer from a choc::buffer::BufferView.
choc::buffer::FrameRange createFrameRange(std::integral auto start, std::integral auto end)
Creates a FrameRange from any integral type.
TimeRange timeRangeFromSamples(juce::Range< int64_t > sampleRange, double sampleRate)
Creates a TimeRange from a range of samples.
Represents a duration in beats.
constexpr double inBeats() const
Returns the position as a number of beats.
Represents a position in beats.
Represents a duration in real-life time.
constexpr double inSeconds() const
Returns the TimeDuration as a number of seconds.
Represents a position in real-life time.
A Sequence::Position is an iterator through a Sequence.
TimePosition getTime() const
Returns the current time of the Position.
void set(TimePosition)
Sets the Position to a new time.
Type
A enumeration of the curve classes available.
ID for objects of type EditElement - e.g.
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.
Holds the state of a process call.
Describes the time and type of the speed fade in/outs.
A set of options that can be used in conjunction with the elastiquePro Mode to fine tune the algorith...
juce::String toString() const
Save the current options as a string.
Holds a graph in an order ready for processing and a sorted map for quick lookups.
Holds some really basic properties of a node.
Passed into Nodes when they are being initialised, to give them useful contextual information that th...