Mapping Instrument & Effect Property Values to Labels

I should be doing a million other things but I obviously have a problem, and have found myself mapping the values of properties like LFO1 Destination to the labels in the official GUI so when I finish up for the night I might be able to do something worthwhile with my favourite synth, TAL NoiseMaker.

I’m doing this because the properties of this particular synth (and some others on my machine) display as 0.0 - 1.0 for instance, when to be useful it should be: off / filter / osc1 / osc2 / osc1 & 2 / pulse width / freq mod / lfo2 rate - in the case of TAL NoiseMaker’s LFO1 Destination.

I had a quick look around, and found this:

I also found /usr/lib/lv2/calf.lv2/Monosynth.ttl which suggests I might simply be able to update TAL-NoiseMaker.ttl with something like this:

lv2:scalePoint [ rdfs:label “Sawtooth”; rdf:value 0 ] ;

in the relevant places, and we’re good to go? Is that all there is to it?

I will get this instrument mapped over the next couple of days tho, then I’ll know what wave 0.37 is hahaha. Would be good if I could get it working in the GUI, and even better if everyone could benefit from my tedium.

Also, is this the best place to ask about things like this? (I did select the development forum, but I’m still new around here)

OK, I really should be doing other things right now tho.

2 Likes

This is as good a place as any to ask such questions.

The mapping of lv2 parameters is inconsistent and incomplete. I have looked at it a couple of times but run out of time and it was never as trival as I had hoped. I hope you are able to make progress with this. Keep us informed of progress. It would be good to have a way to map things in a more consistent and complete way!

I’ll have a play after dinner.

My biggest gripes are inconsistent placement of common controls like volumes, the visual presence of unused properties, and the unintuitive placement of related controls across pages.

I haven’t looked at the code, and haven’t coded in Python for at least a decade, but there is probably a way to filter out properties named ‘unused’ or similar, and re-order the controls so that Volume is always first, and similarly named controls (lfo1, osc2, etc) are somehow sensibly distributed.

I have a full plate at the moment, but do intend to dive into the code at some stage. I did buy this device to make music not spend more time in front of the computer, so very much appreciate your sacrifice in terms of your efforts in that department.

I also knew full well what I was getting myself into tho haha. If I couldn’t tinker I would lose interest. I’m sure it won’t be long before I’m once again hating having to be so pedantic about my indentation.

It’s about degrees of abstraction and can lead to XML levels of complexity pretty quickly. You have to operate on a really carefully defined layer that correctly defines precisely what you are dealing with and then leave the other layers to respond to that control shim…

So it’s not just a volume parameter in the GUI but it’s an expression/volume combined object that responds to devices that identify themselves as volume pedals as well as encoder devices that declare themselves as volume controls for this specific engine . . . :slight_smile:

The fluid synth layer often produces this sort of discussion, because its a sample player and as such can’t modify Attack, Decay, Sustain excetera, as people occasionally request.
In such a widely equipped engine world you really can’t make any assumptions and that before we discuss control ranges and transfer functions between different engines. How could you ensure that engine A’s sine wave generator which sets it’s level from 0-255 and Engine B which uses 0->1.000000 both have the same amplitude control law? and how do we deal with headroom…?

Big issues. certainly. In the modular synth world the solution is possibly a little more considered. You don’t make the output ‘volume’ control a characteristic of the engine but you output at a nominal level and then control the signal on a subsequent module. This is actually a better approach from the possible features point of view but scatters an awful lot of level amplifiers around complicated snapshots, and that’s before you deal with the fact that an engine can be controlled via MIDI in this way :smiley:

I am not happy to be coding all the time and making no music but I think you recognise the mentality that results in this situation. It is never finished and each time I stop to play I find something else that blocks creativity and just have to fix it.

Python is an intetesting language. I’m okay with indentation now but those bloody colons still catch me out!

