@codexo/exojs 0.8.2 → 0.8.4
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 +124 -0
- package/dist/esm/audio/AudioAnalyser.d.ts +36 -0
- package/dist/esm/audio/AudioAnalyser.js +148 -0
- package/dist/esm/audio/AudioAnalyser.js.map +1 -1
- package/dist/esm/audio/BeatDetector.d.ts +62 -0
- package/dist/esm/audio/BeatDetector.js +77 -0
- package/dist/esm/audio/BeatDetector.js.map +1 -1
- package/dist/esm/audio/dsp/mel.js +70 -0
- package/dist/esm/audio/dsp/mel.js.map +1 -0
- package/dist/esm/debug/RenderPassInspectorLayer.d.ts +71 -0
- package/dist/esm/debug/RenderPassInspectorLayer.js +201 -0
- package/dist/esm/debug/RenderPassInspectorLayer.js.map +1 -0
- package/dist/esm/debug/index.d.ts +1 -0
- package/dist/esm/debug/index.js +1 -0
- package/dist/esm/debug/index.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js +5 -1
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js.map +1 -1
- package/dist/esm/rendering/index.d.ts +2 -0
- package/dist/esm/rendering/mesh/Mesh.d.ts +4 -47
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
- package/dist/esm/rendering/mesh/MeshShader.d.ts +183 -0
- package/dist/esm/rendering/mesh/MeshShader.js +231 -0
- package/dist/esm/rendering/mesh/MeshShader.js.map +1 -0
- package/dist/esm/rendering/texture/DataTexture.d.ts +115 -0
- package/dist/esm/rendering/texture/DataTexture.js +173 -0
- package/dist/esm/rendering/texture/DataTexture.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2Backend.js +42 -1
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +12 -1
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuBackend.d.ts +1 -0
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +60 -7
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +2 -1
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +13 -0
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +636 -83
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -1
- package/dist/exo.esm.js +1452 -102
- package/dist/exo.esm.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,130 @@ All notable changes to ExoJS are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.8.4] - 2026-05-14
|
|
8
|
+
|
|
9
|
+
### Site / Guide
|
|
10
|
+
|
|
11
|
+
- **Full English Guide complete through Parts 3–8.** Drawing (5 chapters),
|
|
12
|
+
Input (4), Audio (4), Effects (3), Advanced (5), and Recipes (8) are now
|
|
13
|
+
written, editorially consistent, and source-verified against the engine
|
|
14
|
+
API as shipped in v0.8.3.
|
|
15
|
+
|
|
16
|
+
- **v0.8.3 feature chapters integrated.** Audio-reactive visualization (5.5),
|
|
17
|
+
Custom mesh shaders (6.4), and Render pipeline debugging (7.6) reflect the
|
|
18
|
+
`BeatDetector` visual getters, `AudioAnalyser` mel/log spectrum API,
|
|
19
|
+
`MeshShader` dual GLSL + WGSL class shape, and `RenderPassInspectorLayer`.
|
|
20
|
+
|
|
21
|
+
- **Full-guide editorial and source/API verification pass.** All 38 drafted
|
|
22
|
+
chapters verified for internal consistency, correct API surface references,
|
|
23
|
+
and accurate example cross-links. Terminology, heading conventions, and
|
|
24
|
+
cross-part forward/backward references aligned in a single pass.
|
|
25
|
+
|
|
26
|
+
- **39 English Guide routes verified across dark/light and desktop/mobile
|
|
27
|
+
screenshot matrix.** 156/156 captures successful (39 routes × 2 themes ×
|
|
28
|
+
2 viewports).
|
|
29
|
+
|
|
30
|
+
### Release Tooling
|
|
31
|
+
|
|
32
|
+
- **`scripts/release.mjs` replaced by `scripts/release.ts`.** Script is now
|
|
33
|
+
type-checked as part of the TypeScript codebase.
|
|
34
|
+
|
|
35
|
+
- **`scripts/generate-release-notes.ts`.** New standalone tool that extracts
|
|
36
|
+
a version section from `CHANGELOG.md`, resolves the previous semver tag from
|
|
37
|
+
git history or the changelog, fills the `.github/templates/release-notes.md`
|
|
38
|
+
template, and writes the result to a specified output path. Exposed as
|
|
39
|
+
`npm run release:notes`.
|
|
40
|
+
|
|
41
|
+
- **GitHub Releases now receive populated changelog-driven markdown bodies.**
|
|
42
|
+
The CI release workflow calls `release:notes` and passes the rendered file
|
|
43
|
+
to `gh release create --notes-file`.
|
|
44
|
+
|
|
45
|
+
- **Historical tag support.** `release:notes` can generate notes for any past
|
|
46
|
+
tag (e.g., `v0.8.2`, `v0.8.3`) — useful for backfilling GitHub Release pages.
|
|
47
|
+
|
|
48
|
+
- **Windows-safe `npm pack --dry-run` in `verify:package`.** The `--cache`
|
|
49
|
+
flag now points to a project-local `.npm-cache` directory, avoiding the
|
|
50
|
+
cross-user npm cache permission error that affected `verify:release` on
|
|
51
|
+
Windows CI.
|
|
52
|
+
|
|
53
|
+
### Verification
|
|
54
|
+
|
|
55
|
+
- Engine: typecheck clean, lint:strict 0/0, 1338/1338 tests, `verify:release` green.
|
|
56
|
+
- Site: 494-page build clean, `check-ts` 0 errors / 0 warnings.
|
|
57
|
+
- Screenshot smoke: 36/36. Guide visual matrix: 156/156 captures.
|
|
58
|
+
|
|
59
|
+
## [0.8.3] - 2026-05-10
|
|
60
|
+
|
|
61
|
+
### Engine — Rendering
|
|
62
|
+
|
|
63
|
+
- **`MeshShader` class with dual GLSL + WGSL support.** The 0.8.2
|
|
64
|
+
`MeshShaderConfig` plain interface is replaced by a `MeshShader` class
|
|
65
|
+
accepting `glsl: { vertex, fragment }` and/or `wgsl` source. The WebGPU
|
|
66
|
+
mesh renderer now has a parallel render path for custom-shader meshes
|
|
67
|
+
inside the same render pass, switching pipeline + bind groups between
|
|
68
|
+
batched default draws and per-shader custom draws. New methods
|
|
69
|
+
`getDeclaredUniforms()` and `detectUniformDrift()` parse uniform
|
|
70
|
+
declarations from both languages for CI-style drift checking. **Breaking
|
|
71
|
+
change against the 0.8.2 plain-interface shape; clean break, no
|
|
72
|
+
backwards-compat shim — the 0.8.x series is pre-1.0.**
|
|
73
|
+
|
|
74
|
+
- **`DataTexture` for CPU-uploaded GPU textures.** New primitive whose
|
|
75
|
+
pixels live in a CPU-side typed array. Mutate the `buffer` directly and
|
|
76
|
+
call `commit()` to upload the whole array, or `commitRect(x, y, w, h)`
|
|
77
|
+
for partial uploads (cheaper for ring-buffer patterns like
|
|
78
|
+
spectrograms). Formats: `r8` / `r32f` / `rgba8` / `rgba32f`; TypeScript
|
|
79
|
+
narrows the buffer typed-array kind from the format. Bring-your-own
|
|
80
|
+
buffer via `options.data` (`Uint8Array | Float32Array | ArrayBuffer`)
|
|
81
|
+
for SharedArrayBuffer / Worker / pool scenarios. Extends `Texture` so
|
|
82
|
+
it's accepted everywhere a `Texture` is.
|
|
83
|
+
|
|
84
|
+
### Engine — Audio
|
|
85
|
+
|
|
86
|
+
- **`BeatDetector` visual getters for per-frame polling.** New derived
|
|
87
|
+
getters `pulse`, `barPulse`, `justBeat`, `secondsSinceLastBeat` and
|
|
88
|
+
method `subdivisionPhase(division)`. All pure derivations from existing
|
|
89
|
+
state — no new event-handling glue required for typical "pulse on the
|
|
90
|
+
beat" / "trigger on every 16th note" visuals. Mutable fields
|
|
91
|
+
`pulseHalfLife` (default 0.15s), `barPulseHalfLife` (0.3s), and
|
|
92
|
+
`justBeatWindow` (0.03s) tune the envelopes.
|
|
93
|
+
|
|
94
|
+
- **`AudioAnalyser` mel and log spectrum mapping.** New methods
|
|
95
|
+
`getSpectrumMel` / `getSpectrumMelFloat` / `getSpectrumLog` /
|
|
96
|
+
`getSpectrumLogFloat` produce perceptually-weighted or octave-uniform
|
|
97
|
+
band sequences from the linear FFT bins. Filterbanks are built from
|
|
98
|
+
the previously-orphaned `dsp/mel.ts` utilities and cached per
|
|
99
|
+
`(bands, fMin, fMax)` combination — rebuild only on parameter change.
|
|
100
|
+
Default 32 bands, 20 Hz to 20 kHz (clamped to nyquist).
|
|
101
|
+
|
|
102
|
+
- **`source` as constructor option for `AudioAnalyser` and `BeatDetector`.**
|
|
103
|
+
Additive ergonomic for one-shot construction:
|
|
104
|
+
`new AudioAnalyser({ source: music, fftSize: 1024 })`. The setter
|
|
105
|
+
remains usable for runtime source switches.
|
|
106
|
+
|
|
107
|
+
### Engine — Debug
|
|
108
|
+
|
|
109
|
+
- **`RenderPassInspectorLayer`.** New debug layer (in the
|
|
110
|
+
`@codexo/exojs/debug` subpath) that lists every `RenderNode` with an
|
|
111
|
+
active filter chain each frame, showing total pass count, per-drawable
|
|
112
|
+
filter sequence, bounding-box dimensions, and mask/cache flags. For
|
|
113
|
+
deep per-pass inspection (intermediate render-target contents, shader
|
|
114
|
+
source, uniform values), use Spector.js or Chrome DevTools' WebGPU
|
|
115
|
+
panel — the engine now emits debug-group labels around filter and
|
|
116
|
+
mesh-custom-shader passes (`WebGpuShaderFilter pass`,
|
|
117
|
+
`MeshShader (custom)`, `WebGpuMaskCompositor pass`) so external capture
|
|
118
|
+
tools display meaningful pass names.
|
|
119
|
+
|
|
120
|
+
### Site / Docs
|
|
121
|
+
|
|
122
|
+
- New guide chapter stubs: `6.4 Custom mesh shaders`,
|
|
123
|
+
`5.5 Audio-reactive visualization`, `7.6 Render pipeline debugging`.
|
|
124
|
+
- API doc auto-regenerated for `MeshShader` and `DataTexture`.
|
|
125
|
+
|
|
126
|
+
### Verification
|
|
127
|
+
|
|
128
|
+
- Engine: 103/103 suites, 1338/1338 tests, lint:strict 0/0, typecheck clean.
|
|
129
|
+
- Site: build green (494 pages), check-ts 0/0.
|
|
130
|
+
|
|
7
131
|
## [0.8.2] - 2026-05-09
|
|
8
132
|
|
|
9
133
|
### Engine
|
|
@@ -12,6 +12,21 @@ export interface AudioAnalyserOptions {
|
|
|
12
12
|
minDecibels?: number;
|
|
13
13
|
/** Maximum dBFS rendered by the byte-domain spectrum getters. Default -30. */
|
|
14
14
|
maxDecibels?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Optional initial source. Equivalent to `analyser.source = value` after
|
|
17
|
+
* construction; provided for ergonomic one-shot construction. The setter
|
|
18
|
+
* remains usable for runtime source switches.
|
|
19
|
+
*/
|
|
20
|
+
source?: AudioAnalyserSource;
|
|
21
|
+
}
|
|
22
|
+
/** Mapping options for {@link AudioAnalyser.getSpectrumMel} and {@link AudioAnalyser.getSpectrumLog}. */
|
|
23
|
+
export interface SpectrumMappingOptions {
|
|
24
|
+
/** Number of output bands. Default 32. */
|
|
25
|
+
bands?: number;
|
|
26
|
+
/** Lower frequency bound in Hz. Default 20. */
|
|
27
|
+
fMin?: number;
|
|
28
|
+
/** Upper frequency bound in Hz. Default 20000 (clamped to nyquist at runtime). */
|
|
29
|
+
fMax?: number;
|
|
15
30
|
}
|
|
16
31
|
/**
|
|
17
32
|
* Lightweight visualisation analyser backed by a Web Audio AnalyserNode.
|
|
@@ -32,6 +47,8 @@ export declare class AudioAnalyser {
|
|
|
32
47
|
private _floatSpectrum;
|
|
33
48
|
private _byteWaveform;
|
|
34
49
|
private _floatWaveform;
|
|
50
|
+
private _melCache;
|
|
51
|
+
private _logCache;
|
|
35
52
|
/**
|
|
36
53
|
* Create an `AudioAnalyser` with the given options. The underlying
|
|
37
54
|
* `AnalyserNode` is created immediately if the `AudioContext` is already
|
|
@@ -75,12 +92,31 @@ export declare class AudioAnalyser {
|
|
|
75
92
|
};
|
|
76
93
|
/** Return overall RMS energy across all bins, normalised 0..1. */
|
|
77
94
|
getRms(): number;
|
|
95
|
+
/**
|
|
96
|
+
* Mel-scaled spectrum, byte domain (0..255). Output length = `bands`.
|
|
97
|
+
* Each output bin is a triangular-weighted sum of the linear FFT bins
|
|
98
|
+
* underneath it, mel-spaced from `fMin` to `fMax`.
|
|
99
|
+
*/
|
|
100
|
+
getSpectrumMel(into?: Uint8Array<ArrayBuffer>, options?: SpectrumMappingOptions): Uint8Array<ArrayBuffer>;
|
|
101
|
+
/** Mel-scaled spectrum, float domain (dBFS values). Output length = `bands`. */
|
|
102
|
+
getSpectrumMelFloat(into?: Float32Array<ArrayBuffer>, options?: SpectrumMappingOptions): Float32Array<ArrayBuffer>;
|
|
103
|
+
/**
|
|
104
|
+
* Log-scaled spectrum, byte domain (0..255). Output length = `bands`.
|
|
105
|
+
* Bins are spaced so each output covers an equal fraction of the
|
|
106
|
+
* `log2(fMax / fMin)` octave range — useful when you want a
|
|
107
|
+
* frequency-axis visualization where each octave gets the same width.
|
|
108
|
+
*/
|
|
109
|
+
getSpectrumLog(into?: Uint8Array<ArrayBuffer>, options?: SpectrumMappingOptions): Uint8Array<ArrayBuffer>;
|
|
110
|
+
/** Log-scaled spectrum, float domain (dBFS values). Output length = `bands`. */
|
|
111
|
+
getSpectrumLogFloat(into?: Float32Array<ArrayBuffer>, options?: SpectrumMappingOptions): Float32Array<ArrayBuffer>;
|
|
78
112
|
/**
|
|
79
113
|
* Disconnect the analyser tap, release the internal `AnalyserNode`, and
|
|
80
114
|
* cancel any pending setup callbacks. Call when the analyser is no longer
|
|
81
115
|
* needed to avoid memory leaks.
|
|
82
116
|
*/
|
|
83
117
|
destroy(): void;
|
|
118
|
+
private _getMelFilterbank;
|
|
119
|
+
private _getLogRanges;
|
|
84
120
|
private _setupAnalyser;
|
|
85
121
|
private _connectSource;
|
|
86
122
|
private _resolveToAudioNode;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAudioContextReady, getAudioContext, onAudioContextReady } from './audio-context.js';
|
|
2
|
+
import { buildMelFilterbank } from './dsp/mel.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Lightweight visualisation analyser backed by a Web Audio AnalyserNode.
|
|
@@ -20,6 +21,10 @@ class AudioAnalyser {
|
|
|
20
21
|
_floatSpectrum;
|
|
21
22
|
_byteWaveform;
|
|
22
23
|
_floatWaveform;
|
|
24
|
+
// Cached mel/log filterbanks. Key: `${bands}|${fMin}|${fMax}|${fftSize}`.
|
|
25
|
+
// Filterbank construction is non-trivial; caching avoids per-frame allocation.
|
|
26
|
+
_melCache = new Map();
|
|
27
|
+
_logCache = new Map();
|
|
23
28
|
/**
|
|
24
29
|
* Create an `AudioAnalyser` with the given options. The underlying
|
|
25
30
|
* `AnalyserNode` is created immediately if the `AudioContext` is already
|
|
@@ -31,6 +36,7 @@ class AudioAnalyser {
|
|
|
31
36
|
smoothingTimeConstant: options?.smoothingTimeConstant ?? 0.8,
|
|
32
37
|
minDecibels: options?.minDecibels ?? -100,
|
|
33
38
|
maxDecibels: options?.maxDecibels ?? -30,
|
|
39
|
+
source: options?.source ?? null,
|
|
34
40
|
};
|
|
35
41
|
const binCount = this._options.fftSize >> 1;
|
|
36
42
|
this._byteSpectrum = new Uint8Array(binCount);
|
|
@@ -43,6 +49,9 @@ class AudioAnalyser {
|
|
|
43
49
|
else {
|
|
44
50
|
onAudioContextReady.once(this._setupAnalyser, this);
|
|
45
51
|
}
|
|
52
|
+
if (options?.source !== undefined && options.source !== null) {
|
|
53
|
+
this.source = options.source;
|
|
54
|
+
}
|
|
46
55
|
}
|
|
47
56
|
// -----------------------------------------------------------------------
|
|
48
57
|
// Source setter
|
|
@@ -198,6 +207,81 @@ class AudioAnalyser {
|
|
|
198
207
|
return this.getBandEnergy(0, nyquist);
|
|
199
208
|
}
|
|
200
209
|
// -----------------------------------------------------------------------
|
|
210
|
+
// Spectrum mapping (mel / log scaling)
|
|
211
|
+
//
|
|
212
|
+
// The raw FFT bins from AnalyserNode are linearly distributed across
|
|
213
|
+
// 0..nyquist, which gives bass a few bins and treble hundreds. For
|
|
214
|
+
// visualisations you typically want perceptually-weighted spacing.
|
|
215
|
+
//
|
|
216
|
+
// Both `getSpectrumMel` and `getSpectrumLog` operate on the byte-domain
|
|
217
|
+
// spectrum (0..255) and produce byte output by default. The float
|
|
218
|
+
// variants operate on dBFS values and produce float output.
|
|
219
|
+
//
|
|
220
|
+
// Filterbanks are cached on the analyser; they only rebuild when fftSize
|
|
221
|
+
// or the (bands, fMin, fMax) parameters change.
|
|
222
|
+
// -----------------------------------------------------------------------
|
|
223
|
+
/**
|
|
224
|
+
* Mel-scaled spectrum, byte domain (0..255). Output length = `bands`.
|
|
225
|
+
* Each output bin is a triangular-weighted sum of the linear FFT bins
|
|
226
|
+
* underneath it, mel-spaced from `fMin` to `fMax`.
|
|
227
|
+
*/
|
|
228
|
+
getSpectrumMel(into, options) {
|
|
229
|
+
const bandCount = options?.bands ?? 32;
|
|
230
|
+
const out = into ?? new Uint8Array(bandCount);
|
|
231
|
+
const filterbank = this._getMelFilterbank(bandCount, options?.fMin ?? 20, options?.fMax ?? 20000);
|
|
232
|
+
if (!this._analyser || filterbank === null) {
|
|
233
|
+
out.fill(0);
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
this._analyser.getByteFrequencyData(this._byteSpectrum);
|
|
237
|
+
applyFilterbank(this._byteSpectrum, filterbank, out);
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
/** Mel-scaled spectrum, float domain (dBFS values). Output length = `bands`. */
|
|
241
|
+
getSpectrumMelFloat(into, options) {
|
|
242
|
+
const bandCount = options?.bands ?? 32;
|
|
243
|
+
const out = into ?? new Float32Array(bandCount);
|
|
244
|
+
const filterbank = this._getMelFilterbank(bandCount, options?.fMin ?? 20, options?.fMax ?? 20000);
|
|
245
|
+
if (!this._analyser || filterbank === null) {
|
|
246
|
+
out.fill(0);
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
this._analyser.getFloatFrequencyData(this._floatSpectrum);
|
|
250
|
+
applyFilterbank(this._floatSpectrum, filterbank, out);
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Log-scaled spectrum, byte domain (0..255). Output length = `bands`.
|
|
255
|
+
* Bins are spaced so each output covers an equal fraction of the
|
|
256
|
+
* `log2(fMax / fMin)` octave range — useful when you want a
|
|
257
|
+
* frequency-axis visualization where each octave gets the same width.
|
|
258
|
+
*/
|
|
259
|
+
getSpectrumLog(into, options) {
|
|
260
|
+
const bandCount = options?.bands ?? 32;
|
|
261
|
+
const out = into ?? new Uint8Array(bandCount);
|
|
262
|
+
const ranges = this._getLogRanges(bandCount, options?.fMin ?? 20, options?.fMax ?? 20000);
|
|
263
|
+
if (!this._analyser || ranges === null) {
|
|
264
|
+
out.fill(0);
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
this._analyser.getByteFrequencyData(this._byteSpectrum);
|
|
268
|
+
applyLogRanges(this._byteSpectrum, ranges, out);
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
/** Log-scaled spectrum, float domain (dBFS values). Output length = `bands`. */
|
|
272
|
+
getSpectrumLogFloat(into, options) {
|
|
273
|
+
const bandCount = options?.bands ?? 32;
|
|
274
|
+
const out = into ?? new Float32Array(bandCount);
|
|
275
|
+
const ranges = this._getLogRanges(bandCount, options?.fMin ?? 20, options?.fMax ?? 20000);
|
|
276
|
+
if (!this._analyser || ranges === null) {
|
|
277
|
+
out.fill(0);
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
this._analyser.getFloatFrequencyData(this._floatSpectrum);
|
|
281
|
+
applyLogRanges(this._floatSpectrum, ranges, out);
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
// -----------------------------------------------------------------------
|
|
201
285
|
// Lifecycle
|
|
202
286
|
// -----------------------------------------------------------------------
|
|
203
287
|
/**
|
|
@@ -215,6 +299,34 @@ class AudioAnalyser {
|
|
|
215
299
|
// -----------------------------------------------------------------------
|
|
216
300
|
// Private helpers
|
|
217
301
|
// -----------------------------------------------------------------------
|
|
302
|
+
_getMelFilterbank(bands, fMin, fMax) {
|
|
303
|
+
if (!this._analyser)
|
|
304
|
+
return null;
|
|
305
|
+
const ctx = getAudioContext();
|
|
306
|
+
const fftSize = this._options.fftSize;
|
|
307
|
+
const clampedFmax = Math.min(fMax, ctx.sampleRate / 2);
|
|
308
|
+
const key = `${bands}|${fMin}|${clampedFmax}|${fftSize}`;
|
|
309
|
+
let cached = this._melCache.get(key);
|
|
310
|
+
if (cached === undefined) {
|
|
311
|
+
cached = buildMelFilterbank(bands, fMin, clampedFmax, fftSize, ctx.sampleRate);
|
|
312
|
+
this._melCache.set(key, cached);
|
|
313
|
+
}
|
|
314
|
+
return cached;
|
|
315
|
+
}
|
|
316
|
+
_getLogRanges(bands, fMin, fMax) {
|
|
317
|
+
if (!this._analyser)
|
|
318
|
+
return null;
|
|
319
|
+
const ctx = getAudioContext();
|
|
320
|
+
const fftSize = this._options.fftSize;
|
|
321
|
+
const clampedFmax = Math.min(fMax, ctx.sampleRate / 2);
|
|
322
|
+
const key = `${bands}|${fMin}|${clampedFmax}|${fftSize}`;
|
|
323
|
+
let cached = this._logCache.get(key);
|
|
324
|
+
if (cached === undefined) {
|
|
325
|
+
cached = buildLogRanges(bands, fMin, clampedFmax, fftSize, ctx.sampleRate);
|
|
326
|
+
this._logCache.set(key, cached);
|
|
327
|
+
}
|
|
328
|
+
return cached;
|
|
329
|
+
}
|
|
218
330
|
_setupAnalyser(audioContext) {
|
|
219
331
|
const node = audioContext.createAnalyser();
|
|
220
332
|
node.fftSize = this._options.fftSize;
|
|
@@ -305,6 +417,42 @@ class AudioAnalyser {
|
|
|
305
417
|
}
|
|
306
418
|
}
|
|
307
419
|
}
|
|
420
|
+
function buildLogRanges(bandCount, fMin, fMax, fftSize, sampleRate) {
|
|
421
|
+
const binCount = fftSize >> 1;
|
|
422
|
+
const nyquist = sampleRate / 2;
|
|
423
|
+
const logMin = Math.log(fMin);
|
|
424
|
+
const logMax = Math.log(fMax);
|
|
425
|
+
const ranges = [];
|
|
426
|
+
for (let b = 0; b < bandCount; b++) {
|
|
427
|
+
const lowHz = Math.exp(logMin + ((logMax - logMin) * b) / bandCount);
|
|
428
|
+
const highHz = Math.exp(logMin + ((logMax - logMin) * (b + 1)) / bandCount);
|
|
429
|
+
const startBin = Math.max(0, Math.min(binCount - 1, Math.floor((lowHz / nyquist) * binCount)));
|
|
430
|
+
const endBin = Math.max(startBin, Math.min(binCount - 1, Math.ceil((highHz / nyquist) * binCount) - 1));
|
|
431
|
+
ranges.push({ startBin, endBin });
|
|
432
|
+
}
|
|
433
|
+
return ranges;
|
|
434
|
+
}
|
|
435
|
+
function applyFilterbank(spectrum, bands, out) {
|
|
436
|
+
for (let b = 0; b < bands.length; b++) {
|
|
437
|
+
const { startBin, weights } = bands[b];
|
|
438
|
+
let sum = 0;
|
|
439
|
+
for (let i = 0; i < weights.length; i++) {
|
|
440
|
+
sum += spectrum[startBin + i] * weights[i];
|
|
441
|
+
}
|
|
442
|
+
out[b] = sum;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function applyLogRanges(spectrum, ranges, out) {
|
|
446
|
+
for (let b = 0; b < ranges.length; b++) {
|
|
447
|
+
const { startBin, endBin } = ranges[b];
|
|
448
|
+
let sum = 0;
|
|
449
|
+
const count = endBin - startBin + 1;
|
|
450
|
+
for (let i = startBin; i <= endBin; i++) {
|
|
451
|
+
sum += spectrum[i];
|
|
452
|
+
}
|
|
453
|
+
out[b] = sum / count;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
308
456
|
|
|
309
457
|
export { AudioAnalyser };
|
|
310
458
|
//# sourceMappingURL=AudioAnalyser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioAnalyser.js","sources":["../../../../src/audio/AudioAnalyser.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AudioAnalyser.js","sources":["../../../../src/audio/AudioAnalyser.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,27 @@ export interface BeatDetectorOptions {
|
|
|
23
23
|
* switches active time signature via hysteresis. Set false to lock to 4/4.
|
|
24
24
|
*/
|
|
25
25
|
enableTimeSignatureDetection?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Optional initial source. Equivalent to constructing then assigning
|
|
28
|
+
* `detector.source = value`; provided for ergonomic one-shot construction.
|
|
29
|
+
* The setter remains usable for runtime source switches.
|
|
30
|
+
*/
|
|
31
|
+
source?: BeatDetectorSource;
|
|
32
|
+
/**
|
|
33
|
+
* Half-life in seconds for the {@link BeatDetector.pulse} envelope.
|
|
34
|
+
* Default 0.15 — `pulse` halves every 150ms after each beat.
|
|
35
|
+
*/
|
|
36
|
+
pulseHalfLife?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Half-life in seconds for the {@link BeatDetector.barPulse} envelope.
|
|
39
|
+
* Default 0.3 — slower than beat pulse for downbeat-emphasized visuals.
|
|
40
|
+
*/
|
|
41
|
+
barPulseHalfLife?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Time window in seconds for {@link BeatDetector.justBeat}. Default 0.03
|
|
44
|
+
* — true for the visual frame(s) within 30ms of a beat onset.
|
|
45
|
+
*/
|
|
46
|
+
justBeatWindow?: number;
|
|
26
47
|
}
|
|
27
48
|
export interface BeatInfo {
|
|
28
49
|
/** audioContext.currentTime when the beat occurred. */
|
|
@@ -114,6 +135,15 @@ export declare class BeatDetector {
|
|
|
114
135
|
private _timeSignature;
|
|
115
136
|
private _nextDownbeatTime;
|
|
116
137
|
private _lookahead;
|
|
138
|
+
/**
|
|
139
|
+
* Half-life in seconds for the {@link pulse} envelope. Mutable; default 0.15.
|
|
140
|
+
* Smaller values give a snappier pulse, larger values a longer afterglow.
|
|
141
|
+
*/
|
|
142
|
+
pulseHalfLife: number;
|
|
143
|
+
/** Half-life for the {@link barPulse} envelope. Mutable; default 0.3. */
|
|
144
|
+
barPulseHalfLife: number;
|
|
145
|
+
/** Time window for {@link justBeat}. Mutable; default 0.03 (30ms). */
|
|
146
|
+
justBeatWindow: number;
|
|
117
147
|
constructor(options?: BeatDetectorOptions);
|
|
118
148
|
get source(): BeatDetectorSource;
|
|
119
149
|
set source(value: BeatDetectorSource);
|
|
@@ -132,6 +162,38 @@ export declare class BeatDetector {
|
|
|
132
162
|
get timeSignature(): TimeSignature;
|
|
133
163
|
get nextDownbeatTime(): number;
|
|
134
164
|
get lookahead(): readonly UpcomingBeat[];
|
|
165
|
+
/**
|
|
166
|
+
* Seconds elapsed since the most recent beat, derived from {@link beatPhase}
|
|
167
|
+
* and {@link tempo}. Returns 0 when the detector hasn't locked yet.
|
|
168
|
+
*/
|
|
169
|
+
get secondsSinceLastBeat(): number;
|
|
170
|
+
/**
|
|
171
|
+
* 0..1 envelope, peaks at 1.0 the moment a beat fires and halves every
|
|
172
|
+
* {@link pulseHalfLife} seconds. Drives "pulse on the beat" visuals
|
|
173
|
+
* with a single multiplication: `sprite.scale = 1 + clock.pulse * 0.3`.
|
|
174
|
+
*/
|
|
175
|
+
get pulse(): number;
|
|
176
|
+
/**
|
|
177
|
+
* Like {@link pulse} but resets on downbeats and decays per
|
|
178
|
+
* {@link barPulseHalfLife}. Useful for emphasizing the first beat of
|
|
179
|
+
* each bar (e.g. brighter flash on "1" vs "2,3,4").
|
|
180
|
+
*/
|
|
181
|
+
get barPulse(): number;
|
|
182
|
+
/**
|
|
183
|
+
* True for the visual frame(s) within {@link justBeatWindow} seconds of
|
|
184
|
+
* a beat onset. Use for one-shot triggers (strobe flash, particle burst,
|
|
185
|
+
* sample retrigger). Default window 30ms covers a typical 60fps frame.
|
|
186
|
+
*/
|
|
187
|
+
get justBeat(): boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Phase 0..1 within a subdivision of the current beat. `division` is the
|
|
190
|
+
* number of subdivisions per beat: 2 for 8th notes, 4 for 16th notes,
|
|
191
|
+
* 3 for triplets. Use to drive sub-beat-resolution effects:
|
|
192
|
+
*
|
|
193
|
+
* const sixteenth = clock.subdivisionPhase(4);
|
|
194
|
+
* if (sixteenth < 0.05) flash();
|
|
195
|
+
*/
|
|
196
|
+
subdivisionPhase(division: number): number;
|
|
135
197
|
destroy(): void;
|
|
136
198
|
private _setup;
|
|
137
199
|
private _onWorkletMessage;
|
|
@@ -705,6 +705,15 @@ class BeatDetector {
|
|
|
705
705
|
_timeSignature = { numerator: 4, denominator: 4 };
|
|
706
706
|
_nextDownbeatTime = 0;
|
|
707
707
|
_lookahead = Object.freeze([]);
|
|
708
|
+
/**
|
|
709
|
+
* Half-life in seconds for the {@link pulse} envelope. Mutable; default 0.15.
|
|
710
|
+
* Smaller values give a snappier pulse, larger values a longer afterglow.
|
|
711
|
+
*/
|
|
712
|
+
pulseHalfLife;
|
|
713
|
+
/** Half-life for the {@link barPulse} envelope. Mutable; default 0.3. */
|
|
714
|
+
barPulseHalfLife;
|
|
715
|
+
/** Time window for {@link justBeat}. Mutable; default 0.03 (30ms). */
|
|
716
|
+
justBeatWindow;
|
|
708
717
|
constructor(options) {
|
|
709
718
|
this._options = {
|
|
710
719
|
minBpm: options?.minBpm ?? 50,
|
|
@@ -715,13 +724,25 @@ class BeatDetector {
|
|
|
715
724
|
settlingMs: options?.settlingMs ?? 1500,
|
|
716
725
|
melBands: options?.melBands ?? 24,
|
|
717
726
|
enableTimeSignatureDetection: options?.enableTimeSignatureDetection ?? true,
|
|
727
|
+
// Visual-state options aren't part of worklet config; cached in the public
|
|
728
|
+
// fields below but kept in the Required<> shape for type completeness.
|
|
729
|
+
source: options?.source ?? null,
|
|
730
|
+
pulseHalfLife: options?.pulseHalfLife ?? 0.15,
|
|
731
|
+
barPulseHalfLife: options?.barPulseHalfLife ?? 0.3,
|
|
732
|
+
justBeatWindow: options?.justBeatWindow ?? 0.03,
|
|
718
733
|
};
|
|
734
|
+
this.pulseHalfLife = this._options.pulseHalfLife;
|
|
735
|
+
this.barPulseHalfLife = this._options.barPulseHalfLife;
|
|
736
|
+
this.justBeatWindow = this._options.justBeatWindow;
|
|
719
737
|
if (isAudioContextReady()) {
|
|
720
738
|
this._setup(getAudioContext());
|
|
721
739
|
}
|
|
722
740
|
else {
|
|
723
741
|
onAudioContextReady.once(this._setup, this);
|
|
724
742
|
}
|
|
743
|
+
if (options?.source !== undefined && options.source !== null) {
|
|
744
|
+
this.source = options.source;
|
|
745
|
+
}
|
|
725
746
|
}
|
|
726
747
|
// -----------------------------------------------------------------------
|
|
727
748
|
// Source setter (polymorphic tap)
|
|
@@ -802,6 +823,62 @@ class BeatDetector {
|
|
|
802
823
|
return this._lookahead;
|
|
803
824
|
}
|
|
804
825
|
// -----------------------------------------------------------------------
|
|
826
|
+
// Visual derived state — pure getters for per-frame polling
|
|
827
|
+
// -----------------------------------------------------------------------
|
|
828
|
+
/**
|
|
829
|
+
* Seconds elapsed since the most recent beat, derived from {@link beatPhase}
|
|
830
|
+
* and {@link tempo}. Returns 0 when the detector hasn't locked yet.
|
|
831
|
+
*/
|
|
832
|
+
get secondsSinceLastBeat() {
|
|
833
|
+
if (this._tempo === 0)
|
|
834
|
+
return 0;
|
|
835
|
+
return this._beatPhase * (60 / this._tempo);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* 0..1 envelope, peaks at 1.0 the moment a beat fires and halves every
|
|
839
|
+
* {@link pulseHalfLife} seconds. Drives "pulse on the beat" visuals
|
|
840
|
+
* with a single multiplication: `sprite.scale = 1 + clock.pulse * 0.3`.
|
|
841
|
+
*/
|
|
842
|
+
get pulse() {
|
|
843
|
+
if (this._tempo === 0)
|
|
844
|
+
return 0;
|
|
845
|
+
return Math.pow(0.5, this.secondsSinceLastBeat / this.pulseHalfLife);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Like {@link pulse} but resets on downbeats and decays per
|
|
849
|
+
* {@link barPulseHalfLife}. Useful for emphasizing the first beat of
|
|
850
|
+
* each bar (e.g. brighter flash on "1" vs "2,3,4").
|
|
851
|
+
*/
|
|
852
|
+
get barPulse() {
|
|
853
|
+
if (this._tempo === 0 || this._barLength === 0)
|
|
854
|
+
return 0;
|
|
855
|
+
const secondsPerBeat = 60 / this._tempo;
|
|
856
|
+
const lastDownbeat = this._nextDownbeatTime - this._barLength * secondsPerBeat;
|
|
857
|
+
const elapsed = Math.max(0, getAudioContext().currentTime - lastDownbeat);
|
|
858
|
+
return Math.pow(0.5, elapsed / this.barPulseHalfLife);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* True for the visual frame(s) within {@link justBeatWindow} seconds of
|
|
862
|
+
* a beat onset. Use for one-shot triggers (strobe flash, particle burst,
|
|
863
|
+
* sample retrigger). Default window 30ms covers a typical 60fps frame.
|
|
864
|
+
*/
|
|
865
|
+
get justBeat() {
|
|
866
|
+
return this._tempo > 0 && this.secondsSinceLastBeat < this.justBeatWindow;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Phase 0..1 within a subdivision of the current beat. `division` is the
|
|
870
|
+
* number of subdivisions per beat: 2 for 8th notes, 4 for 16th notes,
|
|
871
|
+
* 3 for triplets. Use to drive sub-beat-resolution effects:
|
|
872
|
+
*
|
|
873
|
+
* const sixteenth = clock.subdivisionPhase(4);
|
|
874
|
+
* if (sixteenth < 0.05) flash();
|
|
875
|
+
*/
|
|
876
|
+
subdivisionPhase(division) {
|
|
877
|
+
if (!Number.isFinite(division) || division <= 0)
|
|
878
|
+
return 0;
|
|
879
|
+
return (this._beatPhase * division) % 1;
|
|
880
|
+
}
|
|
881
|
+
// -----------------------------------------------------------------------
|
|
805
882
|
// Lifecycle
|
|
806
883
|
// -----------------------------------------------------------------------
|
|
807
884
|
destroy() {
|