Problems using Tracktion engine within a plugin

I’ll be honest, I’m not 100% sure if we’ve tried running the Engine as plugin on a Linux test machine yet. It’s still very much a work-in-progress. We do have some time allocated, hopefully at the at the end of Jan to complete work on the Engine as a plugin though.

hi @dave96, do you think you’ll finally be able to look at the engine issues this month or has this been re-scheduled? thanks and let us know once you have some updates. I can test stuff if needed. cheers

I think we should be able to take a look at this over the next few weeks, we’re still catching up with things after NAMM and doing final prep for the W11 launch though so this isn’t quite top of the priority list yet though.

Having said that, I did make some changes (available on the develop branch) in December that should have fixed the triggerAndWaitForCallback issue. Are you still seeing this?

Also, it’s been a while since I’ve looked in to this thread and I have lots of correspondence with people about similar but distinct use cases and they all blur in to one in my head.
Could you perhaps provide a summary of the specific issues you face and need fixed?

Hi, thanks for your answer.
I’ll try with the fixed triggerAndWaitForCallback and see how far I get. So far this was the only issue I could see, but I don’t know if there will be others after this one. I’ll report here once I get some time to try,

As for the use case, long story short, I’m trying to build a sequencer + sampler using tracktion engine and have it running in a raspberry pi using linux-based ELK’s OS which gives multichannel audio and very low latency. The easiest way to run JUCE apps in ELK’s OS is to compile them as VST plugins. In this way only VST host software written by ELK interfaces with the audio/midi I/O devices and no custom code needs to be written for JUCE to work with ELK’s drivers. That’s why I need tracktion engine to run inside a plugin. I made many tests, first with macOS, then with ubuntu (virtual machine) and also in the raspberrypi with ELK OS. With macOS tracktion engine seems to work fine inside a plugin, but with both linux cases (ubuntu and ELK) I got the triggerAndWaitForCallback error.

I’ll try to see if with your recent fixes the engine works well in ubuntu. Then I’ll try with ELK as well, although in that case I might also need to change some things in JUCE because ELK OS uses a dual-kernel strategy (much like Bela) and some functions might need to be replace to be real-time safe, but this should not affect tracktion code.

Besides that, there are a number of tracktion engine things which do not yet work inside a plugin like MIDI (in or out, can’t remember), but that have been discussed in another thread and are not particular to my use case (and you already have in the roadmap).

Hope this helps!!

Ok, yes the MIDI out and some clean-up of the settings files (which are listed on the roadmap) are scheduled for very soon.

If there are any Elk specific issues you do find, let us know as we’ve not got a board to test with. Hopefully any changes required will be minor.

Currently I also don’t have a pi to do the actual tests, but hopefully I’ll have a pi soon and/or guys at ELK will be able to help as well. My guess is that no tracktion engine code should be adapted as long as it works in plugin mode and it does not try to access hardware audio/midi devices on its own. As said before, some changes might be needed in JUCE though most similar to the changes required for bela.

Quick update: I’m still getting the triggerAndWaitForCallback error in my Ubuntu VM setup. I did recompile the EngineInPluginDemo example project from the develop tracktion engine branch and I got same error. There’s nothing particular I’m doing here (except using a VM maybe?) so if you tested the EngineInPluginDemo example in a linux machine and it worked then I must have messed up something and I should try again.

What’s the stack trace?

Is this because during the initialisation, the JUCE message thread isn’t being set correctly?

And what host are you testing hosting the EngineInPluginDemo plugin in?
(My Linux VM is down at the moment so I can’t actually run this myself).

Hi,

I’m testing on Ubuntu 18.04.03, under a macOS mojave host and virtualbox virtualization software. This is the stacktrace when checkpointing the triggerAndWaitForCallback function:

root@ffont-VirtualBox:/home/ffont# gdb /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage...(no debugging symbols found)...done.
(gdb) b MessageThreadCallback:triggerAndWaitForCallback
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (MessageThreadCallback:triggerAndWaitForCallback) pending.
(gdb) run -d -c /media/sf_Shared/builds_linux/sushi_EngineInPluginDemo2_vst2.json
Starting program: /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage -d -c /media/sf_Shared/builds_linux/sushi_EngineInPluginDemo2_vst2.json
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 3225 is executing new program: /tmp/.mount_Sushi-gjZkfw/AppRun
process 3225 is executing new program: /tmp/.mount_Sushi-gjZkfw/usr/bin/sushi
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
SUSHI - Sensus Universal Sound Host Interface
Copyright 2016-2018 MIND Music Labs, Stockholm
[New Thread 0x7ffff48c7700 (LWP 3233)]
[New Thread 0x7fffeffff700 (LWP 3234)]
[New Thread 0x7fffef7fe700 (LWP 3235)]
[New Thread 0x7fffd4607700 (LWP 3236)]
[New Thread 0x7fffd3e06700 (LWP 3237)]
[New Thread 0x7fffd3605700 (LWP 3238)]
[New Thread 0x7fffd2e04700 (LWP 3239)]
[New Thread 0x7fffd2603700 (LWP 3240)]
[New Thread 0x7fffd1e02700 (LWP 3241)]
[New Thread 0x7fffd1601700 (LWP 3242)]
[New Thread 0x7fffd0e00700 (LWP 3243)]
[New Thread 0x7fffb3fff700 (LWP 3244)]
[New Thread 0x7fffb37fe700 (LWP 3245)]
[New Thread 0x7fffb2ffd700 (LWP 3246)]
[New Thread 0x7fffb27fc700 (LWP 3247)]
Creating Default Controllers...
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
*** ERROR: Rogue call to triggerAndWaitForCallback()
*** ERROR: triggerAndWaitForCallback() unable to complete
Finding MIDI I/O
MIDI output: MIDI Output
MIDI input: MIDI Input
opening MIDI out device:MIDI Output
*** ERROR: Failed to open MIDI output MIDI Output
Audio block size: 512  Rate: 44100
Rebuilding Wave Device List...
Wave In: Input 1 (enabled): 0 (L)
Wave In: Input 2 (enabled): 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
Default MIDI In: MIDI Input
Rebuilding Wave Device List...
Wave In: Input 1 (enabled): 0 (L)
Wave In: Input 2 (enabled): 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
Default MIDI In: MIDI Input
Rebuilding Wave Device List...
Wave In: Input 1 (enabled): 0 (L)
Wave In: Input 2 (enabled): 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
Default MIDI In: MIDI Input

