Sending an array to a SoulPatch

Is there any support (now / on the way) for reading an array into the top level input of a soul patch?

I’m writing a drawable lfo ui which needs to communicate it’s shape to my soul code somehow

Adding more options to the types that can be passed into event streams is certainly on the to-do-list. Up to now we’ve not needed to implement this for patches, because if there’s no GUI, a DAW can only send floats to parameters anyway. But certainly when people start attaching custom GUIs and other fancy logic, there’ll be a need for all kind of different structure as well.

I’ve actually spent a lot of time working towards this in the last couple of months, and the mechanism for handling this data is all in place (it’s the reason behind the switch to choc/containers/choc_Value.h at main · Tracktion/choc · GitHub for passing data to the performer class), but as we don’t yet have any closely-coupled GUI frameworks, there’s not yet anywhere that we could add a binding for this from any kind of user code that lives inside a patch.

How exactly were you planning on coding the bit that sends this data?

(Of course, I should add that if you’re using the SOUL → C++ generator, you can already add your own C++ code that deals directly with any kind of input on the processor class)

At the moment I’ve got Nick Thompson’s blueprint (ReactJS) hooked up to a very basic juce wrapper which just call’s setValue() on the parameters exposed by a SOULPatchAudioProcessor instance.

The whole project is actually a synth running quite happily as a vst3 in Ableton. Everything except ui is implemented in SOUL, the only problem is I’ve now hit a case where I need to communicate in 2d and I haven’t discovered anyway of talking to my soul patch other than in audio, midi and floats.

Currently I’m having a look at your soul_venue classes to see if they’ve got this. Is that where the performer class is?. Else if I’m totally confined to 1d then plan C is to essentially treat a soul patch like a “voice” object, implement the LFO in JUCE and modulate the parameters of each SOUL patch/voice using the output of their respective LFO instances.

What do you recommend?

Oh and no, I’m not using the c++ generator. This is hosting a live soul patch using your c++ helper classes. The hot reloading has been very helpful :slight_smile:

Right - yes, you’re trying to do something that’s not supported in the patch API as it stands right now.

Extending the patch API to make it possible to push custom data into it is… sort of a good FR, though if you think about it, it’s a bit of an edge-case.

If you’re compiling a native plugin project, and you’re loading one piece of soul code which requires custom interfacing to your native C++, then the JIT isn’t really adding much value. The JIT really comes into its own when there’s a host loading lots of different non-native plugins, and in that case, widening its interface to let you write native C++ that can send other types doesn’t make much sense.

If you are only loading one piece of specific soul code and building a native plugin, then using the c++ generator might make more sense.

However, I guess there could be a use-case for broader case where you have a plugin which can load a range of different soul algorithms which have some common but custom feature which this special host would want to pass into them.

Understandable, will give it a crack with the c++ generator and see how it goes. Sounds a bit smoother than what I was about to attempt. Thankyou in advance for the saved time :+1:

I’ve been keeping an eye on the SOUL discussions here on the forums, and I’m very excited to see SOUL meet Blueprint organically like this :smiley: This is something that’s been on my radar for a while now, and I’m very excited to see what SOUL might mean for Blueprint and vice versa.

I’m curious about @callumtw42’s goal here too, because I think there’s a ton of value in the hot-reloading feature of the SOUL Patch API especially when running side by side with the hot-reloading feature of Blueprint. Of course, I take your point @jules that this is a bit of an edge case and that the goal could be satisfied by the C++ export.

I’m wondering though if it’s not quite like the sample loading feature that SOUL already has? I see this as maybe more of a wavetable sample loading feature than a generic API extension for 2D value types. Maybe in that sense this feature request is less of an edge case.

I second that. Though the JIT compiler isn’t making much difference to the end product in this case,
from a workflow perspective, I think the ability to skip the c++ recompilation step is a big win.

With the current setup, I simply trigger 1 release build and from then on, every save
gives me instant feedback from the program.

As it happens, reusing elements of that ui to build a wavetable editor is next on the todo list.

I suppose the argument comes down largely to what the patch API actually aims to be. At the moment
I’m trying to treat it as something which exposes a soul patch as a general purpose audio processing
unit which I can feed audio / midi / data from my host to get back a result.

In reality though, it seems to be exposing the soul patch as something more like a plugin, which
ofcourse must provide a more limited interface to be compliant with DAW’s.

So I suppose the question is:

If we want the ability to tell our soul patch

“use this f(X) to give me back a Y”
(achievable entirely by allowing float arrays)

Rather than being limited to:
“use this X to give me back a Y”

should it be the developers job to…

A: hack the above functionality into a c++'ified soul patch and lose the benefits of the JIT compiler?

or

B: provide a DAW compliant host for their soul patch? (assuming their even interested in DAW’s)

I should add that I really believe both blueprint and SOUL are gamechangers for audio software
and nothing would make me more excited than to see a marriage between the two (with perhaps the
addition of hooks, sass and typescript if theres time left over) :slight_smile:

Maybe the best way to answer this is to list out some of the ways we’re aiming to let people use it…

  • Use a generic host to load a generic patch. Eventually, when we add support for patches to contain arbitrary scripted GUIs/other logic, then those scripts will be able to talk to the SOUL processor and do any kind of data exchange you need with the running code, all with hot-reloading, so this is the ideal solution, just not done yet :slight_smile:

  • Use your own host (either app or a plugin) which uses the patch loader DLL to hot-reload your own soul code, but which also adds some extra custom smarts for handling special types of patch. We never added an ability for patch parameters to take complex types… but… actually now that I’ve modified everything to use choc::value classes, this could be quite an easy addition. I do still think this is a bit of an edge-case, but maybe easy enough to add that I’ll consider it.

  • Use your own host with our not-yet-released lower-level JIT engine. We’re still tinkering with the exact API for this so haven’t released it yet, but this will open up the “performer” layer below patches, so you’ll get raw access to all the endpoints with any type, and you call the render function directly. This will let you do anything that the language supports, so will be the most versatile solution.

  • Generating C++ - yep, hot-reloading is really cool for development, but in a lot of cases people will be baking a finished product as a plugin where that’s not needed, and C++ will make a lot of sense. You talk about needing to “hack” extra functionality into the generated C++, but we’ve tried really hard to generate code which can be customised via separate classes, without the need to make messy modifications to the generated code itself. If anyone does find a situation where they can’t do something reasonable without modifying the generated code, let us know and we’ll try to find a way to avoid that!

  • Modifying/extending the SOUL source code itself before passing it to the compiler. Don’t forget about this approach! The patch API has an class which lets you add a pre-processing stage to the code before it gets compiled. (The faust folks use this to let you mix soul + faust in the same program). If I was quickly rigging up a test environment where I wanted my host to inject arrays into hot-reloaded code, a really simple way to do this is to have a special token in my source code, e.g. $PUT_ARRAY_HERE!$ and just string-replace that with the text of the appropriate soul array declaration.
    This kind of source-modification seems a bit hacky if you come from a C++ background, but is pretty powerful when you have a JIT, and I certainly imagine that in the long run, a lot of bigger soul projects will have scripts when generate some of the soul code so that they can really optimise the amount of CPU that particular patches use.

2 Likes

Oh - and I just thought of another option:

  • In a patch, you can also provide your own ExternalDataProvider which you could use to provide an arbitrary value for an external variable, which could be an array…
1 Like

Thanks Jules,

Will have a play around with options 5 and 6. If either of these allow my soul patch to continuously read in an externally writable fixed size array of floats without stopping the audio, then it’s problem solved.

Thanks @jules. Very helpful, and very exciting!