I wonder if the best place to fix this is in the LV2 description files rather than the Zynthian code. It may mean some extensive reworking of configurations and adds a maintenance overhead. I’m not sure about adding another layer of abstraction but maybe that would work.

Good luck with this. I hope you make progress

hahaha we’re all obviously cut from a similar cloth.

I go backwards and forwards every time. I know that practically, no matter how much I abstract something there will be exceptions and edge cases, and try to make the most elegant solution I can for the current problem.

Then specs change, or with personal stuff I discover a new use case, or a strange exception, and abstract it out, and things get ridiculous and I rope them in, and then I find a more elegant way to abstract it so I refactor it all, and then months later, even if things went perfectly well and it’s beautiful and elegant and robust, it still doesn’t actually do anymore more than it did previously, and by then I’m too busy with something else to use it for my original purpose.

I exaggerate. Slightly. Maybe.

Not yet understanding how the Zynthian code is structured I didn’t grok everything in your message @wyleu, but for now, assuming I can figure out how to massage the ttl files into some kind of order, I’m happy to sponsor my favourite instruments by way of keeping things updated manually - assuming I can figure things out.

I don’t know anything about the LV2 specifications etc, so agree with regards to automated mappings etc. I was hoping modifying the order and page layout of the GUI was as simply as filtering an array, but understand things rarely are as simple as one hopes, even if they seem that way. Sometimes for good reason too.

I’m not sure I’m going to get the time to play tonight after all, I’m still sending emails (or should be) but will report back with updates if I make any progress.

That’s certainly something I’d love to see someone write up a basic tutorial for Zynthian users on how to put together an LV2 plugin . . .

I couldn’t help myself, I had to have a play.

I grabbed the values for osc1waveform by comparing Reaper’s native UI values to the NoiseMaker GUI. There’s only three options here so I figured it would be a good one to start with.

I modified a version of /usr/lib/lv2/TAL-NoiseMaker.lv2/TAL-NoiseMaker.ttl by adding the following lines to the osc1waveform section:

    lv2:scalePoint [ rdfs:label "Saw"; rdf:value 0.0 ] ;
    lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0.5 ] ;
    lv2:scalePoint [ rdfs:label "Noise"; rdf:value 1.0 ] ;

Then pushed it back to the Zynthian, added a NoiseMaker layer aaaand… no dice. I restarted the GUI, same same. Restarted the Zynthian, same same.

Back on my computer I took another look at Calf’s Monosynth.ttl and compared it to the NoiseMaker one. I found that the NoiseMaker one was missing the rdf prefix, so I added the following line to the top of TAL-NoiseMaker.ttl:

@prefix rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# .

Pushed it back to the Zynthian, added a NoiseMaker layer aaaaaand… no dice. I restarted the GUI, added a new NoiseMaker layer aaand IT WORKED! I now know what waveform I have selected for my oscillators when using NoiseMaker!

I will get onto mapping the rest of the values over the next couple of days. For now I gotta get back to other things, it’s not like I should be working or anything… Ooops!

Edit: Oh, and what is best practice for getting modifications like this included in the Zynthian repo? I suppose I should figure out how to submit a pull request on GitHub?

3 Likes

In other news, I’ve got half of TAL NoiseMaker mapped, and figured out the toggles too, it’s amazing how much more sense it makes when you understand what the rotary controllers are doing.

If you find an instrument or effect with a toggle, that is currently acting as a 0-1 slider, you just add the following line to the relevant section of the ttl file:

    lv2:portProperty lv2:toggled ;

This is way more fun than the work I should be doing, which is proving to be a problem…

3 Likes

OK, some progress.

If you want to see what I’ve been doing, load up TAL NoiseMaker and have a look around. Aside from the presets, not much use at the moment. OSC1 destination is 0.5 or something, with OSC2 destination being 0.0. The filter type is 0.67, what’s that?!

