Closing Edit and loading another Edit

I want to load another Edit at runtime. I’m playing around with the StepSequencerDemo Project and read this topic saving-and-opening-edits-tracktion-engine but I’m not sure.

I tried this but got an assertion:

std::unique_ptr<te::Edit> edit = std::make_unique<te::Edit>( engine, te::createEmptyEdit (engine), te::Edit::forEditing, nullptr, 0 );

        loadProjectButton.onClick = [this]
        {
            edit = te::loadEditFromFile (engine, juce::File::getSpecialLocation(juce::File::userDocumentsDirectory).getChildFile("TestTracktionFile.xml"));
            
        };

Assertion on void selectableObjectAboutToBeDeleted (te::Selectable*) override

What’s the right way to handle this?

Hi!

So to be clear, by assertion you mean a jassert is being called, right? Or is it that your code isn’t compiling? Because what you’ve given in your second code snippet shouldn’t compile… You can’t assign a ValueTree to a te::Edit.

I’ve found it’s best to create a te::Edit using the te::Edit::Options constructor. Here’s some sample code to help:

edit = te::Edit(te::Edit::Options {
    engine,
    te::loadEditFromFile(engine, myEditFile, {}),
    te::ProjectItemID::createNewID(0),
    te::Edit::forEditing,
    nullptr,
    te::Edit::getDefaultNumUndoLevels(),
    [=] { return myEditFile; }
});

Of course in your case, since you’re using a std::unique_ptr<>, that constructor would be in std::make_unique<> call.

…and just to add;

There are two abbreviated versions for creating an edit.

edit = te::createEmptyEdit(engine, editFile);

or, if you already have an edit file…

edit = te::loadEditFromFile(engine, editFile);

These are what I use most of the time. They may be useful for you.

Thanks @JojoTheMAN21 and @bwall for your quick response!

Yes, I meant jassert. The code does compile although the types don’t fit together.

I tried the short and long version…

        loadProjectButton.onClick = [this]
        {
            editFile = juce::File::getSpecialLocation(juce::File::userDocumentsDirectory).getChildFile("TestTracktionFile.xml");

            //edit = te::createEmptyEdit(engine, editFile);
                        
            edit = std::make_unique<te::Edit>(te::Edit::Options {
                engine,
                te::loadEditFromFile(engine, editFile, {}),
                te::ProjectItemID::createNewID(0),
                te::Edit::forEditing,
                nullptr,
                te::Edit::getDefaultNumUndoLevels(),
                [=] { return editFile; }
            });
        };

…but got jassert again at

    void selectableObjectAboutToBeDeleted (te::Selectable*) override
    {
        jassertfalse;
    }

JUCE Message Thread (1): EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)

What I want to archive is to start the app with a new Edit. Save it and load another Edit etc.

I re-created your app, using a TextButton with that onClick code to open a Tracktion edit from my computer. It opens it up successfully, so I think the assertion is coming from something else?

That’s interesting! I started from scratch with StepSequencerDemo. I haven’t any problems with the original code. With the loadProjectButton code I got jassert again. Should I check my JUCE project configuration? Here is my project setup: GitHub - toxvox/StepSequencerDemo

Thanks for sharing your code. I opened it up and I’m getting the jassert as well. But now I see your issue!

As I learned from Jules on this post, you need to safely close the old edit by detaching all UI and listeners from it before reopening the new one. I think that’s what that jassertfalse is warning about; the edit pointer is being deleted without detaching!

The way I’d go about this is to basically “re-open” the StepSequencerDemo window when you want to open a different edit file. One method you could do is to pass in the requested edit file as a parameter into the StepSequencerDemo’s constructor. When the Load Project button is clicked, you could trigger an std::function that was also passed in as a parameter to the constructor that resets mainWindow in your Main.cpp with the newly requested edit file.

1 Like

Thanks for clarifying! I’ll give the “re-open” method a try.

I tried to follow your instructions but I couldn’t access to mainWindow from StepSequencerDemo.

I’ve worked on another workaround with the same approach. I added a EditFileManager to learn how can Edits safely load, closed and saved.

Main.h

    void initialise (const juce::String&) override
    {
        mainWindow.reset (new MainWindow ("StepSequencerDemo", new EditFileManager, *this));
    }

