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

Modulation Depth Range #1355

Open
spessasus opened this issue Jul 21, 2024 · 3 comments
Open

Modulation Depth Range #1355

spessasus opened this issue Jul 21, 2024 · 3 comments

Comments

@spessasus
Copy link
Contributor

spessasus commented Jul 21, 2024

Is your feature request related to a problem?

This RPN is not implemented in fluidsynth.
This document, section 3.4.4

Expected behavior

this MIDI file should have more and more modulation range depth with each note played.

Describe the solution you'd like

Since there's no Modulation Sensitivity (or anything similar) source in the sf2 modulator source enum, we have to give it special treatment.

My proposed solution is to use a modulation multiplier.

The sf2 spec assumes 50 cents depth by default, so when the RPN gets called, calculate a modulation multiplier by dividing the target depth range by 50. Then when running the DSP chain, simply multiply vibLfoToPitch or modLfoToPitch (or both) by that amount.

That way, if the soundfont creator wanted a more subtle vibrato effect on a given instrument (25 cents for example) and the MIDI composer would want to double the modulation depth (by setting it via RPN to 100 cents, which is the double of MIDI spec default), the final depth would be 50 cents, so double the initial depth, instead of suddenly jumping from 25 cents to 100.

Describe alternatives you've considered

Another solution could be finding all modulators that use mod wheel as source and vibLfoToPitch as destination and overriding the amount with the set range, but I think it could be quite destructive because of the problem mentioned above.

Additional context

My implementation of the solution 1

@derselbst
Copy link
Member

When implementing this feature, we should consider introducing a new general controller flag, like FLUID_MOD_MODWHEELSENS, because just like pitch wheel sens, mod wheel depth is also an RPN. Doing so would allow us to add a default modulator for this purpose.

Reading through the RPN, it suggests that it should work together with CC1 Modulation Depth controller. This does have a default modulator in place. Potentially this existing default modulator could be modified, by adding a second source for the new FLUID_MOD_MODWHEELSENS, similar as it's done for the pitch:

fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */
FLUID_MOD_GC /* CC =0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_BIPOLAR /* P=1 */
| FLUID_MOD_POSITIVE /* D=0 */
);
fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */
FLUID_MOD_GC /* CC=0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
);
/* Also see the comment in gen.h about GEN_PITCH */
fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */
fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */

On the other hand, changing that existing modulator may cause incompatibilities when existing implementations try to override that modulator. Probably adding a new default modulator would be safer.

What would be the maximum default amount in cents? The RPN suggests a minimum of 600. That sounds like a reasonable proposal. Implementations are free to override the modulator, if they need a bigger amount.

Another solution could be finding all modulators that use mod wheel as source and vibLfoToPitch as destination and overriding the amount with the set range, but I think it could be quite destructive because of the problem mentioned above.

This would also be a nice solution, possibly an even more generic one. However, I would restrict this to that single default modulator for CC1, since the RPN suggests they work hand in hand.

fluid_mod_set_source1(&default_mod2viblfo_mod, MODULATION_MSB, /* Index=1 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
);
fluid_mod_set_source2(&default_mod2viblfo_mod, 0, 0); /* no second source */
fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */
fluid_mod_set_amount(&default_mod2viblfo_mod, 50);

@spessasus
Copy link
Contributor Author

On the other hand, changing that existing modulator may cause incompatibilities when existing implementations try to override that modulator. Probably adding a new default modulator would be safer.

That's a bad idea IMO. Some soundfonts ( for example the one attached to #1059 ) like to disable the stock vib lfo and use mod lfo for vibrato instead. Adding a new modulator would result in a messed up vibrato since the new modulator using mod wheel sensitivity would not get disabled. And changing the current modulator would break things too since modulators disabling it won't be considered "identical". The proposed solution of an internal depth multiplier will respond to all of the modulators.

Or an even simpler solution: Apply the multiplier to the CC itself! For example if the depth is 100 cents, it's twice the default, so multiplier is too. And then if CC1 is set to 127, it's effectively interpreted as 254, leading to increased depth on all modulators, no matter what destination they use. I don't know if fluid is able to handle CC values above 127, but if it is, then that would be the perfect solution IMO, because:

@spessasus
Copy link
Contributor Author

Alright, to sum it up:

Solution requirements

The solution must:

  • work with the stock modulator with accurate cent depth
  • work with the stock modulator being changed. The value must adjust accordingly
  • work with the stock modulator disabled and cc1 being linked to something else (like modLfo for example)

Proposed solution v2

Here's my solution which meets all the requirements:

  • On Modulation Depth RPN, divide the value by 50 cents and store it somewhere in the channel object as modulationMultiplier.
  • when modulating a voice (whether at the beginning or on a cc change): if the source (or secondary source) is CC#1, mutliply it by the modulationMultiplier AFTER transforming.
  • For example if RPN sets depth to 100 cents, in the uni positive linear transform and value of 127, the final source value will be linearPositiveUnipolarTransform(127 / 127) * 2 = 2.

This meets all of our requirements:

  • stock modulator: depth of 100 means multiplier of 2, leading to the final vibLfoToPitch being 100 cents.
  • overriden modulator (25 cents for example): depth of 100 means a multiplier of 2, leading to the final vibLfoToPitch being 100 cents. Creator of the sf wanted a more subtle vibrato so here it is.
  • disabled and new modulator (for example vibLfo being disabled and modLfo replacing it): depth of 100 means multiplier of 2, leading to the final modLfoToPitch being 100 cents, just like with the regular modulator.

Should be relatively easy to implement (forget that comment about multiplying the controller value, this will work too and won't require promoting cc table to int).

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

No branches or pull requests

2 participants