OK, so lets fix it. Replace your existing /usr/lib/lv2/TAL-NoiseMaker.lv2/TAL-NoiseMaker.ttl with the attached file:

TAL-NoiseMaker.ttl (34.2 KB)

Reboot the UI, and take another look. As you can see, lots of progress! Here’s what I’ve done:

  • Renamed the controls from their LV2 symbol, so they better reflect the original GUI
  • Manually mapped the following controls: Filter Type, OSC1 Tune & Wavetype, OSC2 Tune & Wavetype, LFO 1 Wavetype & Destination, LFO 2 Wavetype & Destination, Master Transpose, ADSR Destination, Portamento Mode, Voices, Envelope Destination & Speed

If you blow something up, and didn’t backup your original, you can download the it from here:

I found the LV2 documentation quite useful, but TAL NoiseMaker is quite idiosyncratic, and normalises all values to 0-1. According to a newsgroup post I found, this is against recommendations, which is why I had to manually map values.

The LV2 documentation mentions conversions and different unit types, but I couldn’t get these to work. I will continue to play, especially with volume and dB units, but @wyleu was spot on when he said it wasn’t straightforward. There’'s still lots of little things I could fix (formatting for unit types etc) but at least TAL NoiseMaker is now a functional instrument on the Zynthian.

OK, I’m going to be up late finishing the work I should have done today heh. But first a quick play!

Edit: Oh forgot to mention, in the LV2 documentation there’s mention of a notOnGUI port property: LV2 Port Properties but Zynthian ignores this. I know I have zero knowledge of the Zynthian code-base right now, but logic suggests that supporting this property would be an easy way to remove unused or invisible parameters from the Zynthian GUI?

On TAL NoiseMaker for instance, there are two properties actually called unused, and several ‘Port’ properties that probably aren’t supposed to be manipulated from the rotary controllers on the GUI.

2 Likes

TAL-NoiseMaker.ttl (34.3 KB)

Couple of fixes:

  • OSC is not an acronym, so is now Osc
  • Oscillator 3 is actually the Sub Oscillator (Rectangle Wave according to the manual) so has been renamed in the interface

Edit: Actually, the sub oscillator is probably the sine wave one might expect. Despite saying “Sub Osc (rectangle)” on Page 1 of the user manual it contradicts itself on Page 14 by saying “The Sub Osc produces a sine wave.”

Still some fixes required. There needs to be some consistency with naming of envelopes, and I’d love to do something about some properties that use negative values so currently display zero as 0.5. I don’t want to have to remap every value between zero and one tho, I’ll have to spend some more time trying to figure out the conversions, but what’s the factor that can be multiplied to get both negative and positive numbers? I’ve always sucked at math.

Another cool thing would be to get proper units (dB/Hz/etc) displayed instead of 0.0-0.1.

Totally usable tho. Tonight I was able to follow this tutorial on the Zynthian, and accurately reproduce the sounds, something that would have been impossible if I’d have done the work I should have been doing today haha.

Speaking of which!

1 Like

The transfer function is a first order polynomial

y = mx + c

Where y is your output value and x your input
values for m can be positive or negative and would be floats

This is your gain setting. ie you set this to the total range of the control

You then adjust the c setting which again is a + or - float. . .

So for each instance of a different type you’d store an m & a c value . . .

Extending this to a different law from linear involves extra parameters as you would need to express the type of law being applied and suitable parameters to tune that function…

There’s some conceptual stuff about how specific controls behave so at what point does a low frequency oscillator amount control switch from a linear feel to a logarithmic one for instance ? It kind of depends…

The Nord Modular 1 worked fairly hard to give you numbers but by the G2 this had faded to info like frequency displayed as Hz/ Harmonic / Ratio with far less specific numbers with the knob position giving most of the visual feedback.

Presumably in our world we’d push that sort of setting and choice back to the user and allow them to build it precisely their way.

