Skip to content

Conversation

@jpetso
Copy link

@jpetso jpetso commented Nov 7, 2025

RipplerX has had a few nice updates since it was originally added in zynthian/zynthian-sys@c8d4720 plus custom dsp.ttl file in #20. See past forum discussion here. (CC @johannesmenzel.)

I thought I'll see if I can update it from v1.3.4 to v1.5.8. It was a little bit of effort but it's working now. To do this, I built both v1.3.4 and v1.5.18 on my Zynthian. Then I went through the diff between both dsp.ttl files and applied the changes to Zynthian's modified dsp.ttl in classic three-way-merge fashion. Then I spent way too long pondering how the new parameters should be organized into 4-knob input groups.

This is the result. I split the change up into two commits:

  • The first one for simpler changes: Updated parameter value ranges, added new enum values for existing parameters, and added the new "Sidechain" audio input pair.
  • The second one for adding new parameters and reorganizing input groups.

Noteworthy features that I'm aware of:

  • Much improved behavior when hitting the polyphony limit.
    • Most importantly, if you keep holding a note, it now prioritizes that one over a newer note that is not held anymore.
    • New boolean parameters "Reuse Voices" and "Fadeout Repeated Notes". Not sure right now how they work exactly.
  • New mallet parameters: mallet type (various samples, or a purely mathematical impulse), mallet filter, pitch factor and keytrack modifier for mallet pitch. The mallet is now very versatile.
  • Tension parameters for noise attack, decay and release.
  • New resonator model types: "Marimba2", "Bell", and "Djembe".
  • Resonator "Material" and "Tone" parameters can now be velocity-modulated as well.
  • More noise parameters can now be velocity-modulated. In particular, all four noise envelope parameters as well as the filter section's "Frequency" and "Q" parameters.
  • The range of all velocity modulation parameters has been expanded: originally 0 to 1, now -1 to 1. Specifying a value below 0 means that if you hit the note harder, the parameter value will decrease rather than increase.

Challenges and questions:

  • I gave my best shot at assigning (and reassigning) display groups, but please review my changes carefully because this is my first patch for Zynthian.
    • Notably, I removed the "Global Mix" group and folded Mallet/Noise mix parameter into their respective "Setup" groups. Plus a few other changes that made either logical or practical sense in my opinion.
    • The original UI can be a little cleaner because some of the parameters are only relevant for certain resonator models or coupling modes. Not sure if there is any infrastructure to selectively show parameters.
  • The set of Velocity Modulation groups, in particular, poses a challenge for organization.
    • In the original UI, it's on a separate page that overlays (some of) the original knobs. Should the velocity-modulated parameters be listed close to their base parameters, or in a separate group just below the base parameter group, or all velocity modulation parameters grouped together at the very bottom?
    • After playing with the parameters a bit, I decided to pull velocity modulation parameters closer to their base parameters in most cases. I left them in separate groups for "Noise - Filter" (splitting the filter parameters feels awkward) and for "Noise - Envelope" (the envelope base parameters should stay together to keep the whole envelope graph on one page). The final set of groups feels right to me, but feel free to play with it if you don't like it.
  • plug:a_cut and plug:b_cut parameters changed their min/max values to a completely different range: previously 20 to 20000, now -1 to 1.
    • Do we need to worry about migrations?
  • plug:mallet_type (new parameter) has a bunch of "Reserved" values as well as "User File". I left those enum values out from the custom dsp.ttl, because they're not really useful in the Zynthian interface anyway?
    • The "User File" enum option (i.e. use custom filter sample) could be useful, not sure how it would be integrated though.
  • plug:a_partials and plug:b_partials have new enumeration values labelled "1" and "2", listed only after the existing ones "4", "8", "16", "32" and "64". The internal rdf:value for the new "1" and "2" values is larger than the original ones and presumably can't be listed first if rdf:value determines the order?
    • The original UI appends "1" and "2" at the end of its options list too, so I guess it's okay to keep the weird order.
  • I put the new "Mallet Keytrack" parameter (modifies mallet pitch) next to "Mallet Pitch" in the "Mallet - Setup" group, I think that makes the most sense in regular use. Although logically it could also go into the newly introduced "Keyboard" group.

