Dear all,

sorry for stirring things up!

I am currently implementing a program for scanning soundfonts for optimizations, and one possible optimization is to use a modulator instead of a manual value list. To adapt a modulator to a value list, one must have a good understanding of the possibilities for tweaking the transfer function.

Most things in the soundfont specification are quite clear, but some are "underspecified".

The first topic is how the range of a controller is originally mapped onto the range from zero to one.

My naive assumption is that most transforms happen on the unit interval from zero to one and that the minimum value maps to zero and the maximum value maps to one.

However, if you read the specification for the standard modulator for channel pressure, it says that the value 0 is mapped to 0 (good!) and the value 127 is mapped to 127/128 (instead of 1??).

Even better is the default modulator for velocity to attenuation: a value of 0 does not exist (I understand the reason for that), so 1 is mapped to the maximum 127/128 (again not to 1) and 127 is mapped to 1/127 (since the domain is now one value shorter).

A 14-bit controller should have the same problem in principle, but 16383/16384 is an acceptable "one" for me...

Well, you cannot argue with a specification, and there may have been valid hardware-based reasons for that definition caused by the original soundcards. But it is quirky nontheless.

The second topic are the curves for "convex" and "concave". It is already mentioned in the FluidSynth code that the definition in the spec is not helpful at all. For concave, the formula

    output = log(sqrt(value^2)/(max value)^2)

is used which yields nonsensical values, regardless of whether these are the original controller values or whether they have already been transformed onto the interval [0..1].

But even the implementation in FluidSynth does not help me. It reads:

    return ((i == 0)
            ? 0
            : ((i == FLUID_VEL_CB_SIZE - 1)
                ? 1
: ((-200.0L * 2 / FLUID_PEAK_ATTENUATION) * gcem::log(((FLUID_VEL_CB_SIZE - 1) - i) / (FLUID_VEL_CB_SIZE - 1.0L)) / GCEM_LOG_10)
                ));

At least someone dares to return 0 and 1 for the left and right interval ends! But in between, I am not convinced how this fits into the original logic shown in the images, especially the scaling with -something/log10 looks suspicious. Great, there is a log function as in the original specification, but in my opinion log ruins everything ;-)

When I looked at the concave and convex pictures, I thought that they really look like circle quadrants (where typically x² + y² = 1) which could lead to the following functions:

        concave:   y = 1 - sqrt(1- x²)
        convex:    y = sqrt(1 - (1 - x²))

So we have the squares and square roots already mentioned in the specification function; hence this could therefore be a viable approach. The big advantage of a circle segment is that it is symmetrical, which is definitely not the case with a logarithm function.

I can understand that this may be considered an esoteric discussion, as we are mostly talking about minimal differences in modulation values. But I would like to have a solid mathematical foundation for my algorithms.

Can anyone help me here?


Best regards,
Prof. Spock


_______________________________________________
fluid-dev mailing list
fluid-dev@nongnu.org
https://lists.nongnu.org/mailman/listinfo/fluid-dev

Reply via email to