EditFileManager.h

        newProjectButton.onClick  = [this] {
            stepSequencer = std::make_unique<StepSequencerDemo>();
            stepSequencer->setBounds(0, 30, 600, 400);
            addAndMakeVisible(stepSequencer.get());
        };
        
        closeProjectButton.onClick = [this] {
            if(stepSequencer != nullptr)
            {
                removeChildComponent(stepSequencer.get());
                stepSequencer.reset();
            }
        };
        
        loadProjectButton.onClick = [this] {
            editFile = new juce::File(juce::File::getSpecialLocation(juce::File::userDocumentsDirectory).getChildFile("TestTracktionFile.xml"));
            stepSequencer = std::make_unique<StepSequencerDemo>(editFile);
            stepSequencer->setBounds(0, 30, 600, 400);
            addAndMakeVisible(stepSequencer.get());
        };

StepSequencerDemo.h

    StepSequencerDemo(juce::File* editFileToLoad = nullptr)
    {
        
        if(editFileToLoad != nullptr)
        {
            DBG("Load project from file");
            edit = te::loadEditFromFile(engine, *editFileToLoad);
        }
        
        else
        {
            DBG("Create empty project");
            edit = te::createEmptyEdit(engine, editFile);
        }

Unfortunately I got other problems after loading second time:

JUCE Assertion failure in tracktion_DeviceManager.cpp:255

void DeviceManager::closeDevices()
{
    CRASH_TRACER
    TRACKTION_ASSERT_MESSAGE_THREAD

    jassert (activeContexts.isEmpty());

https://github.com/toxvox/StepSequencerDemo

Maybe I fooling around too much and need to check another solution.

I tried running your code, but my Tracktion code was out of date, I tried updating it and it’s giving me all sorts of errors… a project to worry about later, I guess!
It’s possible that the jassert is being called because some listeners haven’t been detached yet. Definitely a warning that something wasn’t ready to be deleted. Could also maybe the way the std::unique_ptr stepSequencer is being handled? Idk.

Here’s the way I implemented it while I was testing a while ago. It opens the mainWindow with a new project, or can open it with a project file:

StepSequencerDemo.h

//constructor for opening a project file
StepSequencerDemo(File editFile, std::function<void(File, bool)> reopenRequest) :
    edit(te::Edit::Options{
        engine,
        te::loadEditFromFile(engine, editFile, {}),
        te::ProjectItemID::createNewID(0),
        te::Edit::forEditing,
        nullptr,
        te::Edit::getDefaultNumUndoLevels(),
        [=] { return editFile; }
    })
{
    //other start up code here...
}

//constructor for creating a new project
StepSequencerDemo(std::function<void(File, bool)> reopenRequest) :
    edit{ engine, te::createEmptyEdit(engine), te::Edit::forEditing, nullptr, 0 }
{
    //other start up code here...
}
...
te::Edit edit; // this doesn't need to be an std::unique_ptr

Main.cpp

void initialise (const juce::String&) override
{
    open(File(), true);
}

void open(File file, bool newProject)
{
    auto reopenRequest = [=](File differentFile, bool newProjectWanted)
    {
        open(differentFile, newProjectWanted);
    });

    if (newProject)
    {
        mainWindow.reset(new MainWindow("StepSequencerDemo", new StepSequencerDemo(reopenRequest), *this));
    }
    else
    {
        mainWindow.reset(new MainWindow("StepSequencerDemo", new StepSequencerDemo(file, reopenRequest), *this));
    }
}

Then, when your load project button is pressed, simply call reopenRequest(projectFile). Or if the new project button is pressed, call reopenRequest(File(), true).

Also, side note: I’d recommend moving te::Engine from StepSequencerDemo.h to Main.cpp and passing it in to the StepSequencerDemo class. Creating the engine is a memory/CPU intensive process and can cause lag. Also, there’s no need to delete it and remake it every time a project is opened/created!

It works, thank you @JojoTheMAN21 ! I mixed your code with mine and have a good base for the next steps. Your side note to remove te::Engine from StepSequencerDemo.h was the solution!

Now I can load, close and save tracktionedit files…

Is it generally correct that I have to manage all sample files that refers to the .tracktionedit by myself? Or exists a helper function for “SaveAs” operations

Sample files are stored as references in the Edit file (usually as relative paths by default) so you’ll need to zip those up alongside the Edit file if you’re distributing them.