Unrelated to the upgrade:

  • The polyphony limit (and some other settings too, I think?) is not exposed through an LV2 parameter. It would be nice to have this configurable in Chain Control, but I guess that should be tackled by upstream RipplerX instead of worked around?

After the change, the list of parameters is now grouped like this:

  • Resonator Setup: a on, b on, stereoizer
  • Resonator Mix: coupling (a+b or a>b), a+b mix, a>b split, gain
  • Resonator Pitch: A coarse pitch, A fine pitch, B coarse pitch, B fine pitch
  • Resonator A - Model: model, partials, ratio, tube radius
  • Resonator A - Material: material, velmod material, hit position, velmod hitpos
  • Resonator A - Tone: tone, velmod tone, inharmonic, velmod inharmonic
  • Resonator A - Attenuation: decay, velmod decay, release, cut
  • (...Resonator B groups same as the 4 Resonator A groups above...)
  • Mallet Setup: mallet mix, velmod mallet mix, pitch, mallet keytrack
  • Mallet Material: mallet type, stiffness, velmod stiffness
  • Mallet Tone: resonance, velmod resonance, filter
  • Noise Setup: noise mix, velmod noise mix, noise DC (OSC intensity)
  • Noise Filter: resonance, freq, q, filter mode
  • Noise Filter - Velocity Modulation: velmod resonance, velmod freq, velmod q
  • Noise Envelope: ADSR
  • Noise Envelope - Velocity Modulation: velmod equivalents for ADSR
  • Noise Envelope - Tension: tension parameters for attack, decay and release (but not sustain)
  • Keyboard: pitchbend range, reuse voices, fadeout repeated notes

Here is a Release build for ARM that works on the Zynthian, at least for my RPi 5. Needless to say, this goes hand in hand with the amended dsp.ttl from this PR.

From v1.3.4, Zynthian's original snapshot.
This commit applies changes to existing LV2 parameters,
and adds the new input audio port group ("Sidechain").
@jpetso jpetso changed the title Vangelis lv2-custom/RipplerX.lv2: Update dsp.ttl to v1.5.18 Nov 7, 2025
@jpetso
Copy link
Author

jpetso commented Nov 7, 2025

Sorry about the bad initial title! I was so sure I had pasted a more relevant one into the PR submission form.

@johannesmenzel
Copy link
Contributor

Hi @jpetso,

since you CC me I'll share some thoughts, even though the decision about if, how and when it'll be updated is to the project owners and I have nothing to do about that.

I think this will mainly work, here some questions about it anyway. When comparing the your ttl with the current "official" one from the upstream release, I see:

  • many default values of "-2.23517e-08" a.k.a. almost 0 instead of "0", why is that?
  • at least one changed minimum/maximum value ("Noise Release"). Do you know what the consequences are on parameter scaling and so on? I don't.
  • You deleted the stepped value "User File" in "Mallet Type", maybe someone wants to add the user file via VNC
  • Some of the group names are not that intuitive (yes, mine where also not so intuitive)

Tell me what you think.

@jpetso
Copy link
Author

jpetso commented Nov 10, 2025

Hi @johannesmenzel, thanks for having a look! As the original designer of these groups, your feedback is important to take into consideration. Let's hop right into your questions.

many default values of "-2.23517e-08" a.k.a. almost 0 instead of "0", why is that?

This came directly from the original .ttl file that was generated by the RipplerX build system, and I decided not to change it. The RipplerX source code does indeed specify it as 0.0f, like here:

        std::make_unique<juce::AudioParameterFloat>("vel_mallet_mix", "Vel Mallet Mix", -1.0f, 1.0f, 0.0f),
        std::make_unique<juce::AudioParameterFloat>("vel_mallet_res", "Vel Mallet Resonance", -1.0f, 1.0f, 0.0f),
        std::make_unique<juce::AudioParameterFloat>("vel_mallet_stiff", "Vel Mallet Stiffness", -1.0f, 1.0f, 0.0f),

My understanding is that the build system (powered by JUCE) then compiles this code into a helper binary, which reads out the min/max/default values of each parameter and generates the actual .ttl file from it. I don't have enough insight into why a constant 0.0f would turn out as not-exactly-zero, but I'm sure there's an explanation that makes a lot of sense.

Thankfully, the Zynthian interface also shows it as rounded down to 0.

