Map juce::Sliders to a midi controller knob?

I am looking for a convenient way to let the user midi map sliders and buttons of my plugin to an external midi controller. It should be similar to the midi mapping functionality in Abelton.

I just found this juce tutoial… but it seems cumbersome, to manually connect everything. So is there a better way to go?

this is how most Juce plugins would handle this.

Basically, you don’t directly handle midi mapping in your plugin. You create a parameter visible to the host that the host can automate, and then the user can use the host’s automation features to control your plugin’s parameters.

So, for example, if you create a juce::Slider and connect it to a juce::RangedAudioParameter, then in Ableton you will be able to use the normal system of Midi map → click on a parameter → move a midi controller. Then when you move your midi controller, the host will be controlling your plugin’s RanngedAudioParameter, which will make your juce::Slider move.

Our older software captures MIDI CC messages, and allows mapping those messages to specific parameters. But these days, the approach mentioned above (using the DAW’s automation features to handle MIDI control of parameters) is probably the way to go.

1 Like

Okay, thank you.
However in my plugin, I have variuos combinations of properties which are dependend on the plugin state. Therefore I am adding and deleting properties/juce::values to a simple ValueTree. I need a way to also midi controlle them.

Alternatively, I could prepare all possible combinations as parameters in a AudioProcessorValueTreeState as you propose, but it would be 20.000 combinations… Or is there actually a way, to add and delete parameters in the AudioProcessorValueTreeState? That would be extremly awesome!

For what it’s worth, if you’re going to make a standalone version of your plugin, you might want to offer midi mapping within it. In this case you’ll have to set up a way of handling this yourself. The same is true if you want to use MIDI to modify things which aren’t easily represented by parameters.

I’d suggest making some sort of abstraction which connects a Value to a MIDI CC. We did this with an RAII mechanism which looks quite a bit like the JUCE attachment classes, but pairs with an object owned by the processor which takes the midi input, looks for CC messages, and modifies the value.

That’s a great point. Is custom MIDI CC mapping also a good idea for mobile versions, or is it less applicable there?

1 Like

Here’s a quick little Midi CC mapper class I drafted since I was thinking about it. Would something like this work?

1 Like

Looks good at first glance. One of the things we did however was to make connections to a Value instead of a Parameter, primarily because there were a few things in our upcoming product which needed to work with a controller, but weren’t really meaningful in automation (or for which saved/loaded automation data didn’t make sense). This worked for us because a good chunk of our backend uses Value, although obviously this doesn’t make sense for something that can’t be controlled by one.

Nonetheless, that should work for making a standalone of a plugin with parameters MIDI mappable, as long as there’s some user facing way to change which controller maps to which parameter ( :cry:)

1 Like

awesome, thanks for your input!

ugh, yeah… that’s the less fun part about adding this feature haha.

But it does seem worth it, so I’ll figure something out…

1 Like

My PluginGuiMagic has that functionality built in. A little box that shows the last nudged CC and that you can drag onto any knob to control its parameter.
Thanks for the idea to control non parameter values as well, I will add that to my list…

3 Likes

A Value that’s part of a ValueTree, or one that’s a member by itself?

In any case, Value is not thread-safe, so there are potential pitfalls there, depending on how it’s used. (And I think in standalone apps, MIDI inputs are on their own threads?)

2 Likes

Good shout. Not only where you use the value but where it is set from is important to consider…

potentially. In the plugin standalone target it is the audio thread, because it is handled in processBlock(). Impossible to say for all other standalone apps

1 Like

Right you are. I was thinking of non-plugin standalones, where you would use the MidiInputCallback class for MIDI. In that case, MIDI comes in on “a high-priority system thread”… I just couldn’t remember if each MIDI device is given its own thread, or if input for all devices comes in on a single thread.

Yes, good point. We realized this as well and call the actual set methods on the message thread regardless of where the callback comes from.

OK, and how do you access those values from the audio thread? Is there an atomic acting as an intermediate value holder?

They never are accessed on the audio thread. Our processor does calculations which rely on those values on the message thread whose results are stored in an atomic pointer which points to an array which is used by the audio thread.

Ah, I see. Sounds clever! Are you using Value::Listeners to trigger those calculations when new CC data arrives?

Not especially, just what the product calls for.

Yes, but called from an asyncUpdater.