lfocomp 0.1.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/CHANGELOG.md +24 -0
- package/README.md +179 -0
- package/lfo-comp.js +140 -0
- package/lfo-engine.js +468 -0
- package/lfo-ui.js +1272 -0
- package/lfo.js +1878 -0
- package/package.json +74 -0
- package/types/lfo-comp.d.ts +68 -0
- package/types/lfo-engine.d.ts +150 -0
- package/types/lfo-ui.d.ts +147 -0
- package/types/lfo.d.ts +350 -0
package/types/lfo.d.ts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas-based LFO panel with waveform display, shape selector, parameter
|
|
3
|
+
* sliders, and a drag handle for wiring to any modulation target.
|
|
4
|
+
*/
|
|
5
|
+
export class LFOWidget {
|
|
6
|
+
/**
|
|
7
|
+
* @param {HTMLElement} container Where the widget is appended.
|
|
8
|
+
* @param {string} lfoId Engine LFO id.
|
|
9
|
+
* @param {object} [opts]
|
|
10
|
+
* @param {string} [opts.color] Accent color hex.
|
|
11
|
+
* @param {string} [opts.label] Display label.
|
|
12
|
+
* @param {function} [opts.onConnect] (lfoId, element, routeId) => void
|
|
13
|
+
* @param {function} [opts.onDisconnect] (routeId) => void
|
|
14
|
+
*/
|
|
15
|
+
constructor(container: HTMLElement, lfoId: string, opts?: {
|
|
16
|
+
color?: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
onConnect?: Function;
|
|
19
|
+
onDisconnect?: Function;
|
|
20
|
+
});
|
|
21
|
+
_lfoId: string;
|
|
22
|
+
_color: string;
|
|
23
|
+
_label: string;
|
|
24
|
+
_onConnect: Function;
|
|
25
|
+
_onDisconnect: Function;
|
|
26
|
+
/** @type {Map<string, ModIndicator>} routeId → indicator */
|
|
27
|
+
_indicators: Map<string, ModIndicator>;
|
|
28
|
+
_latestValue: number;
|
|
29
|
+
_unsub: Function;
|
|
30
|
+
_rafHandle: number;
|
|
31
|
+
_destroyed: boolean;
|
|
32
|
+
_build(container: any): void;
|
|
33
|
+
_root: HTMLDivElement;
|
|
34
|
+
_led: HTMLDivElement;
|
|
35
|
+
_canvas: HTMLCanvasElement;
|
|
36
|
+
_ctx: CanvasRenderingContext2D;
|
|
37
|
+
_shapesEl: HTMLDivElement;
|
|
38
|
+
_bipolarBtn: HTMLButtonElement;
|
|
39
|
+
_rateInput: HTMLInputElement;
|
|
40
|
+
_depthInput: HTMLInputElement;
|
|
41
|
+
_phaseInput: HTMLInputElement;
|
|
42
|
+
_offsetInput: HTMLInputElement;
|
|
43
|
+
_jitterInput: HTMLInputElement;
|
|
44
|
+
_skewInput: HTMLInputElement;
|
|
45
|
+
_handle: HTMLDivElement;
|
|
46
|
+
_addParam(container: any, labelText: any, min: any, max: any, defaultVal: any, step: any, param: any, fmt: any, scale?: string): HTMLInputElement;
|
|
47
|
+
_refreshShapeButtons(): void;
|
|
48
|
+
_refreshBipolarBtn(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Reflect effective (post-chain-modulation) rate and depth onto the param
|
|
51
|
+
* sliders so they visually track the modulated position. Only touches the
|
|
52
|
+
* DOM when the effective value actually differs from the displayed value to
|
|
53
|
+
* avoid unnecessary repaints.
|
|
54
|
+
*/
|
|
55
|
+
_syncChainedSliders(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Remove any indicators whose routes have been deleted externally — e.g. when
|
|
58
|
+
* the target LFO is destroyed and engine.destroyLFO cleans up incoming routes.
|
|
59
|
+
* Called each tick so stale badges are cleared within one frame.
|
|
60
|
+
*/
|
|
61
|
+
_pruneDeadRoutes(): void;
|
|
62
|
+
_recordSample(currentValue: any): void;
|
|
63
|
+
_wfBuf: HTMLCanvasElement;
|
|
64
|
+
_wfBufCtx: CanvasRenderingContext2D;
|
|
65
|
+
_wfHistory: Float32Array<ArrayBuffer>;
|
|
66
|
+
_wfSubpx: any;
|
|
67
|
+
_wfLastTime: number;
|
|
68
|
+
_wfPrevValue: any;
|
|
69
|
+
_wfNeedsFullRedraw: boolean;
|
|
70
|
+
/** Composite _wfBuf → visible canvas + cursor dot. Called from RAF. */
|
|
71
|
+
_rafDraw(): void;
|
|
72
|
+
/** Full repaint of _wfBuf from _wfHistory. */
|
|
73
|
+
_wfPaintFull(bCtx: any, W: any, H: any): void;
|
|
74
|
+
/**
|
|
75
|
+
* Scroll _wfBuf left by intShift, fill background+grid in new strip, then
|
|
76
|
+
* draw only the new right-edge stroke from _wfHistory.
|
|
77
|
+
*/
|
|
78
|
+
_wfPaintIncremental(bCtx: any, W: any, H: any, intShift: any): void;
|
|
79
|
+
_wfDrawGrid(bCtx: any, W: any, H: any, x0: any, x1: any): void;
|
|
80
|
+
/** Stroke _wfHistory[xFrom..xTo] onto bCtx. */
|
|
81
|
+
_wfDrawHistoryStroke(bCtx: any, W: any, H: any, xFrom: any, xTo: any): void;
|
|
82
|
+
_updateLed(value: any): void;
|
|
83
|
+
_attachDragHandlers(handle: any): void;
|
|
84
|
+
_connectTo(element: any): void;
|
|
85
|
+
/**
|
|
86
|
+
* Programmatically connect this LFO to an element.
|
|
87
|
+
* @param {HTMLElement} element
|
|
88
|
+
* @param {object} [opts]
|
|
89
|
+
* @param {number} [opts.depth=0.5]
|
|
90
|
+
* @returns {string|null} routeId, or null if rejected (duplicate, self-mod).
|
|
91
|
+
*/
|
|
92
|
+
connect(element: HTMLElement, opts?: {
|
|
93
|
+
depth?: number;
|
|
94
|
+
}): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Remove a specific modulation route.
|
|
97
|
+
* @param {string} routeId
|
|
98
|
+
*/
|
|
99
|
+
disconnectRoute(routeId: string): void;
|
|
100
|
+
/** Disconnect all routes from this widget. */
|
|
101
|
+
disconnectAll(): void;
|
|
102
|
+
/** Tear down the widget and all its connections. */
|
|
103
|
+
destroy(): void;
|
|
104
|
+
get element(): HTMLDivElement;
|
|
105
|
+
get lfoId(): string;
|
|
106
|
+
get color(): string;
|
|
107
|
+
get label(): string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* lfo-ui.js — LFO widget, modulation indicators, and drag-to-assign wiring.
|
|
111
|
+
*
|
|
112
|
+
* Provides:
|
|
113
|
+
* LFOWidget — Canvas-based LFO panel with controls and a drag handle.
|
|
114
|
+
* ModIndicator — Floating badge anchored to a connected element showing
|
|
115
|
+
* depth and a remove button. Also draws a range arc on range inputs.
|
|
116
|
+
*
|
|
117
|
+
* No external dependencies. Requires lfo-engine.js.
|
|
118
|
+
*/
|
|
119
|
+
export const LFO_COLORS: string[];
|
|
120
|
+
/**
|
|
121
|
+
* Floating badge anchored to a connected input element.
|
|
122
|
+
* Shows depth, allows drag-to-adjust, and a remove button.
|
|
123
|
+
*/
|
|
124
|
+
export class ModIndicator {
|
|
125
|
+
/**
|
|
126
|
+
* @param {HTMLElement} element Connected input.
|
|
127
|
+
* @param {string} routeId
|
|
128
|
+
* @param {string} lfoId
|
|
129
|
+
* @param {string} color
|
|
130
|
+
* @param {string} lfoLabel
|
|
131
|
+
* @param {function} onRemove Called when the user removes this connection.
|
|
132
|
+
*/
|
|
133
|
+
constructor(element: HTMLElement, routeId: string, lfoId: string, color: string, lfoLabel: string, onRemove: Function);
|
|
134
|
+
_element: HTMLElement;
|
|
135
|
+
_routeId: string;
|
|
136
|
+
_lfoId: string;
|
|
137
|
+
_color: string;
|
|
138
|
+
_onRemove: Function;
|
|
139
|
+
_badge: HTMLDivElement;
|
|
140
|
+
_arcCanvas: HTMLCanvasElement;
|
|
141
|
+
_rafId: number;
|
|
142
|
+
_buildBadge(label: any): void;
|
|
143
|
+
_depthLabel: HTMLSpanElement;
|
|
144
|
+
_buildArcCanvas(): void;
|
|
145
|
+
_arcCtx: CanvasRenderingContext2D;
|
|
146
|
+
_updateDepthLabel(): void;
|
|
147
|
+
_startPositioning(): void;
|
|
148
|
+
_reposition(): void;
|
|
149
|
+
_badgeSize: {
|
|
150
|
+
bw: number;
|
|
151
|
+
bh: number;
|
|
152
|
+
};
|
|
153
|
+
_updateArc(): void;
|
|
154
|
+
destroy(): void;
|
|
155
|
+
get routeId(): string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* lfo-engine.js — Core LFO math engine, no DOM dependencies.
|
|
159
|
+
*
|
|
160
|
+
* Manages LFO instances, a global RAF tick loop, and a modulation routing
|
|
161
|
+
* graph. Routes can target HTML elements (range/number inputs) or other
|
|
162
|
+
* LFO parameters (for chaining).
|
|
163
|
+
*
|
|
164
|
+
* Usage:
|
|
165
|
+
* import { engine, SHAPES } from './lfo-engine.js';
|
|
166
|
+
* const id = engine.createLFO({ shape: 'sine', rate: 2 });
|
|
167
|
+
* const routeId = engine.addRoute(id, 'element', sliderEl, null, { depth: 0.5 });
|
|
168
|
+
*/
|
|
169
|
+
export const SHAPES: string[];
|
|
170
|
+
/**
|
|
171
|
+
* Programmatically connect an LFO to an HTML element.
|
|
172
|
+
* Equivalent to calling widget.connect(element, opts).
|
|
173
|
+
*
|
|
174
|
+
* @param {LFOWidget} widget
|
|
175
|
+
* @param {HTMLElement} element
|
|
176
|
+
* @param {object} [opts]
|
|
177
|
+
* @param {number} [opts.depth=0.5]
|
|
178
|
+
* @returns {string|null} routeId, or null if connection was rejected.
|
|
179
|
+
*/
|
|
180
|
+
export function connect(widget: LFOWidget, element: HTMLElement, opts?: {
|
|
181
|
+
depth?: number;
|
|
182
|
+
}): string | null;
|
|
183
|
+
/**
|
|
184
|
+
* Create a fully wired LFO widget.
|
|
185
|
+
*
|
|
186
|
+
* @param {HTMLElement|object} containerOrOpts
|
|
187
|
+
* If an HTMLElement, the widget is appended to it and opts is the second arg.
|
|
188
|
+
* If an object, treated as opts — caller is responsible for appending widget.element.
|
|
189
|
+
* @param {object} [opts]
|
|
190
|
+
* @param {string} [opts.shape='sine'] Initial waveform shape.
|
|
191
|
+
* @param {number} [opts.rate=1] Initial rate in Hz.
|
|
192
|
+
* @param {number} [opts.depth=1] Initial depth 0–1.
|
|
193
|
+
* @param {number} [opts.phase=0] Initial phase offset 0–1.
|
|
194
|
+
* @param {number} [opts.offset=0] DC offset -1–1.
|
|
195
|
+
* @param {string} [opts.color] Accent color. Auto-assigned if omitted.
|
|
196
|
+
* @param {string} [opts.label] Widget label. Auto-assigned if omitted.
|
|
197
|
+
* @param {function} [opts.onConnect] Called when a connection is made.
|
|
198
|
+
* @param {function} [opts.onDisconnect] Called when a connection is removed.
|
|
199
|
+
*
|
|
200
|
+
* @returns {{ lfoId: string, widget: LFOWidget }}
|
|
201
|
+
*/
|
|
202
|
+
export function createLFO(containerOrOpts?: HTMLElement | object, opts?: {
|
|
203
|
+
shape?: string;
|
|
204
|
+
rate?: number;
|
|
205
|
+
depth?: number;
|
|
206
|
+
phase?: number;
|
|
207
|
+
offset?: number;
|
|
208
|
+
color?: string;
|
|
209
|
+
label?: string;
|
|
210
|
+
onConnect?: Function;
|
|
211
|
+
onDisconnect?: Function;
|
|
212
|
+
}): {
|
|
213
|
+
lfoId: string;
|
|
214
|
+
widget: LFOWidget;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Remove a modulation route and its UI indicator.
|
|
218
|
+
* Prefer calling widget.disconnectRoute(routeId) directly when you have the widget.
|
|
219
|
+
* This function handles programmatic routes created via connect() or drag-to-assign.
|
|
220
|
+
*
|
|
221
|
+
* @param {string} routeId
|
|
222
|
+
*/
|
|
223
|
+
export function disconnect(routeId: string): void;
|
|
224
|
+
/** Global engine singleton — import and use directly. */
|
|
225
|
+
export const engine: LFOEngine;
|
|
226
|
+
/**
|
|
227
|
+
* Get all currently active modulation routes.
|
|
228
|
+
* @returns {object[]}
|
|
229
|
+
*/
|
|
230
|
+
export function getRoutes(): object[];
|
|
231
|
+
export function injectStyles(): void;
|
|
232
|
+
/** Deterministic pseudo-random in [-1, 1] for a given integer seed. */
|
|
233
|
+
export function seededRand(seed: any): number;
|
|
234
|
+
/** Smoothly interpolated random (cubic Hermite) for non-integer phase. */
|
|
235
|
+
export function smoothRand(seed: any, phase: any): number;
|
|
236
|
+
declare class LFOEngine {
|
|
237
|
+
/** @type {Map<string, LFOState>} */
|
|
238
|
+
_lfos: Map<string, LFOState>;
|
|
239
|
+
/** @type {Map<string, RouteState>} */
|
|
240
|
+
_routes: Map<string, RouteState>;
|
|
241
|
+
/** @type {Map<HTMLElement, ElementState>} */
|
|
242
|
+
_elementStates: Map<HTMLElement, ElementState>;
|
|
243
|
+
/** @type {Set<function>} */
|
|
244
|
+
_subscribers: Set<Function>;
|
|
245
|
+
_lastTs: any;
|
|
246
|
+
_rafId: number;
|
|
247
|
+
_running: boolean;
|
|
248
|
+
_tick(ts: any): void;
|
|
249
|
+
start(): void;
|
|
250
|
+
stop(): void;
|
|
251
|
+
/**
|
|
252
|
+
* Create a new LFO instance.
|
|
253
|
+
* @param {object} opts
|
|
254
|
+
* @param {string} [opts.shape='sine'] Waveform shape. One of SHAPES.
|
|
255
|
+
* @param {number} [opts.rate=1] Frequency in Hz.
|
|
256
|
+
* @param {number} [opts.depth=1] Modulation depth multiplier 0–1.
|
|
257
|
+
* @param {number} [opts.phase=0] Initial phase offset 0–1.
|
|
258
|
+
* @param {number} [opts.offset=0] DC offset added to output, -1–1.
|
|
259
|
+
* @param {boolean} [opts.bipolar=true] If false, output is remapped 0–1.
|
|
260
|
+
* @returns {string} LFO id.
|
|
261
|
+
*/
|
|
262
|
+
createLFO(opts?: {
|
|
263
|
+
shape?: string;
|
|
264
|
+
rate?: number;
|
|
265
|
+
depth?: number;
|
|
266
|
+
phase?: number;
|
|
267
|
+
offset?: number;
|
|
268
|
+
bipolar?: boolean;
|
|
269
|
+
}): string;
|
|
270
|
+
/**
|
|
271
|
+
* Remove an LFO and all its routes.
|
|
272
|
+
* @param {string} id
|
|
273
|
+
*/
|
|
274
|
+
destroyLFO(id: string): void;
|
|
275
|
+
/**
|
|
276
|
+
* Add a modulation route.
|
|
277
|
+
*
|
|
278
|
+
* For element routes: LFO modulates an HTML input element.
|
|
279
|
+
* engine.addRoute(lfoId, 'element', inputEl, null, { depth: 0.5 });
|
|
280
|
+
*
|
|
281
|
+
* For LFO-chain routes: LFO modulates another LFO's rate or depth.
|
|
282
|
+
* engine.addRoute(lfoId, 'lfo', targetLfoId, 'rate', { depth: 0.3 });
|
|
283
|
+
*
|
|
284
|
+
* @param {string} sourceId
|
|
285
|
+
* @param {'element'|'lfo'} targetType
|
|
286
|
+
* @param {HTMLElement|string} target Element or target LFO id.
|
|
287
|
+
* @param {string|null} targetParam 'rate'|'depth' for lfo, null for element.
|
|
288
|
+
* @param {object} opts
|
|
289
|
+
* @param {number} [opts.depth=0.5] Modulation depth 0–1.
|
|
290
|
+
* @param {number} [opts.min] Override element min.
|
|
291
|
+
* @param {number} [opts.max] Override element max.
|
|
292
|
+
* @param {number} [opts.step] Override element step.
|
|
293
|
+
* @returns {string|null} Route id, or null if the route was rejected (e.g. self-modulation).
|
|
294
|
+
*/
|
|
295
|
+
addRoute(sourceId: string, targetType: "element" | "lfo", target: HTMLElement | string, targetParam: string | null, opts?: {
|
|
296
|
+
depth?: number;
|
|
297
|
+
min?: number;
|
|
298
|
+
max?: number;
|
|
299
|
+
step?: number;
|
|
300
|
+
}): string | null;
|
|
301
|
+
/**
|
|
302
|
+
* Remove a modulation route.
|
|
303
|
+
* @param {string} routeId
|
|
304
|
+
*/
|
|
305
|
+
removeRoute(routeId: string): void;
|
|
306
|
+
/**
|
|
307
|
+
* Set the depth of an existing route.
|
|
308
|
+
* @param {string} routeId
|
|
309
|
+
* @param {number} depth 0–1
|
|
310
|
+
*/
|
|
311
|
+
setRouteDepth(routeId: string, depth: number): void;
|
|
312
|
+
/**
|
|
313
|
+
* Enable or disable a route without removing it.
|
|
314
|
+
* @param {string} routeId
|
|
315
|
+
* @param {boolean} enabled
|
|
316
|
+
*/
|
|
317
|
+
setRouteEnabled(routeId: string, enabled: boolean): void;
|
|
318
|
+
/**
|
|
319
|
+
* Set a parameter on an LFO.
|
|
320
|
+
* @param {string} lfoId
|
|
321
|
+
* @param {string} param 'shape'|'rate'|'depth'|'phase'|'offset'|'bipolar'|'baseRate'|'baseDepth'|'jitter'|'skew'
|
|
322
|
+
* @param {*} value
|
|
323
|
+
*/
|
|
324
|
+
setParam(lfoId: string, param: string, value: any): void;
|
|
325
|
+
/**
|
|
326
|
+
* Get a parameter from an LFO.
|
|
327
|
+
* @param {string} lfoId
|
|
328
|
+
* @param {string} param
|
|
329
|
+
* @returns {*}
|
|
330
|
+
*/
|
|
331
|
+
getParam(lfoId: string, param: string): any;
|
|
332
|
+
/** Get the current output value of an LFO. @param {string} lfoId @returns {number} */
|
|
333
|
+
getValue(lfoId: string): number;
|
|
334
|
+
/**
|
|
335
|
+
* Subscribe to tick notifications. Called each frame for every LFO.
|
|
336
|
+
* @param {function(lfoId: string, value: number): void} fn
|
|
337
|
+
* @returns {function} Unsubscribe callback.
|
|
338
|
+
*/
|
|
339
|
+
subscribe(fn: any): Function;
|
|
340
|
+
getLFOs(): LFOState[];
|
|
341
|
+
getLFO(id: any): LFOState;
|
|
342
|
+
getAllRoutes(): RouteState[];
|
|
343
|
+
getRoute(id: any): RouteState;
|
|
344
|
+
getRoutesByElement(element: any): RouteState[];
|
|
345
|
+
getElementMeta(element: any): ElementState;
|
|
346
|
+
_initElementState(element: any, opts: any): void;
|
|
347
|
+
_cleanupElementState(element: any): void;
|
|
348
|
+
_computeValue(lfo: any, dt: any): any;
|
|
349
|
+
}
|
|
350
|
+
export {};
|