setBfree

Preface

Overview

LV2

OpenGL

PUGL

LV2

This explanation of how our setBfree LV2 GUI plugin, which we will refer to as the UI, is designed to augment this LV2 Tutorial created by its developrs. Because of the importance to us that LV2 flourishes it will be greatly expanded and broken into many sections. For now it is just an elaboration on the Overview's outline.

The meat and potato function in ui.c that handles going from the UI to the setBfree plugin is notifyPlugin which calculates the current ui midi value of an object and passes it to b3_forge_message which forges(adds) the data to a buffer. For both the functions forge_message_kv and forge_message_str the value and uri are passed in instead of the current uri values being read, although the forge itself is still read from the B3ui instance, &ui->forge.

The URI/URID relationship is briefly touched on in the Overview, but for our two plugins, what's most important is that they both read from the same header file uris.h, Here's what's going on there.

An LV2_URID is an integer id for setBfree functionality from the structure setBfreeURIs. In both lv2.c and ui.c the map_setbfree_uris function is called by the respective instatiate function. From this point on, both plugins have the same uris values mapped. When one does something to a buffer, or other action, passing the uris value informs the other what's going on.

Also mentioned in the Overview, the port_event function in ui.c receives notification from the base setBfree plugin of changes. It's got a few juicy LV2 features as it tests what to do with the data in the buffer pointer that's passed in.

We'll start with getting an atom out of the buffer, LV2_Atom* atom = (LV2_Atom*)buffer, which gets us in the unrestrictive place to store data in LV2. From this atom we create an atom object, LV2_Atom_Object* obj = (LV2_Atom_Object*)atom, which gives us traits wrapped with the data we can query to figure out what we've got. For example, we can now get the uris type that goes with the atom and do the apropriate action, if (obj->body.otype == ui->uris.sb3_activekeys)...

As the type of data is so flexible, very different actions are possible depending on the uris. For things like changes in configuration, functions are called that have values that are pulled out of the atom before being passed, for example this pointer to a key, char *kk = (char*) LV2_ATOM_BODY(key); For this snippet we call the function, cfg_parse_config(ui, kk, vv);, but the Grand Kahuna of these calls is processCCevent(ui, k, v); for midi control data.

The other idiom in these atom/uris checks is to update the values in ui and then get back into PUGL space. An example with some meat on the bones here are the current keyboard keys, memcpy(ui->active_keys, data, 5 * sizeof(unsigned int)); Then the call to puglPostRedisplay(ui->view); gets PUGL back in play to update the graphics. Remember from the Overview that this will call the function onRedisplay

That last example is a goodun as the data includes LV2_Atom_Vector* voi = (LV2_Atom_Vector*)LV2_ATOM_BODY(a0). The LV2_Atom_Vector is pulled from the atom object, and this gives us a vector with multiple atom bodies. That's how when we pass in data we can get the values for up to five keys in one shot.

That's enough to get us started with the functionality of what fits in with what. Now we'll go over the flow of an app dealing with the plugin the LV2 way.

We start where LV2 plugins start to find everything else in the source code with the manifest.ttl. We'll go ahead and write the whole thing here as this gets us going for everything we're gonna need:

@prefix lv2: .
@prefix rdfs: .
@prefix ui: .

a lv2:Plugin ;
lv2:binary ;
rdfs:seeAlso .

First @prefix says that we've got some shorthand going on. From here on in lv2 will refer to the base code of LV2(lv2core. ui not only now used for the ui extension, but it tips us off that the setBfree plugin will be using this feature.

We have the address for the plugin with . The a tells us it's an LV2 plugin. We've got the binary name, and also very important, we have the the file name for where the action is, b_synth.ttl. We also have files manifest.ttl.in, for the LV2 setBfree plugin, and manifest.ui.ttl.in, for the setBfree LV2 ui extension plugin, but what's important for this description is that the latter file gets us to the big boy file for the ui, b_synth.ui.ttl.in.

We'll just cover one contribution of the main ttl file for the setBfree LV2 plugin that'll get us to its main sourcecode file, lv2.c. In b_synth.ttl.in, we have declarations that define the names and types of ports available for the plugin. Here are the parts we're gonna discuss. This is pararphased to make it easier to follow with ... for code not included:

lv2:port
[
a atom:AtomPort ,
lv2:InputPort ;
atom:bufferType atom:Sequence ;
...
lv2:index 0 ;
...
a atom:AtomPort ,
lv2:OutputPort ;
atom:bufferType atom:Sequence ;
...
lv2:index 1 ;
...
a lv2:AudioPort ,
lv2:OutputPort ;
lv2:index 2 ;
...
a lv2:AudioPort ,
lv2:OutputPort ;
lv2:index 3 ;

For our needs here, what's important is that we have four ports, two input and two output. The two atom ports as type atom:Sequence, tells us they're for MIDI data, and the other two are for the audio data declared as a lv2:AudioPort. The lv2:index from 0 to 3 is what our program'll use to identify which port is pickin and a grinnin. Now we're in the lv2.c file, the belly of the beast of the base LV2 setBfree plugin.

As we didn't get to it in the Overview section, we'll do a little lv2.c here. We'll go over the whole shooting match when we expand this doc, but for now we'll confine ourselves to enough to be able to weave the ports identified above into the rest of our plugin.

In the Overview we mentioned that the base plugin uses b_instance, set to inst as an interface to the organ components in setBfree's source code. We'll expand on that a little to the function initSynth, that starts all the components in the *inst passed in and sets default values.

Then we get to our first port fun when our *inst is passed into the function synthSound allong with our two audio ports which have been assigned to outL and outR in the same instance. How these audio ports are assigned is the connection between our index from the .ttl file to this final passing of the organ fragment to the audio output. The grandmama of this processis the function connect_port.

In connect_port a port integer is checked against an enumeration for our index numbers from the .ttl file where 2 and 3 result in our outL and outR audio ports respectively, and 0,1 are assigned to the midi ports midiin and midiout. We're almost there; we just need to describe our midi control functions between the UI and our base LV2 setBfree plugin.

Here we're back to our technique of setting variables in our parent plugin's central structure B3S, that will be used later. This time we pass in an int that matches our 0 to 3 port indexes from the .ttl file. They're tested agains an enumeration for the type of port, and then the data is assigned to either midiin,midiout,outL, or outR. Alas wearrive at the end of our LV2 journey for this inning with the function that gets this all going, run.

After all is up and kicking, run in lv2.c is kind of like onDisplay in our UI plugin as it is what is usually called after changes have been made in our central structure to update results from the variable changes. Here we're just going to briefly finish the path of our port interaction in run to show it's workflow.

For our port data, once in run the variable is tested for existence. Note that this wasn't a flag set to on/off; it's a pointer to the actual data. So once existence is discovered, the appropriate action can be chosen whether it's midi in, midi out, or out left/out right audio data. The usual suspects from our intro for LV2 including forge, atoms, uris mappings n such are used in this process, but we'll just show one trick to wrap this up.

As different actions are occuring depending on the type of port and other meta data queried, an update_gui_now variable can be set and later in run if it tests to true, the appropriate action for the UI is executed. So within run, midi control data from the UI can be handled in the main plugin and then all the relevant data is then sent back to the UI where port_event will execute the appropriate action and for most changes gets us back to our old friend onDisplay.

Voila! The circle is now unbroken. We have a framework LV2 document build on as we figure out our verbosity to completeness ratio until this explanation is complete.