juce-framework / JUCE

JUCE is an open-source cross-platform C++ application framework for desktop and mobile applications, including VST, VST3, AU, AUv3, LV2 and AAX audio plug-ins.
https://juce.com
Other
6.3k stars 1.67k forks source link

[Bug]: LV2Parameter::getText() for enum value returns the label for a wrong scalePoint #1272

Open atsushieno opened 9 months ago

atsushieno commented 9 months ago

Detailed steps on how to reproduce the bug

JUCE AudioPluginHost shows wrong labels for an enumeration parameter on LV2 plugins.

For example, I use my own (modified) version of ADLplug FM synth emulator JUCE plugin, and try to choose chip type. There are 6 values like "MAME YM2612", "Nuked OPN2", "GENS 2.10 OPN2", ... but after the first item, AudioPluginHost shows the label for the last enumerated item repeatedly, like:

image

(The choice of the plugin is not important; any enumeration parameter results in the same.)

LV2 TTL for this particular case is defined as:

plug:emulator
    a lv2:Parameter ;
    rdfs:label "Emulator" ;
    rdfs:range atom:Float ;
    lv2:default 0 ;
    lv2:minimum 0 ;
    lv2:maximum 5    ;
    lv2:portProperty lv2:enumeration ;
    lv2:scalePoint [
        rdfs:label "MAME YM2612" ;
        rdf:value 0 ;
    ], [
        rdfs:label "Nuked OPN2" ;
        rdf:value 0.2 ;
    ], [
        rdfs:label "GENS 2.10 OPN2" ;
        rdf:value 0.4 ;
    ], [
        rdfs:label "<Reserved 3>" ;
        rdf:value 0.6 ;
    ], [
        rdfs:label "Neko Project II Kai OPNA" ;
        rdf:value 0.8 ;
    ], [
        rdfs:label "MAME YM2608" ;
        rdf:value 1 ;
    ] .

Code wise, when hosting an LV2 plugin, juce::lv2_host::LV2Parameter is instantiated when some parameter information is needed, and getText() is invoked when enum label text is needed:

        if (info.isEnum && ! info.scalePoints.empty())
        {
            // The normalised value might not correspond to the exact value of a scale point.
            // In this case, we find the closest label by searching the midpoints of the scale
            // point values.
            const auto index = std::distance (midPoints.begin(),
                                              std::lower_bound (midPoints.begin(), midPoints.end(), denormalised));
            jassert (isPositiveAndBelow (index, info.scalePoints.size()));
            return info.scalePoints[(size_t) index].label;
        }

This code seems to result in wrong index for denormalised value (which seems to correctly represent an index for scalePoints, and scalePoints contents seem correct as well, as long as I observed on my debugger).

midPoints are ranged between 0 <= x <= 1.0, while denormalised is an integer index to scalePoints, so there is a mismatch on the value range.

What is the expected behaviour?

The enum label (getText() return value) should be the corresponding text to the value for each float value (scale point).

Operating systems

macOS

What versions of the operating systems?

macOS Ventura 13.5.2.

Architectures

ARM, 64-bit

Stacktrace

No response

Plug-in formats (if applicable)

LV2

Plug-in host applications (DAWs) (if applicable)

JUCE AudioPluginHost

Testing on the develop branch

The bug is present on the develop branch

Code of Conduct

reuk commented 9 months ago

I think the problem is that the scale points in the generated .ttl should not be normalised, i.e. the default/minimum/maximum and scalepoint values for the parameter should all be in the same domain.

atsushieno commented 9 months ago

I agree to the idea that the scalepoints should be in the domain (no idea what is the source of those values), but since the .ttl content conforms to the LV2 specification the enum label texts should be correctly listed anyways.