`juce7` technical preview branch

I definitely wonder what additional decoupling could take place.

In my own code I’m trying to follow a scheme where both the editor and the processor know about a shared “State” class, but know nothing about each other and compile separately, so you can compile/test a test app that would only have the top Component of the editor along with the state, or just the processor with the state.

While not 100% what you’re after, what I’ve done in my own code is divide everything into (at least) 3 units. “State”, “UI”, “Processor”.

Each is a JUCE-style module, where both the UI and Processor depend on the “State” classes (which isn’t APVTS), but never on each other - the UI classes can even compile without ever including juce_audio_basics, and you can test them in a non-plugin app that spawns the State.

Technically, because juce::AudioProcessorEditor depends on AudioProcessor, the createEditor() function has to be aware of the processor and editor concrete classes.

So what I did to solve it, was to have a single class in the plugin cpp file, that inherits from the ‘real’ processor (that knows nothing about the editor), and only then implements createEditor() with the real editor, only forwarding the state class into a juce::Component that knows nothing on the processor.

That allowed me quite a lot of flexibility in the design, including for example chain sub-processors together as internals of another plugin without ever needing to build their editor, unit tests for processors and editors, way faster compile time, etc.


I don’t think AudioProcessor and AudioProcessorEditor are too coupled by themselves, and I don’t have particularly clean implementations in that respect. The wrappers are more coupled, but from the user side the most problematic thing happens in AudioProcessorParameter (and hence APVTS), as we discussed elsewhere, because UI thread and audio thread changes are merged.

So… can you possibly tell us a little about 7.0 and MIDI 2.0 support?


Can’t wait to see how you guys decided to do the LV2 plugin + host implementation!

And of course how you will handle some of the scrutiny from the community during integration testing :wink:

Lets go! :rocket:

LV2 support is now live on the preview branch, for plugins:

…and for plugin hosts:

I’ve also pushed some other bits of work to the branch:

It’s now possible for plugins to query the system time provided in the audio callback in supported formats:

There’s a new demo project, showing how plugins can be loaded inside other plugins:

The event loop in plugins on Linux has been overhauled, which should improve stability. Previously, plugins on Linux had their own “message thread” which was separate from the host’s message thread. Now, plugin UIs will use the host-provided idle callback (or equivalent), meaning that they will update on the host’s main thread, which should reduce the possibility of deadlocks and data races. This work spanned multiple commits, so I won’t link them here.

We’ve also updated the view-sizing logic for VST2 and VST3 plugins, especially on Windows. This is intended to ensure that plugin editors display at the correct size, regardless of global scale factor, desktop scale factor, and per-monitor scaling - and, importantly, editors should display at the correct size, even when the host is itself a JUCE plugin. Again, this work is spread across a few commits.


We have also added support for ASWG metadata tags in WAV files:


Hi JUCE Developers,

Thanks for the work on JUCE 7. You’re probably still busy with it :upside_down_face:
Just want to ask a small thing about future graphic improvements (again).

Seeing that you now implemented a Metal-layer backend, I wonder what direction JUCE will take in general for graphics. Let me summarize quickly:

  1. OpenGL is deprecated on Apple devices. Newer SDKs will eventually just remove the GL functions. So there is no incentive to improve juce_opengl, right?

  2. The easiest alternative on macOS / iOS is Core Graphics + Metal-layer. The performance will probably improve over time, especially on newer ARM Macs. Therefore the focus on that in JUCE 7.

  3. Since it’s too platform specific, there will be no juce_metal module (who wants to use it anyway :joy:).

  4. Vulkan + MoltenVK is not a good choice for Mac. Obvious that Apple has a big interest in pushing Metal. Additionally driver support on Windows is still not good enough, so no juce_vulkan either.

  5. Skia is big. Probably too big to drag it into JUCE. And you mentioned that performance improvements are questionable. Same for DAWN.

  6. A rewrite / extension (let’s say in JUCE 8) of juce::Graphics to improve that is too complicated.

