LV2 components I'd like to see

It’s the way the project developed from the start, so it’s an approach that spirals round the outer growing surface of zynthian, specific interests and a rigourous PR system ensures that what gets done gets bedded in.
webconf reports are the best place to report quirks, of any nature. . .

But here’s the long form answer.

It’s simply a success problem of what has worked up to now. Please don’t take this as an excuse or an attempt to divert, it’s really not that, just an observation with some expectation involved.

Much development has taken place on audio flow within the guts of the system and the addition of plugins has become both possible and apparent. LV2 appears to be our defacto structure and we want these components to be as standard as possible, so we wish to rigorously define the development path so this core element can propagate throu the zynthian world as easily as possible.
And then document it heavily. That’s my drive here. and I can indulge as little or as much as I want.
As can anyone else. I really like collaborative approaches to problems but as someone who is retired, I have several interests and I try to overlap them so that one can feed others.
I play with mechancial clocks more than zynthians at the moment, but I apply what I learn to both.

Outside of Jofe we have no control over where the development takes place, and obvious none ,really, with Jofe. So at the cutting edge we tend to surf on the current wave, trying to ensure by discussion, ribbing, and bowls of soup quite what get’s addressed next.

I’ve been trying to code and document lv2 & ctrldev components for some time now for various projects, and to aid that wrote the Developers documentation section on the premise that we would have an increasing number of heavily musically inclined users with a desire to do something ‘cody’. The odds of these people having involved themselves in Code, previously is low. So we wrote a lot of instructions to get them to understand the process that feed the developer pipeline most effectively.
It may make people install vsc, but if they’ve read carefully, they wil also be ablse to submit a Bug report or Feature request, so that’s a good thing!

The issue with quirks eta l, is finding them. Many of us have built and maintained large code monoliths and much else besides, so we know how it’s done, but we don’t have the pressures that commercial development teams do. The desire to release version 2.0 for the next trade show in Las Vegas or were ever is not as imperative, so we get a certain amount of features go wizzing by but essential bits in the code base get made solid. It is nothing more than a JACK environment with a bit of MIDI manipulation at the edges after all. . .

The other side of this is quite when is something ‘Finished’ ? I need a 50Hz notch filter to clean up hum pick up on a Microphone listening to a clock. This isn’t a zynthian project, but it’s, perhaps, something that it’s omission could allow the LV2 collection of Zynthian to be described as Unfinished…?

As a said at the beginning development tends to move cyclically around the outer edge, and passes over a similar topic at a time set by that circling, which, as the project grows, get s longer and longer, unless we recruit more developers….

Which if the gibhub fork statistics are anything to go by, is happening.

So for all of us, the best thing we can do is use, and report because our very success, and there certainly has been some, throws up more problems. I’ve found success problems far more involved than failure. Failure code just dies away. Successes open up till the questions, in a flash, change from an incredulous “ooooh it can do that…?” to a scornful “What you mean it can’t do that …?”

We collect quirks and the report section at the bottom of the wecconf page is a good place to put them. IAnd I rather enjoy doing semi literate calls to arms!!

It’s good here ain’t it…?

Nurse!!,

Nurse !!!?!!

wyleu feel tired. . . ,

wyleu want lie down. . . ,

The syringe…?

Thank you Nurse . . ..

Wyleu just pawn in game of life . . .

Wyleu lie down now . . .

5 Likes

I was able to compile a simple gain plugin, fine! I had to compile the lv2-ttl-generator.c manually. This makefile prosa is still a bit over my head, but I’ll get into it.

@riban Can you tell me what the purposes are of your riban-lv2 folder in your repo? I see that the readme tells people to run the make prompt from there. It seems like its supposed to hold the compiled deb packages. Like I adapted your folder structure I had to compile my plugin from the flugins source folder. What I’m struggling with especially is how these Makefiles interact with each other (build all, build one plugin) and how I do state relative paths in these. Seems for the CLI commands there I have to anticipate from which current working directory the compilation is done.

Also, could you elaborate on your typical build process? There seem to be prompts in your makefile aiming at auttomatically copy built binaries to /usr/lib/lv2. And whats a simple process ro update the plugin database without updating the entire system?

My GitHub repo is organised with DPF and each plugin as separate directories. The root directory has a Makefile that will build all plugins and the DPF Makefile that is included in others, to support the framework. Each plugin directory has a Makefile that will build just that plugin.

I have only enabled LV2 in each Makefile because I only maintain these for zynthian.

The riban-lv2 directory is used to build a Debian package file (.deb). Calling `make deb` should do this.

