Audio Parameters Model
Learn how AudioUI's parameter model works, from value systems to scale functions and MIDI integration.
AudioUI uses a sophisticated parameter model that bridges the gap between user interface controls and audio processing. This model handles value conversion, quantization, scaling, and MIDI integration automatically.
Why Parameters Matter
Audio applications need to handle three different value domains:
- Real values: What your audio engine uses (e.g., 1000 Hz, -6 dB)
- Normalized values: What the UI uses internally (0.0 to 1.0)
- MIDI values: What external controllers send (0 to 127, or higher resolution)
The parameter model automatically converts between these domains, ensuring your UI always reflects the correct quantized state and handles MIDI input correctly.
The Tripartite Value System
AudioUI uses a three-domain value system with MIDI as the pivot point:
MIDI Value (The Source of Truth)
- Type: Integer
- Range: 0 to (2^Resolution - 1)
- Characteristics: Linear, discrete, quantized
- Role: Central pivot point for all conversions
Normalized Value (UI Reality)
- Type: Float
- Range: 0.0 to 1.0
- Characteristics: Linear with respect to control travel and MIDI values
- Usage: Used by visual components and host automation
Real Value (Audio Reality)
- Type:
number | boolean | string - Range: Defined by parameter type
- Characteristics: Can be logarithmic, exponential, discrete, or boolean
- Usage: What your application logic actually uses
Parameter Types
AudioUI supports three parameter types:
Continuous Parameters
Used for variable controls like volume, frequency, pan, etc.
import { createContinuousParameter } from "@cutoff/audio-ui-react";
import { Knob } from "@cutoff/audio-ui-react";
import { useState } from "react";
const cutoffParam = createContinuousParameter({
paramId: "cutoff",
label: "Cutoff",
min: 20,
max: 20000,
unit: "Hz",
scale: "log",
defaultValue: 1000,
});
export default function ContinuousParameterExample() {
const [value, setValue] = useState(1000);
return (
<Knob
parameter={cutoffParam}
value={value}
onChange={(e) => setValue(e.value)}
/>
);
}Boolean Parameters
Used for on/off controls like mute, solo, bypass.
import { createBooleanParameter } from "@cutoff/audio-ui-react";
import { Button } from "@cutoff/audio-ui-react";
import { useState } from "react";
const powerParam = createBooleanParameter({
paramId: "power",
label: "Power",
latch: true,
defaultValue: false,
});
export default function BooleanParameterExample() {
const [value, setValue] = useState(false);
return (
<Button
parameter={powerParam}
value={value}
onChange={(e) => setValue(e.value)}
/>
);
}Discrete Parameters
Used for selecting from a set of options like waveforms, filter types. The option definitions (value, label, optional midiValue) define the parameter structure; for how this relates to visual content (OptionView) in components, see Options vs OptionView.
import { createDiscreteParameter } from "@cutoff/audio-ui-react";
import { CycleButton } from "@cutoff/audio-ui-react";
import { useState } from "react";
const waveformParam = createDiscreteParameter({
paramId: "waveform",
label: "Waveform",
options: [
{ value: "sine", label: "Sine" },
{ value: "square", label: "Square" },
{ value: "sawtooth", label: "Saw" },
{ value: "triangle", label: "Tri" },
],
defaultValue: "sine",
});
export default function DiscreteParameterExample() {
const [value, setValue] = useState("sine");
return (
<CycleButton
parameter={waveformParam}
value={value}
onChange={(e) => setValue(e.value)}
/>
);
}Creating Parameters
Continuous Parameters
import { createContinuousParameter } from "@cutoff/audio-ui-react";
// Basic linear parameter
const volumeParam = createContinuousParameter({
paramId: "volume",
label: "Volume",
min: 0,
max: 100,
unit: "%",
defaultValue: 50,
});
// Logarithmic parameter (for frequency)
const frequencyParam = createContinuousParameter({
paramId: "frequency",
label: "Frequency",
min: 20,
max: 20000,
unit: "Hz",
scale: "log",
defaultValue: 1000,
});
// Bipolar parameter (for pan)
const panParam = createContinuousParameter({
paramId: "pan",
label: "Pan",
min: -100,
max: 100,
unit: "%",
bipolar: true,
defaultValue: 0,
});
// With step quantization
const attackParam = createContinuousParameter({
paramId: "attack",
label: "Attack",
min: 0,
max: 1000,
unit: "ms",
step: 1, // 1ms increments
defaultValue: 0,
});Boolean Parameters
import { createBooleanParameter } from "@cutoff/audio-ui-react";
// Toggle (latch) parameter
const powerParam = createBooleanParameter({
paramId: "power",
label: "Power",
latch: true,
defaultValue: false,
});
// Momentary parameter
const recordParam = createBooleanParameter({
paramId: "record",
label: "Record",
latch: false,
defaultValue: false,
});Discrete Parameters
import { createDiscreteParameter } from "@cutoff/audio-ui-react";
// Basic discrete parameter
const filterTypeParam = createDiscreteParameter({
paramId: "filterType",
label: "Filter Type",
options: [
{ value: "lowpass", label: "Low Pass" },
{ value: "bandpass", label: "Band Pass" },
{ value: "highpass", label: "High Pass" },
],
defaultValue: "lowpass",
});
// With MIDI values (structure is compatible with MIDI 2.0 Property Exchange valueSelect typeHint)
const waveformParam = createDiscreteParameter({
paramId: "waveform",
label: "Waveform",
options: [
{ value: "sine", label: "Sine", midiValue: 0 },
{ value: "square", label: "Square", midiValue: 42 },
{ value: "sawtooth", label: "Saw", midiValue: 84 },
{ value: "triangle", label: "Tri", midiValue: 127 },
],
defaultValue: "sine",
});Scale Functions
Scale functions transform how values are interpreted. They affect the control feel, not the step grid.
Linear Scale (Default)
Standard linear mapping. Used for pan, modulation depth, etc.
const panParam = createContinuousParameter({
paramId: "pan",
label: "Pan",
min: -100,
max: 100,
scale: "linear", // or omit (default)
defaultValue: 0,
});Logarithmic Scale
Used for volume/dB, frequency (musical intervals).
const volumeParam = createContinuousParameter({
paramId: "volume",
label: "Volume",
min: -60,
max: 6,
unit: "dB",
scale: "log",
defaultValue: 0,
});
const frequencyParam = createContinuousParameter({
paramId: "frequency",
label: "Frequency",
min: 20,
max: 20000,
unit: "Hz",
scale: "log",
defaultValue: 1000,
});Exponential Scale
Used for envelope curves, some filter parameters.
const attackParam = createContinuousParameter({
paramId: "attack",
label: "Attack",
min: 0,
max: 1000,
unit: "ms",
scale: "exp",
defaultValue: 0,
});Bipolar Mode
Bipolar parameters are centered around zero. This affects both default values and visual rendering.
const panParam = createContinuousParameter({
paramId: "pan",
label: "Pan",
min: -100,
max: 100,
bipolar: true, // Centers at zero
defaultValue: 0, // Defaults to center (0.5 normalized)
});
// Visual rendering:
// - ValueRing starts at 12 o'clock (center)
// - ValueStrip fills from center
// - Controls show centered indicatorImportant: The bipolar flag is independent of the numeric range. A parameter can be bipolar even if min >= 0.
Step Quantization
The step parameter defines a linear grid in the real value domain, regardless of scale type.
When Step Makes Sense
- Linear scales: Always works well
- Log scales with linear units: Works when the unit is linear (e.g., dB)
- Exp scales with linear units: Works when the unit is linear (e.g., milliseconds)
// Volume with log scale: step makes sense (linear dB increments)
const volumeParam = createContinuousParameter({
paramId: "volume",
label: "Volume",
min: -60,
max: 6,
step: 0.5, // 0.5 dB increments
unit: "dB",
scale: "log",
});
// Attack time with exp scale: step makes sense (linear ms increments)
const attackParam = createContinuousParameter({
paramId: "attack",
label: "Attack",
min: 0,
max: 1000,
step: 1, // 1 ms increments
unit: "ms",
scale: "exp",
});When Step May Not Make Sense
For frequency (Hz) with log scale, linear Hz steps don't align with musical perception. Consider omitting step or using a very small value:
// Frequency with log scale: step may not make sense
const freqParam = createContinuousParameter({
paramId: "frequency",
label: "Frequency",
min: 20,
max: 20000,
// No step - allow smooth control across logarithmic range
unit: "Hz",
scale: "log",
});MIDI Resolution
MIDI resolution determines the quantization grid. Higher resolution provides finer control.
// Standard 7-bit MIDI (0-127)
const standardParam = createContinuousParameter({
paramId: "mod",
label: "Mod Wheel",
min: 0,
max: 127,
midiResolution: 7, // 7-bit (0-127)
defaultValue: 0,
});
// High resolution (internal precision)
const preciseParam = createContinuousParameter({
paramId: "cutoff",
label: "Cutoff",
min: 20,
max: 20000,
midiResolution: 32, // High resolution for internal precision
defaultValue: 1000,
});Available resolutions: 7, 8, 14, 16, 32, 64.
Integration Examples
Frequency Control
import { createContinuousParameter } from "@cutoff/audio-ui-react";
import { Knob, frequencyFormatter } from "@cutoff/audio-ui-react";
import { useState } from "react";
const cutoffParam = createContinuousParameter({
paramId: "cutoff",
label: "Cutoff",
min: 20,
max: 20000,
unit: "Hz",
scale: "log",
defaultValue: 1000,
});
export default function FrequencyControlExample() {
const [value, setValue] = useState(1000);
return (
<Knob
parameter={cutoffParam}
value={value}
onChange={(e) => setValue(e.value)}
valueFormatter={frequencyFormatter}
/>
);
}Volume Control
import { createContinuousParameter } from "@cutoff/audio-ui-react";
import { Slider } from "@cutoff/audio-ui-react";
import { useState } from "react";
const volumeParam = createContinuousParameter({
paramId: "volume",
label: "Volume",
min: -60,
max: 6,
unit: "dB",
scale: "log",
step: 0.5,
defaultValue: 0,
});
export default function VolumeControlExample() {
const [value, setValue] = useState(0);
return (
<Slider
parameter={volumeParam}
value={value}
onChange={(e) => setValue(e.value)}
orientation="vertical"
/>
);
}Waveform Selection
import { createDiscreteParameter } from "@cutoff/audio-ui-react";
import { CycleButton, OptionView } from "@cutoff/audio-ui-react";
import { useState } from "react";
const waveformParam = createDiscreteParameter({
paramId: "waveform",
label: "Waveform",
options: [
{ value: "sine", label: "Sine" },
{ value: "square", label: "Square" },
{ value: "sawtooth", label: "Saw" },
{ value: "triangle", label: "Tri" },
],
defaultValue: "sine",
});
export default function WaveformSelectionExample() {
const [value, setValue] = useState("sine");
return (
<CycleButton
parameter={waveformParam}
value={value}
onChange={(e) => setValue(e.value)}
>
<OptionView value="sine">Sine</OptionView>
<OptionView value="square">Square</OptionView>
<OptionView value="sawtooth">Saw</OptionView>
<OptionView value="triangle">Tri</OptionView>
</CycleButton>
);
}Ad-Hoc vs Parameter Model
Components support two modes:
Ad-Hoc Mode
Simple props for quick prototyping:
<Knob
value={value}
onChange={(e) => setValue(e.value)}
min={20}
max={20000}
label="Cutoff"
/>Parameter Model Mode
Full parameter definition for production use:
const cutoffParam = createContinuousParameter({
paramId: "cutoff",
label: "Cutoff",
min: 20,
max: 20000,
unit: "Hz",
scale: "log",
defaultValue: 1000,
});
<Knob
parameter={cutoffParam}
value={value}
onChange={(e) => setValue(e.value)}
/>Benefits of Parameter Model:
- Consistent value handling
- Automatic MIDI integration
- Proper quantization
- Scale function support
- Better integration with audio engines
Component Compatibility
Not all components support all parameter types:
| Component | Supported Types | Notes |
|---|---|---|
| Knob | continuous | Variable control |
| Slider | continuous | Linear fader |
| Button | boolean | Toggle or momentary |
| CycleButton | discrete | Option selection |
Components validate parameter types and will error if given an incompatible parameter.
Best Practices
- Use parameter model for production: Provides better integration and consistency
- Choose appropriate scales: Log for frequency/volume, linear for pan/modulation
- Set step when it makes sense: Use for integer values or meaningful increments
- Use bipolar for centered controls: Pan, balance, etc.
- Set MIDI resolution appropriately: Higher for internal precision, standard (7-bit) for MIDI CC
- Provide units: Helps with formatting and user understanding
Next Steps
- Read Options vs OptionView for the distinction between option definitions and OptionView visual content in discrete controls
- Learn about Interaction System for user input handling
- Explore Control Primitives for building custom parameter-based controls
- Check out Default Components Styling for theming default components
- See component documentation for parameter-specific features