11namespace tracktion {
inline namespace engine
15 bool deleteEdit,
bool silenceOnBackup,
bool reverse)
17 AudioFile targetFile (e, params.destFile);
31 return new EditRenderJob (e, params, deleteEdit, silenceOnBackup, reverse);
40 return new EditRenderJob (e, destFile, ro, itemID, silenceOnBackup, reverse);
45 bool silenceOnBackup_,
bool reverse_)
49 editDeleter (p.edit, deleteEdit),
50 silenceOnBackup (silenceOnBackup_),
52 thumbnailToUpdate (256, e.getAudioFileFormatManager().readFormatManager,
53 e.getAudioFileManager().getAudioThumbnailCache())
60 renderOptions (ro, nullptr),
63 silenceOnBackup (silenceOnBackup_), reverse (reverse_),
64 thumbnailToUpdate (256, e.getAudioFileFormatManager().readFormatManager,
65 e.getAudioFileManager().getAudioThumbnailCache())
73 if (! editDeleter.willDeleteObject())
74 if (editDeleter !=
nullptr)
75 editDeleter->getTransport().editHasChanged();
93 if (params.edit ==
nullptr || itemID.isValid())
98 auto contextUpdater =
std::async (std::launch::async, [
this, &context]
100 while (! context.completed)
102 context.shouldExit = shouldExit();
104 if (context.shouldExit)
107 juce::Thread::sleep (100);
116 jassert (! renderOptions.markedRegion);
117 jassert (! renderOptions.selectedClips);
118 jassert (! renderOptions.selectedTracks);
121 params.edit = edit.get();
122 params.destFile = proxy.getFile();
123 params.tracksToDo = renderOptions.getTrackIndexes (*edit);
124 params.category = ProjectItem::Category::none;
126 editDeleter.setOwned (edit.release());
132 if (params.separateTracks)
133 renderSeparateTracks();
135 renderPasses.
add (
new RenderPass (*
this, params,
"Edit Render"));
145 if (
auto pass = renderPasses.
getFirst())
147 if (pass->task ==
nullptr)
150 if (pass->task ==
nullptr || pass->task->runJob() == ThreadPoolJob::jobHasFinished)
161 if (result.items.size() > 0
162 || (params.category == ProjectItem::Category::none && proxy.getFile().
existsAsFile()))
165 return result.result.
wasOk();
172 : owner (j), r (renderParams), desc (description), originalCategory (r.category),
173 tempFile (r.destFile,
juce::TemporaryFile::useHiddenFile)
175 r.category = ProjectItem::Category::none;
176 r.destFile = tempFile.getFile();
179EditRenderJob::RenderPass::~RenderPass()
181 auto errorMessage (task !=
nullptr ? task->errorMessage :
juce::String());
182 owner.setLastError (errorMessage);
183 const bool completedOk = task !=
nullptr ? task->getCurrentTaskProgress() == 1.0f :
false;
186 if (owner.editDeleter.willDeleteObject())
190 if (! errorMessage.isEmpty() && owner.silenceOnBackup)
191 owner.generateSilence (tempFile.getFile());
193 if (tempFile.getFile().existsAsFile() && (completedOk || owner.silenceOnBackup))
194 tempFile.overwriteTargetFileWithTemporary();
196 tempFile.getTargetFile().deleteFile();
199 r.destFile = tempFile.getTargetFile();
200 r.category = originalCategory;
207 if (r.destFile.existsAsFile())
209 if (tempReverseFile.getFile().existsAsFile())
210 tempReverseFile.overwriteTargetFileWithTemporary();
213 if (! r.destFile.existsAsFile())
216 if (r.category != ProjectItem::Category::none && r.destFile.existsAsFile())
228 if (proj->isReadOnly())
230 r.edit->engine.getUIBehaviour().showWarningMessage (
TRANS(
"Couldn't add the new file to the project (because this project is read-only)"));
236 if (! r.createMidiFile && errorMessage.isNotEmpty())
239 r.destFile.deleteFile();
245 newItemDesc <<
TRANS(
"Rendered from edit") << r.edit->getName().quoted() <<
" " <<
TRANS(
"On") <<
" "
248 if (
auto item = proj->createNewItem (r.destFile,
249 r.createMidiFile ? ProjectItem::midiItemType()
250 : ProjectItem::waveItemType(),
251 r.destFile.getFileNameWithoutExtension().trim(),
256 jassert (item->getID().isValid());
257 owner.result.items.add (item);
268 owner.engine.getAudioFileManager().checkFileForChangesAsync (AudioFile (owner.engine, r.destFile));
271bool EditRenderJob::RenderPass::initialise()
274 jassert (r.sampleRateForAudio > 7000);
279 r.edit->initialiseAllPlugins();
280 r.edit->getTransport().stop (
false,
true);
283 if (r.tracksToDo.countNumberOfSetBits() > 0
284 && r.destFile.hasWriteAccess()
285 && ! r.destFile.isDirectory())
294 CreateNodeParams cnp { *processState };
295 cnp.sampleRate = r.sampleRateForAudio;
296 cnp.blockSize = r.blockSizeForAudio;
297 cnp.allowedClips = r.allowedClips.isEmpty() ? nullptr : &r.allowedClips;
298 cnp.allowedTracks = r.tracksToDo.isZero() ? nullptr : &tracksToDo;
299 cnp.forRendering =
true;
300 cnp.includePlugins = r.usePlugins;
301 cnp.includeMasterPlugins = r.useMasterPlugins;
302 cnp.addAntiDenormalisationNoise = r.addAntiDenormalisationNoise;
303 cnp.includeBypassedPlugins =
false;
304 cnp.allowClipSlots = r.edit->engine.getEngineBehaviour().areClipSlotsEnabled();
312 std::move (node), std::move (playHead), std::move (playHeadState), std::move (processState),
313 &owner.progress, &owner.thumbnailToUpdate);
314 return task->errorMessage.isEmpty();
322void EditRenderJob::renderSeparateTracks()
330 jassert (params.separateTracks);
331 auto originalTracksToDo = params.tracksToDo;
334 for (
int i = 0; i <= originalTracksToDo.getHighestBit(); ++i)
336 if (originalTracksToDo[i])
338 if (
auto track =
dynamic_cast<Track*
> (
getAllTracks (*params.edit)[i]))
340 if (track->isPartOfSubmix())
343 auto ft =
dynamic_cast<FolderTrack*
> (track);
344 auto at =
dynamic_cast<AudioTrack*
> (track);
346 if (ft ==
nullptr && at ==
nullptr)
354 if (! ft->isSubmixFolder())
357 for (
auto* subTrack : ft->getAllSubTracks (true))
359 const int subTrackIndex = subTrack->getIndexInEditTrackList();
361 if (originalTracksToDo[subTrackIndex])
362 tracksToDo.
setBit (subTrackIndex);
366 auto getDescription = [at, ft]
368 return ft !=
nullptr ?
TRANS(
"Rendering Submix Track") +
" " +
juce::String (ft->getFolderTrackNumber()) +
"..."
369 :
TRANS(
"Rendering Track") +
" " +
juce::String (at->getAudioTrackNumber()) +
"...";
372 auto file = proxy.getFile();
373 auto trackFile = file.getSiblingFile (file.getFileNameWithoutExtension()
374 +
" " + track->getName()
375 +
" " +
TRANS(
"Render") +
" 0"
376 + file.getFileExtension());
379 .getFullPathName()));
380 params.tracksToDo = tracksToDo;
383 renderPasses.
add (
new RenderPass (*
this, params, getDescription()));
387 createdFiles.
add (params.destFile);
393 for (
auto f : createdFiles)
396 params.tracksToDo = originalTracksToDo;
399bool EditRenderJob::generateSilence (
const juce::File& fileToWriteTo)
405 if (os ==
nullptr || params.audioFormat ==
nullptr)
408 const int numChans = params.mustRenderInMono ? 1 : 2;
410 (
unsigned int) numChans,
411 params.bitDepth, {}, 0));
413 if (writer ==
nullptr)
417 auto numToDo = (SampleCount) tracktion::toSamples (params.time.getLength(), params.sampleRateForAudio);
418 auto blockSize =
std::min (4096, (
int) numToDo);
419 SampleCount numDone = 0;
425 while (numDone < numToDo)
430 auto numThisTime =
std::min ((
int) numToDo, blockSize);
431 writer->writeFromAudioSampleBuffer (buffer, 0, numThisTime);
433 progress = (
float) (numDone / (
float) numToDo);
434 thumbnailToUpdate.
addBlock (numDone, buffer, 0, numThisTime);
436 numDone += numThisTime;
void add(const ElementType &newElement)
void addBlock(int64 sampleNumberInSource, const AudioBuffer< float > &newData, int startOffsetInBuffer, int numSamples) override
BigInteger & setBit(int bitNumber)
std::unique_ptr< FileOutputStream > createOutputStream(size_t bufferSize=0x8000) const
bool replaceWithText(const String &textToWrite, bool asUnicode=false, bool writeUnicodeHeaderBytes=false, const char *lineEndings="\r\n") const
static String createLegalPathName(const String &pathNameToFix)
bool existsAsFile() const
bool isEmpty() const noexcept
ObjectClass * getFirst() const noexcept
void clear(bool deleteObjects=true)
ObjectClass * add(ObjectClass *newObject)
void removeObject(const ObjectClass *objectToRemove, bool deleteObject=true)
bool wasOk() const noexcept
static Result ok() noexcept
bool shouldExit() const noexcept
static Time JUCE_CALLTYPE getCurrentTime() noexcept
String toString(bool includeDate, bool includeTime, bool includeSeconds=true, bool use24HourClock=false) const
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 completeRender() override
This is called once after all the render blocks have completed.
static Ptr getOrCreateRenderJob(Engine &, Renderer::Parameters &, bool deleteEdit, bool silenceOnBackup, bool reverse)
Returns a job that will have been started to generate the Render described by the params.
~EditRenderJob() override
Destructor.
@ forRendering
Creates an Edit for rendering, not output device playback.
A context passed to the Options struct which will get updated about load process and can be signaled ...
The Engine is the central class for all tracktion sessions.
RenderManager & getRenderManager() const
Returns the RenderManager instance.
ProjectManager & getProjectManager() const
Returns the ProjectManager instance.
An ID representing one of the items in a Project.
Job::Ptr getRenderJobWithoutCreating(const AudioFile &audioFile)
This will return a Ptr to an existing render job for an audio file or nullptr if no job is in progres...
Represents a set of user properties used to control a render operation, using a ValueTree to hold the...
Renderer::Parameters getRenderParameters(Edit &, SelectionManager *, TimeRange markedRegion)
Returns a set of renderer parameters which can be used to describe a render operation.
static void turnOffAllPlugins(Edit &)
Deinitialises all the plugins for the Edit.
static bool checkTargetFile(Engine &, const juce::File &)
Cheks a file for write access etc.
#define TRANS(stringLiteral)
void ignoreUnused(Types &&...) noexcept
std::unique_ptr< Edit > loadEditForExamining(ProjectManager &pm, ProjectItemID itemID, Edit::EditRole role)
Uses the ProjectManager to find an Edit file and open it.
juce::Array< Track * > toTrackArray(Edit &edit, const juce::BigInteger &tracksToAdd)
Returns an Array of Track[s] corresponding to the set bits of all tracks in an Edit.
juce::Array< Track * > getAllTracks(const Edit &edit)
Returns all the tracks in an Edit.
std::unique_ptr< tracktion::graph::Node > createNodeForEdit(EditPlaybackContext &epc, std::atomic< double > &audibleTimeToUpdate, const CreateNodeParams ¶ms)
Creates a Node to play back an Edit with live inputs and outputs.
Project::Ptr getProjectForEdit(const Edit &e)
Tries to find the project that contains this edit (but may return nullptr!)
static bool reverse(Engine &, const juce::File &source, const juce::File &destination, std::atomic< float > &progress, juce::ThreadPoolJob *job=nullptr, bool canCreateWavIntermediate=true)
Reverses a file updating a progress value and checking the exit status of a given job.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.