pnpm add @audio-ui/core
import { createEnvelopeController, CURVE_EPSILON, CURVE_HIT_TOLERANCE, CURVE_RATE, CURVE_SAMPLES, /* … */ } from "@audio-ui/core/envelope";| Adapters | @audio-ui/react/envelope, @audio-ui/elements/envelope |
|---|---|
| Used by | arrangement, dawproject, use-arrangement |
| Export | Kind | What it is |
|---|---|---|
createEnvelopeController | function | An envelope's behavior with no view attached: its points, held or controlled, every way of moving, adding, removing and bending them, and what it looks like as a snapshot. |
EnvelopeChangeEventDetails | type | What onValueChange receives with each change: the index of the point or segment, the reason, the event behind it, and cancel() to refuse the change. |
EnvelopeChangeEventReason | type | Why the points changed: "drag", "keyboard", "add", "remove" or "curve" (a bend). |
EnvelopeCommitEventDetails | type | What onValueCommitted receives once a gesture ends: the index of the point or segment, the reason and the event behind it. |
EnvelopeCommitEventReason | type | Why the points were committed: the same reasons as a change. |
EnvelopeController | interface | What createEnvelopeController returns: the snapshot to draw from, and a call for each gesture and key that moves, adds, removes, bends or straightens. |
EnvelopeOptions | interface | The options of createEnvelopeController: the points, held or controlled, how few and how many there may be, the grid they snap to, the keyboard step, the formatting and the callbacks. |
EnvelopeSnap | type | A grid step in normalized units: one for both axes, or one for each (0 leaves that axis free). |
EnvelopeSnapshot | interface | What a view draws an envelope from. |
Breakpoint | type | A point of the ramp, and how the segment leaving it bends: curve from -1 to 1, 0 or unset for a straight line. |
CURVE_EPSILON | const | A curve this close to flat is taken as straight, and dropped. |
CURVE_HIT_TOLERANCE | const | How far from a curve, as a share of the graph's height, a press still counts as on it. |
CURVE_RATE | const | How far a segment can bend: the rate of its exponential at the ends of -1…1, which puts the middle of the segment at 0.4% of its rise (or 99.6%). |
CURVE_SAMPLES | const | Straight lines between samples of a bent segment: where its drawing starts, before it adds vertices where the bend is tight. |
CURVE_STEP_MULTIPLIER | const | Keyboard step for a segment's curve, as a multiple of step. |
clampToNeighbors | function | Clamp a candidate point's x between its neighbors in base (so points can never cross or reorder) and y to [0,1]. |
curveForMidpoint | function | The curve that puts a segment's midpoint at height y, or null when the segment is flat and has nothing to bend. |
curveKeyDelta | function | How far a key bends a segment, or null for a key that does not: up lifts the handle, so a rising segment bends one way and a falling one the other. |
curveSamples | function | Where along a bent segment to put the vertices of its drawing: evenly in length, and evenly in height, so a steep stretch is sampled as finely as a flat one and does not turn into a corner. |
insertPoint | function | point added to base. |
LARGE_STEP_MULTIPLIER | const | Shift + arrows move this many steps at once. |
levelAt | function | Where a bent segment is at a share t of its length, as a level. |
polylinePoints | function | Polyline coordinates: flat lead-in from x=0 to the first point, the points themselves with each bent segment sampled between them, flat lead-out from the last point to x=1. |
roundCoord | function | One decimal place is plenty of precision for a 0-100 SVG coordinate, and keeps the points string short during drag. |
samePoints | function | Whether two lists hold the same points, curves included, in the same order. |
sanitizePoints | function | The points kept on the graph: each within 0 and 1, a curve within -1 and 1 (dropped when it is flat), in order along x. |
segmentNear | function | The segment that has the point (x, y) on it or close to it, or null: one that is not flat, since a flat segment has nothing to bend. |
segmentShape | function | Where along a bent segment a share t of its length lands, as a share of its rise: the exponential of an analog envelope stage, (e^(k·t) - 1) / (e^k - 1) with k the curve times CURVE_RATE. |
snapTo | function | Rounds a normalized coordinate to the nearest multiple of step; a step of 0 leaves it. |
sortPoints | function | The points in order along x. |
Usage
An envelope is a list of breakpoints, each a point from 0 to 1 on both axes, with a curve from -1 to 1 for how the segment leaving it bends (0, or none, for a straight line). The controller holds that list and every gesture on it, and follows the contract every controller shares:
import { createEnvelopeController } from "@audio-ui/core/envelope";
const envelope = createEnvelopeController({
defaultValue: [
{ x: 0, y: 0 },
{ x: 0.1, y: 1, curve: -0.5 },
{ x: 0.4, y: 0.6 },
{ x: 1, y: 0 },
],
minPoints: 2,
snap: 0.05,
onValueCommitted: (points) => save(points),
});
const unsubscribe = envelope.subscribe(() => draw(envelope.getSnapshot()));| Option | Meaning |
|---|---|
value, defaultValue | The points, controlled, or the uncontrolled start (an ADSR-like shape unless given) |
minPoints, maxPoints | How few (2 by default) and how many points it may have |
snap | A grid step for both axes, or { x, y } for each, 0 leaving an axis free; Cmd or Ctrl held skips it |
step | What an arrow key moves a point by, 0.01 by default |
format, locale | How formatPoint writes a point for a screen reader |
disabled | Takes every input away |
The input
Each gesture has its own call, given the element it happens on and the graph's element, which gives the coordinates:
pressPoint(index, event, handle, graph)drags a point, or removes it with Alt held; a point stays between its neighbours, so points never cross.pressSegment(index, event, handle, graph)drags a segment's handle to bend it, andpressBend(event, graph), an Alt + press on the graph, bends the segment under the pointer.addPointAt({ x, y }, event)adds a point and returns its index, ornullatmaxPoints;removePoint(index, event)takes one away, down tominPoints.straightenSegment(index, event)andstraightenAt({ x, y }, event)make a bent segment straight again.keyPoint(index, event)moves a focused point with the arrows, Shift for a larger step, and removes it with Delete or Backspace;keySegment(index, event)bends a focused segment with Up and Down and straightens it with Delete. Each returnstruewhen it handled the key.
The snapshot holds the points and what is being dragged (draggingIndex, draggingSegmentIndex), with blockedIndex and isAddBlocked for a moment after a removal or an addition was refused, for a view to show it. dispose() releases that feedback's timers when the view goes.
Geometry
The rest of the module draws the envelope: polylinePoints is the polyline through the points with each bent segment sampled, levelAt the level along a bent segment, curveForMidpoint the curve that puts a segment's middle at a height, and segmentNear the segment under a point. sortPoints, sanitizePoints, clampToNeighbors and insertPoint keep a list valid.
The Envelope draws this controller in React.