at least one changed minimum/maximum value ("Noise Release"). Do you know what the consequences are on parameter scaling and so on? I don't.

"Noise Release" (a.k.a. plug:noise_rel) actually stays the same, with a value range from 1 to 5000. I did mention two points in the initial comment of this PR that touch on this question:

In "Noteworthy features that I'm aware of":

  • The range of all velocity modulation parameters has been expanded: originally 0 to 1, now -1 to 1. Specifying a value below 0 means that if you hit the note harder, the parameter value will decrease rather than increase.

In "Challenges and questions":

  • plug:a_cut and plug:b_cut parameters changed their min/max values to a completely different range: previously 20 to 20000, now -1 to 1.
    • Do we need to worry about migrations?

I agree that we should understand and answer this before merging my change. I don't have a good understanding of what Zynthian or JUCE would do to ensure that a previously stored value (e.g. from a Zynthian snapshot) gets clamped to the valid range. And in the current state, there definitely isn't anything that would transform the old range 20 to 20000 linearly to the new range -1 to 1.

You deleted the stepped value "User File" in "Mallet Type", maybe someone wants to add the user file via VNC

That's a good point, perhaps we should allow this option. I think I saw in a parameter list somewhere that Zynthian also has a file selection control, not sure if this would be suitable here though?

Some of the group names are not that intuitive (yes, mine where also not so intuitive)

Yes, and please note my remarks on this topic at the top of the "Challenges and questions" section in the top comment.

I actually thought your "Material" and "Tone" groups for resonators A and B were rather nice, I think the same names/concepts work pretty well in the "Mallet" section as its number of parameters increased and groups had to be expanded.

Splitting "Noise - Velocity Modulation" into two different "Velocity Modulation" groups for "Noise - Filter" and "Noise - Envelope", respectively, makes the names longer but I remains intuitive and logical. Personally, I think because the two "Velocity Modulation" groups are right next to their base parameter groups instead of the very bottom, the Noise section is actually more usable than before. Feel free to disagree and make suggestions.

This leaves the resonator section for the most awkward grouping. The group names I used are definitely a trade-off between what logically makes the most sense, and what saves space.

Here's a rough parameter grouping that represents the organization of the RipplerX codebase with regards to resonators:

  • Global: resonator A on/off, resonator B on/off, resonator gain, coupling type (A+B or A>B), A+B mix, stereoizer
    • "stereoizer" is not actually related to resonators but rather a global post-processor, so theoretically it would go into "Global Mix" if I hadn't eliminated that.
    • Global plugin owns N voices for polyphony.
  • Per voice: coarse pitch, fine pitch, A>B split (if coupling type is A>B)
    • Voice owns resonators, mallet and noise.
  • Per resonator: model type, number of partials (don't apply to tube models)
    • Resonator owns N partials, a waveguide, and a filter.
  • Per partial - don't apply to tube models: ratio, inharmonic, material (a.k.a. damp), tone, hit position
  • Waveguide - only applies to tube models: tube radius
  • Both partials and waveguide: decay, release
  • Filter: (low)cut

There are a few challenges in there. One is that there are 5 per-partial parameters, or 9 if you include velocity modulation. Ratio does not get modulated. 10 if you include number of partials. Tube models only have a single parameter (radius), no modulation either. Low cut is a filter and I didn't feel comfortable grouping it together with three other per-partial parameters in "Tone", while "Material" seemed like a random assortment of number of partials, two per-partial parameters, and the tube radius for a different model type.

So I wanted to move (low)cut out of the per-partials group, while putting as many per-partial parameters together as possible. The result is two exclusively per-partial groups, now also called "Material" and "Tone", both with velocity modulation. The "ratio" and "tube radius" parameters are indeed thrown in with "model type" and "number of partials" into a new "Model" group. Ideally they'd be separated out, but two extra groups per resonator seemed like a bad trade-off.

This leaves decay, release and (low)cut, all of which apply to both non-tube and tube models. I called this "Attenuation", because all three take away a part of the resonator sound. Admittedly this is not a great grouping.

Another approach would be something like this:

  • Move decay, release and (low)cut into the "Model" group. The four parameters would be:
    • Resonator A/B - Model: model type, decay, release, (low)cut
  • Have three full groups for non-tube models (with partials) and one group for tube models with its single parameter:
    • Resonator A/B - Non-tube models #1: number of partials, ratio, inharmonic, velmod inharmonic
    • Resonator A/B - Non-tube models #2: material, velmod material, tone, velmod tone
    • Resonator A/B - Non-tube models #2: hit position, velmod hitpos
    • Resonator A/B - Tube models: tube radius

This would increase the number of groups for each resonator from 4 to 5, but might be preferable in terms of clarity?

Happy to take suggestions for a different kind of grouping, if you can think of something good.

I'm relatively happy with the three global resonator setup groups, "Setup", "Mix" and "Pitch". We could consider moving "resonator gain" into "Setup" analogously to "mallet mix" and "noise mix" parameters in their respective "Mallet - Setup" and "Noise - Setup" groups. Then "Resonator Mix" would be renamed to "Resonator Coupling". (Mallet and noise mix can be velocity-modulated so I'd rather leave them in these groups, a "Global Mix" group would have to span at least two groups anyway and I'm not sure it's really an improvement that way.)

