Parameter Display Text

Let a node say what its numbers mean

Parameter Display Text

A tracker parameter is a byte. The parameter window draws that byte, so a filter cutoff reads 176 and an oscillator model reads 3.

A processor can do better. Override one method, return a short string, and the window draws the string instead.

Column Without With
Model 1 Supersaw
Cutoff 176 2.4 kHz
Osc 2 Level 0 off
Osc 2 Tune 128 +0.0 st

The method

QString parameterDisplayText(int parameterIndex, Value value) const override;

Return an empty string to keep the number. That is the default, so a processor that does not override this behaves exactly as it does today.

The three rules

Return text, and only text. Not a colour, not a widget, not a layout. The view draws. The processor describes. That boundary is what lets the method be tested with no window, no engine and no audio device.

Keep it pure. Take the index and the value, return a string. Do not read engine state and do not write anything. The window calls this while the audio block drive runs on the same thread, so a method that read live state would be reading a value in the middle of a change.

Never call it from process(). Building a string allocates. The block drive must not allocate, because an unpredictable block time is heard later as a dropped buffer. The parameter window is the only caller, and it calls only when the value has changed.

A worked example

Take the oscillator level. Zero means the slot is disabled, which is a different statement from "quiet", so zero gets its own word:

case ColOsc2Level:
    return (raw == 0) ? QStringLiteral("off")
        : QStringLiteral("%1%").arg(u() * 100.0, 0, 'f', 0);

This is the case authors get wrong. 0% and off look alike and mean different things to a person reading the instrument.

Show the map the instrument actually uses

A cutoff knob is exponential. A time knob is squared. If the text is computed from a different formula than the sound, the two drift apart and the text becomes a lie that is hard to catch.

Call the same helper the audio path calls:

case ColCutoff: {
    const double hz = 40.0 * std::pow(400.0, u());
    return (hz >= 1000.0)
        ? QStringLiteral("%1 kHz").arg(hz / 1000.0, 0, 'f', 1)
        : QStringLiteral("%1 Hz").arg(hz, 0, 'f', 0);
}

Say nothing about a no-value

Every byte column in the tracker uses 0xFF to mean "this row says nothing". That is not a value, so do not describe it:

if (parameterIndex != ColNote && raw == BYTE_NO_VALUE) {
    return QString();
}

The same collision has a second consequence worth knowing: because the maximum byte and the no-value sentinel are the same number, a slider can never reach 0xFF. The parameter window caps its sliders one step below it.

Where to look

MonosynthProcessor::parameterDisplayText is the reference implementation. It covers every hard case in one processor: an enumeration, a unit whose prefix changes, a value squared across its range, a signed value around a centre, and a zero with its own word.

In this section
Topics
reference media audio tracker parameters
See also