CUI

Components / Pickers

Knob, fader and level meter

A rotary knob, a vertical fader and a level meter: the controls of audio, synth and mixer panels.

Functions: cui_fader, cui_knob, cui_level_meter

Knob, fader and level meter

Usage

static float cutoff = 2400, pan, gain_db;
static const float marks[] = { 6, 0, -6, -12, -24, -48 };

cui_knob(ui, "Cutoff", &cutoff, 20, 20000, .unit = "Hz", .log_scale = true, .reset = 1000);
cui_knob(ui, "Pan", &pan, -1, 1, .bipolar = true, .format = "%+.2f");
cui_fader(ui, "Vocals", &gain_db, -48, 6, .unit = "dB", .ticks = marks, .tick_count = 6,
          .meter = peaks, .meter_channels = 2);           /* peaks: linear, 1.0 = 0 dBFS */
cui_level_meter(ui, "master", peaks, 2, .horizontal = true, .segmented = true, .scale = true);

A knob turns by dragging up and down: 200 points cover the whole range, and holding Shift makes it ten times finer from wherever the pointer is. A ring shows the value against a 270 degree track; bipolar grows it from the middle for pan or EQ gain, and log_scale makes equal turns multiply the value, which is what frequency and time knobs need. The value and the label sit under the knob in a column as wide as the longest value, so a row of knobs never shifts while one turns.

A fader is a track with a thumb: press the thumb to drag it from where it is, or press the track and the thumb jumps there first. ticks puts a labelled scale beside the track, and meter draws level bars right next to it on the fader's own dB scale, so a channel strip is a single call. Faders are vertical unless horizontal is set.

Both take step, format and unit, and both answer the same keys and gestures once focused: the arrows step (1% of the range without a step), Shift+arrows and Page Up and Page Down step ten times as far, Home and End go to the ends, and double-click, Delete or Backspace go back to reset, which is 0 kept within the range (0 dB, the middle of -1 to 1, or the minimum of 20 to 20000). The wheel turns a control only after it has been clicked or tabbed to, so scrolling a page past a mixer never moves a level.

The level meter takes linear sample peaks, where 1.0 is 0 dBFS. Bars jump up to each new peak and fall back at decay dB per second (20 by default), the peak mark holds for hold seconds (1.5) and then falls, and a clip light at the loud end stays on after a peak reaches 0 dBFS until it is clicked. The colors run green to -18 dB, amber to -6 dB and red above. scale labels the dB marks that fit, segmented draws LED segments, and min_db, max_db, length, thickness and horizontal set the shape. Up to eight channels share one meter. Each control is a single painted node, so a mixer with dozens of strips costs little more than one.

Keyboard

  • A knob turns by dragging up and down: 200 points cover the whole range, and holding Shift makes it ten times finer from wherever the pointer is.
  • Both take step, format and unit, and both answer the same keys and gestures once focused: the arrows step (1% of the range without a step), Shift+arrows and Page Up and Page Down step ten times as far, Home and End go to the ends, and double-click, Delete or Backspace go back to reset, which is 0 kept within the range (0 dB, the middle of -1 to 1, or the minimum of 20 to 20000).
  • Up to eight channels share one meter.

Options of cui_fader

OptionTypeDefaultDescription
.labelconst char *under a vertical fader, before a horizontal one; the id when NULL
.formatconst char *"%.1f"default "%.1f"
.unitconst char *A unit shown after the value.
.lengthfloat160track length, default 160
.horizontalboolthe track runs left to right, with the label before it, instead of bottom to top
.stepfloatThe amount one step changes the value by.
.resetfloat0 (clamped to the range)double-click value, default 0 (clamped to the range)
.ticksconst float *values to mark and label beside the track, e.g. dB marks; at most 32
.tick_countintentries in ticks
.meterconst float *linear sample peaks, one per channel, for a level meter beside the track; NULL for none
.meter_channelsintentries in meter, up to 8; a dB fader's range sets the meter's scale
.hide_valueboolHides the value's text.
.disabledboolGreyed out, and takes no input.

Options of cui_knob

OptionTypeDefaultDescription
.labelconst char *under the knob; the id when NULL
.formatconst char *"%.0f"printf format for the value, default "%.0f"
.unitconst char *after the value, e.g. "Hz" or "dB"
.sizefloat48diameter, default 48
.stepfloat0 = continuous (keys move 1% of the range)
.resetfloat0 (clamped to the range)double-click value, default 0 (clamped to the range)
.bipolarboolthe arc grows from the middle: pan, EQ gain
.log_scaleboolequal turns multiply the value: frequency, time (min > 0)
.hide_valueboolHides the value's text.
.disabledboolGreyed out, and takes no input.
.colorcui_colorthe control colorthe value arc, default the control color

Options of cui_level_meter

OptionTypeDefaultDescription
.min_dbfloat-60 and +6default -60 and +6
.max_dbfloat-60 and +6default -60 and +6
.lengthfloat160default 160
.thicknessfloat6each channel's bar, default 6
.horizontalboolbars run left to right instead of bottom to top
.scalebooldB labels beside the bars
.segmentedboolLED segments instead of a solid bar
.holdfloat1.5, < 0 nonepeak hold, seconds; default 1.5, < 0 none
.decayfloat20fall rate, dB per second; default 20

Related

  • Emoji picker: A searchable grid of every emoji in nine groups, with the recently used ones first.
  • Ruler and histogram sliders: Two sliders that show context: a ruler that slides under a fixed marker, and a range slider drawn over the distribution of the data.
  • Wheel picker and time dial: Touch-style pickers: drums of options you spin, and a clock face for setting a time.
  • Time picker: Picks a time of day from scrolling columns of hours and minutes.