Problems using Tracktion engine within a plugin

Yeah, can you first try just changing the waiter.wait (50); on line 165 to waiter.wait (-1); and see what happens?

I have a feeling that this is happening because you’re running this in a straight main() function without having a JUCE message loop which means the async message can never be dispatched.

If this was running on the main JUCE message thread, callBlocking happens synchronously which would be fine.

Can you put a breakpoint in juce::MessageManager::getInstance() to see if it’s actually been initialised and started at this point in your plugin?

If that were the case, maybe a change in TracktionEngine code could take that into account and also make the call synchronously if MessageManager::getInstanceWithoutCreating() returns nullptr?

But that would assume the code to be single-threaded in that case, which could not be necessarily true…

Honestly, I can’t begin to comprehend what would need to change for Engine to work without a message loop. There’s thousands of places which rely on this behaviour for all sorts of reasons. If you don’t have a message loop, I just don’t think that’s a feasible use-case for us to support at the moment.

Thanks for your answers @dave96 @yfede. These are the results of what @dave96 suggested:

  • I tried compiling with waiter.wait (-1) and in this case the program hangs forever. Hence it looks like triggerAndWaitForCallback() never returns.

  • Setting breakpoint to juce::MessageManager::getInstance() stops the debugger before the app hangs infinitely (because I’m still using waiter.wait (-1)). In fact it calls the function a number of times. This is the stack trace the first time getInstance is called:

Thread 5 "sushi" hit Breakpoint 1, juce::MessageManager::getInstance ()
    at ../../../../../modules/juce/modules/juce_events/messages/juce_MessageManager.cpp:47
47	    if (instance == nullptr)
(gdb) bt
#0  0x00007ffff1d31091 in juce::MessageManager::getInstance() ()
    at ../../../../../modules/juce/modules/juce_events/messages/juce_MessageManager.cpp:47
#1  0x00007ffff1d32046 in juce::initialiseJuce_GUI() ()
    at ../../../../../modules/juce/modules/juce_events/messages/juce_MessageManager.cpp:461
#2  0x00007ffff1bb222d in SharedMessageThread::run() (this=0x5555559fc800)
    at ../../../../../modules/juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:239
#3  0x00007ffff1c9402f in juce::Thread::threadEntryPoint() (this=0x5555559fc800)
    at ../../../../../modules/juce/modules/juce_core/threads/juce_Thread.cpp:96
#4  0x00007ffff1c9411c in juce::juce_threadEntryPoint(void*) (userData=0x5555559fc800)
    at ../../../../../modules/juce/modules/juce_core/threads/juce_Thread.cpp:118
#5  0x00007ffff1cb821c in juce::threadEntryProc(void*) (userData=0x5555559fc800)
    at ../../../../../modules/juce/modules/juce_core/native/juce_posix_SharedCode.h:923
#6  0x00007ffff71e66db in start_thread (arg=0x7fffd3526700) at pthread_create.c:463
#7  0x00007ffff5bad88f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

This I did not try yet. What would be the purpose of this?

Also about the discussion of the message loop I don’t know much of it, but I assume this is something in the JUCE architecture. Here I’m trying to run the EngineInPlugin example demo app of traction as a plugin (VST2 ideally) inside a host, so I’m not modifying internals or hacking the Engine/JUCE in any way. I’m running this in a Linux VM, but I’ve been reported same problem happening in native linux machine. I’m currently also setting up a raspberry pi with raspbian as a native linux machine so I can repeat the same tests there and see what happens.

What Linux distro are you using?

With the wait time set to -1, if you pause the debugger at that point and print out the stack traces of all the other threads, is there something else blocking the message queue?

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.

For my Linux VM I’m using ubuntu studio (18.04).

However I’ve been finally able to make some experiments with native linux with a raspberry pi and Raspbian buster (latest version). These are my findings:

  • EngineInPluginDemo project works well as standalone. I assume in my VM I could not get it working because my audio was not configured properly.

  • EngineInPluginDemo as plugin (VST2) still produces the same triggerAndWaitForCallback() error. This is testing with the JUCE AudioPluingHost which I was able to compile (with VST2 support).

I guess this is both good news and bad news. Bad news because it does not work. Good news because the problem does not seem to be platform/distro specific (I’ve seen same problem with pi+AudioPluginHost, ubuntu+Carla and ubuntu+Sushi) and is easy to reproduce. Therefore I’m pretty sure the awesome Tracktion team will fix it whenever you have some time to polish the Tracktion-inside-plugin compatibility.

