11#include "../../playback/audionodes/tracktion_AudioNode.h"
12#include "../../playback/audionodes/tracktion_WaveAudioNode.h"
13#include "../../playback/audionodes/tracktion_TrackCompAudioNode.h"
14#include "../../playback/audionodes/tracktion_SpeedRampAudioNode.h"
15#include "../../playback/audionodes/tracktion_PluginAudioNode.h"
16#include "../../playback/audionodes/tracktion_FadeInOutAudioNode.h"
18namespace tracktion {
inline namespace engine
21static inline HashCode hashValueTree (HashCode startHash,
const juce::ValueTree& v)
25 for (
int i = v.getNumProperties(); --i >= 0;)
26 startHash ^= v[v.getPropertyName (i)].toString().hashCode64();
28 for (
int i = v.getNumChildren(); --i >= 0;)
29 startHash ^= hashValueTree (startHash, v.getChild (i));
34static inline HashCode hashPlugin (
const juce::ValueTree& effectState, Plugin& plugin)
39 for (
int param = plugin.getNumAutomatableParameters(); --param >= 0;)
41 if (
auto ap = plugin.getAutomatableParameter (param))
43 auto& ac = ap->getCurve();
45 if (ac.getNumPoints() == 0)
51 for (
int i = 0; i < ac.getNumPoints(); ++i)
53 const auto p = ac.getPoint (i);
77 void invalidateCache()
79 clipEffects.cachedClipProperties =
nullptr;
80 clipEffects.cachedHash = ClipEffects::hashNeedsRecaching;
93 else if (
matchesAnyOf (i, { speed, loopStart, loopLength, loopStartBeats, loopLengthBeats, autoTempo }))
103 void valueTreeChildOrderChanged (
juce::ValueTree&,
int,
int)
override {}
106 void handleAsyncUpdate()
override
108 clipEffects.invalidateAllEffects();
121ClipEffects::~ClipEffects()
132struct VariantConverter<
tracktion::engine::ClipEffect::EffectType>
134 static tracktion::engine::ClipEffect::EffectType fromVar (
const var& v)
140 if (s ==
"volume")
return ClipEffect::EffectType::volume;
141 if (s ==
"fadeInOut")
return ClipEffect::EffectType::fadeInOut;
142 if (s ==
"tapeStartStop")
return ClipEffect::EffectType::tapeStartStop;
143 if (s ==
"stepVolume")
return ClipEffect::EffectType::stepVolume;
144 if (s ==
"pitchShift")
return ClipEffect::EffectType::pitchShift;
145 if (s ==
"warpTime")
return ClipEffect::EffectType::warpTime;
146 if (s ==
"normalise")
return ClipEffect::EffectType::normalise;
147 if (s ==
"makeMono")
return ClipEffect::EffectType::makeMono;
148 if (s ==
"reverse")
return ClipEffect::EffectType::reverse;
149 if (s ==
"invert")
return ClipEffect::EffectType::invert;
150 if (s ==
"filter")
return ClipEffect::EffectType::filter;
152 return ClipEffect::EffectType::none;
155 static var toVar (
const tracktion::engine::ClipEffect::EffectType& t)
161 case ClipEffect::EffectType::none:
return {};
162 case ClipEffect::EffectType::volume:
return "volume";
163 case ClipEffect::EffectType::fadeInOut:
return "fadeInOut";
164 case ClipEffect::EffectType::tapeStartStop:
return "tapeStartStop";
165 case ClipEffect::EffectType::stepVolume:
return "stepVolume";
166 case ClipEffect::EffectType::pitchShift:
return "pitchShift";
167 case ClipEffect::EffectType::warpTime:
return "warpTime";
168 case ClipEffect::EffectType::normalise:
return "normalise";
169 case ClipEffect::EffectType::makeMono:
return "makeMono";
170 case ClipEffect::EffectType::reverse:
return "reverse";
171 case ClipEffect::EffectType::invert:
return "invert";
172 case ClipEffect::EffectType::filter:
return "filter";
179}
namespace tracktion {
inline namespace engine {
186 static constexpr SampleCount defaultBlockSize = 1024;
190 SampleCount blockSizeToUse)
191 : engine (e), destination (dest),
192 source (src), blockSize (blockSizeToUse)
217 const SampleCount blockSize;
225 : edit (o.clip.edit), state (v), clipEffects (o), destinationFile (edit.engine)
227 state.addListener (
this);
232 return createValueTree (IDs::EFFECT,
236void ClipEffect::createEffectAndAddToValueTree (
Edit& edit,
juce::ValueTree parent, ClipEffect::EffectType effectType,
int index)
240 if (effectType == EffectType::filter)
245 af->setProcessingEnabled (
false);
246 af->flushPluginStateToValueTree();
248 auto v = createValueTree (IDs::EFFECT,
251 v.addChild (af->state, -1,
nullptr);
254 parent.
addChild (v, index, &undoManager);
270 parent.
addChild (ClipEffect::create (effectType), index, &undoManager);
274juce::String ClipEffect::getTypeDisplayName (EffectType t)
278 case EffectType::volume:
return TRANS(
"Volume");
279 case EffectType::fadeInOut:
return TRANS(
"Fade in/out");
280 case EffectType::tapeStartStop:
return TRANS(
"Tape stop/start");
281 case EffectType::stepVolume:
return TRANS(
"Step volume");
282 case EffectType::pitchShift:
return TRANS(
"Pitch shift");
283 case EffectType::warpTime:
return TRANS(
"Warp time");
284 case EffectType::normalise:
return TRANS(
"Normalise");
285 case EffectType::makeMono:
return TRANS(
"Mono");
286 case EffectType::reverse:
return TRANS(
"Reverse");
287 case EffectType::invert:
return TRANS(
"Phase Invert");
288 case EffectType::filter:
return TRANS(
"Plugin");
289 case EffectType::none:
290 default:
return "<" +
TRANS (
"None") +
">";
298 m.addSectionHeader (heading);
301 m.addItem ((
int) e, getTypeDisplayName (e));
304 addItems (
TRANS(
"Volume"), { EffectType::fadeInOut, EffectType::stepVolume, EffectType::volume });
305 addItems (
TRANS(
"Time/Pitch"), { EffectType::pitchShift, EffectType::tapeStartStop, EffectType::warpTime });
306 addItems (
TRANS(
"Effects"), { EffectType::filter, EffectType::reverse });
307 addItems (
TRANS(
"Mastering"), { EffectType::makeMono, EffectType::normalise, EffectType::invert });
310ClipEffect::EffectType ClipEffect::getType()
const
315HashCode ClipEffect::getIndividualHash()
const
317 return hashValueTree (0, state);
323 auto index = parent.
indexOf (state);
324 HashCode hash = index ^
static_cast<HashCode
> (clipEffects.clip.
itemID.getRawID());
326 for (
int i = 0; i <= index; ++i)
327 if (
auto ce = clipEffects.getClipEffect (parent.
getChild (i)))
328 hash ^= ce->getIndividualHash() * (i + 1);
334AudioFile ClipEffect::getSourceFile()
const
336 if (
auto ce = clipEffects.getClipEffect (state.
getSibling (-1)))
337 return ce->getDestinationFile();
342AudioFile ClipEffect::getDestinationFile()
const
344 if (destinationFile.isNull())
345 destinationFile = TemporaryFileManager::getFileForCachedFileRender (edit,
getHash());
347 return destinationFile;
352 return clipEffects.clip;
360bool ClipEffect::isUsingFile (
const AudioFile& af)
const
362 return getDestinationFile() == af;
365void ClipEffect::valueTreeChanged()
367 clipEffects.effectChanged();
370void ClipEffect::invalidateDestination()
382 SampleCount blockSizeToUse = defaultBlockSize,
383 double prerollTimeSeconds = 0)
384 : ClipEffectRenderJob (e, dest, src, blockSizeToUse),
385 node (n), prerollTime (prerollTimeSeconds)
393 callBlocking ([
this] { createAndPrepareRenderContext(); });
405 bool ok = progress == 1.0f;
409 ok = destination.deleteFile();
412 ok = renderContext->writer->file.getFile().moveFileTo (destination.getFile());
416 renderContext->writer->file.deleteFile();
424 int numDestChannels, SampleCount blockSizeToUse,
double prerollTimeS)
425 : blockSize (blockSizeToUse)
430 streamRange = { 0.0, source.getLength() };
431 jassert (! streamRange.isEmpty());
433 auto sourceInfo = source.getInfo();
434 jassert (sourceInfo.numChannels > 0 && sourceInfo.sampleRate > 0.0 && sourceInfo.bitsPerSample > 0);
441 if (sourceInfo.metadata.getValue (
"MetaDataSource",
"None") ==
"AIFF")
442 sourceInfo.metadata.clear();
445 numDestChannels, sourceInfo.sampleRate,
446 std::max (16, sourceInfo.bitsPerSample),
447 sourceInfo.metadata, 0);
454 renderingBuffer.get(),
455 renderingBufferChannels, 0, (
int) blockSize,
457 AudioRenderContext::playheadJumped,
true);
460 numPreBlocks = (
int)
std::ceil ((prerollTimeS * sourceInfo.sampleRate) / blockSize);
462 auto prerollTimeRounded = numPreBlocks * blockSizeToUse / writer->getSampleRate();
464 streamTime = -prerollTimeRounded;
466 auto prerollStart = streamRange.getStart() - prerollTimeRounded;
468 localPlayhead.setPosition (prerollStart);
469 localPlayhead.playLockedToEngine ({ prerollStart, streamRange.getEnd() });
475 auto blockLength = blockSize / writer->getSampleRate();
476 SampleCount samplesToWrite =
juce::roundToInt ((streamRange.getEnd() - streamTime) * writer->getSampleRate());
477 auto blockEnd =
std::min (streamTime + blockLength, streamRange.getEnd());
478 rc->streamTime = { streamTime, blockEnd };
481 if (numPreBlocks-- > 0)
483 audioNode.prepareForNextBlock (*rc);
484 audioNode.renderOver (*rc);
486 rc->continuity = AudioRenderContext::contiguous;
487 streamTime = blockEnd;
492 auto numSamplesDone = (
int)
std::min (samplesToWrite, blockSize);
493 rc->bufferNumSamples = numSamplesDone;
495 if (numSamplesDone > 0)
497 audioNode.prepareForNextBlock (*rc);
498 audioNode.renderOver (*rc);
501 rc->continuity = AudioRenderContext::contiguous;
502 streamTime = blockEnd;
504 const float prog = (
float) ((streamTime - streamRange.getStart()) / streamRange.getLength()) * 0.9f;
508 if (numSamplesDone <= 0 || ! writer->isOpen()
509 || ! writer->appendBuffer (*renderingBuffer, numSamplesDone))
512 localPlayhead.stop();
513 writer->closeForWriting();
515 if (numSamplesDone <= 0)
516 progressToUpdate = 1.0f;
524 const SampleCount blockSize;
525 int numPreBlocks = 0;
532 double streamTime = 0;
537 const double prerollTime = 0;
539 void createAndPrepareRenderContext()
543 node->getAudioNodeProperties (props);
546 blockSize, prerollTime);
551 allNodes.
add (node.
get());
556 renderContext->writer->getSampleRate(),
557 (
int) renderContext->blockSize,
559 renderContext->rc->playhead
562 node->prepareAudioNodeToPlay (info);
574 sourceLengthSeconds (sourceLength)
581 auto sourceInfo = source.getInfo();
582 jassert (sourceInfo.numChannels > 0 && sourceInfo.sampleRate > 0.0 && sourceInfo.bitsPerSample > 0);
585 if (sourceInfo.metadata.getValue (
"MetaDataSource",
"None") ==
"AIFF")
586 sourceInfo.metadata.clear();
588 reader.reset (AudioFileUtils::createReaderFor (engine, source.getFile()));
590 if (reader ==
nullptr || reader->lengthInSamples == 0)
593 sourceLengthSamples =
static_cast<SampleCount
> (sourceLengthSeconds * reader->sampleRate);
596 sourceInfo.numChannels, sourceInfo.sampleRate,
597 std::max (16, sourceInfo.bitsPerSample),
598 sourceInfo.metadata, 0);
600 return writer->isOpen();
616 double sourceLengthSeconds = 0;
617 SampleCount position = 0, sourceLengthSamples = 0;
619 SampleCount getNumSamplesForCurrentBlock()
const
621 return std::min (blockSize, sourceLengthSamples - position);
635 clip (c), warpTimeManager (wtm)
638 auto tm = clip.getTimeStretchMode();
641 proxyInfo->speedRatio = 1.0;
642 proxyInfo->mode = (tm != TimeStretcher::disabled && tm != TimeStretcher::melodyne)
643 ? tm : TimeStretcher::defaultMode;
652 proxyInfo->audioSegmentList = AudioSegmentList::create (clip, warpTimeManager, source);
654 if (proxyInfo !=
nullptr
656 && proxyInfo->render (engine, source, *writer,
nullptr, progress))
660 source.getFile().copyFileTo (destination.getFile());
677 auto volState = state.getChildWithName (IDs::PLUGIN);
679 if (! volState.isValid())
681 volState = VolumeAndPanPlugin::create();
682 volState.setProperty (IDs::volume, decibelsToVolumeFaderPosition (0.0f),
nullptr);
683 state.addChild (volState, -1,
nullptr);
694 jassert (! timeRange.isEmpty());
696 auto n =
new WaveAudioNode (sourceFile, timeRange, 0.0, {}, {},
700 getDestinationFile(), sourceFile, 128);
710 if (plugin !=
nullptr)
711 sm.selectOnly (*plugin);
714HashCode VolumeEffect::getIndividualHash()
const
716 return plugin !=
nullptr ? hashPlugin (state, *plugin) : 0;
722 if (plugin ==
nullptr || (plugin->isAutomationNeeded()
723 && v == plugin->state
730void VolumeEffect::valueTreeChanged()
732 ClipEffect::valueTreeChanged();
738void VolumeEffect::timerCallback()
740 if (juce::Component::isMouseButtonDownAnywhere())
751 auto um = &getUndoManager();
752 fadeIn.referTo (state, IDs::fadeIn, um);
753 fadeOut.referTo (state, IDs::fadeOut, um);
755 fadeInType.referTo (state, IDs::fadeInType, um, AudioFadeCurve::linear);
756 fadeOutType.referTo (state, IDs::fadeOutType, um, AudioFadeCurve::linear);
764 if (in + fadeOut > l)
766 const double scale = l / (in + fadeOut);
768 fadeOut = fadeOut * scale;
781 if (fadeIn + out > l)
783 const double scale = l / (fadeIn + out);
784 fadeIn = fadeIn * scale;
785 fadeOut = out * scale;
797 AudioFile destFile (getDestinationFile());
799 jassert (! timeRange.isEmpty());
803 auto blockSize = AudioNodeRenderJob::defaultBlockSize;
807 effectRange = { effectRange.getStart() * speedRatio,
808 effectRange.getEnd() * speedRatio };
810 const TimeRange fadeInRange (effectRange.getStart(), effectRange.getStart() + fadeIn);
811 const TimeRange fadeOutRange (effectRange.getEnd() - fadeOut, effectRange.getEnd());
815 case EffectType::fadeInOut:
819 fadeInType, fadeOutType);
823 case EffectType::tapeStartStop:
828 fadeInType, fadeOutType);
834 case EffectType::none:
835 case EffectType::volume:
836 case EffectType::stepVolume:
837 case EffectType::pitchShift:
838 case EffectType::warpTime:
839 case EffectType::normalise:
840 case EffectType::makeMono:
841 case EffectType::reverse:
842 case EffectType::invert:
843 case EffectType::filter:
855HashCode FadeInOutEffect::getIndividualHash()
const
859 return ClipEffect::getIndividualHash()
861 ^
static_cast<HashCode
> (effectRange.getStart().inSeconds() * 3526.9)
862 ^
static_cast<HashCode
> (effectRange.getEnd().inSeconds() * 53625.3);
870 const bool newEffect = ! (state.
hasProperty (IDs::noteLength) && state.
hasProperty (IDs::crossfadeLength));
872 auto um = &getUndoManager();
873 noteLength.referTo (state, IDs::noteLength, um, BeatDuration::fromBeats (0.25));
874 crossfade.referTo (state, IDs::crossfadeLength, um, 0.01);
875 pattern.referTo (state, IDs::pattern, um);
879 state.
setProperty (IDs::noteLength, noteLength.get().inBeats(), um);
880 state.
setProperty (IDs::crossfadeLength, crossfade.get(), um);
882 Pattern (*this).toggleAtInterval (2);
886StepVolumeEffect::~StepVolumeEffect()
888 notifyListenersOfDeletion();
891int StepVolumeEffect::getMaxNumNotes()
895 auto pos = c.getPosition();
897 auto startTime = pos.getStartOfSource();
898 auto startBeat = ts.toBeats (startTime);
901 return (
int)
std::ceil ((endBeat - startBeat) / noteLength);
910 auto destFile = getDestinationFile();
916 auto fade = crossfade.
get();
917 auto halfCrossfade = TimeDuration::fromSeconds (fade / 2.0);
923 auto cache = p.getPattern();
926 auto pos = c.getPosition();
928 auto length = noteLength.get();
929 auto startTime = pos.getStart();
931 auto startBeat = ts.toBeats (pos.getStart() + toDuration (effectRange.getStart()));
932 auto endBeat = ts.toBeats (pos.getEnd());
933 auto numNotes =
std::min (p.getNumNotes(), (
int)
std::ceil ((endBeat - startBeat) / length));
935 auto beat = startBeat;
937 for (
int i = 0; i <= numNotes; ++i)
941 beat = beat + length;
945 auto s = ts.toTime (beat) - toDuration (startTime);
946 beat = beat + length;
947 auto e = ts.toTime (beat) - toDuration (startTime);
949 nonMuteTimes.
add ({ s - halfCrossfade,
950 e + halfCrossfade });
954 auto lastTime = nonMuteTimes.
getLast();
956 for (
int i = nonMuteTimes.
size() - 1; --i >= 0;)
960 if (thisTime.getEnd() >= lastTime.getStart())
962 thisTime = thisTime.withEnd (lastTime.getEnd());
963 nonMuteTimes.
remove (i + 1);
970 for (
auto& t : nonMuteTimes)
974 auto waveNode =
new WaveAudioNode (sourceFile, timeRange, 0.0, {}, {},
976 auto compNode = createTrackCompAudioNode (waveNode,
977 TrackCompManager::TrackComp::getMuteTimes (nonMuteTimes),
978 nonMuteTimes, TimeDuration::fromSeconds (fade));
990 sm.selectOnly (
this);
996 return TRANS(
"Step Volume Effect Editor");
999HashCode StepVolumeEffect::getIndividualHash()
const
1003 return ClipEffect::getIndividualHash()
1005 ^
static_cast<HashCode
> (effectRange.getStart().inSeconds() * 3526.9)
1006 ^
static_cast<HashCode
> (effectRange.getEnd().inSeconds() * 53625.3);
1010StepVolumeEffect::Pattern::Pattern (
StepVolumeEffect& o) : effect (o), state (o.state)
1014StepVolumeEffect::Pattern::Pattern (
const Pattern& other) noexcept
1015 : effect (other.effect), state (other.state)
1019bool StepVolumeEffect::Pattern::getNote (
int index)
const noexcept
1021 return getPattern()[index];
1024void StepVolumeEffect::Pattern::setNote (
int index,
bool value)
1026 if (getNote (index) != value)
1028 auto p = getPattern();
1029 p.setBit (index, value);
1037 b.parseString (state[IDs::pattern].toString(), 2);
1041void StepVolumeEffect::Pattern::setPattern (
const juce::BigInteger& b)
noexcept
1046void StepVolumeEffect::Pattern::clear()
1051int StepVolumeEffect::Pattern::getNumNotes()
const
1053 return getPattern().getHighestBit();
1056void StepVolumeEffect::Pattern::shiftChannel (
bool toTheRight)
1058 auto c = getPattern();
1069void StepVolumeEffect::Pattern::toggleAtInterval (
int interval)
1071 auto c = getPattern();
1073 for (
int i = effect.getMaxNumNotes(); --i >= 0;)
1074 c.setBit (i, (i % interval) == 0);
1079void StepVolumeEffect::Pattern::randomiseChannel()
1084 for (
int i = 0; i < effect.getMaxNumNotes(); ++i)
1095 if (! pitchState.isValid())
1097 pitchState = PitchShiftPlugin::create();
1098 state.
addChild (pitchState, -1,
nullptr);
1104void PitchShiftEffect::initialise()
1106 if (plugin !=
nullptr)
1107 for (
auto ap : plugin->getAutomatableParameters())
1115 jassert (! timeRange.isEmpty());
1117 auto n =
new WaveAudioNode (sourceFile, timeRange, 0.0, {}, {},
1123 getDestinationFile(), sourceFile, 512, 1.0);
1133 if (plugin !=
nullptr)
1134 sm.selectOnly (*plugin);
1137HashCode PitchShiftEffect::getIndividualHash()
const
1139 return hashPlugin (state, *plugin);
1145 if (plugin !=
nullptr
1146 && v == plugin->state
1148 && plugin->isAutomationNeeded())
1154void PitchShiftEffect::valueTreeChanged()
1156 ClipEffect::valueTreeChanged();
1162void PitchShiftEffect::timerCallback()
1164 if (juce::Component::isMouseButtonDownAnywhere())
1167 inhibitor =
nullptr;
1181 double sourceLength)
1185 sourceLength, *warpTimeManager, getClip());
1188HashCode WarpTimeEffect::getIndividualHash()
const
1190 return warpTimeManager->getHash();
1195 warpTimeManager->setSourceFile (getSourceFile());
1198void WarpTimeEffect::editFinishedLoading()
1201 editLoadedCallback =
nullptr;
1208 : plugin (p), callback (std::move (cb))
1210 if (plugin->isProcessingEnabled())
1241 void timerCallback()
override
1243 for (
int i = jobs.size(); --i >= 0;)
1245 if (jobs[i]->progress >= 1.0f)
1257 callBlocking ([
this]() { plugin->setProcessingEnabled (
true); callback(); });
1262 callBlocking ([
this]() { plugin->setProcessingEnabled (
false); callback(); });
1274ScopedPluginUnloadInhibitor::ScopedPluginUnloadInhibitor (
PluginUnloadInhibitor& o) : owner (o) { owner.increase(); }
1275ScopedPluginUnloadInhibitor::~ScopedPluginUnloadInhibitor() { owner.decrease(); }
1282 auto um = &getUndoManager();
1283 currentCurve.referTo (state, IDs::currentCurve, um);
1284 lastHash.referTo (state, IDs::hash,
nullptr);
1286 auto pluginState = state.getChildWithName (IDs::PLUGIN);
1287 jassert (pluginState.isValid());
1289 if (pluginState.isValid())
1291 pluginState.setProperty (IDs::process,
false,
nullptr);
1292 callBlocking ([
this, pluginState]() { plugin = edit.
getPluginCache().getOrCreatePluginFor (pluginState); });
1295 if (plugin !=
nullptr)
1297 auto loadCallback = [
this]()
1299 if (plugin !=
nullptr)
1301 if (plugin->isProcessingEnabled())
1303 for (
int i = 0; i < plugin->getNumAutomatableParameters(); i++)
1304 plugin->getAutomatableParameter (i)->addListener (
this);
1308 for (
int i = 0; i < plugin->getNumAutomatableParameters(); i++)
1309 plugin->getAutomatableParameter (i)->removeListener (
this);
1319 double sourceLength)
1326 jassert (! timeRange.isEmpty());
1331 if (plugin !=
nullptr)
1333 plugin->setProcessingEnabled (
true);
1341 if (pluginUnloadInhibitor !=
nullptr)
1342 pluginUnloadInhibitor->increaseForJob (30 * 1000, job);
1347void PluginEffect::flushStateToValueTree()
1349 if (plugin !=
nullptr)
1350 plugin->flushPluginStateToValueTree();
1360 if (plugin !=
nullptr)
1362 sm.selectOnly (*plugin);
1364 plugin->showWindowExplicitly();
1368HashCode PluginEffect::getIndividualHash()
const
1374 if (plugin !=
nullptr && (plugin->isProcessingEnabled() || lastHash == 0))
1377 lastHash = (
juce::int64) hashPlugin (state, *plugin);
1386 if (plugin !=
nullptr
1387 && v == plugin->state
1388 && plugin->isAutomationNeeded())
1397void PluginEffect::valueTreeChanged()
1402void PluginEffect::timerCallback()
1404 if (juce::Component::isMouseButtonDownAnywhere())
1407 inhibitor =
nullptr;
1413 ClipEffect::valueTreeChanged();
1415 if (inhibitor ==
nullptr)
1431 if (! calculatedGain)
1433 calculatedGain =
true;
1435 float lmin, lmax, rmin, rmax;
1436 reader->readMaxLevels (0, sourceLengthSamples, lmin, lmax, rmin, rmax);
1438 auto maxLevel =
juce::jmax (-lmin, lmax, -rmin, rmax);
1439 gainFactor = dbToGain (
float (maxGain)) / maxLevel;
1442 auto todo = (
int) getNumSamplesForCurrentBlock();
1446 reader->read (&scratch.
buffer, 0, todo, position,
true,
true);
1450 writer->appendBuffer (scratch.
buffer, todo);
1453 progress =
float (position) /
float (sourceLengthSamples);
1455 return position >= sourceLengthSamples;
1458 const double maxGain = 1.0;
1460 bool calculatedGain =
false;
1461 float gainFactor = 0.0f;
1467 auto um = &getUndoManager();
1468 maxLevelDB.referTo (state, IDs::level, um, 0.0);
1471NormaliseEffect::~NormaliseEffect()
1473 notifyListenersOfDeletion();
1481 sourceFile, sourceLength, maxLevelDB.
get());
1491 sm.selectOnly (
this);
1496 return TRANS(
"Normalise Effect Editor");
1508 auto sourceInfo = source.getInfo();
1509 jassert (sourceInfo.numChannels > 0 && sourceInfo.sampleRate > 0.0 && sourceInfo.bitsPerSample > 0);
1512 if (sourceInfo.metadata.getValue (
"MetaDataSource",
"None") ==
"AIFF")
1513 sourceInfo.metadata.clear();
1515 reader.reset (AudioFileUtils::createReaderFor (engine, source.getFile()));
1517 if (reader ==
nullptr || reader->lengthInSamples == 0)
1520 sourceLengthSamples =
static_cast<SampleCount
> (sourceLengthSeconds * reader->sampleRate);
1523 1, sourceInfo.sampleRate,
1524 std::max (16, sourceInfo.bitsPerSample),
1525 sourceInfo.metadata, 0);
1527 return writer->isOpen();
1533 auto todo = (
int) getNumSamplesForCurrentBlock();
1536 reader->read (&input.
buffer, 0, todo, position,
true,
true);
1538 if (reader->numChannels == 1)
1540 writer->appendBuffer (input.
buffer, todo);
1546 if (srcChannels == chLR)
1551 else if (srcChannels == chL)
1555 else if (srcChannels == chR)
1564 writer->appendBuffer (output.
buffer, todo);
1568 progress =
float(position) /
float(sourceLengthSamples);
1570 return position >= sourceLengthSamples;
1573 const SrcChannels srcChannels;
1579 auto um = &getUndoManager();
1580 channels.referTo (state, IDs::channels, um, 0);
1583MakeMonoEffect::~MakeMonoEffect()
1585 notifyListenersOfDeletion();
1592 sourceLength, (SrcChannels) channels.
get());
1602 sm.selectOnly (
this);
1607 return TRANS(
"Make Mono Editor");
1619 auto todo = (
int) getNumSamplesForCurrentBlock();
1623 reader->read (&scratch.
buffer, 0, todo, sourceLengthSamples - position - todo,
true,
true);
1627 writer->appendBuffer (scratch.
buffer, todo);
1630 progress =
float(position) /
float(sourceLengthSamples);
1632 return position >= sourceLengthSamples;
1642 double sourceLength)
1657 auto todo = (
int) getNumSamplesForCurrentBlock();
1661 reader->read (&scratch.
buffer, 0, todo, position,
true,
true);
1665 writer->appendBuffer (scratch.
buffer, todo);
1668 progress =
float (position) /
float (sourceLengthSamples);
1670 return position >= sourceLengthSamples;
1680 double sourceLength)
1692 case EffectType::volume:
return new VolumeEffect (v, ce);
1693 case EffectType::fadeInOut:
1701 case EffectType::invert:
return new InvertEffect (v, ce);
1702 case EffectType::filter:
return new PluginEffect (v, ce);
1712 : Job (e, destFile),
1713 sourceFile (source), lastFile (source.getFile()),
1714 jobs (std::move (j)), originalNumTasks (jobs.size())
1726 if (! sourceFile.isValid())
1728 juce::Thread::sleep (100);
1732 if (currentJob ==
nullptr)
1734 currentJob = jobs.removeAndReturn (0);
1736 if (currentJob !=
nullptr)
1737 if (! currentJob->setUpRender())
1742 if (currentJob->renderNextBlock())
1744 if (! currentJob->completeRender())
1747 auto& afm = engine.getAudioFileManager();
1748 afm.releaseFile (currentJob->destination);
1750 if (! currentJob->destination.isNull())
1751 callBlocking ([&afm, fileToValidate = currentJob->destination]
1753 afm.validateFile (fileToValidate, true);
1754 jassert (fileToValidate.isValid());
1757 lastFile = currentJob->destination.getFile();
1758 currentJob =
nullptr;
1763 const float jobShare = 1.0f /
std::max (1, originalNumTasks);
1764 progress = (numJobsCompleted * jobShare) + (jobShare * (currentJob !=
nullptr ? currentJob->progress.load() : 0.0f));
1766 return currentJob ==
nullptr && jobs.isEmpty();
1771 return lastFile.copyFileTo (proxy.getFile()) && jobs.isEmpty();
1778 const int originalNumTasks;
1779 int numJobsCompleted = 0;
1788 clip.edit.getTransport().forceOrphanFreezeAndProxyFilesPurge();
1790 const double length = sourceFile.getLength();
1794 for (
auto ce : objects)
1796 const AudioFile af (ce->getDestinationFile());
1798 if (af.getFile().existsAsFile() && af.isValid())
1804 inputFile = j->destination;
1809 AudioFile firstFile (jobs.
isEmpty() ? inputFile : jobs.getFirst()->source);
1811 return new AggregateJob (clip.edit.engine, destFile, firstFile, std::move (jobs));
int size() const noexcept
void remove(int indexToRemove)
void add(const ElementType &newElement)
ElementType & getReference(int index) noexcept
ElementType getLast() const noexcept
void triggerAsyncUpdate()
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
void reverse(int channel, int startSample, int numSamples) const noexcept
void addFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples, Type gainToApplyToSource=Type(1)) noexcept
const Type * getReadPointer(int channelNumber) const noexcept
void applyGain(int channel, int startSample, int numSamples, Type gain) noexcept
static AudioChannelSet JUCE_CALLTYPE stereo()
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Type get() const noexcept
String getFileExtension() const
File getSiblingFile(StringRef siblingFileName) const
File withFileExtension(StringRef newExtension) const
bool isValid() const noexcept
const String & toString() const noexcept
int64 nextInt64() noexcept
static Random & getSystemRandom() noexcept
bool isEmpty() const noexcept
ObjectClass * add(ObjectClass *newObject)
int64 hashCode64() const noexcept
static String toHexString(IntegerType number)
void stopTimer() noexcept
bool isTimerRunning() const noexcept
void startTimer(int intervalInMilliseconds) noexcept
virtual void valueTreePropertyChanged(ValueTree &treeWhosePropertyHasChanged, const Identifier &property)
void removeChild(const ValueTree &child, UndoManager *undoManager)
ValueTree getChild(int index) const
int getNumChildren() const noexcept
bool isValid() const noexcept
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
void addListener(Listener *listener)
int indexOf(const ValueTree &child) const noexcept
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
ValueTree getParent() const noexcept
ValueTree getChildWithName(const Identifier &type) const
void removeProperty(const Identifier &name, UndoManager *undoManager)
ValueTree getSibling(int delta) const noexcept
bool hasProperty(const Identifier &name) const noexcept
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.
Base class for nodes in an audio playback graph.
An audio scratch buffer that has pooled storage.
juce::AudioBuffer< float > & buffer
The buffer to use.
HashCode getHash() const
Returns the hash for this effect.
virtual void sourceChanged()
Callback to indicate the destination file has changed.
TimeDuration getEffectsLength() const
Returns the length of the effect.
TimeRange getEffectsRange() const
Returns the range of the file that the effect should apply to.
double getSpeedRatioEstimate() const
Returns the speed ratio of the clip or an estimate of this if the clip is auto tempo.
juce::ValueTree state
The ValueTree of the Clip state.
const EditItemID itemID
Every EditItem has an ID which is unique within the edit.
The Tracktion Edit class!
PluginCache & getPluginCache() noexcept
Returns the PluginCache which manages all active Plugin[s] for this Edit.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
bool isLoading() const
Returns true if the Edit's not yet fully loaded.
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.
AudioFileFormatManager & getAudioFileFormatManager() const
Returns the AudioFileFormatManager that maintains a list of available audio file formats.
UIBehaviour & getUIBehaviour() const
Returns the UIBehaviour class.
An AudioNode that fades its input node in/out at given times.
The base class that all generator jobs derive from.
Manages a list of items that are currently selected.
An AudioNode that speeds up and slows down its input node in/out at given times.
virtual Plugin::Ptr showMenuAndCreatePlugin(Plugin::Type, Edit &)
Should show the new plugin window and creates the Plugin the user selects.
The built-in Tracktion volume/pan plugin.
bool renderNextBlock() override
During a render process this will be repeatedly called.
A WarpTimeManager contains a list of WarpMarkers and some source material and maps times from a linea...
TimePosition getWarpEndMarkerTime() const
Sets position in warped region of the redered file end point.
An AudioNode that plays back a wave file.
#define TRANS(stringLiteral)
constexpr Type jmax(Type a, Type b)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
int roundToInt(const FloatType value) noexcept
Declarations from this namespaces are inlined into tracktion.
legacy::EditTimeRange toEditTimeRange(TimeRange r)
@temporary
bool matchesAnyOf(const Type &needle, const std::initializer_list< Type > &haystack)
Returns true if the needle is found in the haystack.
Holds some really basic properties of a node.
Passed into AudioNodes when they are being initialised, to give them useful contextual information th...
Represents a duration in real-life time.
Represents a position in real-life time.
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.
Takes an AudioNode and renders it to a file.
bool completeRender() override
This is called once after all the render blocks have completed.
bool renderNextBlock() override
During a render process this will be repeatedly called.
bool setUpRender() override
Subclasses should override this to set-up their render process.
bool setUpRender() override
Subclasses should override this to set-up their render process.
bool completeRender() override
This is called once after all the render blocks have completed.
virtual bool completeRender()=0
This is called once after all the render blocks have completed.
virtual bool setUpRender()=0
Subclasses should override this to set-up their render process.
virtual bool renderNextBlock()=0
During a render process this will be repeatedly called.
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &sourceFile, double sourceLength) override
Subclasses should return a job that can render the source.
bool renderNextBlock() override
During a render process this will be repeatedly called.
juce::ReferenceCountedObjectPtr< ClipEffect::ClipEffectRenderJob > createRenderJob(const AudioFile &, double sourceLength) override
Subclasses should return a job that can render the source.
bool renderNextBlock() override
During a render process this will be repeatedly called.
bool setUpRender() override
Subclasses should override this to set-up their render process.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &, double sourceLength) override
Subclasses should return a job that can render the source.
juce::String getSelectableDescription() override
Subclasses must return a description of what they are.
bool renderNextBlock() override
During a render process this will be repeatedly called.
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &sourceFile, double sourceLength) override
Subclasses should return a job that can render the source.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
juce::String getSelectableDescription() override
Subclasses must return a description of what they are.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &sourceFile, double sourceLength) override
Subclasses should return a job that can render the source.
void curveHasChanged(AutomatableParameter &) override
Called when the automation curve has changed, point time, value or curve.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &, double sourceLength) override
Subclasses should return a job that can render the source.
bool renderNextBlock() override
During a render process this will be repeatedly called.
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &, double sourceLength) override
Subclasses should return a job that can render the source.
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &sourceFile, double sourceLength) override
Subclasses should return a job that can render the source.
juce::String getSelectableDescription() override
Subclasses must return a description of what they are.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &sourceFile, double sourceLength) override
Subclasses should return a job that can render the source.
bool hasProperties() override
Return true here to show a properties button in the editor and enable the propertiesButtonPressed cal...
void sourceChanged() override
Callback to indicate the destination file has changed.
juce::ReferenceCountedObjectPtr< ClipEffectRenderJob > createRenderJob(const AudioFile &, double sourceLength) override
Subclasses should return a job that can render the source.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.