Controlling Plugins

In this example we show how to control LV2 plugin control values in real time, in two ways:

Audio Setup

For this example we'll create two LV2 plugins: a software synthesizer and a reverb; we'll feed the output of the synth into the reverb and connect the reverb to the main system outputs.



local synth = Lv2.Plugin("http://calf.sourceforge.net/plugins/Monosynth", "Velo Bee")

local reverb = Lv2.Plugin("http://calf.sourceforge.net/plugins/Reverb")
reverb.control("amount", 0.0)

synth => reverb => Audio.StereoOutput("main", true)


Note that we create the synthesizer plugin with second argument after the plugin URL, this is the name of an LV2 preset that will be loaded when the plugin is created. We can also change to a different preset using the plugin preset method.

Also note that we set the initial value of the reverb amount to zero using the plugin control method which allows directly changing the plugin's control port values.

Controller Setup

First we'll show how to control the plugin by sending MIDI control messages. Any controller that sends MIDI control change (CC) messages will work. We get a reference to the controller by creating a MIDI input and connecting it to our controller.


local controller = Midi.Input("synthControl")



Now we want to use one or more of the controller dials to control the synthisizer, we will use MIDI input's onControl method to register a callback function that will be called every time a CC message is received:



controller.onControl(function(cc, m) {

When we detect the dial that sends MIDI control change number 16, we call the synth control method using the value from the CC message, scaled from 0-127 to 0-100:


	if(cc.controller == 16) {
		synth.control("o12_mix", cc.value / 1.27)
	}

We can also use more complex math to scale the incoming CC value. In this example we'll add a controller for the synth filter cutoff value and restrict the values to a minimum of 5 (Hz) and a maximum of 170 (Hz), using the controller that sends MIDI CC number 17 on the same control surface:


	else if(cc.controller == 17) {
		local value = 5 + cc.value * 1.3
		synth.control("cutoff", value)
	}
})


Sample Melody

Now we'll define a sample melody to play. We specify the notes in ABC notation and use the Midi.abc() method to read into a Midi.Sequence:



local melody = Midi.abc(@"K:C
z2 d2 f2 e2| ^d2 ^d2 e2 B2| ^c2 A2 =d4-|  d4 =c4-|
c4 ^A4| =A6- A3/2x/2| G4- G3/2x/2 AG| F2 D2 x2 d2|
e2 d2 e2 ^f2| g2 d2 g4-| g4 =f4-| f4 ^d4|
=d6- d3/2x/2| x2 c2 ^d2 =d2| ^c2 ^c2 d2 A2| B2 G2 c4|")


To schedule the melody to start at the first measure:



synth.schedule(melody, 1)


Scheduling Control Changes

Another way to change plugin control values is to schedule the value change at a point in the transport using an Lv2.ControlChange message.

In this case we'll schedule two changes to the reverb amount: it was originally set to zero, we'll bump it up to 0.3 at the beginning of measure 5 and then up again to 0.7 at measure 9.



reverb.schedule(Lv2.ControlChange("amount", 0.3), 5)
reverb.schedule(Lv2.ControlChange("amount", 0.7), 9)


Scheduled changes like this may work for many situations but will be too abrupt for others. When that is the case we can achieve a smoother response by sending smaller control change events within a short interval.

In this case we will ramp the reverb amount from 0.7 back down to 0 over the course of measure 13 by sending a total of 32 control changes on each 32nd note:




for(local i = 0; i < 32; i++) {
    local value = 0.7 * (1 - i / 32.0)
	local change = Lv2.ControlChange("amount", value)
    reverb.schedule(change, 13 + i / 32.0)
}


Finally, set the tempo and start the default clock:



Time.def.tempo = 180
Time.def.start()



Complete Script

local synth = Lv2.Plugin("http://calf.sourceforge.net/plugins/Monosynth", "Velo Bee")

local reverb = Lv2.Plugin("http://calf.sourceforge.net/plugins/Reverb")
reverb.control("amount", 0.0)

synth => reverb => Audio.StereoOutput("main", true)

local controller = Midi.Input("synthControl")

controller.onControl(function(cc, m) {

	if(cc.controller == 16) {
		synth.control("o12_mix", cc.value / 1.27)
	}

	else if(cc.controller == 17) {
		local value = 5 + cc.value * 1.3
		synth.control("cutoff", value)
	}
})

local melody = Midi.abc(@"K:C
z2 d2 f2 e2| ^d2 ^d2 e2 B2| ^c2 A2 =d4-|  d4 =c4-|
c4 ^A4| =A6- A3/2x/2| G4- G3/2x/2 AG| F2 D2 x2 d2|
e2 d2 e2 ^f2| g2 d2 g4-| g4 =f4-| f4 ^d4|
=d6- d3/2x/2| x2 c2 ^d2 =d2| ^c2 ^c2 d2 A2| B2 G2 c4|")

synth.schedule(melody, 1)

reverb.schedule(Lv2.ControlChange("amount", 0.3), 5)
reverb.schedule(Lv2.ControlChange("amount", 0.7), 9)

for(local i = 0; i < 32; i++) {
    local value = 0.7 * (1 - i / 32.0)
	local change = Lv2.ControlChange("amount", value)
    reverb.schedule(change, 13 + i / 32.0)
}

Time.def.tempo = 180
Time.def.start()


List of All ExamplesDemo Applications


Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.