Please let me know if there’s any other thing you think I could try as a workaround solution. Thanks for your answers and your time! @RolandMR @dave96 @yfede

1. reaper + wineasio/wine-rt:

+ fast
+ stable
+ easy
+ mighty monster
+ jsx-plug-ins
+ scriptable
+ readable + writable text based file format (version control!, recovering)
+ exportable fine grained readable + writable options (version control!, recovering)
+ templates for tracks projects
+ sub-projects
+ efficient (can be used without mouse)
+ great routing flexibility
+ plug-ins can be sand-boxed (not to crash host)
+ native plug-ins can be used in other hosts (cockos vst-plug-ins-bundle, even jsx fx can be used)
+ high quality native plug-ins (fast, good sounding)
+ very good price (you might even try the fully functional demo as long as you like)
+ many updates
+ updates don't break things, because they are severely tested by the community
+ many useful scripts from the community
+ unique features like automation items
+ lots of community tutorials
+ great active, supportive and collaborative community
+ developers listening to the community
+ good sound quality
+ small size
+ pretty good manual
+ precise audio/MIDI timing
+ configurable MIDI timing resolution

- must be configured and fine-tuned a lot to be useful
- no native linux plug-ins (but honestely: you don't miss much)
- downside of wine: never do an update, if you have something working
- downside of wine: not really real-time, even with wineasio
- expect to read a lot and watch lots of tutorials, but then also automate a lot of your tasks
- GUI is not pretty (but can be enhanced a bit via themes)
- some bugs related to tempo-changes
- many goodies are hidden


2. reaper/linux + airwave:

+ like above, almost there
+ linux-vst-plugins (and wine-vst via airwave)

- stability issues with vst
- no lv2 or ladspa plug-ins

3. renoise/linux + airwave:

+ fast
+ stable
+ steep learning curve (tracker)
+ ladspa, dssi and linux-vst plug-ins
+ proper alsa and jack support
+ text based file format (version control!, recovering), xml based, unreadable but theoretically writable
+ scriptable
+ efficient (can be used without mouse) 
+ good price
+ integrated fx have ok quality
+ active community
+ powerful sampling
+ good sound quality
+ ok-ish size 
+ manual mainly online. good but too sketchy
+ very precise audio/MIDI timing

- don't use dual head with wine-vst!
- no lv2
- no tape-like track recording
- no proper jack-midi support (needs amidid)
- awkward routing
- native plugins can't be used in other hosts, but native sample-instruments can via renoise
- updates rarely
- only reasonable export is audio stems
- no good SMF import
- two ways to do automation with different time-lines that interact in confusing ways (tracking and "painting")

4. bitwig/linux + airwave:

+ stable and reliable (when not using wine-vst)
+ ok routing
+ integrated fx and instruments have ok quality
+ GUI is separated from the audio engine which results in reliable audio performance
+ plug-ins can be sand-boxed (not to crash host)
+ GUI almost never crashes with the audio engine, so there is a great chance to be able to save your project
+ GUI is pretty (but lots of animations may slow your machine down)
+ good sound quality
+ lots of built-in instruments, plugins, presets and sample libraries
+ had no audio engine crash while recording
+ lower latency with alsa instead of jack
+ nice modulators
+ good manual
+ precise audio/MIDI timing
+ fine MIDI timing resolution

- no proper MIDI support (no multichannel tracks, ridiculous SMF import/export, yada yada)
- overpriced
- no proper jack-midi support (you need amidid)
- when using wine-vst make sure to close window before open next wine-vst plugin, otherwise crashes the plugin
- updates breaking plugins almost regularly
- no lv2, no ladspa
- not scriptable (just controllers)
- binary format for native plugins and project files.
- inefficient (can't be used without mouse)
- only reasonable export is audio stems.
- native plugins can't be used in other hosts
- pricey subscription model with seldom updates
- no proper issue tracking (mail the developer)
- no communication about upcoming features
- developers seem to ignore the community
- no collaboration inside the user base aka community
- huge size
- many features hidden
- incompatible changes to file format between versions
- did once corrupt a project while saving so that I could not open it any longer and not fix it (because of the binary file format)

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…