//#region src/math.d.ts /** * clamp value between min and max */ export declare function clamp(value: number, min: number, max: number): number; /** * Normalize the value from 0 to 1, spreading the range evenly. * * This is the linear mapping and takes no curve of its own; a `Scale` builds * whatever curve it needs on top of it. */ export declare function normalizeValue(value: number, min: number, max: number): number; /** * Convert normalized values back to raw values, spreading the range evenly. * * The inverse of {@link normalizeValue}. */ export declare function rawValue(normalizedValue: number, min: number, max: number): number; /** * Put a value on the grid the caller asked for, rounding a half step upwards. * * The rounding is done on the quotient rather than by comparing the distance * to the two neighbours, because both of those carry error of their own. The * quotient is cleared of its artefact first: `0.15 / 0.1` is 1.4999999999999998, * and a value sitting exactly on a half step would otherwise fall to whichever * side the last bit happened to land on — 0.25 rounded up while 0.15 and 0.35 * rounded down. */ export declare function stepValue(value: number, step: number): number; export declare function toFixed(x: number, fractionDigits?: number): number; /** * Drop the binary artefact from a computed value. * * Arithmetic on doubles leaves debris in the last couple of digits, and it * accumulates: adding 0.1 to 5 twelve times gives 5.699999999999998 rather * than 5.7, and the display of a control shows exactly that. Rounding to the * digits a double can carry removes it, and adds nothing back — the value was * already the result of a calculation whose own error is that size or larger. * * This is not the same as rounding to a `step`. {@link stepValue} puts a value * on a grid the caller asked for and is a decision about the value; this only * removes what was never in the value to begin with. * * @param significantDigits how many digits to keep. The default is the only * one that is purely artefact removal; a smaller number starts discarding real * precision. * * @example * toPrecision(5.1 + 0.1) // 5.2, rather than 5.199999999999999 */ export declare function toPrecision(x: number, significantDigits?: number): number; export declare function radian(degree: number): number; export declare function degree(radian: number): number; export declare function mapValue(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number; export declare function dbToGain(db: number): number; export declare function gainToDb(gain: number): number; //#endregion //#region src/scales.d.ts /** * How a value is distributed across the travel of a control. * * `normalize` and `denormalize` are inverses of each other: the position is * 0 at `min` and 1 at `max`, and everything in between is up to the scale. * * `min` and `max` are arguments rather than baked into the scale, so a scale * holds no state and can be a module level constant. Passing the same object * on every render therefore costs nothing. * * @example * ```ts * exponentialScale.denormalize(0.5, 20, 20000) // 632.45… * ``` */ interface Scale { /** Value to its position on the travel, 0-1. */ normalize: (value: number, min: number, max: number) => number; /** Position on the travel, 0-1, back to a value. */ denormalize: (position: number, min: number, max: number) => number; } /** * Equal travel gives an equal change in value. * * The right default for anything already linear in perception: dB values, * pan, percentages, MIDI note numbers, semitones. */ export declare const linearScale: Scale; /** * The power law of JUCE's `NormalisableRange::skew`, applied to `value - min`. * * Use it when the value has to agree with a JUCE or iPlug2 parameter — a * plugin UI in a WebView, say, where the knob must sit exactly where the * host's automation curve puts it. {@link skewWithCenterValue} gives the * factor that places a chosen value at the middle of the travel. * * `skew < 1` gives the lower end more travel, `skew > 1` the upper end. * * For new designs prefer {@link exponentialScale} or {@link curveScale}: the * slope of this curve is either zero or infinite at `min`, so the bottom of * the range is a dead zone or jumps. * * @param skew the JUCE skew factor */ export declare function skewScale(skew: number): Scale; /** * The skew factor for {@link skewScale} that puts `centerValue` at the middle * of the travel — JUCE's `NormalisableRange::setSkewForCentre`. */ export declare function skewWithCenterValue(centerValue: number, min: number, max: number): number; /** * Equal travel gives an equal *ratio*, so an octave — or a percentage — takes * the same distance wherever it falls. * * The scale for frequency (a filter cutoff over 20-20000 Hz), free running * rates, and delay times. * * Requires `min` and `max` to be non-zero and of the same sign, since no * ratio reaches zero or crosses it. Use {@link curveScale} for a range that * starts at 0. */ export declare const exponentialScale: Scale; /** * An exponential bend that still passes exactly through `min` and `max`, so * unlike {@link exponentialScale} it works on a range that starts at 0 or * crosses it, and unlike {@link skewScale} its slope is neither zero nor * infinite at either end. * * The general purpose taper, and the same family as the curve of an envelope * segment (SuperCollider's `CurveWarp`). * * - `curve > 0` gives the lower end more travel — envelope times from 0 ms, * delay times, anything that wants fine control near the bottom * - `curve < 0` gives the upper end more travel — a volume fader over * -60..+6 dB that should be precise around 0 dB * - near 0 it is indistinguishable from {@link linearScale}, and is treated * as linear to avoid dividing by zero * * {@link curveWithCenterValue} gives the curve that places a chosen value at * the middle of the travel. * * @param curve how hard the curve bends, and in which direction */ export declare function curveScale(curve: number): Scale; /** * {@link skewScale} mirrored about the middle of the range, so both halves * bend the same way — JUCE's `symmetricSkew`. * * For a bipolar control whose centre matters: detune over -100..+100 cents, * or a bipolar filter envelope amount, where the fine adjustment is around 0 * rather than at either end. * * `skew < 1` gives the middle more travel, `skew > 1` the two ends. * * @param skew the JUCE skew factor */ export declare function symmetricSkewScale(skew: number): Scale; /** * The curve for {@link curveScale} that puts `centerValue` at the middle of * the travel — the counterpart of {@link skewWithCenterValue}. */ export declare function curveWithCenterValue(centerValue: number, min: number, max: number): number; /** * How a value is scaled: the range it lives in, how it is rounded, and how it * is distributed across the travel. * * `AxisOptions` of `@tremolo-ui/dom` extends this, so a drag and a * wheel / keyboard nudge run the same value pipeline. */ interface ValueRange { min: number; max: number; /** * Rounding applied to the value. Left unrounded when omitted. */ step?: number; /** * How the value is distributed across the travel. * * @default linearScale */ scale?: Scale; } //#endregion //#region src/midi.d.ts export declare const whiteKeys: readonly ["A", "B", "C", "D", "E", "F", "G"]; type WhiteKey = (typeof whiteKeys)[number]; export declare const noteKeys: readonly ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; type NoteKey = (typeof noteKeys)[number]; export declare function parseNoteName(noteName: string): { letter: WhiteKey; accidental: "#" | "##" | "b" | "bb" | ""; octave: number; }; /** * Convert noteName to noteNumber */ export declare function noteNumber(noteName: string): number; /** * Convert noteNumber to noteName * C-1 = 0 * G9 = 127 * @param noteNumber noteNumber */ export declare function noteName(noteNumber: number): `${NoteKey}${number}`; /** * Convert noteNumber to noteKey */ export declare function noteKey(noteNumber: number): NoteKey; /** * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3' */ export declare function isWhiteKey(note: number | string): boolean; /** * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3' */ export declare function isBlackKey(note: number | string): boolean; /** * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3' * @param detune [cent] * @param a4 A4 frequency [Hz] * @returns frequency [Hz] */ export declare function noteToFrequency(note: number | string, detune?: number, a4?: number): number; /** * Centre of the 14-bit pitch bend range: no bend. * * The range is not symmetric — 0 is 8192 below centre and 16383 is 8191 above * — so a wheel at rest reports exactly this rather than half of the maximum. */ export declare const PITCH_BEND_CENTER = 8192; /** * A 14-bit pitch bend (0-16383) as -1 to 1, with 0 at rest. * * Each side of the centre is divided by its own length, since there is one * more step below it than above: 0 gives -1 and 16383 gives 1 exactly. * Dividing both sides by 8192 would never quite reach 1. * * @param value pitch bend: 0 ~ 16383 */ export declare function normalizePitchBend(value: number): number; /** * Semitones above the root, for each supported scale. * * Every entry starts at 0 and stays inside one octave, so a scale is a set of * pitch classes rather than a set of notes: {@link inScale} compares against * it with the octave taken out. * * `ionian` and `aeolian` are the same sets as `major` and `naturalMinor`; both * spellings are here because both are what someone reaches for depending on * whether they are thinking in keys or in modes. */ export declare const scaleIntervals: { readonly major: readonly [0, 2, 4, 5, 7, 9, 11]; readonly naturalMinor: readonly [0, 2, 3, 5, 7, 8, 10]; readonly harmonicMinor: readonly [0, 2, 3, 5, 7, 8, 11]; readonly melodicMinor: readonly [0, 2, 3, 5, 7, 9, 11]; readonly ionian: readonly [0, 2, 4, 5, 7, 9, 11]; readonly dorian: readonly [0, 2, 3, 5, 7, 9, 10]; readonly phrygian: readonly [0, 1, 3, 5, 7, 8, 10]; readonly lydian: readonly [0, 2, 4, 6, 7, 9, 11]; readonly mixolydian: readonly [0, 2, 4, 5, 7, 9, 10]; readonly aeolian: readonly [0, 2, 3, 5, 7, 8, 10]; readonly locrian: readonly [0, 1, 3, 5, 6, 8, 10]; readonly majorPentatonic: readonly [0, 2, 4, 7, 9]; readonly minorPentatonic: readonly [0, 3, 5, 7, 10]; readonly blues: readonly [0, 3, 5, 6, 7, 10]; readonly wholeTone: readonly [0, 2, 4, 6, 8, 10]; readonly chromatic: readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; }; type ScaleName = keyof typeof scaleIntervals; /** * Whether a note belongs to a scale, regardless of the octave either sits in. * * @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3' * @param root the note the scale is built on, in the same two forms * * @example * ```ts * inScale('F#4', 'D3', 'major') // true * ``` */ export declare function inScale(note: number | string, root: number | string, name: ScaleName): boolean; /** * The notes of a scale, ascending from `root`. * * The octave above the root is not included: ask for more `octaves` instead, so * that concatenating the result of two calls does not repeat a note. * * @param root noteNumber: 0 ~ 127 or noteName e.g. 'C3' * @param octaves how many octaves to cover * * @example * ```ts * scaleNotes('C3', 'majorPentatonic') // [48, 50, 52, 55, 57] * ``` */ export declare function scaleNotes(root: number | string, name: ScaleName, octaves?: number): number[]; //#endregion //#region src/unit.d.ts /** * The SI prefixes {@link unitFormat} chooses between. * * Deliberately narrower than the full SI set: yocto through yotta are of no * use to an audio control, and every extra prefix is one more symbol `parse` * has to tell apart from a unit. */ type SIPrefix = 'p' | 'n' | 'µ' | 'm' | '' | 'k' | 'M' | 'G'; interface UnitFormatOptions { /** * The prefix the stored value is already in. * * A control that keeps milliseconds in `value` is `{ base: 'm' }` with a * unit of `'s'`: 1500 then displays as `1.5s`, and `parse` gives 1500 back. * * @default '' */ base?: SIPrefix; /** * Whether to scale the number and pick a prefix at all. * * Turn it off for anything that is not an SI quantity. dB, %, cents and * semitones do not take prefixes, and `-6dB` read as "-6 deci-B" is wrong * rather than merely unusual. * * @default true */ prefixes?: boolean; /** * Digits after the decimal point. The number is left as-is when omitted. */ digits?: number; /** * Text placed between the number and the unit. * @default '' */ separator?: string; } /** The `format` / `parse` pair a `NumberInput` takes. */ interface UnitFormatter { format: (value: number) => string; parse: (text: string) => number; } /** * Build the `format` and `parse` of a unit, as one pair. * * They are returned together because they have to agree: a `format` that * writes `1.23kHz` is only useful next to a `parse` that reads it back as * 1230. Spread the result into the input. * * @example * unitFormat('Hz') // 1234 -> '1.23kHz' * unitFormat('s', { base: 'm' }) // value in ms. 1500 -> '1.5s' * unitFormat('s', { base: 'm', digits: 2 }) // 1500 -> '1.50s' * unitFormat('dB', { prefixes: false, digits: 1 }) // -6.25 -> '-6.3dB' * * @example * */ export declare function unitFormat(unit: string, options?: UnitFormatOptions): UnitFormatter; //#endregion export type { NoteKey, SIPrefix, Scale, ScaleName, UnitFormatOptions, UnitFormatter, ValueRange, WhiteKey }; //# sourceMappingURL=index.d.cts.map