Display Progress Bar During Render

I am using the following code to render, which works great! But, now that I am rendering longer Edits, I would like to have a progress bar.

File renderFile{ File::getSpecialLocation(File::userDesktopDirectory).getNonexistentChildFile("render", ".wav") };
te::EditTimeRange range{ 0.0, (edit->getLength() + 2.0) };// add 2.0 seconds to render tail
juce::BigInteger tracksToDo{ 0 };
for (auto i = 0; i < te::getAllTracks(*edit).size(); i++)
	tracksToDo.setBit(i);

if (te::Renderer::renderToFile("Render", renderFile, *edit, range, tracksToDo))
	AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon,
		"Rendered",
		renderFile.getFullPathName());

How would I go about adding the progress bar to my AlertWindow?

If you call renderToFile with useThread=true then it will callback into your app with UIBehaviour::runTaskWithProgressBar

Our implementation looks like this:

    void runTaskWithProgressBar (ThreadPoolJobWithProgress& t)
    {
        jassert (currentTask == nullptr); // must stop or wait for others to finish first.

        CRASH_TRACER

        TransportControl::stopAllTransports (mainWindow.engine, false, true);
        stopBackgroundTasks();

        currentTask = &t;
        TaskRunner runner (currentTask);

        InterrupterComponent c (t);
        addAndMakeVisible (&c);
        c.setBounds (getLocalBounds());

        JUCE_TRY
        {
            while (runner.isThreadRunning())
                if (! MessageManager::getInstance()->runDispatchLoopUntil (10))
                    break;
        }
        JUCE_CATCH_EXCEPTION

        c.exitModalState (0);
        c.setVisible (false);

        currentTask = nullptr;
    }

Out component has a 30 hz callback that updates the progress:

    void timerCallback() override
    {
        const String newText (task.getJobName());
        progress = task.getCurrentTaskProgress();

        if (newText != text)
        {
            text = newText;
            repaint (boxArea);
        }
    }
1 Like

Thanks so much for pointing me in the right direction?

The code below is working for me, …no timer required.

void runTaskWithProgressBar(te::ThreadPoolJobWithProgress& t) override
{
	TaskRunner runner(t);
	while (runner.isThreadRunning())
		if (!MessageManager::getInstance()->runDispatchLoopUntil(10))
			break;
}

//==============================================================================
struct TaskRunner : public Thread
{
	TaskRunner(te::ThreadPoolJobWithProgress& t) : Thread(t.getJobName()), task(t)
	{
		startThread();
	}

	~TaskRunner()
	{
		task.signalJobShouldExit();
		waitForThreadToExit(10000);
	}

	void run() override
	{
		double progress{ 0.0 };
		AlertWindow w("Rendering", {}, AlertWindow::NoIcon);

		w.addProgressBarComponent(progress);
		w.setVisible(true);

		while (!threadShouldExit())
		{
			if (task.runJob() == ThreadPoolJob::jobHasFinished)
				break;
			progress = task.getCurrentTaskProgress();
		}
	}
	te::ThreadPoolJobWithProgress& task;
};

And to provide context for others—the above code is part of my ExtendedUIBehavior class which derives from tracktion_engine::UIBehavior and must be populated to provide functions used by plugins and rendering.

Aren’t you hitting asserts creating the AlertWindow in a thread? I’d think you should make progress a member variable and then create the AlertWindow in the constructor rather than in the thread.

You are, of course, absolutely right! I had been doing only release builds. Doing a debug build showed the jassert.

When I put the AlertWindow in the TaskRunner constructor, the window never appears. So I moved it to runTaskWithProgressBar().

So, below is my modified solution;

void runTaskWithProgressBar(te::ThreadPoolJobWithProgress& t) override
{
    double progress{ 0.0 };
	TaskRunner runner(t, progress);

	AlertWindow w("Rendering", {}, AlertWindow::NoIcon);
	w.addProgressBarComponent(progress);
	w.setVisible(true);

	while (runner.isThreadRunning())
		if (!MessageManager::getInstance()->runDispatchLoopUntil(10))
			break;
}

//==============================================================================
struct TaskRunner : public Thread
{
	TaskRunner(te::ThreadPoolJobWithProgress& t, double& prog) : Thread(t.getJobName()), task(t), progress(prog)
	{
		startThread();
	}

	~TaskRunner()
	{
		task.signalJobShouldExit();
		waitForThreadToExit(10000);
	}

	void run() override
	{
		while (!threadShouldExit())
		{
			progress = task.getCurrentTaskProgress();
			if (task.runJob() == ThreadPoolJob::jobHasFinished)
				break;
		}
	}
	te::ThreadPoolJobWithProgress& task;
	double& progress;
};

Hopefully this code contains no more oversights!

Thank you, again!