@tremolo-ui/functions 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +786 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +445 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +445 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +766 -37
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +45 -3
- package/src/math.ts +64 -36
- package/src/midi.ts +114 -12
- package/src/modifiers.ts +141 -0
- package/src/piano.ts +177 -0
- package/src/scales.ts +338 -0
- package/src/unit.ts +204 -0
- package/src/util.ts +1 -36
- package/src/types.ts +0 -4
package/dist/index.d.ts
CHANGED
|
@@ -4,16 +4,57 @@
|
|
|
4
4
|
*/
|
|
5
5
|
declare function clamp(value: number, min: number, max: number): number;
|
|
6
6
|
/**
|
|
7
|
-
* Normalize the value from 0 to 1
|
|
7
|
+
* Normalize the value from 0 to 1, spreading the range evenly.
|
|
8
|
+
*
|
|
9
|
+
* This is the linear mapping and takes no curve of its own; a `Scale` builds
|
|
10
|
+
* whatever curve it needs on top of it.
|
|
8
11
|
*/
|
|
9
|
-
declare function normalizeValue(
|
|
12
|
+
declare function normalizeValue(value: number, min: number, max: number): number;
|
|
10
13
|
/**
|
|
11
|
-
* Convert normalized values back to raw values.
|
|
14
|
+
* Convert normalized values back to raw values, spreading the range evenly.
|
|
15
|
+
*
|
|
16
|
+
* The inverse of {@link normalizeValue}.
|
|
17
|
+
*/
|
|
18
|
+
declare function rawValue(normalizedValue: number, min: number, max: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Put a value on the grid the caller asked for, rounding a half step upwards.
|
|
21
|
+
*
|
|
22
|
+
* The rounding is done on the quotient rather than by comparing the distance
|
|
23
|
+
* to the two neighbours, because both of those carry error of their own. The
|
|
24
|
+
* quotient is cleared of its artefact first: `0.15 / 0.1` is 1.4999999999999998,
|
|
25
|
+
* and a value sitting exactly on a half step would otherwise fall to whichever
|
|
26
|
+
* side the last bit happened to land on — 0.25 rounded up while 0.15 and 0.35
|
|
27
|
+
* rounded down.
|
|
12
28
|
*/
|
|
13
|
-
declare function rawValue(normalizedValue: number, min: number, max: number, skew?: number): number;
|
|
14
|
-
declare function skewWithCenterValue(centerValue: number, min: number, max: number): number;
|
|
15
29
|
declare function stepValue(value: number, step: number): number;
|
|
16
30
|
declare function toFixed(x: number, fractionDigits?: number): number;
|
|
31
|
+
/**
|
|
32
|
+
* The significant decimal digits a double actually carries. A double holds a
|
|
33
|
+
* little under 16, so anything past this is the binary representation showing
|
|
34
|
+
* through rather than information.
|
|
35
|
+
*/
|
|
36
|
+
declare const SIGNIFICANT_DIGITS = 15;
|
|
37
|
+
/**
|
|
38
|
+
* Drop the binary artefact from a computed value.
|
|
39
|
+
*
|
|
40
|
+
* Arithmetic on doubles leaves debris in the last couple of digits, and it
|
|
41
|
+
* accumulates: adding 0.1 to 5 twelve times gives 5.699999999999998 rather
|
|
42
|
+
* than 5.7, and the display of a control shows exactly that. Rounding to the
|
|
43
|
+
* digits a double can carry removes it, and adds nothing back — the value was
|
|
44
|
+
* already the result of a calculation whose own error is that size or larger.
|
|
45
|
+
*
|
|
46
|
+
* This is not the same as rounding to a `step`. {@link stepValue} puts a value
|
|
47
|
+
* on a grid the caller asked for and is a decision about the value; this only
|
|
48
|
+
* removes what was never in the value to begin with.
|
|
49
|
+
*
|
|
50
|
+
* @param significantDigits how many digits to keep. The default is the only
|
|
51
|
+
* one that is purely artefact removal; a smaller number starts discarding real
|
|
52
|
+
* precision.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* toPrecision(5.1 + 0.1) // 5.2, rather than 5.199999999999999
|
|
56
|
+
*/
|
|
57
|
+
declare function toPrecision(x: number, significantDigits?: number): number;
|
|
17
58
|
declare function integerPart(x: number | string): string | undefined;
|
|
18
59
|
declare function decimalPart(x: number | string): string | undefined;
|
|
19
60
|
declare function radian(degree: number): number;
|
|
@@ -22,6 +63,224 @@ declare function mapValue(value: number, inMin: number, inMax: number, outMin: n
|
|
|
22
63
|
declare function dbToGain(db: number): number;
|
|
23
64
|
declare function gainToDb(gain: number): number;
|
|
24
65
|
//#endregion
|
|
66
|
+
//#region src/modifiers.d.ts
|
|
67
|
+
/**
|
|
68
|
+
* Options for setting the amount of keyboard and mouse wheel changes.
|
|
69
|
+
*/
|
|
70
|
+
type InputEventOption = readonly ['normalized' | 'raw', number];
|
|
71
|
+
/**
|
|
72
|
+
* A modifier key that can carry an amount of its own.
|
|
73
|
+
*
|
|
74
|
+
* `ctrl` and `meta` are kept apart rather than folded into one "command" key:
|
|
75
|
+
* a plugin UI that mirrors a desktop host usually wants the same physical key
|
|
76
|
+
* on every platform, not the platform's own convention.
|
|
77
|
+
*/
|
|
78
|
+
type Modifier = 'shift' | 'alt' | 'ctrl' | 'meta';
|
|
79
|
+
/** The modifier flags of a `WheelEvent` or a `KeyboardEvent`. */
|
|
80
|
+
interface ModifierState {
|
|
81
|
+
shiftKey: boolean;
|
|
82
|
+
altKey: boolean;
|
|
83
|
+
ctrlKey: boolean;
|
|
84
|
+
metaKey: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** One setting per modifier key, with `default` for none of them. */
|
|
87
|
+
type ModifierSetting = number | InputEventOption;
|
|
88
|
+
type ModifierMap<T extends ModifierSetting> = {
|
|
89
|
+
default: T;
|
|
90
|
+
} & Partial<Record<Modifier, T>>;
|
|
91
|
+
/**
|
|
92
|
+
* A single setting, or one per modifier key.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ['raw', 1]
|
|
96
|
+
* { default: ['raw', 1], shift: ['raw', 0.1] }
|
|
97
|
+
*/
|
|
98
|
+
type ModifierValue<T extends ModifierSetting> = T | ModifierMap<T>;
|
|
99
|
+
interface SelectedInputEvent {
|
|
100
|
+
option: InputEventOption;
|
|
101
|
+
/** Which modifier entry was chosen, or `null` for `default`. */
|
|
102
|
+
modifier: Modifier | null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Pick the setting that applies, given the modifier keys being held.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* selectModifier({ default: 1, shift: 0.1 }, event)
|
|
109
|
+
*/
|
|
110
|
+
declare function selectModifier<T extends ModifierSetting>(options: ModifierValue<T>, modifiers?: ModifierState): {
|
|
111
|
+
value: T;
|
|
112
|
+
modifier: Modifier | null;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Turn every entry of a setting into another kind of setting, keeping which
|
|
116
|
+
* modifier each belongs to.
|
|
117
|
+
*
|
|
118
|
+
* A drag sensitivity is a number and a keyboard amount is a tuple, but the two
|
|
119
|
+
* describe the same thing from the caller's side. This carries one over to the
|
|
120
|
+
* other so that a component can hand a sensitivity to {@link applyDelta}
|
|
121
|
+
* without unpicking the modifier map itself — which matters, since naming a
|
|
122
|
+
* modifier is also what takes `step` out of the pipeline.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* mapModifier({ default: 1, shift: 0.1 }, (f) => ['raw', step * f])
|
|
126
|
+
* // { default: ['raw', 1], shift: ['raw', 0.1] }
|
|
127
|
+
*/
|
|
128
|
+
declare function mapModifier<T extends ModifierSetting, U extends ModifierSetting>(options: ModifierValue<T>, fn: (value: T) => U): ModifierValue<U>;
|
|
129
|
+
/**
|
|
130
|
+
* Pick the amount that applies, given the modifier keys being held.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* selectInputEvent({ default: ['raw', 1], shift: ['raw', 0.1] }, event)
|
|
134
|
+
*/
|
|
135
|
+
declare function selectInputEvent(options: ModifierValue<InputEventOption>, modifiers?: ModifierState): SelectedInputEvent;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/scales.d.ts
|
|
138
|
+
/**
|
|
139
|
+
* How a value is distributed across the travel of a control.
|
|
140
|
+
*
|
|
141
|
+
* `normalize` and `denormalize` are inverses of each other: the position is
|
|
142
|
+
* 0 at `min` and 1 at `max`, and everything in between is up to the scale.
|
|
143
|
+
*
|
|
144
|
+
* `min` and `max` are arguments rather than baked into the scale, so a scale
|
|
145
|
+
* holds no state and can be a module level constant. Passing the same object
|
|
146
|
+
* on every render therefore costs nothing.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* exponentialScale.denormalize(0.5, 20, 20000) // 632.45…
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
interface Scale {
|
|
154
|
+
/** Value to its position on the travel, 0-1. */
|
|
155
|
+
normalize: (value: number, min: number, max: number) => number;
|
|
156
|
+
/** Position on the travel, 0-1, back to a value. */
|
|
157
|
+
denormalize: (position: number, min: number, max: number) => number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Equal travel gives an equal change in value.
|
|
161
|
+
*
|
|
162
|
+
* The right default for anything already linear in perception: dB values,
|
|
163
|
+
* pan, percentages, MIDI note numbers, semitones.
|
|
164
|
+
*/
|
|
165
|
+
declare const linearScale: Scale;
|
|
166
|
+
/**
|
|
167
|
+
* The power law of JUCE's `NormalisableRange::skew`, applied to `value - min`.
|
|
168
|
+
*
|
|
169
|
+
* Use it when the value has to agree with a JUCE or iPlug2 parameter — a
|
|
170
|
+
* plugin UI in a WebView, say, where the knob must sit exactly where the
|
|
171
|
+
* host's automation curve puts it. {@link skewWithCenterValue} gives the
|
|
172
|
+
* factor that places a chosen value at the middle of the travel.
|
|
173
|
+
*
|
|
174
|
+
* `skew < 1` gives the lower end more travel, `skew > 1` the upper end.
|
|
175
|
+
*
|
|
176
|
+
* For new designs prefer {@link exponentialScale} or {@link curveScale}: the
|
|
177
|
+
* slope of this curve is either zero or infinite at `min`, so the bottom of
|
|
178
|
+
* the range is a dead zone or jumps.
|
|
179
|
+
*
|
|
180
|
+
* @param skew the JUCE skew factor
|
|
181
|
+
*/
|
|
182
|
+
declare function skewScale(skew: number): Scale;
|
|
183
|
+
/**
|
|
184
|
+
* The skew factor for {@link skewScale} that puts `centerValue` at the middle
|
|
185
|
+
* of the travel — JUCE's `NormalisableRange::setSkewForCentre`.
|
|
186
|
+
*/
|
|
187
|
+
declare function skewWithCenterValue(centerValue: number, min: number, max: number): number;
|
|
188
|
+
/**
|
|
189
|
+
* Equal travel gives an equal *ratio*, so an octave — or a percentage — takes
|
|
190
|
+
* the same distance wherever it falls.
|
|
191
|
+
*
|
|
192
|
+
* The scale for frequency (a filter cutoff over 20-20000 Hz), free running
|
|
193
|
+
* rates, and delay times.
|
|
194
|
+
*
|
|
195
|
+
* Requires `min` and `max` to be non-zero and of the same sign, since no
|
|
196
|
+
* ratio reaches zero or crosses it. Use {@link curveScale} for a range that
|
|
197
|
+
* starts at 0.
|
|
198
|
+
*/
|
|
199
|
+
declare const exponentialScale: Scale;
|
|
200
|
+
/**
|
|
201
|
+
* An exponential bend that still passes exactly through `min` and `max`, so
|
|
202
|
+
* unlike {@link exponentialScale} it works on a range that starts at 0 or
|
|
203
|
+
* crosses it, and unlike {@link skewScale} its slope is neither zero nor
|
|
204
|
+
* infinite at either end.
|
|
205
|
+
*
|
|
206
|
+
* The general purpose taper, and the same family as the curve of an envelope
|
|
207
|
+
* segment (SuperCollider's `CurveWarp`).
|
|
208
|
+
*
|
|
209
|
+
* - `curve > 0` gives the lower end more travel — envelope times from 0 ms,
|
|
210
|
+
* delay times, anything that wants fine control near the bottom
|
|
211
|
+
* - `curve < 0` gives the upper end more travel — a volume fader over
|
|
212
|
+
* -60..+6 dB that should be precise around 0 dB
|
|
213
|
+
* - near 0 it is indistinguishable from {@link linearScale}, and is treated
|
|
214
|
+
* as linear to avoid dividing by zero
|
|
215
|
+
*
|
|
216
|
+
* {@link curveWithCenterValue} gives the curve that places a chosen value at
|
|
217
|
+
* the middle of the travel.
|
|
218
|
+
*
|
|
219
|
+
* @param curve how hard the curve bends, and in which direction
|
|
220
|
+
*/
|
|
221
|
+
declare function curveScale(curve: number): Scale;
|
|
222
|
+
/**
|
|
223
|
+
* {@link skewScale} mirrored about the middle of the range, so both halves
|
|
224
|
+
* bend the same way — JUCE's `symmetricSkew`.
|
|
225
|
+
*
|
|
226
|
+
* For a bipolar control whose centre matters: detune over -100..+100 cents,
|
|
227
|
+
* or a bipolar filter envelope amount, where the fine adjustment is around 0
|
|
228
|
+
* rather than at either end.
|
|
229
|
+
*
|
|
230
|
+
* `skew < 1` gives the middle more travel, `skew > 1` the two ends.
|
|
231
|
+
*
|
|
232
|
+
* @param skew the JUCE skew factor
|
|
233
|
+
*/
|
|
234
|
+
declare function symmetricSkewScale(skew: number): Scale;
|
|
235
|
+
/**
|
|
236
|
+
* The curve for {@link curveScale} that puts `centerValue` at the middle of
|
|
237
|
+
* the travel — the counterpart of {@link skewWithCenterValue}.
|
|
238
|
+
*/
|
|
239
|
+
declare function curveWithCenterValue(centerValue: number, min: number, max: number): number;
|
|
240
|
+
/**
|
|
241
|
+
* How a value is scaled: the range it lives in, how it is rounded, and how it
|
|
242
|
+
* is distributed across the travel.
|
|
243
|
+
*
|
|
244
|
+
* `AxisOptions` of `@tremolo-ui/dom` extends this, so a drag and a
|
|
245
|
+
* wheel / keyboard nudge run the same value pipeline.
|
|
246
|
+
*/
|
|
247
|
+
interface ValueRange {
|
|
248
|
+
min: number;
|
|
249
|
+
max: number;
|
|
250
|
+
/**
|
|
251
|
+
* Rounding applied to the value. Left unrounded when omitted.
|
|
252
|
+
*/
|
|
253
|
+
step?: number;
|
|
254
|
+
/**
|
|
255
|
+
* How the value is distributed across the travel.
|
|
256
|
+
*
|
|
257
|
+
* @default linearScale
|
|
258
|
+
*/
|
|
259
|
+
scale?: Scale;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Move a value by an amount of input, as reported by a wheel or an arrow key.
|
|
263
|
+
*
|
|
264
|
+
* The pipeline matches `createDragValue` of `@tremolo-ui/dom`: scale, then
|
|
265
|
+
* step, then clamp. Which key or which sign of `deltaY` counts as which
|
|
266
|
+
* direction is left to the caller, since it differs per component.
|
|
267
|
+
*
|
|
268
|
+
* @param direction which way, and how many times, to apply the option. The
|
|
269
|
+
* size of one step is `option[1]`, so this is normally `1` or `-1`.
|
|
270
|
+
*
|
|
271
|
+
* @param modifiers the event, for `options` that name a modifier key. See
|
|
272
|
+
* {@link selectInputEvent}.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* // ArrowDown on a slider whose keyboard option is ['raw', 1]
|
|
276
|
+
* applyDelta(value, -1, keyboard, { min, max, step, scale })
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* // Shift+ArrowDown, where `keyboard` is { default: …, shift: ['raw', 0.1] }
|
|
280
|
+
* applyDelta(value, -1, keyboard, range, event)
|
|
281
|
+
*/
|
|
282
|
+
declare function applyDelta(value: number, direction: number, options: ModifierValue<InputEventOption>, { min, max, step, scale }: ValueRange, modifiers?: ModifierState): number;
|
|
283
|
+
//#endregion
|
|
25
284
|
//#region src/midi.d.ts
|
|
26
285
|
declare const whiteKeys: readonly ["A", "B", "C", "D", "E", "F", "G"];
|
|
27
286
|
type WhiteKey = (typeof whiteKeys)[number];
|
|
@@ -62,20 +321,193 @@ declare function isBlackKey(note: number | string): boolean;
|
|
|
62
321
|
* @returns frequency [Hz]
|
|
63
322
|
*/
|
|
64
323
|
declare function noteToFrequency(note: number | string, detune?: number, a4?: number): number;
|
|
324
|
+
/**
|
|
325
|
+
* Semitones above the root, for each supported scale.
|
|
326
|
+
*
|
|
327
|
+
* Every entry starts at 0 and stays inside one octave, so a scale is a set of
|
|
328
|
+
* pitch classes rather than a set of notes: {@link inScale} compares against
|
|
329
|
+
* it with the octave taken out.
|
|
330
|
+
*
|
|
331
|
+
* `ionian` and `aeolian` are the same sets as `major` and `naturalMinor`; both
|
|
332
|
+
* spellings are here because both are what someone reaches for depending on
|
|
333
|
+
* whether they are thinking in keys or in modes.
|
|
334
|
+
*/
|
|
335
|
+
declare const scaleIntervals: {
|
|
336
|
+
readonly major: readonly [0, 2, 4, 5, 7, 9, 11];
|
|
337
|
+
readonly naturalMinor: readonly [0, 2, 3, 5, 7, 8, 10];
|
|
338
|
+
readonly harmonicMinor: readonly [0, 2, 3, 5, 7, 8, 11];
|
|
339
|
+
readonly melodicMinor: readonly [0, 2, 3, 5, 7, 9, 11];
|
|
340
|
+
readonly ionian: readonly [0, 2, 4, 5, 7, 9, 11];
|
|
341
|
+
readonly dorian: readonly [0, 2, 3, 5, 7, 9, 10];
|
|
342
|
+
readonly phrygian: readonly [0, 1, 3, 5, 7, 8, 10];
|
|
343
|
+
readonly lydian: readonly [0, 2, 4, 6, 7, 9, 11];
|
|
344
|
+
readonly mixolydian: readonly [0, 2, 4, 5, 7, 9, 10];
|
|
345
|
+
readonly aeolian: readonly [0, 2, 3, 5, 7, 8, 10];
|
|
346
|
+
readonly locrian: readonly [0, 1, 3, 5, 6, 8, 10];
|
|
347
|
+
readonly majorPentatonic: readonly [0, 2, 4, 7, 9];
|
|
348
|
+
readonly minorPentatonic: readonly [0, 3, 5, 7, 10];
|
|
349
|
+
readonly blues: readonly [0, 3, 5, 6, 7, 10];
|
|
350
|
+
readonly wholeTone: readonly [0, 2, 4, 6, 8, 10];
|
|
351
|
+
readonly chromatic: readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
352
|
+
};
|
|
353
|
+
type ScaleName = keyof typeof scaleIntervals;
|
|
354
|
+
/**
|
|
355
|
+
* Whether a note belongs to a scale, regardless of the octave either sits in.
|
|
356
|
+
*
|
|
357
|
+
* @param note noteNumber: 0 ~ 127 or noteName e.g. 'C3'
|
|
358
|
+
* @param root the note the scale is built on, in the same two forms
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```ts
|
|
362
|
+
* inScale('F#4', 'D3', 'major') // true
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
declare function inScale(note: number | string, root: number | string, name: ScaleName): boolean;
|
|
366
|
+
/**
|
|
367
|
+
* The notes of a scale, ascending from `root`.
|
|
368
|
+
*
|
|
369
|
+
* The octave above the root is not included: ask for more `octaves` instead, so
|
|
370
|
+
* that concatenating the result of two calls does not repeat a note.
|
|
371
|
+
*
|
|
372
|
+
* @param root noteNumber: 0 ~ 127 or noteName e.g. 'C3'
|
|
373
|
+
* @param octaves how many octaves to cover
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* scaleNotes('C3', 'majorPentatonic') // [48, 50, 52, 55, 57]
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
declare function scaleNotes(root: number | string, name: ScaleName, octaves?: number): number[];
|
|
65
381
|
//#endregion
|
|
66
|
-
//#region src/
|
|
382
|
+
//#region src/piano.d.ts
|
|
383
|
+
type NoteRange = {
|
|
384
|
+
first: number;
|
|
385
|
+
last: number;
|
|
386
|
+
};
|
|
67
387
|
/**
|
|
68
|
-
*
|
|
388
|
+
* `[noteRange.first, noteRange.first + 1, ..., noteRange.last]`
|
|
389
|
+
*/
|
|
390
|
+
declare function getNoteRangeArray(noteRange: NoteRange): number[];
|
|
391
|
+
/**
|
|
392
|
+
* The geometry of a drawn keyboard.
|
|
393
|
+
*
|
|
394
|
+
* One description is shared by the drawing and the hit testing, so a key cannot
|
|
395
|
+
* be drawn somewhere other than where it responds.
|
|
396
|
+
*/
|
|
397
|
+
interface PianoLayout {
|
|
398
|
+
noteRange: NoteRange;
|
|
399
|
+
/** Width of a white key, excluding {@link PianoLayout.keyGap}. */
|
|
400
|
+
whiteKeyWidth: number;
|
|
401
|
+
/**
|
|
402
|
+
* Space between two white keys. Part of the slot a white key occupies, so it
|
|
403
|
+
* still belongs to one of the keys for the purpose of hit testing.
|
|
404
|
+
*
|
|
405
|
+
* @default 1
|
|
406
|
+
*/
|
|
407
|
+
keyGap?: number;
|
|
408
|
+
/**
|
|
409
|
+
* Width of a black key, as a fraction of {@link PianoLayout.whiteKeyWidth}.
|
|
410
|
+
*
|
|
411
|
+
* @default 0.65
|
|
412
|
+
*/
|
|
413
|
+
blackKeyWidthRatio?: number;
|
|
414
|
+
/**
|
|
415
|
+
* Height of a black key, as a fraction of the height of the keyboard.
|
|
416
|
+
*
|
|
417
|
+
* @default 0.6
|
|
418
|
+
*/
|
|
419
|
+
blackKeyHeightRatio?: number;
|
|
420
|
+
}
|
|
421
|
+
/** Width of a black key in pixels. */
|
|
422
|
+
declare function blackKeyWidth(layout: PianoLayout): number;
|
|
423
|
+
/** Width of the whole keyboard in pixels. */
|
|
424
|
+
declare function pianoWidth(layout: PianoLayout): number;
|
|
425
|
+
/**
|
|
426
|
+
* Offset of the left edge of a key from the left edge of the keyboard, in
|
|
427
|
+
* pixels.
|
|
428
|
+
*
|
|
429
|
+
* Notes outside `noteRange` are placed too, so the value is negative below
|
|
430
|
+
* `noteRange.first`.
|
|
431
|
+
*/
|
|
432
|
+
declare function notePosition(note: number, layout: PianoLayout): number;
|
|
433
|
+
/**
|
|
434
|
+
* The note drawn at a point, or null where there is none.
|
|
435
|
+
*
|
|
436
|
+
* Black keys are tested first, so they win where they overlap a white one. A
|
|
437
|
+
* white key covers its gap as well as its width, so the whole width of the
|
|
438
|
+
* keyboard belongs to some key and a click cannot fall between two.
|
|
439
|
+
*
|
|
440
|
+
* @param x offset from the left edge of the keyboard, in pixels
|
|
441
|
+
* @param y offset from its top edge, in pixels
|
|
442
|
+
* @param height height of the keyboard, in pixels
|
|
443
|
+
*/
|
|
444
|
+
declare function noteAt(x: number, y: number, height: number, layout: PianoLayout): number | null;
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/unit.d.ts
|
|
447
|
+
/**
|
|
448
|
+
* The SI prefixes {@link unitFormat} chooses between.
|
|
449
|
+
*
|
|
450
|
+
* Deliberately narrower than the full SI set: yocto through yotta are of no
|
|
451
|
+
* use to an audio control, and every extra prefix is one more symbol `parse`
|
|
452
|
+
* has to tell apart from a unit.
|
|
453
|
+
*/
|
|
454
|
+
type SIPrefix = 'p' | 'n' | 'µ' | 'm' | '' | 'k' | 'M' | 'G';
|
|
455
|
+
interface UnitFormatOptions {
|
|
456
|
+
/**
|
|
457
|
+
* The prefix the stored value is already in.
|
|
458
|
+
*
|
|
459
|
+
* A control that keeps milliseconds in `value` is `{ base: 'm' }` with a
|
|
460
|
+
* unit of `'s'`: 1500 then displays as `1.5s`, and `parse` gives 1500 back.
|
|
461
|
+
*
|
|
462
|
+
* @default ''
|
|
463
|
+
*/
|
|
464
|
+
base?: SIPrefix;
|
|
465
|
+
/**
|
|
466
|
+
* Whether to scale the number and pick a prefix at all.
|
|
467
|
+
*
|
|
468
|
+
* Turn it off for anything that is not an SI quantity. dB, %, cents and
|
|
469
|
+
* semitones do not take prefixes, and `-6dB` read as "-6 deci-B" is wrong
|
|
470
|
+
* rather than merely unusual.
|
|
471
|
+
*
|
|
472
|
+
* @default true
|
|
473
|
+
*/
|
|
474
|
+
prefixes?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* Digits after the decimal point. The number is left as-is when omitted.
|
|
477
|
+
*/
|
|
478
|
+
digits?: number;
|
|
479
|
+
/**
|
|
480
|
+
* Text placed between the number and the unit.
|
|
481
|
+
* @default ''
|
|
482
|
+
*/
|
|
483
|
+
separator?: string;
|
|
484
|
+
}
|
|
485
|
+
/** The `format` / `parse` pair a `NumberInput` takes. */
|
|
486
|
+
interface UnitFormatter {
|
|
487
|
+
format: (value: number) => string;
|
|
488
|
+
parse: (text: string) => number;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Build the `format` and `parse` of a unit, as one pair.
|
|
492
|
+
*
|
|
493
|
+
* They are returned together because they have to agree: a `format` that
|
|
494
|
+
* writes `1.23kHz` is only useful next to a `parse` that reads it back as
|
|
495
|
+
* 1230. Spread the result into the input.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* unitFormat('Hz') // 1234 -> '1.23kHz'
|
|
499
|
+
* unitFormat('s', { base: 'm' }) // value in ms. 1500 -> '1.5s'
|
|
500
|
+
* unitFormat('s', { base: 'm', digits: 2 }) // 1500 -> '1.50s'
|
|
501
|
+
* unitFormat('dB', { prefixes: false, digits: 1 }) // -6.25 -> '-6.3dB'
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* <NumberInput.Root {...unitFormat('Hz', { digits: 2 })} value={v} onChange={setV}>
|
|
69
505
|
*/
|
|
70
|
-
|
|
506
|
+
declare function unitFormat(unit: string, options?: UnitFormatOptions): UnitFormatter;
|
|
71
507
|
//#endregion
|
|
72
508
|
//#region src/util.d.ts
|
|
73
|
-
type Operator = '+' | '-' | '*' | '/';
|
|
74
|
-
declare function styleHelper(value: string | number): string;
|
|
75
|
-
declare function styleHelper(value: string | number, op: Operator, influencer?: number): string;
|
|
76
|
-
declare function isEmpty(obj: object): boolean;
|
|
77
509
|
declare function mod(n: number, m: number): number;
|
|
78
510
|
declare function xor(a?: boolean, b?: boolean): boolean;
|
|
79
511
|
//#endregion
|
|
80
|
-
export { type InputEventOption, type NoteKey, type WhiteKey, clamp, dbToGain, decimalPart, degree, gainToDb, integerPart, isBlackKey,
|
|
512
|
+
export { type InputEventOption, type Modifier, type ModifierMap, type ModifierState, type ModifierValue, type NoteKey, type NoteRange, type PianoLayout, SIGNIFICANT_DIGITS, type SIPrefix, type Scale, type ScaleName, type SelectedInputEvent, type UnitFormatOptions, type UnitFormatter, type ValueRange, type WhiteKey, applyDelta, blackKeyWidth, clamp, curveScale, curveWithCenterValue, dbToGain, decimalPart, degree, exponentialScale, gainToDb, getNoteRangeArray, inScale, integerPart, isBlackKey, isWhiteKey, linearScale, mapModifier, mapValue, mod, normalizeValue, noteAt, noteKey, noteKeys, noteName, noteNumber, notePosition, noteToFrequency, parseNoteName, pianoWidth, radian, rawValue, scaleIntervals, scaleNotes, selectInputEvent, selectModifier, skewScale, skewWithCenterValue, stepValue, symmetricSkewScale, toFixed, toPrecision, unitFormat, whiteKeys, xor };
|
|
81
513
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/math.ts","../src/midi.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/math.ts","../src/modifiers.ts","../src/scales.ts","../src/midi.ts","../src/piano.ts","../src/unit.ts","../src/util.ts"],"mappings":";;;;iBAGgB,MAAM,eAAe,aAAa;;;;;;;iBAUlC,eAAe,eAAe,aAAa;;;;;;iBAU3C,SAAS,yBAAyB,aAAa;;;;;;;;;;;iBAe/C,UAAU,eAAe;iBAUzB,QAAQ,WAAW;;;;;;cAStB;;;;;;;;;;;;;;;;;;;;;iBAsBG,YAAY,WAAW;iBAUvB,YAAY;iBAOZ,YAAY;iBAIZ,OAAO;iBAIP,OAAO;iBAIP,SACd,eACA,eACA,eACA,gBACA;iBAKc,SAAS;iBAIT,SAAS;;;;;;KCvHb;;;;;;;;KASA;;UAGK;EACf;EACA;EACA;EACA;;;KAIG,2BAA2B;KAEpB,YAAY,UAAU;EAAqB,SAAS;IAAM,QACpE,OAAO,UAAU;;;;;;;;KAUP,cAAc,UAAU,mBAAmB,IAAI,YAAY;UAEtD;EACf,QAAQ;;EAER,UAAU;;;;;;;;iBAsCI,eAAe,UAAU,iBACvC,SAAS,cAAc,IACvB,YAAY;EACT,OAAO;EAAG,UAAU;;;;;;;;;;;;;;;;iBAiCT,YACd,UAAU,iBACV,UAAU,iBACV,SAAS,cAAc,IAAI,KAAK,OAAO,MAAM,IAAI,cAAc;;;;;;;iBAgBjD,iBACd,SAAS,cAAc,mBACvB,YAAY,gBACX;;;;;;;;;;;;;;;;;;UClHc;;EAEf,YAAY,eAAe,aAAa;;EAExC,cAAc,kBAAkB,aAAa;;;;;;;;cAmBlC,aAAa;;;;;;;;;;;;;;;;;iBAqBV,UAAU,eAAe;;;;;iBAsBzB,oBACd,qBACA,aACA;;;;;;;;;;;;cAmBW,kBAAkB;;;;;;;;;;;;;;;;;;;;;;iBAqDf,WAAW,gBAAgB;;;;;;;;;;;;;iBAmD3B,mBAAmB,eAAe;;;;;iBAiClC,qBACd,qBACA,aACA;;;;;;;;UAkBe;EACf;EACA;;;;EAIA;;;;;;EAMA,QAAQ;;;;;;;;;;;;;;;;;;;;;;;iBAwBM,WACd,eACA,mBACA,SAAS,cAAc,qBACrB,KAAK,KAAK,MAAM,SAAuB,YACzC,YAAY;;;cCpTD;KAED,mBAAmB;cAElB;KAeD,kBAAkB;iBAQd,cAAc;EAOY,QAAA;EACZ;;;;;;iBAQd,WAAW;;;;;;;iBAeX,SAAS,wBAAwB;;;;iBAUjC,QAAQ,qBAAqB;;;;iBAQ7B,WAAW;;;;iBAiBX,WAAW;;;;;;;iBAUX,gBAAgB,uBAAuB,iBAAY;;;;;;;;;;;;cAiBtD;;;;;;;;;;;;;;;;;;KAsBD,yBAAyB;;;;;;;;;;;;iBAarB,QACd,uBACA,uBACA,MAAM;;;;;;;;;;;;;;;iBAuBQ,WACd,uBACA,MAAM,WACN;;;KCxLU;EACV;EACA;;;;;iBAMc,kBAAkB,WAAW;;;;;;;UAa5B;EACf,WAAW;;EAGX;;;;;;;EAQA;;;;;;EAOA;;;;;;EAOA;;;iBA8Bc,cAAc,QAAQ;;iBA0CtB,WAAW,QAAQ;;;;;;;;iBAYnB,aAAa,cAAc,QAAQ;;;;;;;;;;;;iBAenC,OACd,WACA,WACA,gBACA,QAAQ;;;;;;;;;;KC/IE;UAwBK;;;;;;;;;EASf,OAAO;;;;;;;;;;EAUP;;;;EAIA;;;;;EAKA;;;UAIe;EACf,SAAS;EACT,QAAQ;;;;;;;;;;;;;;;;;;iBAiCM,WACd,cACA,UAAS,oBACR;;;iBCvGa,IAAI,WAAW;iBAIf,IAAI,aAAW"}
|