Have a look at the way that CC’s are allocated to encoders (parameter screen swipe to get green question marks, move control . . . . . Done :slight_smile: ) to get an idea of the sort of functionality that already exists around a parameter value.

Yikes! All that sounds scary haha

I was hoping there was a way of using an LV2 Conversion to map say -24 - +24 to 0.0 to 1.0, and figure out how to use the other unit types and render functions to get this sorted. Problem is the conversion property only takes a factor not a function.

Do you think fixing these issues is impossible without diving into the Zynthian source? Or is modifying TTL files just the incorrect way to go about addressing these issues?

Well it comes down to a range of specific fixes. 0 -127 seems to be a base range. It maps well with MIDI and it represents the zynth’s view on the world.

There wont be too many different presentations in the end. so you end up with a few mappings that apply to the different worlds the engines have come from . Csound stuff, for instance, is going to be very float based and it’s only fair to present that information to those users who come from that world :smiley:

I think you are best doing an 80%:20% sort of pass and see what you learn from moding the parameter files

But in a specific instance like TAL NoiseMaker which maps all properties to values between 0.0 and 1.0, shouldn’t it be possible via the relevant TTL file to replicate the plugin’s GUI, which in one example snaps between say -12, 0, 12, 24, but actually the plugin is simply converting all values between 0.0 and 0.24 to -12, everything between 0.25 and 0.49 to 0, etc. The snapping has been done already using scalePoint’s as described above, but there’s no easy (or any?) way to simply map say -1000 - 1000 ms to 0.0 to 0.1 in the LV2 specs :frowning:

Calf Monosynth is quite well mapped, but receives units specific to the property, as per recommendations in the LV2 specs. Unless I’m missing something (which is very likely) mapping everything back to zero to one is the only thing that stops the Zynthian from being able to completely replicate the TAL NoiseMaker GUI experience.

Edit: uh, I’ve edited this like five times and am confusing even myself. I’m very tired. I shall revisit in the morning haha

Just fixed up the TAL Filter TTL file:

TAL-Filter.ttl (7.7 KB)

I’m still of the opinion that the Zynthian UI should respect the notOnGUI Port Property, and by default and wherever possible, display each property of each instrument or effect as it would be displayed on the GUI, including the correct units.

Mapping things beyond that is a different matter, as despite it being against recommendations, LV2s appear to be able to return any number of values, so mapping consistently across instruments without manual mapping layers for instruments that aren’t standards compliant is going to be very difficult.

1 Like

Oh yeah, this needed to be done so bad. Is this ttl file something that we generate or is it something that ships with the plugin? because maybe you can get them (TAL) to merge your changes. I know doing this is hard work by hand but maybe with a guide in the wiki we can crowdsource the work so each one can do their own favorite synths and make them more usable.

On that same page, Is there a file that determines which parameters get to be in which pages of the zynthian layer view, or is this done purely by the order in which they appear?

1 Like

Most of my experiments have failed, and everything I have been able to do so far has been achieved by either enabling the toggle property, or using scalePoints. This isn’t too complicated, I’ll get a guide together soon and propose adding it to the Wiki, but I’m far from an expert, and due to my lack of knowledge and experience, aren’t sure if I couldn’t get certain things working because I was doing something wrong, because of the Zynthian GUI layer, or because of the TAL plugins.

At least as far as TAL NoiseMaker and Filter are concerned, they don’t appear to distribute a TTL file with their plugins. I’m not certain, but would imagine @jofemodo was behind the original TTL files?

If you add something to the wiki then anyone with different skillset might be able to update with their experience or knowledge. We should aim for a mechanism that provides the user with as consistent and useful interface as possible whilst minimising manual maintenance. If the data can be transformed by rules then we can code that but it it requires real inteligence then it may have to be a manual task. LV2 does provide a range of mechanisms to provide useful UI including configuration files and separate GUIs. We should be able to use the config files to present the data to the user but the experience depends on the quality of the data.