11namespace tracktion {
inline namespace engine
14inline HashCode hashDouble (
double d)
noexcept
16 static_assert (
sizeof (
double) ==
sizeof (int64_t),
"double and int64 different sizes");
17 union Val {
double asDouble;
int64_t asInt; } v;
24template <
typename FloatingPo
intType>
27 void process (
const FloatingPointType* inputSamples,
int numSamples, FloatingPointType* outputSamples)
29 for (
int i = 0; i < numSamples; ++i)
31 FloatingPointType currentSample = inputSamples[i];
32 outputSamples[i] = currentSample - lastSample;
33 lastSample = currentSample;
37 FloatingPointType lastSample { (FloatingPointType) 0.0 };
54 if (tdj->isIdenticalTo (file, config))
60 bool isIdenticalTo (
const AudioFile& f, Config c)
62 return file == f && config.sensitivity == c.sensitivity;
68 bool setUpRender()
override {
return reader !=
nullptr && totalNumSamples > 0; }
72 if (transientTimes.size() > 1)
74 auto trimTransients = [
this]() ->
bool
76 const auto minTime = 0.1s;
77 auto lastTime = transientTimes.getLast();
78 const int initialSize = transientTimes.size();
80 for (
int i = transientTimes.size() - 1; --i >= 0;)
82 const auto t = transientTimes.getUnchecked (i);
84 if ((lastTime - t) < minTime)
85 transientTimes.remove (i);
90 return initialSize != transientTimes.size();
93 for (
int i = 10; --i >= 0;)
94 if (! trimTransients())
104 auto numLeft = totalNumSamples - numSamplesRead;
105 auto numToDo = (
int)
std::min ((SampleCount) 32768, numLeft);
111 if (findingNormaliseLevel)
112 processNextNormaliseBuffer (scratch.
buffer);
114 processNextBuffer (scratch.
buffer);
116 numSamplesRead += numToDo;
117 progress = (numSamplesRead / (
float) totalNumSamples)
118 * (findingNormaliseLevel ? 0.5f : 1.0f);
120 if (findingNormaliseLevel && numSamplesRead >= totalNumSamples)
123 std::abs (fileMinMax.
getEnd()));
124 normaliseScale = peak > 0.0f ? 1.0f / peak : 1.0f;
125 reader->setReadPosition (0);
127 findingNormaliseLevel =
false;
130 return ! findingNormaliseLevel && numSamplesRead >= totalNumSamples;
137 SampleCount numSamplesRead = 0, totalNumSamples = 0;
139 double sampleRate = 0;
147 float normaliseScale = -1.0f;
149 int triggerTimer = 0;
150 int countDownTimer = 0;
151 bool findingNormaliseLevel =
true;
154 : Job (e,
AudioFile (e)), file (af), config (c),
155 totalNumSamples (af.getLengthInSamples()),
156 numChannels (af.getNumChannels()),
157 reader (e.getAudioFileManager().cache.createReader (af))
159 TRACKTION_ASSERT_MESSAGE_THREAD
164 if (reader !=
nullptr)
165 sampleRate = reader->getSampleRate();
167 triggerTimer =
int (sampleRate * 50 * 0.001);
182 AudioScratchBuffer scratch (1, numSamples);
183 auto& detectionBuffer = scratch.buffer;
185 detectionBuffer.copyFrom (0, 0, buffer, 0, 0, numSamples);
186 detectionBuffer.applyGain (0, 0, numSamples, normaliseScale);
187 float* data = detectionBuffer.getWritePointer (0);
189 envelopeFollower[0].processEnvelope (data, data, numSamples);
190 envelopeFollower[1].processEnvelope (data, data, numSamples);
192 differentiator.process (data, numSamples, data);
193 envelopeFollower[2].processEnvelope (data, data, numSamples);
195 for (
int i = 0; i < numSamples; i++)
197 const float sample = *data++;
204 if (countDownTimer == 0)
206 int rewindIndex =
int (i - sampleRate * 0.0005);
211 transientTimes.
add (sampleToSeconds (numSamplesRead + rewindIndex));
214 countDownTimer = triggerTimer;
219 TimePosition sampleToSeconds (SampleCount sample)
const
221 return TimePosition::fromSeconds (sampleRate > 0.0 ? sample / sampleRate : 0.0);
229 : edit (c.edit), clip (&c), sourceFile (c.edit.engine)
231 state = c.state.getOrCreateChildWithName (IDs::WARPTIME, &edit.
getUndoManager());
235 const auto clipLen = toPosition (TimeDuration::fromSeconds (
AudioFile (c.edit.engine, clip->
getOriginalFile()).getLength()));
238 if (markers->isEmpty())
251 : edit (e), sourceFile (f), endMarkerEnabled (false), endMarkersLimited (true)
264 if (transientDetectionJob !=
nullptr)
265 transientDetectionJob->removeListener (
this);
267 transientDetectionJob =
nullptr;
276 if (markers->isEmpty())
278 const auto clipLen = toPosition (TimeDuration::fromSeconds (sourceFile.getLength()));
280 if (sourceFile.isValid())
298 return TimeDuration::fromSeconds (
getSourceFile().getLength());
305 while (index < markers->objects.size() && markers->objects.getUnchecked (index)->warpTime < marker.warpTime)
308 auto v = createValueTree (IDs::WARPMARKER,
309 IDs::sourceTime, marker.sourceTime.
inSeconds(),
310 IDs::warpTime, marker.warpTime.
inSeconds());
312 markers->state.addChild (v, index, getUndoManager());
319 if (index == 0 || index == markers->size() - 1)
320 moveMarker (index, markers->objects.getUnchecked (index)->sourceTime);
322 markers->state.removeChild (index, getUndoManager());
327 markers->state.removeAllChildren (getUndoManager());
338 auto m = markers->state.getChild (index);
345 WarpMarker* a = markers->objects.getUnchecked (index - 1);
346 WarpMarker* b = markers->objects.getUnchecked (index);
347 auto srcLen = b->sourceTime - a->sourceTime;
348 double stretchRatio = (newWarpTime - a->warpTime) / srcLen;
350 if (stretchRatio < 0.10001)
351 newWarpTime = a->warpTime + srcLen * 0.10001;
352 else if (stretchRatio > 19.9999)
353 newWarpTime = a->warpTime + srcLen * 19.9999;
356 if (index < markers->objects.size() - 1)
358 WarpMarker* a = markers->objects.getUnchecked (index);
359 WarpMarker* b = markers->objects.getUnchecked (index + 1);
360 auto srcLen = b->sourceTime - a->sourceTime;
361 double stretchRatio = (b->warpTime - newWarpTime) / srcLen;
363 if (stretchRatio < 0.10001)
364 newWarpTime = b->warpTime - srcLen * 0.10001;
365 else if (stretchRatio > 19.9999)
366 newWarpTime = b->warpTime - srcLen * 19.9999;
369 if (endMarkersLimited && (index == 0 || (index == markers->objects.size() - 1)))
372 m.setProperty (IDs::warpTime, newWarpTime.
inSeconds(), getUndoManager());
386 auto& markersArray = markers->objects;
388 if (markersArray.isEmpty())
390 visibleWarpRegions.
add (overallTimeRegion);
391 return visibleWarpRegions;
394 auto timeRegion = overallTimeRegion;
399 if (timeRegion.getEnd() > warpedClipLength)
400 timeRegion = timeRegion.withEnd (warpedClipLength);
403 TimeRange warpRegion (overallTimeRegion.getStart(), warpedClipLength);
405 for (
int markerIndex = 0; markerIndex <= markersArray.size(); markerIndex++)
407 if (markerIndex == markersArray.size())
408 warpRegion = warpRegion.withEnd (
std::max (warpRegion.getStart(), warpedClipLength));
410 warpRegion = warpRegion.withEnd (
std::max (warpRegion.getStart(), markersArray.getUnchecked (markerIndex)->warpTime));
412 auto warpRegionConstrained = timeRegion.getIntersectionWith (warpRegion);
414 if (warpRegionConstrained.getLength() > 0s)
416 visibleWarpRegions.
add (warpRegionConstrained);
417 overallTime = overallTime + warpRegionConstrained.getLength();
420 warpRegion = warpRegion.withStart (warpRegion.getEnd());
423 return visibleWarpRegions;
428 auto& markersArray = markers->objects;
430 if (markersArray.isEmpty())
435 auto first = *markersArray.getFirst();
436 auto last = *markersArray.getLast();
438 if (warpTime <= first.warpTime)
443 else if (warpTime > last.warpTime)
447 endMarker =
WarpMarker (sourceLen, sourceLen);
452 auto numMarkers = markersArray.size();
454 while (index < numMarkers && markersArray.getUnchecked (index)->warpTime < warpTime)
458 startMarker = *markersArray.getUnchecked (index - 1);
460 endMarker = *markersArray.getUnchecked (index);
463 const WarpMarker markerRanges (toPosition (endMarker.sourceTime - startMarker.sourceTime),
464 toPosition (endMarker.warpTime - startMarker.warpTime));
468 if (markerRanges.warpTime == 0.0s)
470 sourcePosition = 0.0s;
474 const double warpProportion = (warpTime - startMarker.warpTime) / toDuration (markerRanges.warpTime);
475 sourcePosition = (markerRanges.sourceTime * warpProportion) + toDuration (startMarker.sourceTime);
478 return sourcePosition;
483 auto& markersArray = markers->objects;
485 if (markersArray.isEmpty())
491 for (
auto wm : markersArray)
496 if (before !=
nullptr && after !=
nullptr
497 && (sourceTime >= before->sourceTime && sourceTime <= after->sourceTime))
501 TimeRange source (before ==
nullptr ?
TimePosition() : before->sourceTime,
502 after ==
nullptr ? toPosition (
getSourceLength()) : after->sourceTime);
504 if (source.getLength() == 0.0s)
507 auto prop = (sourceTime - source.getStart()) / source.getLength();
509 TimeRange warped (before ==
nullptr ?
TimePosition() : before->warpTime,
512 return warped.getStart() + (warped.getLength() * prop);
517 jassert (markers->size() != 0);
519 return markers->objects.getFirst()->warpTime;
524 jassert (markers->size() != 0);
526 return markers->objects.getLast()->warpTime;
533 for (
auto wm : markers->objects)
544 return TimePosition::fromSeconds (state.
getProperty (IDs::warpEndMarkerTime, 0.0));
549void WarpTimeManager::editFinishedLoading()
552 config.sensitivity = 0.5f;
553 transientDetectionJob = TransientDetectionJob::getOrCreateDetectionJob (edit.
engine,
getSourceFile(), config);
555 if (transientDetectionJob !=
nullptr)
556 transientDetectionJob->addListener (
this);
558 editLoadedCallback =
nullptr;
566void WarpTimeManager::jobFinished (RenderManager::Job& job,
bool )
568 if (
auto tdj =
dynamic_cast<TransientDetectionJob*
> (&job))
570 transientTimes.second = tdj->getTimes();
571 transientTimes.first =
true;
574 job.removeListener (
this);
575 transientDetectionJob =
nullptr;
584 for (
auto c : warpTimeManagers)
585 if (c->clip == &clip)
599 jassert (! warpTimeManagers.contains (&wtm));
600 warpTimeManagers.addIfNotAlreadyThere (&wtm);
606 jassert (warpTimeManagers.contains (&wtm));
607 warpTimeManagers.removeAllInstancesOf (&wtm);
void add(const ElementType &newElement)
int getNumSamples() const noexcept
const Type * getReadPointer(int channelNumber) const noexcept
static AudioChannelSet JUCE_CALLTYPE stereo()
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
bool isValid() const noexcept
constexpr ValueType getStart() const noexcept
constexpr ValueType getEnd() const noexcept
constexpr Range getUnionWith(Range other) const noexcept
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
const var & getProperty(const Identifier &name) const noexcept
ValueTree getOrCreateChildWithName(const Identifier &type, UndoManager *undoManager)
Base class for Clips that produce some kind of audio e.g.
virtual juce::File getOriginalFile() const =0
Must return the file that the source ProjectItemID refers to.
virtual TimeDuration getSourceLength() const =0
Must return the length in seconds of the source material e.g.
void setCoefficients(float attack, float release) noexcept
Sets the times for the vaious stages of the envelope.
An audio scratch buffer that has pooled storage.
juce::AudioBuffer< float > & buffer
The buffer to use.
The Tracktion Edit class!
juce::UndoManager & getUndoManager() noexcept
Returns the juce::UndoManager used for this Edit.
Engine & engine
A reference to the Engine.
The Engine is the central class for all tracktion sessions.
RenderManager & getRenderManager() const
Returns the RenderManager instance.
WarpTimeFactory & getWarpTimeFactory() const
Returns the WarpTimeFactory instance.
The base class that all generator jobs derive from.
juce::ReferenceCountedArray< Job > getRenderJobsWithoutCreating(const AudioFile &)
Returns all the jobs that may be processing the given file.
WarpTimeManager::Ptr getWarpTimeManager(const Clip &)
Returns a WarpTimeManager for a given clip.
A WarpTimeManager contains a list of WarpMarkers and some source material and maps times from a linea...
void removeAllMarkers()
Removes all WarpMarkers.
TimePosition getWarpedEnd() const
Returns the endTime of the entire warped region.
juce::Array< TimeRange > getWarpTimeRegions(TimeRange overallTimeRegion) const
Time region can be longer than the clip and the returned array will loop over the clip to match the l...
bool isWarpEndMarkerEnabled() const noexcept
Returns true if the end marker is being used as the end of the source material.
TimePosition moveMarker(int index, TimePosition newWarpTime)
Moves a WarpMarker at a given index to a new time.
HashCode getHash() const
Returns a hash representing this warp list.
WarpTimeManager(AudioClipBase &)
Creates a WarpTimeManager to warp a clip.
AudioFile getSourceFile() const
Returns the current source file.
void removeMarker(int index)
Removes a WarpMarker at a given index.
TimePosition warpTimeToSourceTime(TimePosition warpTime) const
Converts a warp time (i.e.
void setSourceFile(const AudioFile &)
Sets a source fiel to warp.
void setWarpEndMarkerTime(TimePosition endTime)
Sets the end time of the source material.
~WarpTimeManager() override
Destructor.
int insertMarker(WarpMarker)
Inserts a new WarpMarker.
TimePosition sourceTimeToWarpTime(TimePosition sourceTime) const
Converts a source time (i.e.
TimePosition getWarpEndMarkerTime() const
Sets position in warped region of the redered file end point.
TimePosition getWarpedStart() const
Returns the start time of the warped region (can be -ve)
TimeDuration getSourceLength() const
Returns the length of the source file.
An audio clip that uses an audio file as its source.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Represents a duration in real-life time.
Represents a position in real-life time.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
bool setUpRender() override
Subclasses should override this to set-up their render process.
bool renderNextBlock() override
During a render process this will be repeatedly called.
bool completeRender() override
This is called once after all the render blocks have completed.
A WarpMarker is a point that maps from a linear "source" time to a "warped" time.
HashCode getHash() const noexcept
Returns a hash for this marker.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.