Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VST2: Implement programs #401

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

AnClark
Copy link
Contributor

@AnClark AnClark commented Jan 3, 2023

I've implemented built-in program access for VST2.

It has been tested in my DPF port of amsynth. Related source code:

@AnClark AnClark force-pushed the patch_vst2-implement-programs branch from d0b6268 to 6e3d846 Compare January 3, 2023 03:38
@falkTX
Copy link
Contributor

falkTX commented Jan 3, 2023

This is not up to spec.
For VST2, programs need to implement the full state of all available programs during runtime.

Say your plugin has 8 programs, and uses states. We need to query the full state of those 8 programs during load so that a save chunk operation is able to store this info.
When a user sets program as index 3, changes some things, moves to index 4, then changes back to index 3 - the same state as it was set before needs to be there.

This is the main reason it is not implemented, it is just a big waste of memory for any non-trivial plugin.

@falkTX
Copy link
Contributor

falkTX commented Jan 3, 2023

A few other notes:

  • we need to store every possible program name, they are meant to be editable from user side. we cant use a single var for this if you want full vst2 preset support
  • setchunk/getchunk has an optional flag to indicate if we are meant to save a single program or the full state, this was not implemented before because dpf used 1 single program for all cases, but if going forward with full vst2 presets the getchunk and setchunk needs to save and restore the full state of all programs (if that flag is passed by the host)

@AnClark
Copy link
Contributor Author

AnClark commented Jan 3, 2023

So when and how DISTRHO::Plugin::initProgramName() and DISTRHO::Plugin::loadProgram() work, according to your original design?

I haven't seen those two methods invoked in VST2 implementation. Seems that they have no effect in VST2 builds. This patch is just to let them work.

In my project, loadProgram() doesn't handle plugin state. Instead it directly ask the synthesizer to load preset by calling a method provided by synthesizer (not DPF's method).

@falkTX
Copy link
Contributor

falkTX commented Jan 3, 2023

So when and how DISTRHO::Plugin::initProgramName() and DISTRHO::Plugin::loadProgram() work, according to your original design?

I haven't seen those two methods invoked in VST2 implementation. Seems that they have no effect in VST2 builds. This patch is just to let them work.

The current DPF implementation is none for this, which is why those methods are not called.

As I said, if we want to expose those we need to have quite a few other extra things on top.
Otherwise the implementation would not be conformant to VST2 spec.

It is not just a matter of mapping the calls, that is not how things work regarding presets in VST2.
If we want to expose the DPF programs in VST2 we need roughly:

  1. allocate char[24] * number of programs, so individually renaming them from host side works
  2. reserve full state of all programs (including both parameters and states) on init, basically caching param values and states for all programs
  3. so we keep in memory the full state of all programs, and switch to that once a program change is initiated from the host (this is important, we need to switch to the in-memory chunk of a program, and not the program call set by dpf)
  4. on chunk save, depending on a flag we either give to the host the whole full state * number of programs, or just the current chunk for the current program
  5. for chunk load it is the same, the host can either replace/load the whole collection of programs or an individual one

again: we cant simply map the vst2 set-program to dpf load program, that is not how vst2 plugins are supposed to work.
we need the whole dance of caching active state for all programs exposed by the plugin, and switching to that during runtime.

say your plugin has 128 parameters and 50 programs.
for a proper vst2 implementation we would need to store 50 * 128 parameters, plus names * 50 everytime we load the plugin.
this quickly grows to consume a LOT of memory, which is why plugins moved to not expose vst2 programs to the host and just have 1 that simulates current/active one alike DPF does.

I am not against having this implemented in VST2 for DPF, but it really needs to be proper.

@AnClark
Copy link
Contributor Author

AnClark commented Jan 3, 2023

Thanks for your patient and detailed explanations!

According to your instruction, the only way conforms to VST2 specification is to cache all the states of programs. The specification is somewhat troublesome on practice, so I understand why the program feature is not implemented.


My implementation seems to be non-standard. Although non-standard, it really fits with my own projects, and in a way it gets rid of the large storage of programs.

This is how my implementation work:

  • The synthesizer itself has its own preset manager. Presets will be loaded on demand.
  • When the host calls initProgramName(), it requests preset manager for preset names.
  • When the host calls loadProgram(), it requests preset manager to actually load a preset by index.

You can view my code in amsynth (mentioned in the head of PR) for details about practical usage.

If this patch is unsuitable for merging, I will keep it myself.

@falkTX
Copy link
Contributor

falkTX commented Jan 3, 2023

the only way conforms to VST2 specification is to cache all the states of programs

yup, so when plugin state (not dpf related, but with the whole plugin market) started to get big and complex, plugins also started to drop vst2 factory preset implementations.
Instead of doing it via VST2 callbacks, plugins started to use their own built-in preset browsers instead.

Now with VST3 the situation is a bit different, so myself and others just stopped caring about this.
And CLAP doesnt support programs/presets yet afaik.

If this patch is unsuitable for merging, I will keep it myself.

you are correct here. and in my opinion we should not add to more vst2 broken plugins that exist out there
the current DPF approach is not the best it could be, but it is a valid implementation at least.

@AnClark
Copy link
Contributor Author

AnClark commented Jan 4, 2023

Why I want to implement program for VST2 is that many VST2 plugins supports it. For example, Memorymoon series, MixCraft's Classic Effects. But they are old plugins (maybe were born 10+ years ago), only providing VST2 versions.

During this discussion, I start to consider that implementing programs for VST2 is not really essential.

The biggest problem is: host-side VST2 program implementation is not universal. For example, my amsynth fork has 3000+ programs, however:

  • REAPER can only display about 500 VST2 factory presets.
  • Carla can display all of them, in drop-down menu.
  • Falcosoft's MidiPlayer can also display all of them.

Also, I spotted that REAPER's factory plugins are also in VST2 versions, but they don't implement programs, instead they use RPL (REAPER user preset file).

So in conclusion I may prefer built-in preset manager instead. (But I will still keep this implementation in my own fork.)


Now with VST3 the situation is a bit different, so myself and others just stopped caring about this.
And CLAP doesnt support programs/presets yet afaik.

By the way, I have found that you use a parameter "Current preset" to allow user to pick a program, and initParamName() and loadProgram() works perfectly in VST3. Even though my project has 3000+ programs, DPF still handles well.

image

However, it could be better if DPF can implement VST3 factory programs as drop-down menu. U-he's VST3 ports supports loading their factory programs (maybe user programs as well) into host's drop-down menu, which is similar to VST2's behavior.

@falkTX
Copy link
Contributor

falkTX commented Jan 5, 2023

However, it could be better if DPF can implement VST3 factory programs as drop-down menu. U-he's VST3 ports supports loading their factory programs (maybe user programs as well) into host's drop-down menu, which is similar to VST2's behavior.

This should be handled by the host I think, DPF already uses a specially designated parameter to list the programs.
See https://github.com/DISTRHO/DPF/blob/main/distrho/src/DistrhoPluginVST3.cpp#L1676

can you show in pics how these are different?
I know VST3 has an alternative way to handle programs, called "units", perhaps that is the difference here?

@AnClark
Copy link
Contributor Author

AnClark commented Jan 6, 2023

can you show in pics how these are different?

mmexport1672974527845

This is the VST3 version of Dexed. It's built with JUCE. Perhaps JUCE handles VST3 programs in another way.


What's more, JUCE allows developer to change the program list on demand. For instance, when user loads another program cartridge, host's preset drop-down menu will be updated conforming to the cartridge.

(Some VST2 plugins also have this feature, e.g. Xhip synthesizer, Memorymoon series.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants