@codexo/exojs 0.6.11 → 0.7.11
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 +1356 -0
- package/dist/esm/audio/AbstractMedia.d.ts +18 -0
- package/dist/esm/audio/AbstractMedia.js +66 -0
- package/dist/esm/audio/AbstractMedia.js.map +1 -1
- package/dist/esm/audio/AudioAnalyser.d.ts +62 -23
- package/dist/esm/audio/AudioAnalyser.js +261 -57
- package/dist/esm/audio/AudioAnalyser.js.map +1 -1
- package/dist/esm/audio/AudioBus.d.ts +45 -0
- package/dist/esm/audio/AudioBus.js +219 -0
- package/dist/esm/audio/AudioBus.js.map +1 -0
- package/dist/esm/audio/AudioFilter.d.ts +9 -0
- package/dist/esm/audio/AudioFilter.js +7 -0
- package/dist/esm/audio/AudioFilter.js.map +1 -0
- package/dist/esm/audio/AudioListener.d.ts +20 -0
- package/dist/esm/audio/AudioListener.js +86 -0
- package/dist/esm/audio/AudioListener.js.map +1 -0
- package/dist/esm/audio/AudioManager.d.ts +31 -0
- package/dist/esm/audio/AudioManager.js +102 -0
- package/dist/esm/audio/AudioManager.js.map +1 -0
- package/dist/esm/audio/BeatDetector.d.ts +121 -0
- package/dist/esm/audio/BeatDetector.js +936 -0
- package/dist/esm/audio/BeatDetector.js.map +1 -0
- package/dist/esm/audio/Envelope.d.ts +44 -0
- package/dist/esm/audio/Envelope.js +60 -0
- package/dist/esm/audio/Envelope.js.map +1 -0
- package/dist/esm/audio/Music.d.ts +8 -0
- package/dist/esm/audio/Music.js +33 -4
- package/dist/esm/audio/Music.js.map +1 -1
- package/dist/esm/audio/OscillatorSound.d.ts +98 -0
- package/dist/esm/audio/OscillatorSound.js +342 -0
- package/dist/esm/audio/OscillatorSound.js.map +1 -0
- package/dist/esm/audio/Sound.d.ts +94 -9
- package/dist/esm/audio/Sound.js +283 -117
- package/dist/esm/audio/Sound.js.map +1 -1
- package/dist/esm/audio/crossFade.d.ts +19 -0
- package/dist/esm/audio/crossFade.js +26 -0
- package/dist/esm/audio/crossFade.js.map +1 -0
- package/dist/esm/audio/dsp/fft.d.ts +22 -0
- package/dist/esm/audio/dsp/mel.d.ts +43 -0
- package/dist/esm/audio/dsp/tempogram.d.ts +51 -0
- package/dist/esm/audio/filters/ChorusFilter.d.ts +47 -0
- package/dist/esm/audio/filters/ChorusFilter.js +139 -0
- package/dist/esm/audio/filters/ChorusFilter.js.map +1 -0
- package/dist/esm/audio/filters/CompressorFilter.d.ts +31 -0
- package/dist/esm/audio/filters/CompressorFilter.js +97 -0
- package/dist/esm/audio/filters/CompressorFilter.js.map +1 -0
- package/dist/esm/audio/filters/DelayFilter.d.ts +23 -0
- package/dist/esm/audio/filters/DelayFilter.js +100 -0
- package/dist/esm/audio/filters/DelayFilter.js.map +1 -0
- package/dist/esm/audio/filters/DuckingFilter.d.ts +31 -0
- package/dist/esm/audio/filters/DuckingFilter.js +152 -0
- package/dist/esm/audio/filters/DuckingFilter.js.map +1 -0
- package/dist/esm/audio/filters/EqualizerFilter.d.ts +29 -0
- package/dist/esm/audio/filters/EqualizerFilter.js +94 -0
- package/dist/esm/audio/filters/EqualizerFilter.js.map +1 -0
- package/dist/esm/audio/filters/GranularFilter.d.ts +56 -0
- package/dist/esm/audio/filters/GranularFilter.js +170 -0
- package/dist/esm/audio/filters/GranularFilter.js.map +1 -0
- package/dist/esm/audio/filters/HighpassFilter.d.ts +19 -0
- package/dist/esm/audio/filters/HighpassFilter.js +62 -0
- package/dist/esm/audio/filters/HighpassFilter.js.map +1 -0
- package/dist/esm/audio/filters/LowpassFilter.d.ts +19 -0
- package/dist/esm/audio/filters/LowpassFilter.js +62 -0
- package/dist/esm/audio/filters/LowpassFilter.js.map +1 -0
- package/dist/esm/audio/filters/PitchShiftFilter.d.ts +42 -0
- package/dist/esm/audio/filters/PitchShiftFilter.js +130 -0
- package/dist/esm/audio/filters/PitchShiftFilter.js.map +1 -0
- package/dist/esm/audio/filters/ReverbFilter.d.ts +24 -0
- package/dist/esm/audio/filters/ReverbFilter.js +107 -0
- package/dist/esm/audio/filters/ReverbFilter.js.map +1 -0
- package/dist/esm/audio/filters/VocoderFilter.d.ts +38 -0
- package/dist/esm/audio/filters/VocoderFilter.js +163 -0
- package/dist/esm/audio/filters/VocoderFilter.js.map +1 -0
- package/dist/esm/audio/filters/WorkletFilter.d.ts +46 -0
- package/dist/esm/audio/filters/WorkletFilter.js +101 -0
- package/dist/esm/audio/filters/WorkletFilter.js.map +1 -0
- package/dist/esm/audio/filters/index.d.ts +12 -0
- package/dist/esm/audio/index.d.ts +15 -1
- package/dist/esm/audio/worklet/registerWorklet.d.ts +10 -0
- package/dist/esm/audio/worklet/registerWorklet.js +44 -0
- package/dist/esm/audio/worklet/registerWorklet.js.map +1 -0
- package/dist/esm/core/Application.d.ts +19 -0
- package/dist/esm/core/Application.js +76 -2
- package/dist/esm/core/Application.js.map +1 -1
- package/dist/esm/core/SceneNode.d.ts +9 -1
- package/dist/esm/core/SceneNode.js +44 -6
- package/dist/esm/core/SceneNode.js.map +1 -1
- package/dist/esm/core/Time.js +1 -1
- package/dist/esm/core/index.d.ts +0 -1
- package/dist/esm/debug/BoundingBoxesLayer.d.ts +18 -0
- package/dist/esm/debug/BoundingBoxesLayer.js +128 -0
- package/dist/esm/debug/BoundingBoxesLayer.js.map +1 -0
- package/dist/esm/debug/DebugLayer.d.ts +29 -0
- package/dist/esm/debug/DebugLayer.js +26 -0
- package/dist/esm/debug/DebugLayer.js.map +1 -0
- package/dist/esm/debug/DebugOverlay.d.ts +48 -0
- package/dist/esm/debug/DebugOverlay.js +117 -0
- package/dist/esm/debug/DebugOverlay.js.map +1 -0
- package/dist/esm/debug/HitTestLayer.d.ts +23 -0
- package/dist/esm/debug/HitTestLayer.js +109 -0
- package/dist/esm/debug/HitTestLayer.js.map +1 -0
- package/dist/esm/debug/PerformanceLayer.d.ts +21 -0
- package/dist/esm/debug/PerformanceLayer.js +175 -0
- package/dist/esm/debug/PerformanceLayer.js.map +1 -0
- package/dist/esm/debug/PointerStackLayer.d.ts +23 -0
- package/dist/esm/debug/PointerStackLayer.js +152 -0
- package/dist/esm/debug/PointerStackLayer.js.map +1 -0
- package/dist/esm/debug/index.d.ts +6 -0
- package/dist/esm/debug/index.js +7 -0
- package/dist/esm/debug/index.js.map +1 -0
- package/dist/esm/index.js +29 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/input/InputManager.d.ts +10 -0
- package/dist/esm/input/InputManager.js +35 -5
- package/dist/esm/input/InputManager.js.map +1 -1
- package/dist/esm/input/InteractionEvent.d.ts +18 -0
- package/dist/esm/input/InteractionEvent.js +29 -0
- package/dist/esm/input/InteractionEvent.js.map +1 -0
- package/dist/esm/input/InteractionManager.d.ts +134 -0
- package/dist/esm/input/InteractionManager.js +546 -0
- package/dist/esm/input/InteractionManager.js.map +1 -0
- package/dist/esm/input/index.d.ts +2 -0
- package/dist/esm/input/interaction-hooks.d.ts +34 -0
- package/dist/esm/input/interaction-hooks.js +35 -0
- package/dist/esm/input/interaction-hooks.js.map +1 -0
- package/dist/esm/math/Circle.d.ts +12 -2
- package/dist/esm/math/Circle.js +82 -14
- package/dist/esm/math/Circle.js.map +1 -1
- package/dist/esm/math/Interval.js +1 -1
- package/dist/esm/math/ObservableVector.d.ts +2 -2
- package/dist/esm/math/ObservableVector.js +4 -2
- package/dist/esm/math/ObservableVector.js.map +1 -1
- package/dist/esm/math/Polygon.d.ts +15 -1
- package/dist/esm/math/Polygon.js +58 -6
- package/dist/esm/math/Polygon.js.map +1 -1
- package/dist/esm/math/Quadtree.d.ts +47 -0
- package/dist/esm/math/Quadtree.js +168 -0
- package/dist/esm/math/Quadtree.js.map +1 -0
- package/dist/esm/math/Random.js +1 -1
- package/dist/esm/math/Size.js +1 -1
- package/dist/esm/math/Vector.js +1 -1
- package/dist/esm/math/collision-detection.js +4 -1
- package/dist/esm/math/collision-detection.js.map +1 -1
- package/dist/esm/math/index.d.ts +2 -0
- package/dist/esm/math/swept-collision.d.ts +90 -0
- package/dist/esm/math/swept-collision.js +255 -0
- package/dist/esm/math/swept-collision.js.map +1 -0
- package/dist/esm/particles/ParticleSystem.js +1 -0
- package/dist/esm/particles/ParticleSystem.js.map +1 -1
- package/dist/esm/particles/affectors/TorqueAffector.js +1 -1
- package/dist/esm/rendering/Container.d.ts +1 -0
- package/dist/esm/rendering/Container.js +19 -0
- package/dist/esm/rendering/Container.js.map +1 -1
- package/dist/esm/rendering/RenderNode.d.ts +27 -0
- package/dist/esm/rendering/RenderNode.js +44 -0
- package/dist/esm/rendering/RenderNode.js.map +1 -1
- package/dist/esm/rendering/View.d.ts +6 -4
- package/dist/esm/rendering/View.js +12 -2
- package/dist/esm/rendering/View.js.map +1 -1
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.d.ts +109 -0
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.js +268 -0
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.js.map +1 -0
- package/dist/esm/rendering/filters/WebGpuShaderFilter.d.ts +111 -0
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js +397 -0
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js.map +1 -0
- package/dist/esm/rendering/index.d.ts +3 -0
- package/dist/esm/rendering/mesh/Mesh.js +1 -0
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
- package/dist/esm/rendering/shader/upgradeFragmentShaderToGl300.d.ts +34 -0
- package/dist/esm/rendering/shader/upgradeFragmentShaderToGl300.js +60 -0
- package/dist/esm/rendering/shader/upgradeFragmentShaderToGl300.js.map +1 -0
- package/dist/esm/rendering/sprite/Sprite.d.ts +6 -1
- package/dist/esm/rendering/sprite/Sprite.js +41 -19
- package/dist/esm/rendering/sprite/Sprite.js.map +1 -1
- package/dist/esm/rendering/video/Video.d.ts +4 -0
- package/dist/esm/rendering/video/Video.js +32 -4
- package/dist/esm/rendering/video/Video.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2Backend.d.ts +4 -4
- package/dist/esm/rendering/webgl2/WebGl2Backend.js +7 -16
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuBackend.d.ts +10 -8
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +30 -40
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
- package/dist/exo.esm.js +8021 -2459
- package/dist/exo.esm.js.map +1 -1
- package/package.json +14 -2
- package/dist/esm/core/Quadtree.d.ts +0 -20
- package/dist/esm/core/Quadtree.js +0 -86
- package/dist/esm/core/Quadtree.js.map +0 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { WorkletFilter } from './WorkletFilter.js';
|
|
2
|
+
|
|
3
|
+
const duckingWorkletSource = `
|
|
4
|
+
class DuckingProcessor extends AudioWorkletProcessor {
|
|
5
|
+
static get parameterDescriptors() {
|
|
6
|
+
return [
|
|
7
|
+
{ name: 'threshold', defaultValue: -20, minValue: -100, maxValue: 0, automationRate: 'k-rate' },
|
|
8
|
+
{ name: 'ratio', defaultValue: 4, minValue: 1, maxValue: 20, automationRate: 'k-rate' },
|
|
9
|
+
{ name: 'attack', defaultValue: 0.03, minValue: 0.001, maxValue: 1, automationRate: 'k-rate' },
|
|
10
|
+
{ name: 'release', defaultValue: 0.3, minValue: 0.001, maxValue: 5, automationRate: 'k-rate' },
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
this._envelope = 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
process(inputs, outputs, parameters) {
|
|
20
|
+
const main = inputs[0];
|
|
21
|
+
const sidechain = inputs[1];
|
|
22
|
+
const output = outputs[0];
|
|
23
|
+
|
|
24
|
+
if (!main || main.length === 0 || !output || output.length === 0) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const threshold = parameters.threshold[0];
|
|
29
|
+
const ratio = parameters.ratio[0];
|
|
30
|
+
const attackCoeff = parameters.attack[0];
|
|
31
|
+
const releaseCoeff = parameters.release[0];
|
|
32
|
+
|
|
33
|
+
const blockLength = main[0]?.length ?? 0;
|
|
34
|
+
for (let i = 0; i < blockLength; i++) {
|
|
35
|
+
// Sidechain RMS sample (mono-mix L+R for detection)
|
|
36
|
+
let scSample = 0;
|
|
37
|
+
if (sidechain && sidechain.length > 0) {
|
|
38
|
+
const left = sidechain[0]?.[i] ?? 0;
|
|
39
|
+
const right = sidechain[1]?.[i] ?? left;
|
|
40
|
+
scSample = (Math.abs(left) + Math.abs(right)) * 0.5;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Envelope follower
|
|
44
|
+
const coeff = scSample > this._envelope ? attackCoeff : releaseCoeff;
|
|
45
|
+
this._envelope += (scSample - this._envelope) * coeff;
|
|
46
|
+
|
|
47
|
+
// Compute gain reduction in dB
|
|
48
|
+
const envDb = 20 * Math.log10(this._envelope + 1e-9);
|
|
49
|
+
const overshoot = Math.max(0, envDb - threshold);
|
|
50
|
+
const reduction = overshoot * (1 - 1 / ratio);
|
|
51
|
+
const gain = Math.pow(10, -reduction / 20);
|
|
52
|
+
|
|
53
|
+
// Apply to all main channels
|
|
54
|
+
for (let ch = 0; ch < main.length; ch++) {
|
|
55
|
+
const mainCh = main[ch];
|
|
56
|
+
const outCh = output[ch];
|
|
57
|
+
if (mainCh && outCh && i < mainCh.length) {
|
|
58
|
+
outCh[i] = mainCh[i] * gain;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
registerProcessor('exojs-ducking', DuckingProcessor);
|
|
67
|
+
`;
|
|
68
|
+
class DuckingFilter extends WorkletFilter {
|
|
69
|
+
// Declared nullable because super() may trigger _onWorkletReady before the
|
|
70
|
+
// subclass constructor body runs (if construction is aborted by a throw).
|
|
71
|
+
_sidechain = null;
|
|
72
|
+
_threshold;
|
|
73
|
+
_ratio;
|
|
74
|
+
_attackMs;
|
|
75
|
+
_releaseMs;
|
|
76
|
+
constructor(options) {
|
|
77
|
+
super();
|
|
78
|
+
if (!options.sidechain) {
|
|
79
|
+
throw new Error('DuckingFilter requires a sidechain AudioBus.');
|
|
80
|
+
}
|
|
81
|
+
this._sidechain = options.sidechain;
|
|
82
|
+
this._threshold = options.threshold ?? -20;
|
|
83
|
+
this._ratio = options.ratio ?? 4;
|
|
84
|
+
this._attackMs = options.attackMs ?? 30;
|
|
85
|
+
this._releaseMs = options.releaseMs ?? 300;
|
|
86
|
+
}
|
|
87
|
+
get _workletName() {
|
|
88
|
+
return 'exojs-ducking';
|
|
89
|
+
}
|
|
90
|
+
get _workletSource() {
|
|
91
|
+
return duckingWorkletSource;
|
|
92
|
+
}
|
|
93
|
+
get _workletOptions() {
|
|
94
|
+
return { numberOfInputs: 2, numberOfOutputs: 1 };
|
|
95
|
+
}
|
|
96
|
+
_onWorkletReady(audioContext) {
|
|
97
|
+
// Guard against partially-constructed instances (constructor threw after super()).
|
|
98
|
+
if (!this._sidechain)
|
|
99
|
+
return;
|
|
100
|
+
// Apply current parameter values
|
|
101
|
+
this._setAudioParam('threshold', this._threshold);
|
|
102
|
+
this._setAudioParam('ratio', this._ratio);
|
|
103
|
+
this._setAudioParam('attack', this._msToCoeff(this._attackMs, audioContext.sampleRate));
|
|
104
|
+
this._setAudioParam('release', this._msToCoeff(this._releaseMs, audioContext.sampleRate));
|
|
105
|
+
// Wire sidechain to input 1 of the worklet
|
|
106
|
+
const sidechain = this._sidechain;
|
|
107
|
+
const sidechainOut = sidechain._getOutputNode();
|
|
108
|
+
if (sidechainOut && this._workletNode) {
|
|
109
|
+
sidechainOut.connect(this._workletNode, 0, 1);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
sidechain.onceSetup(() => {
|
|
113
|
+
const node = sidechain._getOutputNode();
|
|
114
|
+
if (node && this._workletNode) {
|
|
115
|
+
node.connect(this._workletNode, 0, 1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Convert ms time-constant to one-pole filter coefficient at given sample rate. */
|
|
121
|
+
_msToCoeff(ms, sampleRate) {
|
|
122
|
+
const tau = (ms * 0.001) * sampleRate;
|
|
123
|
+
return 1 - Math.exp(-1 / Math.max(1, tau));
|
|
124
|
+
}
|
|
125
|
+
get threshold() { return this._threshold; }
|
|
126
|
+
set threshold(value) {
|
|
127
|
+
this._threshold = Math.max(-100, Math.min(0, value));
|
|
128
|
+
this._setAudioParam('threshold', this._threshold);
|
|
129
|
+
}
|
|
130
|
+
get ratio() { return this._ratio; }
|
|
131
|
+
set ratio(value) {
|
|
132
|
+
this._ratio = Math.max(1, Math.min(20, value));
|
|
133
|
+
this._setAudioParam('ratio', this._ratio);
|
|
134
|
+
}
|
|
135
|
+
get attackMs() { return this._attackMs; }
|
|
136
|
+
set attackMs(value) {
|
|
137
|
+
this._attackMs = Math.max(0.001, value);
|
|
138
|
+
if (this._workletNode) {
|
|
139
|
+
this._setAudioParam('attack', this._msToCoeff(this._attackMs, this._workletNode.context.sampleRate));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
get releaseMs() { return this._releaseMs; }
|
|
143
|
+
set releaseMs(value) {
|
|
144
|
+
this._releaseMs = Math.max(0.001, value);
|
|
145
|
+
if (this._workletNode) {
|
|
146
|
+
this._setAudioParam('release', this._msToCoeff(this._releaseMs, this._workletNode.context.sampleRate));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { DuckingFilter };
|
|
152
|
+
//# sourceMappingURL=DuckingFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DuckingFilter.js","sources":["../../../../../src/audio/filters/DuckingFilter.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AudioFilter } from '../AudioFilter';
|
|
2
|
+
export interface EqualizerFilterOptions {
|
|
3
|
+
low?: number;
|
|
4
|
+
mid?: number;
|
|
5
|
+
high?: number;
|
|
6
|
+
lowFrequency?: number;
|
|
7
|
+
midFrequency?: number;
|
|
8
|
+
highFrequency?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class EqualizerFilter extends AudioFilter {
|
|
11
|
+
private _setup;
|
|
12
|
+
private _low;
|
|
13
|
+
private _mid;
|
|
14
|
+
private _high;
|
|
15
|
+
private _lowFrequency;
|
|
16
|
+
private _midFrequency;
|
|
17
|
+
private _highFrequency;
|
|
18
|
+
constructor(options?: EqualizerFilterOptions);
|
|
19
|
+
get inputNode(): AudioNode;
|
|
20
|
+
get outputNode(): AudioNode;
|
|
21
|
+
get low(): number;
|
|
22
|
+
set low(value: number);
|
|
23
|
+
get mid(): number;
|
|
24
|
+
set mid(value: number);
|
|
25
|
+
get high(): number;
|
|
26
|
+
set high(value: number);
|
|
27
|
+
destroy(): void;
|
|
28
|
+
private _setupNodes;
|
|
29
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { isAudioContextReady, getAudioContext, onAudioContextReady } from '../audio-context.js';
|
|
2
|
+
import { AudioFilter } from '../AudioFilter.js';
|
|
3
|
+
|
|
4
|
+
class EqualizerFilter extends AudioFilter {
|
|
5
|
+
_setup = null;
|
|
6
|
+
_low;
|
|
7
|
+
_mid;
|
|
8
|
+
_high;
|
|
9
|
+
_lowFrequency;
|
|
10
|
+
_midFrequency;
|
|
11
|
+
_highFrequency;
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
super();
|
|
14
|
+
this._low = Math.max(-40, Math.min(40, options.low ?? 0));
|
|
15
|
+
this._mid = Math.max(-40, Math.min(40, options.mid ?? 0));
|
|
16
|
+
this._high = Math.max(-40, Math.min(40, options.high ?? 0));
|
|
17
|
+
this._lowFrequency = options.lowFrequency ?? 250;
|
|
18
|
+
this._midFrequency = options.midFrequency ?? 1500;
|
|
19
|
+
this._highFrequency = options.highFrequency ?? 6000;
|
|
20
|
+
if (isAudioContextReady()) {
|
|
21
|
+
this._setupNodes(getAudioContext());
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
onAudioContextReady.once(this._setupNodes, this);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get inputNode() {
|
|
28
|
+
if (!this._setup)
|
|
29
|
+
throw new Error('EqualizerFilter not yet initialized.');
|
|
30
|
+
return this._setup.lowShelf;
|
|
31
|
+
}
|
|
32
|
+
get outputNode() {
|
|
33
|
+
if (!this._setup)
|
|
34
|
+
throw new Error('EqualizerFilter not yet initialized.');
|
|
35
|
+
return this._setup.highShelf;
|
|
36
|
+
}
|
|
37
|
+
get low() {
|
|
38
|
+
return this._low;
|
|
39
|
+
}
|
|
40
|
+
set low(value) {
|
|
41
|
+
this._low = Math.max(-40, Math.min(40, value));
|
|
42
|
+
if (this._setup) {
|
|
43
|
+
this._setup.lowShelf.gain.setTargetAtTime(this._low, this._setup.lowShelf.context.currentTime, 0.01);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
get mid() {
|
|
47
|
+
return this._mid;
|
|
48
|
+
}
|
|
49
|
+
set mid(value) {
|
|
50
|
+
this._mid = Math.max(-40, Math.min(40, value));
|
|
51
|
+
if (this._setup) {
|
|
52
|
+
this._setup.peaking.gain.setTargetAtTime(this._mid, this._setup.peaking.context.currentTime, 0.01);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
get high() {
|
|
56
|
+
return this._high;
|
|
57
|
+
}
|
|
58
|
+
set high(value) {
|
|
59
|
+
this._high = Math.max(-40, Math.min(40, value));
|
|
60
|
+
if (this._setup) {
|
|
61
|
+
this._setup.highShelf.gain.setTargetAtTime(this._high, this._setup.highShelf.context.currentTime, 0.01);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
destroy() {
|
|
65
|
+
onAudioContextReady.clearByContext(this);
|
|
66
|
+
if (this._setup) {
|
|
67
|
+
this._setup.lowShelf.disconnect();
|
|
68
|
+
this._setup.peaking.disconnect();
|
|
69
|
+
this._setup.highShelf.disconnect();
|
|
70
|
+
this._setup = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
_setupNodes(ctx) {
|
|
74
|
+
const lowShelf = ctx.createBiquadFilter();
|
|
75
|
+
lowShelf.type = 'lowshelf';
|
|
76
|
+
lowShelf.frequency.setValueAtTime(this._lowFrequency, ctx.currentTime);
|
|
77
|
+
lowShelf.gain.setValueAtTime(this._low, ctx.currentTime);
|
|
78
|
+
const peaking = ctx.createBiquadFilter();
|
|
79
|
+
peaking.type = 'peaking';
|
|
80
|
+
peaking.frequency.setValueAtTime(this._midFrequency, ctx.currentTime);
|
|
81
|
+
peaking.gain.setValueAtTime(this._mid, ctx.currentTime);
|
|
82
|
+
const highShelf = ctx.createBiquadFilter();
|
|
83
|
+
highShelf.type = 'highshelf';
|
|
84
|
+
highShelf.frequency.setValueAtTime(this._highFrequency, ctx.currentTime);
|
|
85
|
+
highShelf.gain.setValueAtTime(this._high, ctx.currentTime);
|
|
86
|
+
// Series chain: lowShelf → peaking → highShelf
|
|
87
|
+
lowShelf.connect(peaking);
|
|
88
|
+
peaking.connect(highShelf);
|
|
89
|
+
this._setup = { lowShelf, peaking, highShelf };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { EqualizerFilter };
|
|
94
|
+
//# sourceMappingURL=EqualizerFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EqualizerFilter.js","sources":["../../../../../src/audio/filters/EqualizerFilter.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { WorkletFilter } from './WorkletFilter';
|
|
2
|
+
export interface GranularFilterOptions {
|
|
3
|
+
/** Grain size in seconds. 0.005-0.5. Default 0.05 (50ms). */
|
|
4
|
+
grainSize?: number;
|
|
5
|
+
/** Grains per second (overlap density). 1-500. Default 50. */
|
|
6
|
+
density?: number;
|
|
7
|
+
/** Random offset spread, 0-1. Higher = wider time-window scatter. Default 0.5. */
|
|
8
|
+
spread?: number;
|
|
9
|
+
/** Minimum random pitch ratio per grain. Default 1.0. */
|
|
10
|
+
pitchMin?: number;
|
|
11
|
+
/** Maximum random pitch ratio per grain. Default 1.0. */
|
|
12
|
+
pitchMax?: number;
|
|
13
|
+
/** Dry/wet mix, 0..1. Default 1.0. */
|
|
14
|
+
wet?: number;
|
|
15
|
+
/** Internal circular-buffer length in seconds. Compile-time, not runtime. Default 2. */
|
|
16
|
+
bufferSeconds?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Granular synthesis filter — slices input into small Hann-windowed grains
|
|
20
|
+
* and replays them with randomized parameters. Use cases:
|
|
21
|
+
*
|
|
22
|
+
* - Ambient texture: long grain (200ms+), low density (10-30/s), high
|
|
23
|
+
* spread, pitch range 0.5-1.5x. Creates evolving pad-like sounds.
|
|
24
|
+
* - Glitch: short grain (10-20ms), high density (100+/s), very high
|
|
25
|
+
* spread, pitch range 0.7-1.3x. Stuttering, lo-fi effects.
|
|
26
|
+
* - Time-stretch (low pitch with overlap): grain ~50ms, density 50/s,
|
|
27
|
+
* spread 0, pitch 1.0. Pseudo-stretches the input by buffering.
|
|
28
|
+
* - Pitch cloud: medium grain, medium density, pitchMin 0.5, pitchMax 2.0
|
|
29
|
+
* gives a "shimmer" effect of overlapping pitches.
|
|
30
|
+
*/
|
|
31
|
+
export declare class GranularFilter extends WorkletFilter {
|
|
32
|
+
private _grainSize;
|
|
33
|
+
private _density;
|
|
34
|
+
private _spread;
|
|
35
|
+
private _pitchMin;
|
|
36
|
+
private _pitchMax;
|
|
37
|
+
private _wet;
|
|
38
|
+
private readonly _bufferSeconds;
|
|
39
|
+
constructor(options?: GranularFilterOptions);
|
|
40
|
+
protected get _workletName(): string;
|
|
41
|
+
protected get _workletSource(): string;
|
|
42
|
+
protected get _workletOptions(): AudioWorkletNodeOptions;
|
|
43
|
+
protected _onWorkletReady(): void;
|
|
44
|
+
get grainSize(): number;
|
|
45
|
+
set grainSize(value: number);
|
|
46
|
+
get density(): number;
|
|
47
|
+
set density(value: number);
|
|
48
|
+
get spread(): number;
|
|
49
|
+
set spread(value: number);
|
|
50
|
+
get pitchMin(): number;
|
|
51
|
+
set pitchMin(value: number);
|
|
52
|
+
get pitchMax(): number;
|
|
53
|
+
set pitchMax(value: number);
|
|
54
|
+
get wet(): number;
|
|
55
|
+
set wet(value: number);
|
|
56
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { WorkletFilter } from './WorkletFilter.js';
|
|
2
|
+
|
|
3
|
+
const granularWorkletSource = `
|
|
4
|
+
const sampleRate = globalThis.sampleRate;
|
|
5
|
+
|
|
6
|
+
class GranularProcessor extends AudioWorkletProcessor {
|
|
7
|
+
static get parameterDescriptors() {
|
|
8
|
+
return [
|
|
9
|
+
{ name: 'grainSize', defaultValue: 0.05, minValue: 0.005, maxValue: 0.5, automationRate: 'k-rate' },
|
|
10
|
+
{ name: 'density', defaultValue: 50, minValue: 1, maxValue: 500, automationRate: 'k-rate' },
|
|
11
|
+
{ name: 'spread', defaultValue: 0.5, minValue: 0, maxValue: 1.0, automationRate: 'k-rate' },
|
|
12
|
+
{ name: 'pitchMin', defaultValue: 1.0, minValue: 0.25, maxValue: 4.0, automationRate: 'k-rate' },
|
|
13
|
+
{ name: 'pitchMax', defaultValue: 1.0, minValue: 0.25, maxValue: 4.0, automationRate: 'k-rate' },
|
|
14
|
+
{ name: 'wet', defaultValue: 1.0, minValue: 0, maxValue: 1.0, automationRate: 'k-rate' },
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super();
|
|
20
|
+
const opts = options.processorOptions ?? {};
|
|
21
|
+
const bufferSeconds = opts.bufferSeconds ?? 2;
|
|
22
|
+
this._bufferLength = Math.floor(bufferSeconds * sampleRate);
|
|
23
|
+
this._buffer = new Float32Array(this._bufferLength);
|
|
24
|
+
this._writePos = 0;
|
|
25
|
+
this._timeUntilNextGrainSamples = 0;
|
|
26
|
+
this._grains = []; // { startPos, ageSamples, lengthSamples, pitch }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
process(inputs, outputs, parameters) {
|
|
30
|
+
const input = inputs[0]?.[0];
|
|
31
|
+
const output = outputs[0]?.[0];
|
|
32
|
+
if (!input || !output) return true;
|
|
33
|
+
|
|
34
|
+
const grainSize = parameters.grainSize[0];
|
|
35
|
+
const density = parameters.density[0];
|
|
36
|
+
const spread = parameters.spread[0];
|
|
37
|
+
const pitchMin = parameters.pitchMin[0];
|
|
38
|
+
const pitchMax = parameters.pitchMax[0];
|
|
39
|
+
const wet = parameters.wet[0];
|
|
40
|
+
|
|
41
|
+
const grainSizeSamples = Math.max(8, Math.floor(grainSize * sampleRate));
|
|
42
|
+
const samplesPerGrain = sampleRate / Math.max(1, density);
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < input.length; i++) {
|
|
45
|
+
// Write input to circular buffer
|
|
46
|
+
this._buffer[this._writePos] = input[i];
|
|
47
|
+
this._writePos = (this._writePos + 1) % this._bufferLength;
|
|
48
|
+
|
|
49
|
+
// Spawn new grain if scheduled
|
|
50
|
+
this._timeUntilNextGrainSamples -= 1;
|
|
51
|
+
if (this._timeUntilNextGrainSamples <= 0) {
|
|
52
|
+
// Random offset into recent past, biased by spread
|
|
53
|
+
const maxOffset = Math.floor(spread * this._bufferLength);
|
|
54
|
+
const offset = Math.floor(Math.random() * Math.max(1, maxOffset));
|
|
55
|
+
const startPos = (this._writePos - offset - grainSizeSamples + this._bufferLength) % this._bufferLength;
|
|
56
|
+
// Random pitch in [pitchMin, pitchMax]
|
|
57
|
+
const pitch = pitchMin + Math.random() * Math.max(0, pitchMax - pitchMin);
|
|
58
|
+
|
|
59
|
+
this._grains.push({ startPos, ageSamples: 0, lengthSamples: grainSizeSamples, pitch });
|
|
60
|
+
this._timeUntilNextGrainSamples = samplesPerGrain;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Mix all active grains (apply Hann window)
|
|
64
|
+
let grainSum = 0;
|
|
65
|
+
for (let g = this._grains.length - 1; g >= 0; g--) {
|
|
66
|
+
const grain = this._grains[g];
|
|
67
|
+
if (grain.ageSamples >= grain.lengthSamples) {
|
|
68
|
+
this._grains.splice(g, 1);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const phase = grain.ageSamples / grain.lengthSamples;
|
|
73
|
+
const window = 0.5 * (1 - Math.cos(2 * Math.PI * phase));
|
|
74
|
+
const readPos = grain.startPos + grain.ageSamples * grain.pitch;
|
|
75
|
+
const idx = Math.floor(readPos) % this._bufferLength;
|
|
76
|
+
const safeIdx = (idx + this._bufferLength) % this._bufferLength;
|
|
77
|
+
grainSum += this._buffer[safeIdx] * window;
|
|
78
|
+
|
|
79
|
+
grain.ageSamples++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
output[i] = (1 - wet) * input[i] + wet * grainSum;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
registerProcessor('exojs-granular', GranularProcessor);
|
|
88
|
+
`;
|
|
89
|
+
/**
|
|
90
|
+
* Granular synthesis filter — slices input into small Hann-windowed grains
|
|
91
|
+
* and replays them with randomized parameters. Use cases:
|
|
92
|
+
*
|
|
93
|
+
* - Ambient texture: long grain (200ms+), low density (10-30/s), high
|
|
94
|
+
* spread, pitch range 0.5-1.5x. Creates evolving pad-like sounds.
|
|
95
|
+
* - Glitch: short grain (10-20ms), high density (100+/s), very high
|
|
96
|
+
* spread, pitch range 0.7-1.3x. Stuttering, lo-fi effects.
|
|
97
|
+
* - Time-stretch (low pitch with overlap): grain ~50ms, density 50/s,
|
|
98
|
+
* spread 0, pitch 1.0. Pseudo-stretches the input by buffering.
|
|
99
|
+
* - Pitch cloud: medium grain, medium density, pitchMin 0.5, pitchMax 2.0
|
|
100
|
+
* gives a "shimmer" effect of overlapping pitches.
|
|
101
|
+
*/
|
|
102
|
+
class GranularFilter extends WorkletFilter {
|
|
103
|
+
_grainSize;
|
|
104
|
+
_density;
|
|
105
|
+
_spread;
|
|
106
|
+
_pitchMin;
|
|
107
|
+
_pitchMax;
|
|
108
|
+
_wet;
|
|
109
|
+
_bufferSeconds;
|
|
110
|
+
constructor(options = {}) {
|
|
111
|
+
super();
|
|
112
|
+
this._grainSize = Math.max(0.005, Math.min(0.5, options.grainSize ?? 0.05));
|
|
113
|
+
this._density = Math.max(1, Math.min(500, options.density ?? 50));
|
|
114
|
+
this._spread = Math.max(0, Math.min(1, options.spread ?? 0.5));
|
|
115
|
+
this._pitchMin = Math.max(0.25, Math.min(4, options.pitchMin ?? 1.0));
|
|
116
|
+
this._pitchMax = Math.max(0.25, Math.min(4, options.pitchMax ?? 1.0));
|
|
117
|
+
this._wet = Math.max(0, Math.min(1, options.wet ?? 1.0));
|
|
118
|
+
this._bufferSeconds = options.bufferSeconds ?? 2;
|
|
119
|
+
}
|
|
120
|
+
get _workletName() { return 'exojs-granular'; }
|
|
121
|
+
get _workletSource() { return granularWorkletSource; }
|
|
122
|
+
get _workletOptions() {
|
|
123
|
+
return {
|
|
124
|
+
numberOfInputs: 1,
|
|
125
|
+
numberOfOutputs: 1,
|
|
126
|
+
processorOptions: { bufferSeconds: this._bufferSeconds },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
_onWorkletReady() {
|
|
130
|
+
this._setAudioParam('grainSize', this._grainSize);
|
|
131
|
+
this._setAudioParam('density', this._density);
|
|
132
|
+
this._setAudioParam('spread', this._spread);
|
|
133
|
+
this._setAudioParam('pitchMin', this._pitchMin);
|
|
134
|
+
this._setAudioParam('pitchMax', this._pitchMax);
|
|
135
|
+
this._setAudioParam('wet', this._wet);
|
|
136
|
+
}
|
|
137
|
+
get grainSize() { return this._grainSize; }
|
|
138
|
+
set grainSize(value) {
|
|
139
|
+
this._grainSize = Math.max(0.005, Math.min(0.5, value));
|
|
140
|
+
this._setAudioParam('grainSize', this._grainSize);
|
|
141
|
+
}
|
|
142
|
+
get density() { return this._density; }
|
|
143
|
+
set density(value) {
|
|
144
|
+
this._density = Math.max(1, Math.min(500, value));
|
|
145
|
+
this._setAudioParam('density', this._density);
|
|
146
|
+
}
|
|
147
|
+
get spread() { return this._spread; }
|
|
148
|
+
set spread(value) {
|
|
149
|
+
this._spread = Math.max(0, Math.min(1, value));
|
|
150
|
+
this._setAudioParam('spread', this._spread);
|
|
151
|
+
}
|
|
152
|
+
get pitchMin() { return this._pitchMin; }
|
|
153
|
+
set pitchMin(value) {
|
|
154
|
+
this._pitchMin = Math.max(0.25, Math.min(4, value));
|
|
155
|
+
this._setAudioParam('pitchMin', this._pitchMin);
|
|
156
|
+
}
|
|
157
|
+
get pitchMax() { return this._pitchMax; }
|
|
158
|
+
set pitchMax(value) {
|
|
159
|
+
this._pitchMax = Math.max(0.25, Math.min(4, value));
|
|
160
|
+
this._setAudioParam('pitchMax', this._pitchMax);
|
|
161
|
+
}
|
|
162
|
+
get wet() { return this._wet; }
|
|
163
|
+
set wet(value) {
|
|
164
|
+
this._wet = Math.max(0, Math.min(1, value));
|
|
165
|
+
this._setAudioParam('wet', this._wet);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { GranularFilter };
|
|
170
|
+
//# sourceMappingURL=GranularFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GranularFilter.js","sources":["../../../../../src/audio/filters/GranularFilter.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AudioFilter } from '../AudioFilter';
|
|
2
|
+
export interface HighpassFilterOptions {
|
|
3
|
+
frequency?: number;
|
|
4
|
+
resonance?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class HighpassFilter extends AudioFilter {
|
|
7
|
+
private _node;
|
|
8
|
+
private _frequency;
|
|
9
|
+
private _resonance;
|
|
10
|
+
constructor(options?: HighpassFilterOptions);
|
|
11
|
+
get inputNode(): AudioNode;
|
|
12
|
+
get outputNode(): AudioNode;
|
|
13
|
+
get frequency(): number;
|
|
14
|
+
set frequency(value: number);
|
|
15
|
+
get resonance(): number;
|
|
16
|
+
set resonance(value: number);
|
|
17
|
+
destroy(): void;
|
|
18
|
+
private _setup;
|
|
19
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { isAudioContextReady, getAudioContext, onAudioContextReady } from '../audio-context.js';
|
|
2
|
+
import { AudioFilter } from '../AudioFilter.js';
|
|
3
|
+
|
|
4
|
+
class HighpassFilter extends AudioFilter {
|
|
5
|
+
_node = null;
|
|
6
|
+
_frequency;
|
|
7
|
+
_resonance;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
super();
|
|
10
|
+
this._frequency = options.frequency ?? 1000;
|
|
11
|
+
this._resonance = options.resonance ?? 1;
|
|
12
|
+
if (isAudioContextReady()) {
|
|
13
|
+
this._setup(getAudioContext());
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
onAudioContextReady.once(this._setup, this);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
get inputNode() {
|
|
20
|
+
if (!this._node)
|
|
21
|
+
throw new Error('HighpassFilter not yet initialized.');
|
|
22
|
+
return this._node;
|
|
23
|
+
}
|
|
24
|
+
get outputNode() {
|
|
25
|
+
if (!this._node)
|
|
26
|
+
throw new Error('HighpassFilter not yet initialized.');
|
|
27
|
+
return this._node;
|
|
28
|
+
}
|
|
29
|
+
get frequency() {
|
|
30
|
+
return this._frequency;
|
|
31
|
+
}
|
|
32
|
+
set frequency(value) {
|
|
33
|
+
this._frequency = Math.max(20, Math.min(20000, value));
|
|
34
|
+
if (this._node) {
|
|
35
|
+
this._node.frequency.setTargetAtTime(this._frequency, this._node.context.currentTime, 0.01);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
get resonance() {
|
|
39
|
+
return this._resonance;
|
|
40
|
+
}
|
|
41
|
+
set resonance(value) {
|
|
42
|
+
this._resonance = Math.max(0.0001, value);
|
|
43
|
+
if (this._node) {
|
|
44
|
+
this._node.Q.setTargetAtTime(this._resonance, this._node.context.currentTime, 0.01);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
destroy() {
|
|
48
|
+
onAudioContextReady.clearByContext(this);
|
|
49
|
+
this._node?.disconnect();
|
|
50
|
+
this._node = null;
|
|
51
|
+
}
|
|
52
|
+
_setup(ctx) {
|
|
53
|
+
const node = ctx.createBiquadFilter();
|
|
54
|
+
node.type = 'highpass';
|
|
55
|
+
node.frequency.setValueAtTime(this._frequency, ctx.currentTime);
|
|
56
|
+
node.Q.setValueAtTime(this._resonance, ctx.currentTime);
|
|
57
|
+
this._node = node;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { HighpassFilter };
|
|
62
|
+
//# sourceMappingURL=HighpassFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HighpassFilter.js","sources":["../../../../../src/audio/filters/HighpassFilter.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAQM,MAAO,cAAe,SAAQ,WAAW,CAAA;IACnC,KAAK,GAA4B,IAAI;AACrC,IAAA,UAAU;AACV,IAAA,UAAU;AAElB,IAAA,WAAA,CAAmB,UAAiC,EAAE,EAAA;AAClD,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC;QACxC,IAAI,mBAAmB,EAAE,EAAE;AACvB,YAAA,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QAClC;aAAO;YACH,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;QAC/C;IACJ;AAEA,IAAA,IAAW,SAAS,GAAA;QAChB,IAAI,CAAC,IAAI,CAAC,KAAK;AAAE,YAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC;QACvE,OAAO,IAAI,CAAC,KAAK;IACrB;AAEA,IAAA,IAAW,UAAU,GAAA;QACjB,IAAI,CAAC,IAAI,CAAC,KAAK;AAAE,YAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC;QACvE,OAAO,IAAI,CAAC,KAAK;IACrB;AAEA,IAAA,IAAW,SAAS,GAAA;QAChB,OAAO,IAAI,CAAC,UAAU;IAC1B;IAEA,IAAW,SAAS,CAAC,KAAa,EAAA;AAC9B,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACtD,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;YACZ,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC;QAC/F;IACJ;AAEA,IAAA,IAAW,SAAS,GAAA;QAChB,OAAO,IAAI,CAAC,UAAU;IAC1B;IAEA,IAAW,SAAS,CAAC,KAAa,EAAA;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC;AACzC,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;YACZ,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC;QACvF;IACJ;IAEgB,OAAO,GAAA;AACnB,QAAA,mBAAmB,CAAC,cAAc,CAAC,IAAI,CAAC;AACxC,QAAA,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE;AACxB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACrB;AAEQ,IAAA,MAAM,CAAC,GAAiB,EAAA;AAC5B,QAAA,MAAM,IAAI,GAAG,GAAG,CAAC,kBAAkB,EAAE;AACrC,QAAA,IAAI,CAAC,IAAI,GAAG,UAAU;AACtB,QAAA,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC;AAC/D,QAAA,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC;AACvD,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACrB;AACH;;;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AudioFilter } from '../AudioFilter';
|
|
2
|
+
export interface LowpassFilterOptions {
|
|
3
|
+
frequency?: number;
|
|
4
|
+
resonance?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class LowpassFilter extends AudioFilter {
|
|
7
|
+
private _node;
|
|
8
|
+
private _frequency;
|
|
9
|
+
private _resonance;
|
|
10
|
+
constructor(options?: LowpassFilterOptions);
|
|
11
|
+
get inputNode(): AudioNode;
|
|
12
|
+
get outputNode(): AudioNode;
|
|
13
|
+
get frequency(): number;
|
|
14
|
+
set frequency(value: number);
|
|
15
|
+
get resonance(): number;
|
|
16
|
+
set resonance(value: number);
|
|
17
|
+
destroy(): void;
|
|
18
|
+
private _setup;
|
|
19
|
+
}
|