The `utils/lv2_ttl_generator target builds the ttl generator and is called by other targets, so for example, `make deb` calls the `deb` target which calls the `plugins` target which calls the `utils/lv2_ttl_generator` target.

The install target puts the files in `/usr/lib/lv2` (unless LV2_INSTALL_PREFIX is defined elsewhere) which is one of the search paths for LV2. After running install, you need to use webconf to “Search for plugins” to make them appear in zynthian. (There is a bug that I have not had time to investigate where it seems zynthian needs a reboot after installing LV2 before this works properly.)

Ah, I misunderstood one thing. Your build instructions say

git clone --recursive https://github.com/riban-bw/lv2-plugins.git riban-lv2
cd riban-lv2
make
sudo make install

So, the riban-lv2 directory to cd into is the repository root, not the equally named directory inside.

Also this means I successfully compiled the plugin with the makefile in the plugin source, which doesn’t contain commands to compile stuff in DPF, and I did succeed to compile with the root directories makefile which does include these commands, therefore I had to compile the ttl-gen manually.

So, really looking forward for trying some things. But in that process I realized how not at all self containing my DSP/utilities code was.

Correct! I am glad the instructions are correct - simply misinterpreted.

One of the things I like about Rust cargo, it builds things in an agreed structure.

moved here:

Hello again,

not that I am set to compile plugins I’d like to ask some questions if somebody already passed that line:

  1. For the compilation: Do I get any easier in the process? My current “workflow” is like that:
  • Start VSCode and connect to ssh, typically reload window
  • Make changes in the code and save
  • In PuTTY restart the session previously quit by rebooting the zynthian (login, password etc)
  • cd to compilation path
  • make clean, make, make install
  • In webconf engine menu rescan for plugins and presets (if I only knew the commands for these I could at least prepare a one liner, is it probably update_zynthian_code.sh or regenerate_engines_db.sh all).
  • reboot zynthian
  • test
  • start over
    So how do you even debug on zynthian?
  1. Does anybody know if zynthian knows read only parameters? I tried to display the current gain applied in my AutoLeveler plugin. Tried a parameter which doesn’t do anything in setParameterValue, made it kParameterIsOutput, update that parameter by requestParameterValueChange and the lot. But it doesn’t show its state when changing or doesn’t even display.
  2. Does anybody know if DISTHRO DPF knows something what’s called “OnIdle” or “OnReset” functions elsewhere? I only found sampleRateChanged or activate

So long for now.

regenerate_engines_db.sh
regenerate_lv2_presets.sh <uri_plugin>

Indeed this is not needed except when you install the plugin for first time, move It to a new location or modify the manifest.ttl.
The second command is needed always you install/remove presets for the plugin.

You don’t need this step. You simply reload the zynthian ui and webconf services:

systemctl restart zynthian
systemctl restart zynthian-webconf

Indeed, as i explained above, you only need to do this if you installed new plugins, moved to a different location or changed the manifest.ttl.

Most of times you can simply reload the processor without restarting the UI.
If you installed or removed presets, you can force the rescan from the UI presets menu.

All the best

2 Likes

I use the terminal within VSCode to recompile. You are already connected via ssh so the terminal acts on the remote machine.

Reload window is frustrating. It occurs when the ssh connection has failed, e.g. the development PC has been put into sleep mode.

Zynthian uses a modified version of jalv to host LV2 plugins. Any ports or parameters can be accessed. I am not sure what issue you are having with parameters. You could post a bit of code as an example.

I tend to do the work within the “run” function which is called every jack period and must process before next period, else you trigger xruns. Looking at DPF online documentation for the Plugin class I do not see a function (in a seperate thread) for background (idle) activity.

1 Like

These are wonderful suggestions, I’ll try!

I have no issues with parameters yet. I wonder if I can have a parameter which is shown in zynthian UI, but can not be changed by the user but by the plugin itself (here: show auto gain applied).

I was using Iplug2 before where they had these functions. OnIdle() was called if plugin doesn’t process to hold calculating intensive non DSP functions (UI or similar) to not run these in realtime processes. For example calculating frequency response of all eqs to show graph in UI. OnReset() was called to reset functions, e.g. transport start, samplerate/buffersize change and the lot. So there is a function like sampleratechanged() or similar. I don’t know this DPF so well yet, don’t know if necessary in zynthian.

It occured to me after rebooting zynthian everytime. Now that I learned I might not need to reboot, this might be no problem anymore.

If I read both of your answers, it should be sufficient to just compile from VSCode terminal an reload (a.k.a. replace with itself from zynthian UI) the plugin?

Well, that would be easier than previously.

yes. Replace the plugin with itself is the minimal action after recompiling the plugin or modifying TTL.

2 Likes

Hello @wyleu,

Hope you get along with these things well. I read your documentation and would have some notes. Among these this may be of importance:

"Next we have a function that initializes some Parameters and define a range of values for that handy gain number we’d like to alter.

   void initParameter(uint32_t index, Parameter& parameter) override {
       if (index != 0) return;
       parameter.hints = kParameterIsAutomable;
       parameter.name = "Gain";
       parameter.symbol = "gain";
       parameter.unit = "x";
       parameter.ranges.def = 1.0f;
       parameter.ranges.min = 0.0f;
       parameter.ranges.max = 2.0f;

This applies extra values to the external implementation of this parameter so the two environments can pass data effectively.
index is required to have a value 0 to set these values to what are presumably defaults."

The uint32_1 index is actually the integer parameter ID the framework is calling. Since you have only the gain parameter, its index is 0. Therefore you have an (unnecessary) check, if the index is 0, it does all the stuff, no matter what. You may consider to use a switch to check these. If you want to handle parameters with human readable names you may want to consider using enums.

“Again requiring a 0 index to operate.”

The statement would be: “Requiring a 0 index if the gain parameter with parameter ID=0 is meant. If any other parameter is meant, the index of its ID would be required.”

So in DisthroPluginInfo.h you can add:

enum Parameters {
    kPreGain = 0,
    kThreshPeak,
    kThreshRMS,
    kListen,
    kParametersCount
};

This in fact is the short version of

enum Parameters {
    kPreGain = 0,
    kThreshPeak = 1,
    kThreshRMS = 2,
    kListen = 3,
    kParametersCount = 4
};

and in your cpp:

  void initParameter(uint32_t index, Parameter &parameter) override {
    switch (index) {
      case kPreGain:
        parameter.name = "PreGain";
        parameter.symbol = "pregain";
        parameter.unit = "dB";
        parameter.groupId = kPortGroupLeveler;
        parameter.ranges.def = 0.f;
        parameter.ranges.min = -60.f;
        parameter.ranges.max = 12.f;
        break;
      case kThreshPeak:
        parameter.name = "Threshold Peak";
        parameter.symbol = "threshold_peak";
        parameter.unit = "dB";
        parameter.groupId = kPortGroupLeveler;
        parameter.ranges.def = 0.f;
        parameter.ranges.min = -60.f;
        parameter.ranges.max = 0.f;
        break;
      case kThreshRMS:
        parameter.name = "Threshold RMS";
        parameter.symbol = "threshold_rms";
        parameter.unit = "dB";
        parameter.groupId = kPortGroupLeveler;
        parameter.ranges.def = -12.f;
        parameter.ranges.min = -60.f;
        parameter.ranges.max = 0.f;
        break;
      case kListen:
        parameter.name = "Listen";
        parameter.symbol = "listen";
        parameter.unit = "";
        parameter.groupId = kPortGroupLeveler;
        parameter.ranges.def = 1.f;
        parameter.ranges.min = 0.f;
        parameter.ranges.max = 1.f;
        parameter.hints = kParameterIsBoolean;
        break;
      default:
        break;
    }
  }

Otherwise you would just state:

case 0:
  ...
  break;
case 1:
  ...
  break;
default:
  break;

Of course the switch/case thingy is only a smarter version of if/else if/else, which you could use, but most of the folks there use switch in that regard.

if (index == 0) { ... } else if (index == 1) { ... } ...

Same applies to the “setters” and “getters”.

Of course, if you plan to stay with only one paramter, you could in theory just not bother with plugin ID at all

   void initParameter(uint32_t index, Parameter& parameter) override {
       parameter.hints = kParameterIsAutomable;
       parameter.name = "Gain";
       parameter.symbol = "gain";
       parameter.unit = "x";
       parameter.ranges.def = 1.0f;
       parameter.ranges.min = 0.0f;
       parameter.ranges.max = 2.0f;

or if you want to check the ID anyway and prepare for adding other parameters later:

   void initParameter(uint32_t index, Parameter& parameter) override {
     if (index == 0) { 
       parameter.hints = kParameterIsAutomable;
       parameter.name = "Gain";
       parameter.symbol = "gain";
       parameter.unit = "x";
       parameter.ranges.def = 1.0f;
       parameter.ranges.min = 0.0f;
       parameter.ranges.max = 2.0f;
     }