@guinetik/gcanvas 1.0.4 → 2.0.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/CNAME +1 -0
- package/dist/aizawa.html +27 -0
- package/dist/animations.html +31 -0
- package/dist/basic.html +38 -0
- package/dist/baskara.html +31 -0
- package/dist/bezier.html +35 -0
- package/dist/beziersignature.html +29 -0
- package/dist/blackhole.html +28 -0
- package/dist/blob.html +35 -0
- package/dist/clifford.html +25 -0
- package/dist/cmb.html +24 -0
- package/dist/coordinates.html +698 -0
- package/dist/cube3d.html +23 -0
- package/dist/dadras.html +26 -0
- package/dist/dejong.html +25 -0
- package/dist/demos.css +303 -0
- package/dist/dino.html +42 -0
- package/dist/easing.html +28 -0
- package/dist/events.html +195 -0
- package/dist/fluent.html +647 -0
- package/dist/fluid-simple.html +22 -0
- package/dist/fluid.html +37 -0
- package/dist/fractals.html +36 -0
- package/dist/gameobjects.html +626 -0
- package/dist/gcanvas.es.js +14368 -9093
- package/dist/gcanvas.es.min.js +1 -1
- package/dist/gcanvas.umd.js +1 -1
- package/dist/gcanvas.umd.min.js +1 -1
- package/dist/genart.html +26 -0
- package/dist/gendream.html +26 -0
- package/dist/group.html +36 -0
- package/dist/halvorsen.html +27 -0
- package/dist/home.html +587 -0
- package/dist/hyperbolic001.html +23 -0
- package/dist/hyperbolic002.html +23 -0
- package/dist/hyperbolic003.html +23 -0
- package/dist/hyperbolic004.html +23 -0
- package/dist/hyperbolic005.html +22 -0
- package/dist/index.html +446 -0
- package/dist/isometric.html +34 -0
- package/dist/js/aizawa.js +425 -0
- package/dist/js/animations.js +452 -0
- package/dist/js/basic.js +204 -0
- package/dist/js/baskara.js +751 -0
- package/dist/js/bezier.js +692 -0
- package/dist/js/beziersignature.js +241 -0
- package/dist/js/blackhole/accretiondisk.obj.js +379 -0
- package/dist/js/blackhole/blackhole.obj.js +318 -0
- package/dist/js/blackhole/index.js +409 -0
- package/dist/js/blackhole/particle.js +56 -0
- package/dist/js/blackhole/starfield.obj.js +218 -0
- package/dist/js/blob.js +2276 -0
- package/dist/js/clifford.js +236 -0
- package/dist/js/cmb.js +594 -0
- package/dist/js/coordinates.js +840 -0
- package/dist/js/cube3d.js +789 -0
- package/dist/js/dadras.js +405 -0
- package/dist/js/dejong.js +257 -0
- package/dist/js/dino.js +1420 -0
- package/dist/js/easing.js +477 -0
- package/dist/js/fluent.js +183 -0
- package/dist/js/fluid-simple.js +253 -0
- package/dist/js/fluid.js +527 -0
- package/dist/js/fractals.js +932 -0
- package/dist/js/fractalworker.js +93 -0
- package/dist/js/gameobjects.js +176 -0
- package/dist/js/genart.js +268 -0
- package/dist/js/gendream.js +209 -0
- package/dist/js/group.js +140 -0
- package/dist/js/halvorsen.js +405 -0
- package/dist/js/hyperbolic001.js +310 -0
- package/dist/js/hyperbolic002.js +388 -0
- package/dist/js/hyperbolic003.js +319 -0
- package/dist/js/hyperbolic004.js +345 -0
- package/dist/js/hyperbolic005.js +340 -0
- package/dist/js/info-toggle.js +25 -0
- package/dist/js/isometric.js +851 -0
- package/dist/js/kerr.js +1547 -0
- package/dist/js/lavalamp.js +590 -0
- package/dist/js/layout.js +354 -0
- package/dist/js/lorenz.js +425 -0
- package/dist/js/mondrian.js +285 -0
- package/dist/js/opacity.js +275 -0
- package/dist/js/painter.js +484 -0
- package/dist/js/particles-showcase.js +514 -0
- package/dist/js/particles.js +299 -0
- package/dist/js/patterns.js +397 -0
- package/dist/js/penrose/artifact.js +69 -0
- package/dist/js/penrose/blackhole.js +121 -0
- package/dist/js/penrose/constants.js +73 -0
- package/dist/js/penrose/game.js +943 -0
- package/dist/js/penrose/lore.js +278 -0
- package/dist/js/penrose/penrosescene.js +892 -0
- package/dist/js/penrose/ship.js +216 -0
- package/dist/js/penrose/sounds.js +211 -0
- package/dist/js/penrose/voidparticle.js +55 -0
- package/dist/js/penrose/voidscene.js +258 -0
- package/dist/js/penrose/voidship.js +144 -0
- package/dist/js/penrose/wormhole.js +46 -0
- package/dist/js/pipeline.js +555 -0
- package/dist/js/plane3d.js +256 -0
- package/dist/js/platformer.js +1579 -0
- package/dist/js/rossler.js +480 -0
- package/dist/js/scene.js +304 -0
- package/dist/js/scenes.js +320 -0
- package/dist/js/schrodinger.js +706 -0
- package/dist/js/schwarzschild.js +1015 -0
- package/dist/js/shapes.js +628 -0
- package/dist/js/space/alien.js +171 -0
- package/dist/js/space/boom.js +98 -0
- package/dist/js/space/boss.js +353 -0
- package/dist/js/space/buff.js +73 -0
- package/dist/js/space/bullet.js +102 -0
- package/dist/js/space/constants.js +85 -0
- package/dist/js/space/game.js +1884 -0
- package/dist/js/space/hud.js +112 -0
- package/dist/js/space/laserbeam.js +179 -0
- package/dist/js/space/lightning.js +277 -0
- package/dist/js/space/minion.js +192 -0
- package/dist/js/space/missile.js +212 -0
- package/dist/js/space/player.js +430 -0
- package/dist/js/space/powerup.js +90 -0
- package/dist/js/space/starfield.js +58 -0
- package/dist/js/space/starpower.js +90 -0
- package/dist/js/spacetime.js +559 -0
- package/dist/js/sphere3d.js +229 -0
- package/dist/js/sprite.js +473 -0
- package/dist/js/starfaux/config.js +118 -0
- package/dist/js/starfaux/enemy.js +353 -0
- package/dist/js/starfaux/hud.js +78 -0
- package/dist/js/starfaux/index.js +482 -0
- package/dist/js/starfaux/laser.js +182 -0
- package/dist/js/starfaux/player.js +468 -0
- package/dist/js/starfaux/terrain.js +560 -0
- package/dist/js/study001.js +275 -0
- package/dist/js/study002.js +366 -0
- package/dist/js/study003.js +331 -0
- package/dist/js/study004.js +389 -0
- package/dist/js/study005.js +209 -0
- package/dist/js/study006.js +194 -0
- package/dist/js/study007.js +192 -0
- package/dist/js/study008.js +413 -0
- package/dist/js/svgtween.js +204 -0
- package/dist/js/tde/accretiondisk.js +471 -0
- package/dist/js/tde/blackhole.js +219 -0
- package/dist/js/tde/blackholescene.js +209 -0
- package/dist/js/tde/config.js +59 -0
- package/dist/js/tde/index.js +820 -0
- package/dist/js/tde/jets.js +290 -0
- package/dist/js/tde/lensedstarfield.js +154 -0
- package/dist/js/tde/tdestar.js +297 -0
- package/dist/js/tde/tidalstream.js +372 -0
- package/dist/js/tde_old/blackhole.obj.js +354 -0
- package/dist/js/tde_old/debris.obj.js +791 -0
- package/dist/js/tde_old/flare.obj.js +239 -0
- package/dist/js/tde_old/index.js +448 -0
- package/dist/js/tde_old/star.obj.js +812 -0
- package/dist/js/tetris/config.js +157 -0
- package/dist/js/tetris/grid.js +286 -0
- package/dist/js/tetris/index.js +1195 -0
- package/dist/js/tetris/renderer.js +634 -0
- package/dist/js/tetris/tetrominos.js +280 -0
- package/dist/js/thomas.js +394 -0
- package/dist/js/tiles.js +312 -0
- package/dist/js/tweendemo.js +79 -0
- package/dist/js/visibility.js +102 -0
- package/dist/kerr.html +28 -0
- package/dist/lavalamp.html +27 -0
- package/dist/layouts.html +37 -0
- package/dist/logo.svg +4 -0
- package/dist/loop.html +84 -0
- package/dist/lorenz.html +27 -0
- package/dist/mondrian.html +32 -0
- package/dist/og_image.png +0 -0
- package/dist/opacity.html +36 -0
- package/dist/painter.html +39 -0
- package/dist/particles-showcase.html +28 -0
- package/dist/particles.html +24 -0
- package/dist/patterns.html +33 -0
- package/dist/penrose-game.html +31 -0
- package/dist/pipeline.html +737 -0
- package/dist/plane3d.html +24 -0
- package/dist/platformer.html +43 -0
- package/dist/rossler.html +27 -0
- package/dist/scene-interactivity-test.html +220 -0
- package/dist/scene.html +33 -0
- package/dist/scenes.html +96 -0
- package/dist/schrodinger.html +27 -0
- package/dist/schwarzschild.html +27 -0
- package/dist/shapes.html +16 -0
- package/dist/space.html +85 -0
- package/dist/spacetime.html +27 -0
- package/dist/sphere3d.html +24 -0
- package/dist/sprite.html +18 -0
- package/dist/starfaux.html +22 -0
- package/dist/study001.html +23 -0
- package/dist/study002.html +23 -0
- package/dist/study003.html +23 -0
- package/dist/study004.html +23 -0
- package/dist/study005.html +22 -0
- package/dist/study006.html +24 -0
- package/dist/study007.html +24 -0
- package/dist/study008.html +22 -0
- package/dist/svgtween.html +29 -0
- package/dist/tde.html +28 -0
- package/dist/tetris3d.html +25 -0
- package/dist/thomas.html +27 -0
- package/dist/tiles.html +28 -0
- package/dist/transforms.html +400 -0
- package/dist/tween.html +45 -0
- package/dist/visibility.html +33 -0
- package/package.json +1 -1
- package/readme.md +30 -22
- package/src/game/objects/go.js +7 -0
- package/src/game/objects/index.js +2 -0
- package/src/game/objects/isometric-scene.js +53 -3
- package/src/game/objects/layoutscene.js +57 -0
- package/src/game/objects/mask.js +241 -0
- package/src/game/objects/scene.js +19 -0
- package/src/game/objects/wrapper.js +14 -2
- package/src/game/pipeline.js +17 -0
- package/src/game/ui/button.js +101 -16
- package/src/game/ui/theme.js +0 -6
- package/src/game/ui/togglebutton.js +25 -14
- package/src/game/ui/tooltip.js +12 -4
- package/src/index.js +3 -0
- package/src/io/gesture.js +409 -0
- package/src/io/index.js +4 -1
- package/src/io/keys.js +9 -1
- package/src/io/screen.js +476 -0
- package/src/math/attractors.js +664 -0
- package/src/math/heat.js +106 -0
- package/src/math/index.js +1 -0
- package/src/mixins/draggable.js +15 -19
- package/src/painter/painter.shapes.js +11 -5
- package/src/particle/particle-system.js +165 -1
- package/src/physics/index.js +26 -0
- package/src/physics/physics-updaters.js +333 -0
- package/src/physics/physics.js +375 -0
- package/src/shapes/image.js +5 -5
- package/src/shapes/index.js +2 -0
- package/src/shapes/parallelogram.js +147 -0
- package/src/shapes/righttriangle.js +115 -0
- package/src/shapes/svg.js +281 -100
- package/src/shapes/text.js +22 -6
- package/src/shapes/transformable.js +5 -0
- package/src/sound/effects.js +807 -0
- package/src/sound/index.js +13 -0
- package/src/webgl/index.js +7 -0
- package/src/webgl/shaders/clifford-point-shaders.js +131 -0
- package/src/webgl/shaders/dejong-point-shaders.js +131 -0
- package/src/webgl/shaders/point-sprite-shaders.js +152 -0
- package/src/webgl/webgl-clifford-renderer.js +477 -0
- package/src/webgl/webgl-dejong-renderer.js +472 -0
- package/src/webgl/webgl-line-renderer.js +391 -0
- package/src/webgl/webgl-particle-renderer.js +410 -0
- package/types/index.d.ts +30 -2
- package/types/io.d.ts +217 -0
- package/types/physics.d.ts +299 -0
- package/types/shapes.d.ts +8 -0
- package/types/webgl.d.ts +188 -109
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Effects Primitives
|
|
3
|
+
* Standalone audio effect processors for Web Audio API
|
|
4
|
+
* @module sound/effects
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Flanger effect - Short delay with LFO modulation for jet sweep sound
|
|
9
|
+
* @class Flanger
|
|
10
|
+
*/
|
|
11
|
+
export class Flanger {
|
|
12
|
+
/**
|
|
13
|
+
* Create a flanger effect
|
|
14
|
+
* @param {AudioContext} audioContext - The audio context
|
|
15
|
+
* @param {Object} options - Flanger options
|
|
16
|
+
* @param {number} [options.baseDelay=0.005] - Base delay time in seconds (default: 5ms)
|
|
17
|
+
* @param {number} [options.maxDelay=0.02] - Maximum delay time in seconds (default: 20ms)
|
|
18
|
+
* @param {number} [options.lfoFrequency=0.5] - LFO frequency in Hz (default: 0.5Hz)
|
|
19
|
+
* @param {number} [options.lfoDepth=0.002] - LFO modulation depth in seconds (default: 2ms)
|
|
20
|
+
* @param {number} [options.feedback=0.5] - Feedback amount (0-1, default: 0.5)
|
|
21
|
+
* @param {number} [options.wet=0] - Wet signal level (0-1, default: 0)
|
|
22
|
+
* @param {number} [options.dry=1.0] - Dry signal level (0-1, default: 1.0)
|
|
23
|
+
*/
|
|
24
|
+
constructor(audioContext, options = {}) {
|
|
25
|
+
this.audioContext = audioContext;
|
|
26
|
+
const {
|
|
27
|
+
baseDelay = 0.005,
|
|
28
|
+
maxDelay = 0.02,
|
|
29
|
+
lfoFrequency = 0.5,
|
|
30
|
+
lfoDepth = 0.002,
|
|
31
|
+
feedback = 0.5,
|
|
32
|
+
wet = 0,
|
|
33
|
+
dry = 1.0,
|
|
34
|
+
} = options;
|
|
35
|
+
|
|
36
|
+
// Delay node
|
|
37
|
+
this.delay = audioContext.createDelay(maxDelay);
|
|
38
|
+
this.delay.delayTime.value = baseDelay;
|
|
39
|
+
|
|
40
|
+
// LFO for modulation
|
|
41
|
+
this.lfo = audioContext.createOscillator();
|
|
42
|
+
this.lfo.type = 'sine';
|
|
43
|
+
this.lfo.frequency.value = lfoFrequency;
|
|
44
|
+
|
|
45
|
+
// LFO depth control
|
|
46
|
+
this.lfoDepth = audioContext.createGain();
|
|
47
|
+
this.lfoDepth.gain.value = lfoDepth;
|
|
48
|
+
|
|
49
|
+
// Feedback
|
|
50
|
+
this.feedback = audioContext.createGain();
|
|
51
|
+
this.feedback.gain.value = feedback;
|
|
52
|
+
|
|
53
|
+
// Wet/dry mix
|
|
54
|
+
this.wetGain = audioContext.createGain();
|
|
55
|
+
this.wetGain.gain.value = wet;
|
|
56
|
+
this.dryGain = audioContext.createGain();
|
|
57
|
+
this.dryGain.gain.value = dry;
|
|
58
|
+
|
|
59
|
+
// Output merger
|
|
60
|
+
this.output = audioContext.createGain();
|
|
61
|
+
this.output.gain.value = 1.0;
|
|
62
|
+
|
|
63
|
+
// Connect LFO to delay time
|
|
64
|
+
this.lfo.connect(this.lfoDepth);
|
|
65
|
+
this.lfoDepth.connect(this.delay.delayTime);
|
|
66
|
+
|
|
67
|
+
// Feedback loop
|
|
68
|
+
this.delay.connect(this.feedback);
|
|
69
|
+
this.feedback.connect(this.delay);
|
|
70
|
+
|
|
71
|
+
// Start LFO
|
|
72
|
+
this.lfo.start();
|
|
73
|
+
|
|
74
|
+
// Input node (connect your source here)
|
|
75
|
+
this.input = audioContext.createGain();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Connect the flanger to an audio node
|
|
80
|
+
* @param {AudioNode} source - Source node to connect
|
|
81
|
+
*/
|
|
82
|
+
connect(source) {
|
|
83
|
+
// Dry path
|
|
84
|
+
source.connect(this.dryGain);
|
|
85
|
+
this.dryGain.connect(this.output);
|
|
86
|
+
|
|
87
|
+
// Wet path through flanger
|
|
88
|
+
source.connect(this.delay);
|
|
89
|
+
this.delay.connect(this.wetGain);
|
|
90
|
+
this.wetGain.connect(this.output);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Set wet/dry mix
|
|
95
|
+
* @param {number} wet - Wet level (0-1)
|
|
96
|
+
* @param {number} dry - Dry level (0-1)
|
|
97
|
+
*/
|
|
98
|
+
setMix(wet, dry) {
|
|
99
|
+
this.wetGain.gain.value = wet;
|
|
100
|
+
this.dryGain.gain.value = dry;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Set LFO frequency
|
|
105
|
+
* @param {number} frequency - LFO frequency in Hz
|
|
106
|
+
*/
|
|
107
|
+
setLFOFrequency(frequency) {
|
|
108
|
+
this.lfo.frequency.value = frequency;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Set feedback amount
|
|
113
|
+
* @param {number} feedback - Feedback amount (0-1)
|
|
114
|
+
*/
|
|
115
|
+
setFeedback(feedback) {
|
|
116
|
+
this.feedback.gain.value = feedback;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect and clean up
|
|
121
|
+
*/
|
|
122
|
+
disconnect() {
|
|
123
|
+
try {
|
|
124
|
+
this.lfo.stop();
|
|
125
|
+
this.lfo.disconnect();
|
|
126
|
+
this.delay.disconnect();
|
|
127
|
+
this.feedback.disconnect();
|
|
128
|
+
this.wetGain.disconnect();
|
|
129
|
+
this.dryGain.disconnect();
|
|
130
|
+
this.lfoDepth.disconnect();
|
|
131
|
+
this.input.disconnect();
|
|
132
|
+
this.output.disconnect();
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// Ignore errors
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* DJ Filter - Sweeping lowpass/highpass filter
|
|
141
|
+
* @class DJFilter
|
|
142
|
+
*/
|
|
143
|
+
export class DJFilter {
|
|
144
|
+
/**
|
|
145
|
+
* Create a DJ filter
|
|
146
|
+
* @param {AudioContext} audioContext - The audio context
|
|
147
|
+
* @param {Object} options - Filter options
|
|
148
|
+
* @param {string} [options.type='lowpass'] - Filter type ('lowpass' or 'highpass')
|
|
149
|
+
* @param {number} [options.frequency=20000] - Initial frequency in Hz (default: 20000)
|
|
150
|
+
* @param {number} [options.Q=1] - Q factor/resonance (default: 1)
|
|
151
|
+
*/
|
|
152
|
+
constructor(audioContext, options = {}) {
|
|
153
|
+
this.audioContext = audioContext;
|
|
154
|
+
const {
|
|
155
|
+
type = 'lowpass',
|
|
156
|
+
frequency = 20000,
|
|
157
|
+
Q = 1,
|
|
158
|
+
} = options;
|
|
159
|
+
|
|
160
|
+
this.filter = audioContext.createBiquadFilter();
|
|
161
|
+
this.filter.type = type;
|
|
162
|
+
this.filter.frequency.value = frequency;
|
|
163
|
+
this.filter.Q.value = Q;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Connect the filter to an audio node
|
|
168
|
+
* @param {AudioNode} source - Source node to connect
|
|
169
|
+
*/
|
|
170
|
+
connect(source) {
|
|
171
|
+
source.connect(this.filter);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Set filter frequency
|
|
176
|
+
* @param {number} frequency - Frequency in Hz
|
|
177
|
+
*/
|
|
178
|
+
setFrequency(frequency) {
|
|
179
|
+
this.filter.frequency.value = frequency;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Set Q factor
|
|
184
|
+
* @param {number} Q - Q factor/resonance
|
|
185
|
+
*/
|
|
186
|
+
setQ(Q) {
|
|
187
|
+
this.filter.Q.value = Q;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Set filter type
|
|
192
|
+
* @param {string} type - Filter type ('lowpass' or 'highpass')
|
|
193
|
+
*/
|
|
194
|
+
setType(type) {
|
|
195
|
+
this.filter.type = type;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get the filter node (for chaining)
|
|
200
|
+
* @returns {BiquadFilterNode}
|
|
201
|
+
*/
|
|
202
|
+
getNode() {
|
|
203
|
+
return this.filter;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Disconnect and clean up
|
|
208
|
+
*/
|
|
209
|
+
disconnect() {
|
|
210
|
+
try {
|
|
211
|
+
this.filter.disconnect();
|
|
212
|
+
} catch (e) {
|
|
213
|
+
// Ignore errors
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* EQ Filter Bank - Multiple peaking filters for equalization
|
|
220
|
+
* @class EQFilterBank
|
|
221
|
+
*/
|
|
222
|
+
export class EQFilterBank {
|
|
223
|
+
/**
|
|
224
|
+
* Create an EQ filter bank
|
|
225
|
+
* @param {AudioContext} audioContext - The audio context
|
|
226
|
+
* @param {Object} options - EQ options
|
|
227
|
+
* @param {number} [options.numBands=16] - Number of bands (default: 16)
|
|
228
|
+
* @param {number} [options.minFreq=40] - Minimum frequency in Hz (default: 40)
|
|
229
|
+
* @param {number} [options.maxFreq=12000] - Maximum frequency in Hz (default: 12000)
|
|
230
|
+
* @param {number} [options.Q=4] - Q factor/resonance (default: 4)
|
|
231
|
+
*/
|
|
232
|
+
constructor(audioContext, options = {}) {
|
|
233
|
+
this.audioContext = audioContext;
|
|
234
|
+
const {
|
|
235
|
+
numBands = 16,
|
|
236
|
+
minFreq = 40,
|
|
237
|
+
maxFreq = 12000,
|
|
238
|
+
Q = 4,
|
|
239
|
+
} = options;
|
|
240
|
+
|
|
241
|
+
this.filters = [];
|
|
242
|
+
this.numBands = numBands;
|
|
243
|
+
|
|
244
|
+
// Create peaking filters
|
|
245
|
+
for (let i = 0; i < numBands; i++) {
|
|
246
|
+
const filter = audioContext.createBiquadFilter();
|
|
247
|
+
filter.type = 'peaking';
|
|
248
|
+
|
|
249
|
+
// Logarithmic frequency distribution
|
|
250
|
+
const freq = numBands > 1
|
|
251
|
+
? minFreq * Math.pow(maxFreq / minFreq, i / (numBands - 1))
|
|
252
|
+
: (minFreq + maxFreq) / 2;
|
|
253
|
+
|
|
254
|
+
filter.frequency.value = freq;
|
|
255
|
+
filter.Q.value = Q;
|
|
256
|
+
filter.gain.value = 0; // Start flat
|
|
257
|
+
|
|
258
|
+
this.filters.push(filter);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Connect the EQ bank to an audio node
|
|
264
|
+
* Chains all filters together
|
|
265
|
+
* @param {AudioNode} source - Source node to connect
|
|
266
|
+
*/
|
|
267
|
+
connect(source) {
|
|
268
|
+
let currentNode = source;
|
|
269
|
+
for (const filter of this.filters) {
|
|
270
|
+
currentNode.connect(filter);
|
|
271
|
+
currentNode = filter;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Set gain for a specific band
|
|
277
|
+
* @param {number} bandIndex - Band index (0 to numBands-1)
|
|
278
|
+
* @param {number} gain - Gain in dB
|
|
279
|
+
*/
|
|
280
|
+
setBandGain(bandIndex, gain) {
|
|
281
|
+
if (bandIndex >= 0 && bandIndex < this.filters.length) {
|
|
282
|
+
this.filters[bandIndex].gain.value = gain;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Set gain for multiple bands
|
|
288
|
+
* @param {Array<number>} gains - Array of gain values in dB
|
|
289
|
+
*/
|
|
290
|
+
setBandGains(gains) {
|
|
291
|
+
for (let i = 0; i < Math.min(gains.length, this.filters.length); i++) {
|
|
292
|
+
this.filters[i].gain.value = gains[i];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Get filter node at index (for chaining)
|
|
298
|
+
* @param {number} index - Filter index
|
|
299
|
+
* @returns {BiquadFilterNode}
|
|
300
|
+
*/
|
|
301
|
+
getFilter(index) {
|
|
302
|
+
return this.filters[index];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get all filters (for chaining)
|
|
307
|
+
* @returns {Array<BiquadFilterNode>}
|
|
308
|
+
*/
|
|
309
|
+
getFilters() {
|
|
310
|
+
return this.filters;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Disconnect and clean up
|
|
315
|
+
*/
|
|
316
|
+
disconnect() {
|
|
317
|
+
for (const filter of this.filters) {
|
|
318
|
+
try {
|
|
319
|
+
filter.disconnect();
|
|
320
|
+
} catch (e) {
|
|
321
|
+
// Ignore errors
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* High Shelf Filter - High frequency boost/clarity
|
|
329
|
+
* @class HighShelf
|
|
330
|
+
*/
|
|
331
|
+
export class HighShelf {
|
|
332
|
+
/**
|
|
333
|
+
* Create a high shelf filter
|
|
334
|
+
* @param {AudioContext} audioContext - The audio context
|
|
335
|
+
* @param {Object} options - Filter options
|
|
336
|
+
* @param {number} [options.frequency=4000] - Shelf frequency in Hz (default: 4000)
|
|
337
|
+
* @param {number} [options.gain=3] - Gain in dB (default: 3)
|
|
338
|
+
*/
|
|
339
|
+
constructor(audioContext, options = {}) {
|
|
340
|
+
this.audioContext = audioContext;
|
|
341
|
+
const {
|
|
342
|
+
frequency = 4000,
|
|
343
|
+
gain = 3,
|
|
344
|
+
} = options;
|
|
345
|
+
|
|
346
|
+
this.filter = audioContext.createBiquadFilter();
|
|
347
|
+
this.filter.type = 'highshelf';
|
|
348
|
+
this.filter.frequency.value = frequency;
|
|
349
|
+
this.filter.gain.value = gain;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Connect the filter to an audio node
|
|
354
|
+
* @param {AudioNode} source - Source node to connect
|
|
355
|
+
*/
|
|
356
|
+
connect(source) {
|
|
357
|
+
source.connect(this.filter);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Set shelf frequency
|
|
362
|
+
* @param {number} frequency - Frequency in Hz
|
|
363
|
+
*/
|
|
364
|
+
setFrequency(frequency) {
|
|
365
|
+
this.filter.frequency.value = frequency;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Set gain
|
|
370
|
+
* @param {number} gain - Gain in dB
|
|
371
|
+
*/
|
|
372
|
+
setGain(gain) {
|
|
373
|
+
this.filter.gain.value = gain;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Get the filter node (for chaining)
|
|
378
|
+
* @returns {BiquadFilterNode}
|
|
379
|
+
*/
|
|
380
|
+
getNode() {
|
|
381
|
+
return this.filter;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Disconnect and clean up
|
|
386
|
+
*/
|
|
387
|
+
disconnect() {
|
|
388
|
+
try {
|
|
389
|
+
this.filter.disconnect();
|
|
390
|
+
} catch (e) {
|
|
391
|
+
// Ignore errors
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Advanced Delay - Delay with wet/dry control and feedback
|
|
398
|
+
* @class AdvancedDelay
|
|
399
|
+
*/
|
|
400
|
+
export class AdvancedDelay {
|
|
401
|
+
/**
|
|
402
|
+
* Create an advanced delay effect
|
|
403
|
+
* @param {AudioContext} audioContext - The audio context
|
|
404
|
+
* @param {Object} options - Delay options
|
|
405
|
+
* @param {number} [options.delayTime=0.15] - Delay time in seconds (default: 0.15)
|
|
406
|
+
* @param {number} [options.maxDelay=0.5] - Maximum delay time in seconds (default: 0.5)
|
|
407
|
+
* @param {number} [options.feedback=0.2] - Feedback amount (0-1, default: 0.2)
|
|
408
|
+
* @param {number} [options.wet=0.15] - Wet signal level (0-1, default: 0.15)
|
|
409
|
+
* @param {number} [options.dry=0.85] - Dry signal level (0-1, default: 0.85)
|
|
410
|
+
*/
|
|
411
|
+
constructor(audioContext, options = {}) {
|
|
412
|
+
this.audioContext = audioContext;
|
|
413
|
+
const {
|
|
414
|
+
delayTime = 0.15,
|
|
415
|
+
maxDelay = 0.5,
|
|
416
|
+
feedback = 0.2,
|
|
417
|
+
wet = 0.15,
|
|
418
|
+
dry = 0.85,
|
|
419
|
+
} = options;
|
|
420
|
+
|
|
421
|
+
// Delay node
|
|
422
|
+
this.delay = audioContext.createDelay(maxDelay);
|
|
423
|
+
this.delay.delayTime.value = delayTime;
|
|
424
|
+
|
|
425
|
+
// Feedback
|
|
426
|
+
this.feedbackGain = audioContext.createGain();
|
|
427
|
+
this.feedbackGain.gain.value = feedback;
|
|
428
|
+
|
|
429
|
+
// Wet/dry mix
|
|
430
|
+
this.wetGain = audioContext.createGain();
|
|
431
|
+
this.wetGain.gain.value = wet;
|
|
432
|
+
this.dryGain = audioContext.createGain();
|
|
433
|
+
this.dryGain.gain.value = dry;
|
|
434
|
+
|
|
435
|
+
// Output merger
|
|
436
|
+
this.output = audioContext.createGain();
|
|
437
|
+
this.output.gain.value = 1.0;
|
|
438
|
+
|
|
439
|
+
// Feedback loop
|
|
440
|
+
this.delay.connect(this.feedbackGain);
|
|
441
|
+
this.feedbackGain.connect(this.delay);
|
|
442
|
+
|
|
443
|
+
// Input node (connect your source here)
|
|
444
|
+
this.input = audioContext.createGain();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Connect the delay to an audio node
|
|
449
|
+
* @param {AudioNode} source - Source node to connect
|
|
450
|
+
*/
|
|
451
|
+
connect(source) {
|
|
452
|
+
// Dry path
|
|
453
|
+
source.connect(this.dryGain);
|
|
454
|
+
this.dryGain.connect(this.output);
|
|
455
|
+
|
|
456
|
+
// Wet path
|
|
457
|
+
source.connect(this.delay);
|
|
458
|
+
this.delay.connect(this.wetGain);
|
|
459
|
+
this.wetGain.connect(this.output);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Set delay time
|
|
464
|
+
* @param {number} time - Delay time in seconds
|
|
465
|
+
*/
|
|
466
|
+
setDelayTime(time) {
|
|
467
|
+
this.delay.delayTime.value = time;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Set wet/dry mix
|
|
472
|
+
* @param {number} wet - Wet level (0-1)
|
|
473
|
+
* @param {number} dry - Dry level (0-1)
|
|
474
|
+
*/
|
|
475
|
+
setMix(wet, dry) {
|
|
476
|
+
this.wetGain.gain.value = wet;
|
|
477
|
+
this.dryGain.gain.value = dry;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Set feedback amount
|
|
482
|
+
* @param {number} feedback - Feedback amount (0-1)
|
|
483
|
+
*/
|
|
484
|
+
setFeedback(feedback) {
|
|
485
|
+
this.feedbackGain.gain.value = feedback;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Get the output node (for chaining)
|
|
490
|
+
* @returns {GainNode}
|
|
491
|
+
*/
|
|
492
|
+
getOutput() {
|
|
493
|
+
return this.output;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Disconnect and clean up
|
|
498
|
+
*/
|
|
499
|
+
disconnect() {
|
|
500
|
+
try {
|
|
501
|
+
this.delay.disconnect();
|
|
502
|
+
this.feedbackGain.disconnect();
|
|
503
|
+
this.wetGain.disconnect();
|
|
504
|
+
this.dryGain.disconnect();
|
|
505
|
+
this.input.disconnect();
|
|
506
|
+
this.output.disconnect();
|
|
507
|
+
} catch (e) {
|
|
508
|
+
// Ignore errors
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Advanced Distortion - WaveShaper with dynamic curve updates
|
|
515
|
+
* @class AdvancedDistortion
|
|
516
|
+
*/
|
|
517
|
+
export class AdvancedDistortion {
|
|
518
|
+
/**
|
|
519
|
+
* Create an advanced distortion effect
|
|
520
|
+
* @param {AudioContext} audioContext - The audio context
|
|
521
|
+
* @param {Object} options - Distortion options
|
|
522
|
+
* @param {number} [options.amount=0] - Initial distortion amount (0-1, default: 0)
|
|
523
|
+
* @param {string} [options.oversample='4x'] - Oversampling ('none', '2x', '4x', default: '4x')
|
|
524
|
+
*/
|
|
525
|
+
constructor(audioContext, options = {}) {
|
|
526
|
+
this.audioContext = audioContext;
|
|
527
|
+
const {
|
|
528
|
+
amount = 0,
|
|
529
|
+
oversample = '4x',
|
|
530
|
+
} = options;
|
|
531
|
+
|
|
532
|
+
this.shaper = audioContext.createWaveShaper();
|
|
533
|
+
this.shaper.oversample = oversample;
|
|
534
|
+
this.amount = amount;
|
|
535
|
+
this.updateCurve(amount);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Update the distortion curve
|
|
540
|
+
* @param {number} amount - Distortion amount (0-1)
|
|
541
|
+
*/
|
|
542
|
+
updateCurve(amount) {
|
|
543
|
+
const samples = 44100;
|
|
544
|
+
const curve = new Float32Array(samples);
|
|
545
|
+
const deg = Math.PI / 180;
|
|
546
|
+
|
|
547
|
+
// Blend between clean and distorted based on amount
|
|
548
|
+
const k = amount * 400; // More extreme distortion
|
|
549
|
+
|
|
550
|
+
for (let i = 0; i < samples; i++) {
|
|
551
|
+
const x = (i * 2) / samples - 1;
|
|
552
|
+
if (k === 0) {
|
|
553
|
+
curve[i] = x;
|
|
554
|
+
} else {
|
|
555
|
+
curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
this.shaper.curve = curve;
|
|
560
|
+
this.amount = amount;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Connect the distortion to an audio node
|
|
565
|
+
* @param {AudioNode} source - Source node to connect
|
|
566
|
+
*/
|
|
567
|
+
connect(source) {
|
|
568
|
+
source.connect(this.shaper);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Set distortion amount
|
|
573
|
+
* @param {number} amount - Distortion amount (0-1)
|
|
574
|
+
*/
|
|
575
|
+
setAmount(amount) {
|
|
576
|
+
this.updateCurve(amount);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Get the shaper node (for chaining)
|
|
581
|
+
* @returns {WaveShaperNode}
|
|
582
|
+
*/
|
|
583
|
+
getNode() {
|
|
584
|
+
return this.shaper;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Disconnect and clean up
|
|
589
|
+
*/
|
|
590
|
+
disconnect() {
|
|
591
|
+
try {
|
|
592
|
+
this.shaper.disconnect();
|
|
593
|
+
} catch (e) {
|
|
594
|
+
// Ignore errors
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Advanced Tremolo - LFO amplitude modulation
|
|
601
|
+
* @class AdvancedTremolo
|
|
602
|
+
*/
|
|
603
|
+
export class AdvancedTremolo {
|
|
604
|
+
/**
|
|
605
|
+
* Create an advanced tremolo effect
|
|
606
|
+
* @param {AudioContext} audioContext - The audio context
|
|
607
|
+
* @param {Object} options - Tremolo options
|
|
608
|
+
* @param {number} [options.rate=4] - Tremolo rate in Hz (default: 4)
|
|
609
|
+
* @param {number} [options.depth=0] - Tremolo depth (0-1, default: 0)
|
|
610
|
+
* @param {string} [options.lfoType='sine'] - LFO waveform type (default: 'sine')
|
|
611
|
+
*/
|
|
612
|
+
constructor(audioContext, options = {}) {
|
|
613
|
+
this.audioContext = audioContext;
|
|
614
|
+
const {
|
|
615
|
+
rate = 4,
|
|
616
|
+
depth = 0,
|
|
617
|
+
lfoType = 'sine',
|
|
618
|
+
} = options;
|
|
619
|
+
|
|
620
|
+
// Gain node for amplitude modulation
|
|
621
|
+
this.gain = audioContext.createGain();
|
|
622
|
+
this.gain.gain.value = 1.0;
|
|
623
|
+
|
|
624
|
+
// LFO
|
|
625
|
+
this.lfo = audioContext.createOscillator();
|
|
626
|
+
this.lfo.type = lfoType;
|
|
627
|
+
this.lfo.frequency.value = rate;
|
|
628
|
+
|
|
629
|
+
// LFO depth control
|
|
630
|
+
this.depthGain = audioContext.createGain();
|
|
631
|
+
this.depthGain.gain.value = depth;
|
|
632
|
+
|
|
633
|
+
// Connect LFO to gain
|
|
634
|
+
this.lfo.connect(this.depthGain);
|
|
635
|
+
this.depthGain.connect(this.gain.gain);
|
|
636
|
+
|
|
637
|
+
// Start LFO
|
|
638
|
+
this.lfo.start();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Connect the tremolo to an audio node
|
|
643
|
+
* @param {AudioNode} source - Source node to connect
|
|
644
|
+
*/
|
|
645
|
+
connect(source) {
|
|
646
|
+
source.connect(this.gain);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Set tremolo rate
|
|
651
|
+
* @param {number} rate - Rate in Hz
|
|
652
|
+
*/
|
|
653
|
+
setRate(rate) {
|
|
654
|
+
this.lfo.frequency.value = rate;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Set tremolo depth
|
|
659
|
+
* @param {number} depth - Depth (0-1)
|
|
660
|
+
*/
|
|
661
|
+
setDepth(depth) {
|
|
662
|
+
this.depthGain.gain.value = depth;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Get the gain node (for chaining)
|
|
667
|
+
* @returns {GainNode}
|
|
668
|
+
*/
|
|
669
|
+
getNode() {
|
|
670
|
+
return this.gain;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Disconnect and clean up
|
|
675
|
+
*/
|
|
676
|
+
disconnect() {
|
|
677
|
+
try {
|
|
678
|
+
this.lfo.stop();
|
|
679
|
+
this.lfo.disconnect();
|
|
680
|
+
this.depthGain.disconnect();
|
|
681
|
+
this.gain.disconnect();
|
|
682
|
+
} catch (e) {
|
|
683
|
+
// Ignore errors
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Limiter - Compressor configured as a limiter to prevent clipping
|
|
690
|
+
* @class Limiter
|
|
691
|
+
*/
|
|
692
|
+
export class Limiter {
|
|
693
|
+
/**
|
|
694
|
+
* Create a limiter
|
|
695
|
+
* @param {AudioContext} audioContext - The audio context
|
|
696
|
+
* @param {Object} options - Limiter options
|
|
697
|
+
* @param {number} [options.threshold=-3] - Threshold in dB (default: -3)
|
|
698
|
+
* @param {number} [options.knee=0] - Knee in dB (default: 0)
|
|
699
|
+
* @param {number} [options.ratio=20] - Ratio (default: 20, hard limit)
|
|
700
|
+
* @param {number} [options.attack=0.001] - Attack time in seconds (default: 0.001)
|
|
701
|
+
* @param {number} [options.release=0.1] - Release time in seconds (default: 0.1)
|
|
702
|
+
*/
|
|
703
|
+
constructor(audioContext, options = {}) {
|
|
704
|
+
this.audioContext = audioContext;
|
|
705
|
+
const {
|
|
706
|
+
threshold = -3,
|
|
707
|
+
knee = 0,
|
|
708
|
+
ratio = 20,
|
|
709
|
+
attack = 0.001,
|
|
710
|
+
release = 0.1,
|
|
711
|
+
} = options;
|
|
712
|
+
|
|
713
|
+
this.compressor = audioContext.createDynamicsCompressor();
|
|
714
|
+
this.compressor.threshold.value = threshold;
|
|
715
|
+
this.compressor.knee.value = knee;
|
|
716
|
+
this.compressor.ratio.value = ratio;
|
|
717
|
+
this.compressor.attack.value = attack;
|
|
718
|
+
this.compressor.release.value = release;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Connect the limiter to an audio node
|
|
723
|
+
* @param {AudioNode} source - Source node to connect
|
|
724
|
+
*/
|
|
725
|
+
connect(source) {
|
|
726
|
+
source.connect(this.compressor);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Set threshold
|
|
731
|
+
* @param {number} threshold - Threshold in dB
|
|
732
|
+
*/
|
|
733
|
+
setThreshold(threshold) {
|
|
734
|
+
this.compressor.threshold.value = threshold;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Get the compressor node (for chaining)
|
|
739
|
+
* @returns {DynamicsCompressorNode}
|
|
740
|
+
*/
|
|
741
|
+
getNode() {
|
|
742
|
+
return this.compressor;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Disconnect and clean up
|
|
747
|
+
*/
|
|
748
|
+
disconnect() {
|
|
749
|
+
try {
|
|
750
|
+
this.compressor.disconnect();
|
|
751
|
+
} catch (e) {
|
|
752
|
+
// Ignore errors
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Master Gain - Simple gain node for overall volume control
|
|
759
|
+
* @class MasterGain
|
|
760
|
+
*/
|
|
761
|
+
export class MasterGain {
|
|
762
|
+
/**
|
|
763
|
+
* Create a master gain node
|
|
764
|
+
* @param {AudioContext} audioContext - The audio context
|
|
765
|
+
* @param {number} [volume=1.0] - Initial volume (0-1, default: 1.0)
|
|
766
|
+
*/
|
|
767
|
+
constructor(audioContext, volume = 1.0) {
|
|
768
|
+
this.audioContext = audioContext;
|
|
769
|
+
this.gain = audioContext.createGain();
|
|
770
|
+
this.gain.gain.value = volume;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Connect the gain to an audio node
|
|
775
|
+
* @param {AudioNode} source - Source node to connect
|
|
776
|
+
*/
|
|
777
|
+
connect(source) {
|
|
778
|
+
source.connect(this.gain);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Set volume
|
|
783
|
+
* @param {number} volume - Volume (0-1)
|
|
784
|
+
*/
|
|
785
|
+
setVolume(volume) {
|
|
786
|
+
this.gain.gain.value = volume;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Get the gain node (for chaining)
|
|
791
|
+
* @returns {GainNode}
|
|
792
|
+
*/
|
|
793
|
+
getNode() {
|
|
794
|
+
return this.gain;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Disconnect and clean up
|
|
799
|
+
*/
|
|
800
|
+
disconnect() {
|
|
801
|
+
try {
|
|
802
|
+
this.gain.disconnect();
|
|
803
|
+
} catch (e) {
|
|
804
|
+
// Ignore errors
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|