tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_RenderManager.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14RenderManager::Job::Job (Engine& e, const AudioFile& proxyToUse)
15 : ThreadPoolJobWithProgress ("Render Job"),
16 engine (e), proxy (proxyToUse)
17{
18 selfReference = this;
19
20 // Adds us to the active jobs list which will start us running asynchronously to avoid
21 // a pure virtual call caused by being run before we have finished being constructed
22 engine.getRenderManager().addJobInternal (this);
23
24 // We need to post a message to start our Job to avoid a pure virtual call which can
25 // result if the Job is run by the thread pool before subclasses have finished being constructed
26 postMessage (new UpdateMessage (UpdateMessage::started));
27}
28
29RenderManager::Job::~Job()
30{
31 prepareForJobDeletion();
32
33 jassert (hasFinished);
34 jassert (listeners.isEmpty());
35 jassert (getReferenceCount() == 0);
36}
37
38juce::ThreadPoolJob::JobStatus RenderManager::Job::runJob()
39{
41
43
44 if (! (isInitialised || shouldExit()))
45 {
46 proxy.deleteFile();
47 isInitialised = true;
48
49 if (setUpRender())
50 return jobNeedsRunningAgain;
51 }
52 else
53 {
54 if (! (shouldExit() || renderNextBlock()))
55 return jobNeedsRunningAgain;
56 }
57
58 const bool completedOk = completeRender();
59
60 if (! proxy.isNull() && completedOk)
61 callBlocking ([this]
62 {
63 engine.getAudioFileManager().validateFile (proxy, true);
64 jassert (isMidiFile (proxy.getFile()) || proxy.isValid());
65 });
66
67
68 const juce::ScopedLock sl (finishedLock);
69
70 if (! hasFinished)
71 sendCompletionMessages (completedOk && (! shouldExit()));
72
73 return jobHasFinished;
74}
75
76void RenderManager::Job::cancelJob()
77{
78 // We can't just signal the thread to stop as the completion messages wont get sent which
79 // wont notify our listeners.
80 const juce::ScopedLock sl (finishedLock);
81 signalJobShouldExit();
82
83 if (! hasFinished)
84 sendCompletionMessages (false);
85}
86
87bool RenderManager::Job::decReferenceCountWithoutDeleting() noexcept
88{
89 if (--refCount == 1 && selfReference != nullptr)
90 cancelJob();
91
92 // Ensures the job is deleted on the message thread
93 if (refCount.get() == 0)
94 engine.getRenderManager().deleteJob (this);
95
96 return false;
97}
98
99//==============================================================================
100void RenderManager::Job::sendCompletionMessages (bool success)
101{
103
104 // Removes us from the active jobs list
105 engine.getRenderManager().removeJobInternal (this);
106 hasFinished = true;
107
108 if (success)
109 {
110 postMessage (new UpdateMessage (UpdateMessage::succeded));
111 }
112 else
113 {
114 proxy.deleteFile();
115 postMessage (new UpdateMessage (UpdateMessage::cancelled));
116 }
117}
118
119void RenderManager::Job::handleMessage (const juce::Message& message)
120{
122
123 const UpdateMessage::Type type = UpdateMessage::getType (message);
124
125 switch (type)
126 {
127 case UpdateMessage::started:
128 // starts the job running
129 engine.getRenderManager().addJobToPool (this);
130 listeners.call (&Listener::jobStarted, *this);
131 break;
132
133 case UpdateMessage::succeded:
134 case UpdateMessage::cancelled:
135 if (isRunning())
136 {
137 const juce::ScopedLock sl (messageLock);
138 messagesToResend.addIfNotAlreadyThere (type);
139 startTimer (messageRetryTimeout);
140 }
141 else
142 {
143 listeners.call (&Listener::jobFinished, *this, type == UpdateMessage::succeded);
144 jassert (listeners.isEmpty());
145 selfReference = nullptr;
146 }
147 break;
148
149 case UpdateMessage::unknown:
150 default:
151 break;
152 }
153}
154
155void RenderManager::Job::timerCallback()
156{
157 const juce::ScopedLock sl (messageLock);
158
159 for (int i = messagesToResend.size(); --i >= 0;)
160 postMessage (new UpdateMessage (messagesToResend.removeAndReturn (i)));
161}
162
163//==============================================================================
164RenderManager::RenderManager (Engine& e) : engine (e)
165{
166}
167
168RenderManager::~RenderManager()
169{
170 jassert (danglingJobs.isEmpty());
171 jassert (jobs.isEmpty());
172 jassert (jobsToDelete.isEmpty());
173}
174
176{
178 auto& pool = engine.getBackgroundJobs();
179
180 for (int i = jobs.size(); --i >= 0;)
181 pool.removeJob (jobs.getUnchecked (i), true, 10000);
182
183 for (int i = danglingJobs.size(); --i >= 0;)
184 if (auto j = danglingJobs.getUnchecked (i))
185 j->cleanUpDanglingJob();
186
187 jassert (danglingJobs.isEmpty());
188
189 jobs.clear();
190 jobsToDelete.clear();
191}
192
193//==============================================================================
194AudioFile RenderManager::getAudioFileForHash (Engine& engine, const juce::File& directory, HashCode hash)
195{
196 return AudioFile (engine, directory.getChildFile (getFileRenderPrefix() + juce::String (hash) + ".wav"));
197}
198
200{
202
203 const juce::ScopedLock sl (jobListLock);
204
205 for (auto j : jobs)
206 if (j != nullptr && j->proxy == af)
207 js.add (j);
208
209 return js;
210}
211
213{
214 const juce::ScopedLock sl (jobListLock);
215 return jobs.size();
216}
217
218//==============================================================================
219bool RenderManager::isProxyBeingGenerated (const AudioFile& proxyFile) noexcept
220{
221 return findJob (proxyFile) != nullptr;
222}
223
224float RenderManager::getProportionComplete (const AudioFile& proxyFile, float defaultVal) noexcept
225{
226 if (auto j = findJob (proxyFile))
227 return j->progress;
228
229 return defaultVal;
230}
231
232//==============================================================================
233RenderManager::Job::Ptr RenderManager::findJob (const AudioFile& af) noexcept
234{
235 const juce::ScopedLock sl (jobListLock);
236
237 for (auto j : jobs)
238 if (j != nullptr && j->proxy == af)
239 return j;
240
241 return {};
242}
243
244void RenderManager::addJobInternal (Job* j) noexcept
245{
246 const juce::ScopedLock sl (jobListLock);
247 jobs.addIfNotAlreadyThere (j);
248}
249
250void RenderManager::removeJobInternal (Job* j) noexcept
251{
252 {
253 const juce::ScopedLock sl (jobListLock);
254 jobs.removeAllInstancesOf (j);
255 }
256
257 {
258 const juce::ScopedLock sl2 (deleteListLock);
259 danglingJobs.addIfNotAlreadyThere (j);
260 }
261}
262
263void RenderManager::addJobToPool (Job* j) noexcept
264{
265 engine.getBackgroundJobs().addJob (j, false);
266}
267
268void RenderManager::deleteJob (Job* j)
269{
270 {
271 const juce::ScopedLock sl (deleteListLock);
272 danglingJobs.removeAllInstancesOf (j);
273 jobsToDelete.add (j);
275 }
276
277 if (juce::MessageManager::getInstance()->isThisTheMessageThread())
279}
280
281void RenderManager::handleAsyncUpdate()
282{
283 const juce::ScopedLock sl (deleteListLock);
284 jobsToDelete.clear();
285}
286
287}} // namespace tracktion { inline namespace engine
void handleUpdateNowIfNeeded()
File getChildFile(StringRef relativeOrAbsolutePath) const
static void JUCE_CALLTYPE disableDenormalisedNumberSupport(bool shouldDisable=true) noexcept
ObjectClass * add(ObjectClass *newObject)
The Engine is the central class for all tracktion sessions.
BackgroundJobManager & getBackgroundJobs() const
Returns the BackgroundJobManager instance.
void cleanUp()
Cleans up any remaining jobs - should be called before the manager is deleted.
static juce::StringRef getFileRenderPrefix()
Returns the prefix used for render files.
static AudioFile getAudioFileForHash(Engine &, const juce::File &directory, HashCode hash)
Returns the AudioFile for a particular hash.
juce::ReferenceCountedArray< Job > getRenderJobsWithoutCreating(const AudioFile &)
Returns all the jobs that may be processing the given file.
int getNumJobs() noexcept
Returns the number of jobs in the pool.
bool isProxyBeingGenerated(const AudioFile &proxyFile) noexcept
Returns true if a render is currently being performed for this AudioFile.
float getProportionComplete(const AudioFile &proxyFile, float defaultVal) noexcept
Returns true if a render is currently being performed for this AudioFile.
#define jassert(expression)
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.