Thread 1 "sushi" received signal SIGSEGV, Segmentation fault.
0x00007fffee650ba0 in tracktion_engine::InputDevice::isEnabled() const () from /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
(gdb) bt
#0  0x00007fffee650ba0 in tracktion_engine::InputDevice::isEnabled() const () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#1  0x00007fffee670c17 in tracktion_engine::EditPlaybackContext::rebuildDeviceList() () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#2  0x00007fffee671652 in tracktion_engine::EditPlaybackContext::EditPlaybackContext(tracktion_engine::TransportControl&) ()
    at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#3  0x00007fffee671928 in tracktion_engine::TransportControl::ensureContextAllocated(bool) ()
    at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#4  0x00007fffee590b6b in tracktion_engine::Edit::TreeWatcher::valueTreePropertyChanged(juce::ValueTree&, juce::Identifier const&) ()
    at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#5  0x00007fffee23eabf in juce::ValueTree::SharedObject::sendPropertyChangeMessage(juce::Identifier const&, juce::ValueTree::Listener*) ()
    at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#6  0x00007fffee23f67c in juce::ValueTree::SharedObject::setProperty(juce::Identifier const&, juce::var const&, juce::UndoManager*, juce::ValueTree::Listener*) () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#7  0x00007fffee238570 in juce::ValueTree::setPropertyExcludingListener(juce::ValueTree::Listener*, juce::Identifier const&, juce::var const&, juce::UndoManager*) () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#8  0x00007fffee41c0a3 in EngineInPluginDemo::EngineInPluginDemo() () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#9  0x00007fffee41b40b in createPluginFilter() () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#10 0x00007fffee1899eb in createPluginFilterOfType(juce::AudioProcessor::WrapperType) ()
    at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#11 0x00007fffee152c2c in VSTPluginMain () at /media/sf_Shared/builds_linux/EngineInPluginDemo2.so
#12 0x00005555555e9f1f in sushi::vst2::Vst2xWrapper::init(float) ()
#13 0x00005555555af4ca in sushi::engine::AudioEngine::add_plugin_to_track(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::---Type <return> to continue, or q <return> to quit---

I also tried setting a breakpoint for b juce::MessageManager::getInstance() but it does not seem to be triggering any error nearby.

I’m trying to look in to this but can’t get figure out how to create a json file sushi will load. I just get an error message:
Failed to load tracks from Json config file

Here’s my json file, cobbled together from the examples:

    "host_config" : {
        "samplerate" : 48000
    },
    "tracks" : [
        {
            "name" : "main",
            "mode" : "stereo",
            "inputs" : [
                {
                    "engine_bus" : 0,
                    "track_bus" : 0
                }
            ],
            "outputs" : [
                {
                    "engine_bus" : 0,
                    "track_bus" : 0
                }
            ],
            "plugins" : [
                {
                    "path" : "~/Documents/Elk/EngineInPluginDemo.so",
                    "name" : "synth",
                    "type"   : "vst2x"
                }
            ]
        }
    ],
    "midi" : {}
}

Can the plugin be a relative path to the sushi file? That would be simpler.


What worries me looking at your log file however is that the line Rogue call to triggerAndWaitForCallback() is indicating the Engine object, and hence the plugin isn’t being loaded from a JUCE thread. I forget how this works in Linux but I would have thought that whatever thread instantiates JUCE, gets tagged as the message thread and then the host has to pump its dispatch loop when possible. (The other alternative is the JUCE creates its own message thread and uses that internally to pump the message thread).


Worse than that though, the second error ERROR: triggerAndWaitForCallback() unable to complete indicates that the message thread hasn’t been initialised at all as this signifies that the async message is unable to be dispatched and complete its action.
If this is the case, nothing is going to work.


Having said all that, I was able to build and test this in the JUCE Audio Plugin Host and get similar error messages, the first of which is an assertion that fails in tracktion_AutomationRecordManager.cpp:
jassert (MessageManager::getInstance()->isThisTheMessageThread());

As suspected, this indicates that the message thread is not being associated with current thread.