Now my question. What about Windows and other platforms?

The OpenGL Context (wgl) on Windows is a relic of old times. It’s there, it works, and will probably work forever. But it’s not really developed, since Microsoft wants developers to use DirectX 12 and their proprietary APIs. The only reason it’s still used, is because it basically runs on the work NVidia and AMD puts into their drivers.

Looking at Game / Console developers for example. If they maintain a big game engine, they rarely choose OpenGL. The API driver overhead is too heavy, we all know that. But they also (at this time) rarely support Vulkan and most often prefer DirectX 12 or still even DirectX 11. See Unreal Engine 5.

So what’s the future proof backend for JUCE on Windows? GDI+, Direct 2d, DirectX12 or an improved OpenGL renderer. Or using Vulkan?

The topic somehow gives me no rest. I wish it was a bit more transparent.

By the way: One thing we could do on Windows already, is to implement nv_path_rendering for OpenGL. Probably the easiest measurement. But only for Nvidia drivers.

Any hints or guidance?


Thank you! Will test soon


In juce7 I’m hitting this assert

    /*  If you're building this plugin as an AudioUnit, and you intend to use the plugin in
        Logic Pro or GarageBand, it's a good idea to set version hints on all of your parameters
        so that you can add parameters safely in future versions of the plugin.
        See the documentation for AudioProcessorParameter(int) for more information.
   #if JucePlugin_Build_AU
    jassert (wrapperType == wrapperType_Undefined || param->getVersionHint() != 0);

The documentation is perfectly clear and I would like to set the hint to ‘1’ for my version. BUT I can’t see how I can set this if I subclass a RangedAudioParameter. The RangedParameter doesn’t have a hint, getVersionHint is not virtual, the member version is private. So I’m not quite sure how I go about setting this hint in my case of having custom parmeter types subclassing RangedParam and so on.

(Also note that this fires if I’m building an AU but even if I’m running a VST3 or LV2).

Any thoughts?


I tried Surge with LV2 on mac. I got a plugin which scanned in reaper but didn’t show ports (which @reuk tells me is expected) and didn’t show a UI (which he said wasn’t).

Just dropping instructions here for folks if they want to try it. Basically you need to build against juce7 and then cmake is all set up to detect that it’s juce7 and voila eject an LV2 target. A sample session would be

git clone https://github.com/surge-synthesizer/surge
cd surge
git submodule update --init --recursive
cd libs/JUCE
git remote add upstream https://github.com/juce-framework/JUCE
git fetch upstream
git checkout upstream/juce7
cd ../..
cmake --build ignore/blv2 --target surge-xt_LV2

For that to work I need to merge Support JUCE7 LV2 Builds (if you pull JUCE7) by baconpaul · Pull Request #6083 · surge-synthesizer/surge · GitHub which I will do once ci passes in about 20 minutes.

I haven’t had a chance to try in the plugin host or carla yet but will.

1 Like

RangedAudioParameter has the same constructor signatures as AudioProcessorParameterWithID. AudioProcessorParameterWithID takes a ParameterID as its first constructor argument. ParameterID can optionally be constructed with a custom versionHint.

1 Like

Ok this is merged so these instructions should work module typos and stuff. Will try a bit more later this am!

Ahh OK I’ve figured out why it doesn’t work. this is something we also fixed in the old community LV2 fork.

When I run the audio plugin host I get

JUCE v6.1.6
error: /Users/paul/Library/Audio/Plug-Ins/LV2/Surge XT.lv2/manifest.ttl:10:22: invalid IRI character (escape %20)
lilv_world_load_file(): error: Error loading file `file:///Users/paul/Library/Audio/Plug-Ins/LV2/Surge%20XT.lv2/manifest.ttl'
lilv_world_load_bundle(): error: Error reading file:///Users/paul/Library/Audio/Plug-Ins/LV2/Surge%20XT.lv2/manifest.ttl
error: /Users/paul/Library/Audio/Plug-Ins/LV2/Surge XT.lv2/manifest.ttl:10:22: invalid IRI character (escape %20)
lilv_world_load_file(): error: Error loading file `file:///Users/paul/Library/Audio/Plug-Ins/LV2/Surge%20XT.lv2/manifest.ttl'
lilv_world_load_bundle(): error: Error reading file:///Users/paul/Library/Audio/Plug-Ins/LV2/Surge%20XT.lv2/manifest.ttl

That’s because my plugin name (“Surge XT”) contains a space which becomes the name of the dll but the ttl parser doesn’t work. Here’s the manifest section

	a lv2:Plugin ;
	lv2:binary <libSurge XT.so> ;
	rdfs:seeAlso <dsp.ttl> 

So in the community LV2 fork Kott and I had added


as an argument to juce_add_plugin and then used that as the dll name rather than the one with the space. You could also protect the name in your cmake/projucer also.

When I hand modify the LV2 to rename the library to libSurgeXT.so and modify the manifest files to remove the space, it shows up in the AudioPluginHost no problem. (And spews assertions from that param id thing)

Thanks for reporting. I think there are two problems here:

  • The first problem is that JUCE is writing a malformed manifest.ttl when the LV2’s library name contains a space. We can fix this by percent-encoding the library filename when we write the ttl. When I make this change, the LV2 loads in Carla and the AudioPluginHost, but not in REAPER 6.56…
  • The second problem is that REAPER doesn’t seem to understand percent-encoded library names. When the manifest contains a dylib name that itself contains raw spaces, REAPER fails to parse the manifest. When the manifest contains a percent-encoded dylib name, REAPER successfully parses the manifest, but then fails to load the plugin. My guess is that REAPER is not un-escaping the dylib name before trying to load it. I’ve reported the issue here.

Until the issue is resolved in REAPER, you may be able to work around the issue by using the PRODUCT_NAME and PLUGIN_NAME arguments to juce_add_plugin - the PRODUCT_NAME should not contain spaces for REAPER compatibility, but the user-facing PLUGIN_NAME can still contain spaces. Admittedly, this isn’t a perfect solution as it will apply to other plugin formats.

I can look at adding a proper workaround if we don’t hear anything back from the REAPER developers, or if there are a substantial number of users unwilling to update once the issue is fixed in REAPER. However, I’d rather avoid adding more complexity to JUCE’s build system if possible!


Yeah when we fixed it with our lv2 library name I remember some other tool (like lv2lint or jalv or some such) was giving us problems with the space too so we took the heavy hammer route of adding a dll name argument. I presume the same would happen if you have emoji or other mis encoded utf8 as your plugin name too?

Possibly, yeah. I’ll give the “Audio Plugin Example” a go in jalv and lv2lint too.

Yeah we didn’t try it escaped so that may also fix those tools. Thanks as always for a prompt look!


Looks like that host part doesn’t report PluginPath properly to the plugin.
If I open GUI for GitHub - sjaehn/BOops: Sound glitch effect sequencer LV2 plugin (or any other plugin with resources bundled as separate files) it tries to open resource file with file:// prefix:

openat(AT_FDCWD, "file:///usr/lib64/lv2/BOops.lv2/inc/page12.png", O_RDONLY) = -1 ENOENT (No such file or directory)

With escaping, the plugin/UI load fine in lv2lint and jalv. There are a couple of other lv2lint complaints:

  • The ‘turtle_recall’ extension (used to write out the manifest files) is not a ‘real’ extension. Perhaps we should just avoid declaring this extension in the manifest, given that only the JUCE build system really cares about it.
  • Non-resizable plugins have ui:resize extensionData which is not declared in the manifest. We should probably always declare extensionData for both ui:resize and ui:noUserResize, but then only mark one of these as an optional feature.

Thanks for reporting, I was incorrectly passing the UI bundle URI, rather than the filepath, to the UI’s instantiate function. Adding a call to lilv_file_uri_parse seems to resolve the issue, and the background image of B.Oops.lv2 displays correctly.

We’ll push these changes shortly.

1 Like