Lv2 convertor help needed

I’m writing a python tool to convert native plugin patches to lv2 format for the Zynthian Project. I’ve cobbled together a simple tool that fudges synthv1 xml files to lv2 using a text template. synthv12vl2.

However I’ve realised if I want to make it more flexible and convert more preset types I should really use the python rdflib to create a graph and serialize out the lv2 stuff, especially as I want the tool to be able to take in multiple presets and generate banks of presets in the form

./synth_bankname.lv2/manifest.ttl
/synth_bankname.lv2/preset1.ttl
/synth_bankname.lv2/preset2.ttl

I understand enough that the rdf turtle format uses the form

subject, predicate, object

and the concept of all the resources being urls and the prefix stuff at the top of the ttl files. But I’m not competent enough to work out how I should be creating the sort resources to generate an lv2 preset. Would someone mind showing me how I would use rdflib to create a template lv2 preset that I can use?

References I’ve looked at are

http://ll-plugins.nongnu.org/lv2pftci/ which as some examples in C++ which I’ve never coded in.
https://harryhaaren.blogspot.com/2012/0 … rview.html
and the rdflib docs linked above.

Anything else I should look at?

Hey @Baggypants!

What about using a template engine for generating the TTL files, like this:

https://www.tornadoweb.org/en/stable/template.html

I’m working on it …
I’ve a modified version of your converter templates:

https://gitlab.com/Jofemodo/synthv1-preset-converter/

manifest.template:

@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<{{plugin_name}}_bank_{{bank_name}}> a pset:bank ;
	lv2:appliesTo <{{plugin_url}}> ;
	rdfs:label "{{bank_name}}" .

{% for preset in presets %}
<{{preset['name']}}.ttl>
	a pset:Preset ;
	pset:bank <{{plugin_name}}_bank_{{bank_name}}> ;
	rdfs:seeAlso <{{preset['name']}}.ttl> ;
	lv2:appliesTo <{{plugin_url}}> .
{% end %}

preset.template:

@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<{{ preset['name'] }}.ttl>
	a pset:Preset ;
	lv2:appliesTo <{{ plugin_url }}> ;
	rdfs:label "{{ preset['name'] }}" ;
	{% set i = 1 %}
	lv2:port{% for k in preset['ports'] %} [
		lv2:symbol "{{ k }}" ;
		pset:value {{ preset['ports'][k] }}
    ] {{ (i<len(ports)?";":".") }}{% i += 1 %}{% end %}

I didn’t modified the converter itself., but the generating code should create a data structure like this:

{
  'plugin_name': "synthv1",
  'plugin_url': "http://synthv1.sourceforge.net/lv2",
  'bank_name': "supercool leads",
  'presets': [
    {
      'name': "lead 1",
      'ports': {
        'param1': value1,
        'param2': value2, 
        'param3': value3, 
        ... 
        'paramN': valueN
      }
    }, {
      'name': "lead 2",
      'ports': {
        'param1': value1,
        'param2': value2, 
        'param3': value3, 
        ... 
        'paramN': valueN
      }
    }, {
      'name': "lead 3",
      'ports': {
        'param1': value1,
        'param2': value2, 
        'param3': value3, 
        ... 
        'paramN': valueN
      }
    }, {
    ...
    }, {
      'name': "lead M",
      'ports': {
        'param1': value1,
        'param2': value2, 
        'param3': value3, 
        ... 
        'paramN': valueN
      }
    }
  ]
}

And call the template parser using this data. Something like:

form tornado import template

#Generate data structure ...
data = ...

loader = template.Loader("./")
manifest_ttl = loader.load("manifest.template").generate(*data))
preset_template = loader.load("preset.template")
presets_ttl = {}
for p in data['presets']:
    presets_ttl[p] = preset_template.generate(plugin_url = data['plugin_url'], *p)  

Regards,

1 Like

My simple converter started with a very basic template script. However I felt that getting rdflib to generate a correct RDF turtle file would also meant the output would also be automatically valid. Minimising the risk of an invalid lv2 or hitting weirdo edge cases.

Possibly it would make it more flexable as well. So if people wanted it to generate multiple banks inside one lv2 dir they could, or if they wanted to seperate out a particular set of presets they could do that as well.

More likely in chaining myself to this conceptual perfection I’ve actually choked my project.

Hi @Baggypants!

I’ve finished the rewrite of your preset converter. Now it uses the tornado’s template library/format and you only have to parse native presets and generate a dictionary with the key/values.

It takes a set of source files (a directory) and generates a LV2 bundle, saving every preset in a separate file. Also, it defines a LV2 bank and associate presets to it.

Finally, i’ve improved the architecture for supporting several “source formats” and now it’s easily extensible. Currently i’ve implemented synthv1, padthv1. dx7sysex is on the way :wink:

Enjoy!

5 Likes

OK! I finished the DX7 parser too, so the lv2 preset converter now can manage:

  • synthv1
  • padthv1
  • DX7 sysex

Implementing a new parser is really easy, as you don’t need to know nothing about LV2. Only how to read the parameter/value list from the native format. Somebody wanting to add a new one? OBXd, Raffo, NoizeMaker …

Next step: Integrate the converter in the new webconf preset manager

Thanks a lot to @Baggypants & @C0d3man , who started the thing and opened the way!! :heart:

Enjoy!

5 Likes

I continue improving the LV2 converter. Now it’s capable of parsing the source directory structure and generate multiple banks. I’ve tested with a package containing 170 DX7 SYX files and a bundle with 170 banks and several thousands of presets was generated in seconds. The problem is that Jalv takes so long to load, that i get a timeout. Too many presets! ;-(

OK OK … i will stop to improve the converter and will integrate with webconf, but first, i have to review some contributed code :wink:

Regards,

4 Likes

Hi @zynthianers!

I learnt how to create a standard PyPi package … so the converter is on PyPi now:

So, you can install it simply with:

pip3 install preset2lv2

Yessss! :star_struck::money_mouth_face::star_struck:
My first pip package!! OMG!!!

Now it can be properly integrated in zynthian :medal_sports::medal_sports:

Enjoy!

8 Likes

That totally rocks! Thank you for putting such effort in!

3 Likes

Quoting our good friend:

Awesome job, btw!

1 Like

is it merged with master already?
where do I see it? Presets & Soundfonts?
I would like to look into an obxd implementation.

It’s not integrated in webconf yet, although i left the rails ready to receive the train … it should be quite easy :wink:

You should!!
Take a look to the converter’s code, specially to the 2 “parsers”. Both are extremely compact …

Regards