On Linux there is a class: SharedMessageThread, this seems to designate itself as the message thread with the call to MessageManager::getInstance()->setCurrentThreadAsMessageThread() in the run() method.
However, that then conflicts with the plugin creation which just continues on the current thread.

I’m not sure how this is supposed to work and if on Linux you’re actually getting two threads pretending to be the message thread?
To me, this sounds like a JUCE plugin client bug…

There’s more to this story, duing plugin creation on Linux there’s this line:

    {
        JUCE_AUTORELEASEPOOL
        {
            initialiseJuce_GUI();

            try
            {
                if (audioMaster (nullptr, Vst2::audioMasterVersion, 0, 0, nullptr, 0) != 0)
                {
                   #if JUCE_LINUX
                    MessageManagerLock mmLock;
                   #endif

                    auto* processor = createPluginFilterOfType (AudioProcessor::wrapperType_VST);
                    auto* wrapper = new JuceVSTWrapper (audioMaster, processor);
                    auto* aEffect = wrapper->getAEffect();

so I think is what’s happening is that:
• A message manager lock is created
• The SharedMessageThread is created which starts a thread and designates it as the message thread
• The plugin is instantiated on the calling thread
• Any messages posted during this time will fail to be called back due to the lock being held

I’ll have to have a think about this. I’m not sure what the correct approach is.

Thanks for all these insights @dave96. Let me know if you need me to try something else with ELK’s Sushi at a later time. Although I assume now first step would be to get it working on normal Linux distro with standard plugin hosts. In fact, as I reported some messages above (but did not include in the summary), I’m able to see the same problem under Linux with different plugin hosts. Looking forward to further updates :slight_smile:

Yeah, the problem is that Tracktion Engine assumes that it is being constructed on the message thread and also requires and unblocked message thread to load an Edit.
It appears in the JUCE Linux plugin client neither of these is true.

I guess you could create an empty plugin and then lazily initialise the Engine and Edit object asynchronously after construction and wait for it to complete in prepareToPlay?

Ah interesting. I’m not quite sure how to do this as I’m no JUCE/C++ expert. How do I define a te:Engine engine {...} property and prevent it’s automatic initialization? Then where should I trigger the initialization? I assume after the plugin constructor is done. Should all this be in prepareToPlay then? Sorry for the nob quesitons…

Yeah sorry, it’s a bit fiddly…

I think you’d probably have to make some kind of struct that contains the Engine and Edit that you want to load (basically just encapsulate the existing Engine members), then create a std::unique_ptr to that in your plugin’s member section and then in your prepareToPlay method, check if that pointer is nullptr, if it is, create an instance of your class and assign it.

That should do the construction only once and in prepareToPlay.
Hopefully this will work and you don’t need access to the Engine before that point?

Thanks for the tip. I’m trying to adapt the EngineInPluginDemo example to the setup you’re describing.

First I’m wrapping these lines…

te::Engine engine { ProjectInfo::projectName, nullptr, std::make_unique<PluginEngineBehaviour>() };
te::Edit edit { engine, te::createEmptyEdit(), te::Edit::forEditing, nullptr, 0 };
te::TransportControl& transport { edit.getTransport() };
te::HostedAudioDeviceInterface& audioInterface;

…into a struct (and add a class member with std::unique_ptr to a member of that struct type:

struct TracktionEngineMembers
{
    te::Engine engine{ProjectInfo::projectName, nullptr, std::make_unique<PluginEngineBehaviour>()};
    te::Edit edit{engine, te::createEmptyEdit(), te::Edit::forEditing, nullptr, 0};
    te::TransportControl &transport{edit.getTransport()};
    te::HostedAudioDeviceInterface &audioInterface;
};

std::unique_ptr<TracktionEngineMembers> teMembers;

I modified the plugin constructor (https://github.com/Tracktion/tracktion_engine/blob/c686909a062a44171c0215cc8a0c1fbbcc320a95/examples/EngineInPluginDemo.h#L53-L63) so that it does not call anything related to the tracktion engine:

EngineInPluginDemo()
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
                                           .withOutput ("Output", AudioChannelSet::stereo()))
{   
}

Then in prepareToPlay I want to do the lazy initialization plus calling the other util functions that would setup the inputs for the engine, configure a plugin, etc… However I’m not sure how to do the initialization. I made some attempts but could not get it to compile. I tried this:

void prepareToPlay (double sampleRate, int expectedBlockSize) override
{
        if (teMembers.get() == nullptr)
        {
            teMembers = new TracktionEngineMembers();
            // These 3 functions below used to be in the EngineInPlugin constructor
            teMembers->audioInterface.initialise({});
            setupInputs();
            create4OSCPlugin();
        }

        setLatencySamples (expectedBlockSize);
        teMembers->audioInterface.prepareToPlay(sampleRate, expectedBlockSize);
}

But this gives me compilation errors. I’m quite sure I’m not handling initialization properly but I’m not sure how should I do it. Probably initialize struct members manually one by one? Then the struct definition should be different as well right?

What compiler errors are you getting?
I think the only error would be this line:
teMembers = new TracktionEngineMembers();
which should be:
teMembers.reset (new TracktionEngineMembers();)
or preferably:
teMembers = std::make_unique<TracktionEngineMembers>();


The only other thing I can think of but can’t see in your code is that setupInputs() and create4OSCPlugin() would also need to be updated to use the teMembers variable.

Hi,

I changed the line to teMembers = std::make_unique<TracktionEngineMembers>(); but I’m still getting similar errors. This is the compiler output:

root@ffont-VirtualBox:/media/sf_Shared/tracktion_engine/examples/projects/EngineInPluginDemo/Builds/LinuxMakefile# make CONFIG=Debug -j2
Compiling Main.cpp
In file included from /usr/include/c++/7/memory:80:0,
                 from ../../../../../modules/juce/modules/juce_core/system/juce_StandardHeader.h:46,
                 from ../../../../../modules/juce/modules/juce_core/juce_core.h:196,
                 from ../../../../../modules/juce/modules/juce_audio_basics/juce_audio_basics.h:52,
                 from ../../Source/../JuceLibraryCode/JuceHeader.h:17,
                 from ../../Source/Main.cpp:9:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = EngineInPluginDemo::TracktionEngineMembers; _Args = {}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<EngineInPluginDemo::TracktionEngineMembers>]’:
../../Source/../../../EngineInPluginDemo.h:69:66:   required from here
/usr/include/c++/7/bits/unique_ptr.h:825:30: error: use of deleted function ‘EngineInPluginDemo::TracktionEngineMembers::TracktionEngineMembers()’
     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../Source/Main.cpp:10:0:
../../Source/../../../EngineInPluginDemo.h:176:12: note: ‘EngineInPluginDemo::TracktionEngineMembers::TracktionEngineMembers()’ is implicitly deleted because the default definition would be ill-formed:
     struct TracktionEngineMembers
            ^~~~~~~~~~~~~~~~~~~~~~
../../Source/../../../EngineInPluginDemo.h:176:12: error: uninitialized reference member in ‘struct EngineInPluginDemo::TracktionEngineMembers’
../../Source/../../../EngineInPluginDemo.h:181:41: note: ‘tracktion_engine::HostedAudioDeviceInterface& EngineInPluginDemo::TracktionEngineMembers::audioInterface’ should be initialized
         te::HostedAudioDeviceInterface &audioInterface;
                                         ^~~~~~~~~~~~~~
Makefile:167: recipe for target 'build/intermediate/Debug/Main_90ebc5c2.o' failed
make: *** [build/intermediate/Debug/Main_90ebc5c2.o] Error 1

Seems to be related to some initialization issue with audioInterface. I think this is because in the original code there is this line in the plugin constructor which (I guess) initializes audioInterface member, but I don’t have a replacement for that :S. This syntax is a bit confusing to me because I don’t know if this is indeed initialization code and also if this is happening before the rest of the constructor code and after class members have been autoinitialised. Therefore I’m not sure where should I put a replacement for this. Maybe right before the teMembers->audioInterface.initialise({}); line? However the compiler error seems to be in the initialisation of teMembers so maybe this should happen before?

Thanks for all the help!

EDIT: adding teMembers->audioInterface = te::HostedAudioDeviceInterface(teMembers->engine.getDeviceManager().getHostedAudioDeviceInterface()); before teMembers->audioInterface.initialise({}); results in the same compiler error as before.

For reference, this is my whole current EngineInPluginDemo.h source file:

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             EngineInPluginDemo
  version:          0.0.1
  vendor:           Tracktion
  website:          www.tracktion.com
  description:      Example of how to use the engine in a plugin

  dependencies:     juce_audio_basics, juce_audio_devices, juce_audio_formats, juce_audio_plugin_client, 
                    juce_audio_processors, juce_audio_utils, juce_core, juce_data_structures, juce_events, 
                    juce_graphics, juce_gui_basics, juce_gui_extra, juce_dsp, juce_osc, tracktion_engine
  exporters:        vs2017, xcode_mac, linux_make

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             AudioProcessor
  mainClass:        EngineInPluginDemo

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once

#include "common/Utilities.h"

static String organPatch = "<PLUGIN type=\"4osc\" windowLocked=\"1\" id=\"1069\" enabled=\"1\" filterType=\"1\" presetDirty=\"0\" presetName=\"4OSC: Organ\" filterFreq=\"127.00000000000000000000\" ampAttack=\"0.60000002384185791016\" ampDecay=\"10.00000000000000000000\" ampSustain=\"100.00000000000000000000\" ampRelease=\"0.40000000596046447754\" waveShape1=\"4\" tune2=\"-24.00000000000000000000\" waveShape2=\"4\"> <MACROPARAMETERS id=\"1069\"/> <MODIFIERASSIGNMENTS/> <MODMATRIX/> </PLUGIN>";

//==============================================================================
class PluginEditor : public AudioProcessorEditor
{
public:
    PluginEditor (AudioProcessor& p) : AudioProcessorEditor (p)
    {
        setSize (400, 300);
    }
    
    void paint (Graphics& g) override
    {
        g.fillAll (Colours::darkgrey);
    }
};


//==============================================================================
class EngineInPluginDemo  : public AudioProcessor
{
public:
    //==============================================================================
    EngineInPluginDemo()
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
                                           .withOutput ("Output", AudioChannelSet::stereo()))
    {   
    }

    ~EngineInPluginDemo()
    {
    }

    //==============================================================================
    void prepareToPlay (double sampleRate, int expectedBlockSize) override
    {
        if (teMembers.get() == nullptr)
        {
            teMembers = std::make_unique<TracktionEngineMembers>();

            teMembers->audioInterface = te::HostedAudioDeviceInterface(teMembers->engine.getDeviceManager().getHostedAudioDeviceInterface());

            // These 3 functions below used to be in the EngineInPlugin constructor
            teMembers->audioInterface.initialise({});
            setupInputs();
            create4OSCPlugin();
        }

        setLatencySamples (expectedBlockSize);
        teMembers->audioInterface.prepareToPlay(sampleRate, expectedBlockSize);
    }

    void releaseResources() override {}

    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override
    {
        ScopedNoDenormals noDenormals;
        
        auto totalNumInputChannels  = getTotalNumInputChannels();
        auto totalNumOutputChannels = getTotalNumOutputChannels();

        for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
            buffer.clear (i, 0, buffer.getNumSamples());

        teMembers->audioInterface.processBlock (buffer, midi);
    }

    //==============================================================================
    AudioProcessorEditor* createEditor() override          { return new PluginEditor (*this); }
    bool hasEditor() const override                        { return true;   }

    //==============================================================================
    const String getName() const override                  { return "EngineInPluginDemo"; }
    bool acceptsMidi() const override                      { return true; }
    bool producesMidi() const override                     { return true; }
    double getTailLengthSeconds() const override           { return 0; }

    //==============================================================================
    int getNumPrograms() override                          { return 1; }
    int getCurrentProgram() override                       { return 0; }
    void setCurrentProgram (int) override                  {}
    const String getProgramName (int) override             { return {}; }
    void changeProgramName (int, const String&) override   {}

    //==============================================================================
    void getStateInformation (MemoryBlock&) override {}
    void setStateInformation (const void*, int) override {}

    //==============================================================================
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
    {
        if (layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
            return false;

        if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
            return false;

        return true;
    }

private:
    void setupInputs()
    {
        auto &dm = teMembers->engine.getDeviceManager();
        for (int i = 0; i < dm.getNumMidiInDevices(); i++)
        {
            auto dev = dm.getMidiInDevice (i);
            dev->setEnabled (true);
            dev->setEndToEndEnabled (true);
        }

        teMembers->edit.playInStopEnabled = true;
        teMembers->edit.getTransport().ensureContextAllocated(true);

        if (auto t = EngineHelpers::getOrInsertAudioTrackAt(teMembers->edit, 0))
            if (auto dev = dm.getMidiInDevice (0))
                for (auto instance : teMembers->edit.getAllInputDevices())
                    if (&instance->getInputDevice() == dev)
                        instance->setTargetTrack (*t, 0, true);

        teMembers->edit.restartPlayback();
    }
    
    void create4OSCPlugin()
    {
        //==============================================================================
        if (auto synth = dynamic_cast<te::FourOscPlugin *>(teMembers->edit.getPluginCache().createNewPlugin(te::FourOscPlugin::xmlTypeName, {}).get()))
        {
            XmlDocument doc (organPatch);
            if (auto e = doc.getDocumentElement())
            {
                auto vt = ValueTree::fromXml (*e);
                if (vt.isValid())
                    synth->restorePluginStateFromValueTree (vt);
            }

            if (auto t = EngineHelpers::getOrInsertAudioTrackAt(teMembers->edit, 0))
                t->pluginList.insertPlugin (*synth, 0, nullptr);
        }
    }
    
    //==============================================================================
    class PluginEngineBehaviour : public te::EngineBehaviour
    {
    public:
        bool autoInitialiseDeviceManager() override { return false; }
    };

    struct TracktionEngineMembers
    {
        te::Engine engine{ProjectInfo::projectName, nullptr, std::make_unique<PluginEngineBehaviour>()};
        te::Edit edit{engine, te::createEmptyEdit(), te::Edit::forEditing, nullptr, 0};
        //te::TransportControl &transport{edit.getTransport()};
        te::HostedAudioDeviceInterface &audioInterface;
    };

    //==============================================================================
    std::unique_ptr<TracktionEngineMembers> teMembers;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EngineInPluginDemo)
};

Try this:

 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             EngineInPluginDemo
  version:          0.0.1
  vendor:           Tracktion
  website:          www.tracktion.com
  description:      Example of how to use the engine in a plugin

  dependencies:     juce_audio_basics, juce_audio_devices, juce_audio_formats, juce_audio_plugin_client,
                    juce_audio_processors, juce_audio_utils, juce_core, juce_data_structures, juce_events,
                    juce_graphics, juce_gui_basics, juce_gui_extra, juce_dsp, juce_osc, tracktion_engine
  exporters:        vs2017, xcode_mac, linux_make

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             AudioProcessor
  mainClass:        EngineInPluginDemo

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once

#include "common/Utilities.h"

static String organPatch = "<PLUGIN type=\"4osc\" windowLocked=\"1\" id=\"1069\" enabled=\"1\" filterType=\"1\" presetDirty=\"0\" presetName=\"4OSC: Organ\" filterFreq=\"127.00000000000000000000\" ampAttack=\"0.60000002384185791016\" ampDecay=\"10.00000000000000000000\" ampSustain=\"100.00000000000000000000\" ampRelease=\"0.40000000596046447754\" waveShape1=\"4\" tune2=\"-24.00000000000000000000\" waveShape2=\"4\"> <MACROPARAMETERS id=\"1069\"/> <MODIFIERASSIGNMENTS/> <MODMATRIX/> </PLUGIN>";

//==============================================================================
class PluginEditor : public AudioProcessorEditor
{
public:
    PluginEditor (AudioProcessor& p) : AudioProcessorEditor (p)
    {
        setSize (400, 300);
    }

    void paint (Graphics& g) override
    {
        g.fillAll (Colours::darkgrey);
    }
};


//==============================================================================
class EngineInPluginDemo  : public AudioProcessor
{
public:
    //==============================================================================
    EngineInPluginDemo()
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
                                           .withOutput ("Output", AudioChannelSet::stereo()))
    {
    }

    ~EngineInPluginDemo()
    {
    }

    //==============================================================================
    void prepareToPlay (double sampleRate, int expectedBlockSize) override
    {
        if (teMembers.get() == nullptr)
        {
            teMembers = std::make_unique<TracktionEngineMembers>();

            // These 3 functions below used to be in the EngineInPlugin constructor
            teMembers->audioInterface.initialise({});
            setupInputs();
            create4OSCPlugin();
        }

        setLatencySamples (expectedBlockSize);
        teMembers->audioInterface.prepareToPlay(sampleRate, expectedBlockSize);
    }

    void releaseResources() override {}

    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override
    {
        ScopedNoDenormals noDenormals;

        auto totalNumInputChannels  = getTotalNumInputChannels();
        auto totalNumOutputChannels = getTotalNumOutputChannels();

        for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
            buffer.clear (i, 0, buffer.getNumSamples());

        teMembers->audioInterface.processBlock (buffer, midi);
    }

    //==============================================================================
    AudioProcessorEditor* createEditor() override          { return new PluginEditor (*this); }
    bool hasEditor() const override                        { return true;   }

    //==============================================================================
    const String getName() const override                  { return "EngineInPluginDemo"; }
    bool acceptsMidi() const override                      { return true; }
    bool producesMidi() const override                     { return true; }
    double getTailLengthSeconds() const override           { return 0; }

    //==============================================================================
    int getNumPrograms() override                          { return 1; }
    int getCurrentProgram() override                       { return 0; }
    void setCurrentProgram (int) override                  {}
    const String getProgramName (int) override             { return {}; }
    void changeProgramName (int, const String&) override   {}

    //==============================================================================
    void getStateInformation (MemoryBlock&) override {}
    void setStateInformation (const void*, int) override {}

    //==============================================================================
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
    {
        if (layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
            return false;

        if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
            return false;

        return true;
    }

private:
    void setupInputs()
    {
        auto &dm = teMembers->engine.getDeviceManager();
        for (int i = 0; i < dm.getNumMidiInDevices(); i++)
        {
            auto dev = dm.getMidiInDevice (i);
            dev->setEnabled (true);
            dev->setEndToEndEnabled (true);
        }

        teMembers->edit.playInStopEnabled = true;
        teMembers->edit.getTransport().ensureContextAllocated(true);

        if (auto t = EngineHelpers::getOrInsertAudioTrackAt(teMembers->edit, 0))
            if (auto dev = dm.getMidiInDevice (0))
                for (auto instance : teMembers->edit.getAllInputDevices())
                    if (&instance->getInputDevice() == dev)
                        instance->setTargetTrack (*t, 0, true);

        teMembers->edit.restartPlayback();
    }

    void create4OSCPlugin()
    {
        //==============================================================================
        if (auto synth = dynamic_cast<te::FourOscPlugin *>(teMembers->edit.getPluginCache().createNewPlugin(te::FourOscPlugin::xmlTypeName, {}).get()))
        {
            XmlDocument doc (organPatch);
            if (auto e = doc.getDocumentElement())
            {
                auto vt = ValueTree::fromXml (*e);
                if (vt.isValid())
                    synth->restorePluginStateFromValueTree (vt);
            }

            if (auto t = EngineHelpers::getOrInsertAudioTrackAt(teMembers->edit, 0))
                t->pluginList.insertPlugin (*synth, 0, nullptr);
        }
    }

    //==============================================================================
    class PluginEngineBehaviour : public te::EngineBehaviour
    {
    public:
        bool autoInitialiseDeviceManager() override { return false; }
    };

    struct TracktionEngineMembers
    {
        te::Engine engine { ProjectInfo::projectName, nullptr, std::make_unique<PluginEngineBehaviour>() };
        te::Edit edit { engine, te::createEmptyEdit(), te::Edit::forEditing, nullptr, 0 };
        te::TransportControl& transport { edit.getTransport() };
        te::HostedAudioDeviceInterface& audioInterface { engine.getDeviceManager().getHostedAudioDeviceInterface() };
    };

    //==============================================================================
    std::unique_ptr<TracktionEngineMembers> teMembers;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EngineInPluginDemo)
};```

You can't assign to a reference, you have to initialise it upon construction (that's the main difference between a reference and a pointer).

Hi,

with latest @dave96 suggestions I can compile the plugin, and now I’m getting some other errors (also seem related to triggerAndWaitForCallback but I no longer see the unable to complete message. This is the console output (below stacktrace):

JUCE v5.4.5
JUCE Assertion failure in juce_ChangeBroadcaster.cpp:39
JUCE Assertion failure in juce_ChangeBroadcaster.cpp:39
Creating Default Controllers...
JUCE Assertion failure in tracktion_AutomationRecordManager.cpp:26
JUCE Assertion failure in juce_ChangeBroadcaster.cpp:39
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
Edit loaded in: 56 ms
*** ERROR: Rogue call to triggerAndWaitForCallback()
JUCE Assertion failure in tracktion_AsyncFunctionUtils.h:164
JUCE Assertion failure in tracktion_DeviceManager.cpp:286Rebuilding Wave Device List...
Wave In: Input 1: 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2: 0 (L), 1 (R)
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
Default MIDI In: 

Finding MIDI I/O
MIDI output: MIDI Output
MIDI input: MIDI Input
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
Audio block size: 512  Rate: 44100
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1: 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2: 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: 
Default MIDI Out: 
Default Wave In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1: 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1: 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1: 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1 (enabled): 0 (L)
Wave In: Input 2: 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:547
Rebuilding Wave Device List...
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
JUCE Assertion failure in tracktion_LevelMeasurer.cpp:52
Wave In: Input 1 (enabled): 0 (L)
Wave In: Input 2 (enabled): 1 (L)
Wave Out: Output 1 + 2 (enabled): 0 (L), 1 (R)
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default Wave Out: Output 1 + 2
Default MIDI Out: 
Default Wave In: Input 1
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
Default MIDI In: 
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_EditPlaybackContext.cpp:77
JUCE Assertion failure in tracktion_DeviceManager.cpp:1211
JUCE Assertion failure in tracktion_Modifier.cpp:113
JUCE Assertion failure in tracktion_Modifier.cpp:113
JUCE Assertion failure in tracktion_DeviceManager.cpp:968
JUCE Assertion failure in tracktion_MidiInputDevice.cpp:630
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_MacroParameter.cpp:203
Generating waves: 1622ms
JUCE Assertion failure in tracktion_DeviceManager.cpp:154
JUCE Assertion failure in juce_AsyncUpdater.cpp:82
JUCE Assertion failure in tracktion_EditPlaybackContext.cpp:61
JUCE Assertion failure in tracktion_EditPlaybackContext.cpp:77
Failed to load Event list from Json config file
*** Leaked objects detected: 3 instance(s) of class OwnedArray
[... lots of other leaked objects detected]
JUCE Assertion failure in juce_LeakedObjectDetector.h:90
JUCE Assertion failure in juce_Singleton.h:50
JUCE Assertion failure in juce_Singleton.h:50
JUCE Assertion failure in juce_Singleton.h:50
JUCE Assertion failure in juce_Singleton.h:50
JUCE Assertion failure in juce_Singleton.h:50
JUCE Assertion failure in juce_Singleton.h:50
*** Leaked objects detected: 1 instance(s) of class StringPairArray
JUCE Assertion failure in juce_LeakedObjectDetector.h:90
*** Leaked objects detected: 36 instance(s) of class StringArray
JUCE Assertion failure in juce_LeakedObjectDetector.h:90
JUCE Assertion failure in juce_Singleton.h:50

And this the stacktrace (without setting any breakpoint):

root@ffont-VirtualBox:/home/ffont# gdb /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage...(no debugging symbols found)...done.
(gdb) run -d -c /media/sf_Shared/builds_linux/sushi_EngineInPluginDemo2_vst2.json
Starting program: /media/sf_Shared/Sushi-x86_64-0.8.0.AppImage -d -c /media/sf_Shared/builds_linux/sushi_EngineInPluginDemo2_vst2.json
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 9022 is executing new program: /tmp/.mount_Sushi-njzRLN/AppRun
process 9022 is executing new program: /tmp/.mount_Sushi-njzRLN/usr/bin/sushi
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
SUSHI - Sensus Universal Sound Host Interface
Copyright 2016-2018 MIND Music Labs, Stockholm
[New Thread 0x7ffff48c7700 (LWP 9039)]
[New Thread 0x7fffeffff700 (LWP 9040)]
[New Thread 0x7fffef7fe700 (LWP 9041)]
JUCE v5.4.5
[New Thread 0x7fffd344c700 (LWP 9046)]
[New Thread 0x7fffd2c4b700 (LWP 9047)]
[New Thread 0x7fffd244a700 (LWP 9048)]
[New Thread 0x7fffd1c49700 (LWP 9049)]
JUCE Assertion failure in juce_ChangeBroadcaster.cpp:39

Thread 1 "sushi" received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff5acb187 in kill () at ../sysdeps/unix/syscall-template.S:78
78	../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0  0x00007ffff5acb187 in kill () at ../sysdeps/unix/syscall-template.S:78
#1  0x00007fffedc41ef1 in juce::ChangeBroadcaster::addChangeListener(juce::ChangeListener*) (this=0x555555a26008, listener=0x555555a25ff0)
    at ../../../../../modules/juce/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp:39
#2  0x00007fffee277876 in tracktion_engine::DeviceManager::DeviceManager(tracktion_engine::Engine&) (this=0x555555a25fb0, e=...)
    at ../../../../../modules/tracktion_engine/playback/tracktion_DeviceManager.cpp:182
#3  0x00007fffee4174bf in tracktion_engine::Engine::initialise() (this=0x5555559fedb0)
    at ../../../../../modules/tracktion_engine/utilities/tracktion_Engine.cpp:58
#4  0x00007fffee416f78 in tracktion_engine::Engine::Engine(std::unique_ptr<tracktion_engine::PropertyStorage, std::default_delete<tracktion_engine::PropertyStorage> >, std::unique_ptr<tracktion_engine::UIBehaviour, std::default_delete<tracktion_engine::UIBehaviour> >, std::unique_ptr<tracktion_engine::EngineBehaviour, std::default_delete<tracktion_engine::EngineBehaviour> >) (this=0x5555559fedb0, ps=std::unique_ptr<tracktion_engine::PropertyStorage> = {...}, ub=std::unique_ptr<tracktion_engine::UIBehaviour> = {...}, eb=std::unique_ptr<tracktion_engine::EngineBehaviour> = {...}) at ../../../../../modules/tracktion_engine/utilities/tracktion_Engine.cpp:33
#5  0x00007fffee41719b in tracktion_engine::Engine::Engine(juce::String, std::unique_ptr<tracktion_engine::UIBehaviour, std::default_delete<tracktion_engine::UIBehaviour> >, std::unique_ptr<tracktion_engine::EngineBehaviour, std::default_delete<tracktion_engine::EngineBehaviour> >) (this=0x5555559fedb0, applicationName=..., ub=std::unique_ptr<tracktion_engine::UIBehaviour> = {...}, eb=std::unique_ptr<tracktion_engine::EngineBehaviour> = {...}) at ../../../../../modules/tracktion_engine/utilities/tracktion_Engine.cpp:37
#6  0x00007fffedf1c96c in EngineInPluginDemo::TracktionEngineMembers::TracktionEngineMembers() (this=0x5555559fedb0)
    at ../../Source/../../../EngineInPluginDemo.h:175
#7  0x00007fffedf1cb0d in std::make_unique<EngineInPluginDemo::TracktionEngineMembers>() () at /usr/include/c++/7/bits/unique_ptr.h:825
#8  0x00007fffedf1b170 in EngineInPluginDemo::prepareToPlay(double, int) (this=0x5555559fd0c0, sampleRate=48000, expectedBlockSize=64)
    at ../../Source/../../../EngineInPluginDemo.h:67
#9  0x00007fffedabecc4 in JuceVSTWrapper::resume() (this=0x5555559fdb60)
    at ../../../../../modules/juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:632
#10 0x00007fffedac2001 in JuceVSTWrapper::handleResumeSuspend(JuceVSTWrapper::VstOpCodeArguments) (this=0x5555559fdb60, args=...)
    at ../../../../../modules/juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:1815
#11 0x00007fffedac09d9 in JuceVSTWrapper::dispatcher(int, JuceVSTWrapper::VstOpCodeArguments) (this=0x5555559fdb60, opCode=12, args=...)
    at ../../../../../modules/juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:1217
#12 0x00007fffedac0e4e in JuceVSTWrapper::dispatcherCB(Vst2::AEffect*, int, int, long long, void*, float) (vstInterface=0x5555559fdbc8, opCode=12, index=0, value=1, ptr=0x0, opt=0) at ../../../../../modules/juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:1265
#13 0x00005555555e79c2 in sushi::vst2::Vst2xWrapper::set_enabled(bool) ()
#14 0x00005555555af4f9 in sushi::engine::AudioEngine::add_plugin_to_track(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, sushi::engine::PluginType) ()
#15 0x00005555555c4d98 in sushi::jsonconfig::JsonConfigurator::_make_track(rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> > const&) ()
#16 0x00005555555c6ba4 in sushi::jsonconfig::JsonConfigurator::load_tracks() ()
#17 0x000055555557cb30 in main ()

I’m doing this inside a VM, so maybe some errors might be due to that, specially if it has trouble accessing audio devices. But in any case some errors still seem to be related to message queues, etc.

It shouldn’t be to do with the VM.
It seems that the Linux host still calls prepareToPlay directly after construction on the same thread, not the message thread.

It does seem like the message loop is running at this point though.

The only thing I can think of now is a slightly dodgy hack to post a message to initialise the Engine object and then wait for it to complete in prepare to play.
That might work but it’s not exactly a nice solution if the Engine object takes a while to initialise.

Can you try replacing your prepareToPlay method with this:

    void prepareToPlay (double sampleRate, int expectedBlockSize) override
    {
        if (teMembers.get() == nullptr)
        {
            auto initialiseEngine = [this]
            {
                teMembers = std::make_unique<TracktionEngineMembers>();

                // These 3 functions below used to be in the EngineInPlugin constructor
                teMembers->audioInterface.initialise({});
                setupInputs();
                create4OSCPlugin();
            };

            // Do this async to ensure it happens on the message thread
            if (MessageManager::getInstance()->isThisTheMessageThread())
            {
                initialiseEngine();
            }
            else
            {
                jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager());
                WaitableEvent finishedSignal;
                MessageManager::callAsync ([&]
                                           {
                                               initialiseEngine();
                                               finishedSignal.signal();
                                           });
                finishedSignal.wait (-1);
            }
        }

        setLatencySamples (expectedBlockSize);
        teMembers->audioInterface.prepareToPlay(sampleRate, expectedBlockSize);
    }

Does that manage to execute?