I wonder if we should add a dash to the "Resonator Setup/Mix/Pitch" groups, or perhaps remove dashes everywhere else so we'd have "Resonator A Model", "Mallet Material", "Noise Envelope - Velocity Modulation" and the likes.

In summary, you're right that there's a bit of awkwardness here and there, but one cannot say that I didn't at least put some thought into all these groupings :)

@johannesmenzel
Copy link
Contributor

johannesmenzel commented Nov 10, 2025

"Noise Release" (a.k.a. plug:noise_rel) actually stays the same, with a value range from 1 to 5000.

In the upstream dsp.ttl from their release page (v1.5.18) I read:

plug:noise_rel
	a lv2:Parameter ;
	rdfs:label "Noise Release" ;
	rdfs:range atom:Float ;
	lv2:default 500 ;
	lv2:minimum 1 ;
	lv2:maximum 20000 .

In summary, you're right that there's a bit of awkwardness here and there, but one cannot say that I didn't at least put some thought into all these groupings :)

No, I think the grouping is quite reasonable. My thoughts were solely about the group names and their aestetics/readability. You know, it makes totally sense to have a group of the material parameters of resonator B, which excludes the tube models but I wouldn't necessarily call it "Resonator B - Material (excl. tube models)"

Otherwise it's great, I think!

@jpetso
Copy link
Author

jpetso commented Dec 14, 2025

Apologies for the very late response, I haven't forgotten about this but I'm not very consistent with my open source contributions right now. Thanks for being patient.

In the upstream dsp.ttl from their release page (v1.5.18) I read:

Yep, you're indeed right, the value changed from 5000 to 20000 and I missed this change in my three-way merge. Fixed in this latest upload.

I also added the "User File" option in "Mallet Type" as you suggested.

The group naming is still the same for now, still needs another go at the naming. If anyone else wants to take a shot, by all means give it a go, I don't have to hog this PR.

This commit introduces new parameters that were added since v1.3.4.
It also reorganizes the parameter groups somewhat, for compactness
and consistency (hopefully) in light of the new parameter entries.
@jpetso
Copy link
Author

jpetso commented Dec 14, 2025

No, I think the grouping is quite reasonable. My thoughts were solely about the group names and their aestetics/readability. You know, it makes totally sense to have a group of the material parameters of resonator B, which excludes the tube models but I wouldn't necessarily call it "Resonator B - Material (excl. tube models)"

Okay, in the interest of aesthetics, I took out the "(excl. tube models)" suffix. Perhaps in the future, Zynthian can support a .ttl property to hide or show certain groups and parameters only when certain enum values of another parameter are selected.

I also cut down on the number of dashes (or hyphens, really) in the group names. So "Mallet - Setup" is now "Mallet Setup" for example, and "Noise - Filter - Velocity Modulation" is now "Noise Filter - Velocity Modulation". I did not remove all of them, in the interest of readability ("Resonator A - Model": indicates more clearly that this is not "a model") and group continuation ("Noise Filter - Velocity Modulation" is kind of page #2 of "Noise Filter"). Edit: I also updated the PR description with the updated, less dashful names.

We could also consider just going with a simpler #1, #2, etc. for the multi-page groups that are currently named with dashes. Although I think using words is still better than just numbers, even though not 100% perfect.

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