@fluex/fluexgl-dsp 0.3.4 → 0.4.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/lib/dist/core/classes/AudioClip.d.ts +10 -28
- package/lib/dist/core/classes/AudioClip.d.ts.map +1 -1
- package/lib/dist/core/classes/AudioClip.js +56 -192
- package/lib/dist/core/classes/AudioClipPlayer.d.ts +15 -0
- package/lib/dist/core/classes/AudioClipPlayer.d.ts.map +1 -0
- package/lib/dist/core/classes/AudioClipPlayer.js +31 -0
- package/lib/dist/core/classes/AudioDevice.d.ts +2 -0
- package/lib/dist/core/classes/AudioDevice.d.ts.map +1 -1
- package/lib/dist/core/classes/AudioDevice.js +6 -2
- package/lib/dist/core/classes/Channel.d.ts +16 -34
- package/lib/dist/core/classes/Channel.d.ts.map +1 -1
- package/lib/dist/core/classes/Channel.js +95 -190
- package/lib/dist/core/classes/DpsPipeline.js +2 -2
- package/lib/dist/core/classes/Effector.d.ts.map +1 -1
- package/lib/dist/core/classes/Effector.js +0 -1
- package/lib/dist/core/classes/Master.d.ts +4 -4
- package/lib/dist/core/classes/Master.d.ts.map +1 -1
- package/lib/dist/core/classes/Master.js +11 -5
- package/lib/dist/core/exports.d.ts +1 -0
- package/lib/dist/core/exports.d.ts.map +1 -1
- package/lib/dist/core/exports.js +1 -0
- package/lib/dist/core/functions/rebuild-effect-chain.d.ts +3 -0
- package/lib/dist/core/functions/rebuild-effect-chain.d.ts.map +1 -0
- package/lib/dist/core/functions/rebuild-effect-chain.js +2 -0
- package/lib/dist/index.d.ts +2 -2
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +2 -2
- package/lib/dist/test/linkable-channels.d.ts +2 -0
- package/lib/dist/test/linkable-channels.d.ts.map +1 -0
- package/lib/dist/test/linkable-channels.js +16 -0
- package/lib/dist/utilities/helpers.d.ts +1 -2
- package/lib/dist/utilities/helpers.d.ts.map +1 -1
- package/lib/dist/utilities/helpers.js +5 -5
- package/lib/src/core/classes/AudioClip.ts +62 -236
- package/lib/src/core/classes/AudioClipPlayer.ts +45 -0
- package/lib/src/core/classes/AudioDevice.ts +12 -6
- package/lib/src/core/classes/Channel.ts +88 -221
- package/lib/src/core/classes/DpsPipeline.ts +2 -2
- package/lib/src/core/classes/Effector.ts +1 -3
- package/lib/src/core/classes/Master.ts +24 -12
- package/lib/src/core/exports.ts +2 -1
- package/lib/src/core/functions/rebuild-effect-chain.ts +5 -0
- package/lib/src/index.ts +3 -2
- package/lib/src/utilities/helpers.ts +5 -5
- package/package.json +2 -1
|
@@ -1,289 +1,156 @@
|
|
|
1
1
|
import { v4 } from "uuid";
|
|
2
|
-
|
|
3
|
-
import {
|
|
2
|
+
import { Debug } from "../../utilities/debugger";
|
|
3
|
+
import { AudioClipPlayer } from "./AudioClipPlayer";
|
|
4
4
|
import { AudioClip } from "./AudioClip";
|
|
5
5
|
import { Master } from "./Master";
|
|
6
6
|
|
|
7
|
-
import { ChannelOptions } from "../../typings";
|
|
8
|
-
import { Debug } from "../../utilities/debugger";
|
|
9
|
-
|
|
10
7
|
export class Channel {
|
|
11
8
|
|
|
12
9
|
public id: string = v4();
|
|
13
|
-
public
|
|
14
|
-
public label: string | null;
|
|
15
|
-
|
|
16
|
-
public parentialContext: AudioContext | null = null;
|
|
17
|
-
public parentialMasterChannel: Master | null = null;
|
|
10
|
+
public label: string = "Channel";
|
|
18
11
|
|
|
19
|
-
public
|
|
20
|
-
|
|
21
|
-
public gainNode: GainNode | null = null;
|
|
12
|
+
public input: AudioNode | null = null;
|
|
22
13
|
public stereoPannerNode: StereoPannerNode | null = null;
|
|
23
14
|
public analyserNode: AnalyserNode | null = null;
|
|
24
|
-
public
|
|
25
|
-
|
|
26
|
-
public analyserFloatArrayBuffer = new Float32Array();
|
|
27
|
-
public analyserByteArrayBuffer = new Uint8Array();
|
|
28
|
-
|
|
29
|
-
public audioClipsInputGainNode: GainNode | null = null;
|
|
30
|
-
|
|
31
|
-
public analyserOptions: AnalyserOptions = { fftSize: 32 };
|
|
32
|
-
|
|
33
|
-
constructor(public options: Partial<ChannelOptions> = { maxAudioNodes: 8, maxEffects: 8 }) {
|
|
34
|
-
this.label = options.label ?? null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private rebuildEffectChain() {
|
|
38
|
-
Debug.Log("Attempting to rebuild effect chain.");
|
|
39
|
-
|
|
40
|
-
if (!this.audioClipsInputGainNode || !this.stereoPannerNode) {
|
|
41
|
-
return Debug.Error(
|
|
42
|
-
"Could not rebuild effect chain, because one or more nodes on this channel are undefined.",
|
|
43
|
-
[
|
|
44
|
-
`Channel id: ${this.id}.`,
|
|
45
|
-
`Current amount of effects: ${this.effects.length}.`
|
|
46
|
-
]
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
this.audioClipsInputGainNode.disconnect();
|
|
50
|
-
|
|
51
|
-
for (const effect of this.effects) {
|
|
52
|
-
effect.audioWorkletNode?.disconnect();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const activeEffects = this.effects.filter(function (e) {
|
|
56
|
-
return !!e.audioWorkletNode;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
if (activeEffects.length === 0) {
|
|
60
|
-
this.audioClipsInputGainNode.connect(this.stereoPannerNode);
|
|
61
|
-
} else {
|
|
62
|
-
this.audioClipsInputGainNode.connect(activeEffects[0].audioWorkletNode!);
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < activeEffects.length - 1; i++) {
|
|
65
|
-
const current = activeEffects[i].audioWorkletNode!;
|
|
66
|
-
const next = activeEffects[i + 1].audioWorkletNode!;
|
|
67
|
-
current.connect(next);
|
|
68
|
-
}
|
|
15
|
+
public gainNode: GainNode | null = null;
|
|
16
|
+
public output: AudioNode | null = null;
|
|
69
17
|
|
|
70
|
-
|
|
71
|
-
lastEffectNode.connect(this.stereoPannerNode);
|
|
72
|
-
}
|
|
18
|
+
public context: AudioContext | null = null;
|
|
73
19
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
`Current amount of effects: ${this.effects.length}`
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
20
|
+
private sends: Channel[] = [];
|
|
21
|
+
private audioClipPlayer: AudioClipPlayer | null = null;
|
|
79
22
|
|
|
80
|
-
|
|
23
|
+
constructor(context: AudioContext) {
|
|
24
|
+
this.context = context;
|
|
81
25
|
|
|
82
|
-
this.
|
|
83
|
-
this.parentialContext = master.context;
|
|
26
|
+
this.disconnectAudioNodes(true);
|
|
84
27
|
|
|
85
|
-
this.
|
|
86
|
-
this.stereoPannerNode = new StereoPannerNode(
|
|
87
|
-
this.analyserNode = new AnalyserNode(
|
|
28
|
+
this.input = new GainNode(context);
|
|
29
|
+
this.stereoPannerNode = new StereoPannerNode(context);
|
|
30
|
+
this.analyserNode = new AnalyserNode(context);
|
|
31
|
+
this.gainNode = new GainNode(context);
|
|
32
|
+
this.output = new GainNode(context);
|
|
88
33
|
|
|
89
|
-
this.
|
|
90
|
-
this.analyserByteArrayBuffer = new Uint8Array(this.analyserNode.fftSize);
|
|
34
|
+
this.audioClipPlayer = new AudioClipPlayer(context);
|
|
91
35
|
|
|
92
|
-
this.
|
|
36
|
+
this.input.connect(this.stereoPannerNode);
|
|
37
|
+
this.stereoPannerNode.connect(this.analyserNode);
|
|
38
|
+
this.analyserNode.connect(this.gainNode);
|
|
39
|
+
this.gainNode.connect(this.output);
|
|
93
40
|
|
|
94
|
-
this.
|
|
95
|
-
this.stereoPannerNode.connect(this.gainNode);
|
|
96
|
-
this.gainNode.connect(this.analyserNode);
|
|
97
|
-
this.analyserNode.connect(this.parentialMasterChannel.gainNode);
|
|
41
|
+
this.audioClipPlayer.Send(this);
|
|
98
42
|
}
|
|
99
43
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.options.label = label;
|
|
103
|
-
this.label = label;
|
|
104
|
-
}
|
|
44
|
+
private disconnectAudioNodes(gc?: boolean) {
|
|
105
45
|
|
|
106
|
-
|
|
46
|
+
this.input?.disconnect();
|
|
47
|
+
this.stereoPannerNode?.disconnect();
|
|
48
|
+
this.analyserNode?.disconnect();
|
|
49
|
+
this.gainNode?.disconnect();
|
|
50
|
+
this.output?.disconnect();
|
|
107
51
|
|
|
108
|
-
|
|
109
|
-
|
|
52
|
+
if (gc) {
|
|
53
|
+
this.input = null;
|
|
54
|
+
this.stereoPannerNode = null;
|
|
55
|
+
this.analyserNode = null;
|
|
56
|
+
this.gainNode = null;
|
|
57
|
+
this.output = null;
|
|
58
|
+
}
|
|
110
59
|
}
|
|
111
60
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (this.audioClips.includes(clip)) return Debug.Error("Could not attach audio clip because it is already part of this channel", [
|
|
115
|
-
"Call .DetachAudioClip([clip AudioClip]) before attaching audio clip."
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
clip.InitializeAudioClipOnAttaching(this);
|
|
119
|
-
this.audioClips.push(clip);
|
|
61
|
+
private isInitialized(): boolean {
|
|
62
|
+
return !!(this.context && this.input && this.output);
|
|
120
63
|
}
|
|
121
64
|
|
|
122
|
-
|
|
65
|
+
private isReachable(target: Channel): boolean {
|
|
123
66
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
]);
|
|
127
|
-
|
|
128
|
-
const self: Channel = this;
|
|
67
|
+
const visited: Set<string> = new Set<string>();
|
|
68
|
+
const stack: Channel[] = [this];
|
|
129
69
|
|
|
130
|
-
|
|
131
|
-
clip.parentialChannel = null;
|
|
132
|
-
clip.hasAttachedToChannel = false;
|
|
70
|
+
while (stack.length > 0) {
|
|
133
71
|
|
|
134
|
-
|
|
135
|
-
clip.gainNode?.disconnect();
|
|
72
|
+
const current: Channel = stack.pop() as Channel;
|
|
136
73
|
|
|
137
|
-
|
|
74
|
+
if (current.id === target.id)
|
|
75
|
+
return true;
|
|
138
76
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return self.audioClips.splice(index, 1);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
77
|
+
if (visited.has(current.id))
|
|
78
|
+
continue;
|
|
144
79
|
|
|
145
|
-
|
|
80
|
+
visited.add(current.id);
|
|
146
81
|
|
|
147
|
-
|
|
148
|
-
|
|
82
|
+
for (let i: number = 0; i < current.sends.length; i++)
|
|
83
|
+
stack.push(current.sends[i]);
|
|
149
84
|
}
|
|
150
85
|
|
|
151
86
|
return false;
|
|
152
87
|
}
|
|
153
88
|
|
|
154
|
-
public
|
|
155
|
-
|
|
156
|
-
if (!this.gainNode) return Debug.Error("Could not set channel volume because the channel is not attached to a master channel.", [
|
|
157
|
-
"Attach the channel to a master channel before setting the volume."
|
|
158
|
-
]);
|
|
159
|
-
|
|
160
|
-
this.gainNode.gain.setValueAtTime(volume, this.parentialContext!.currentTime);
|
|
161
|
-
}
|
|
89
|
+
public Send(channel: Channel | Master) {
|
|
162
90
|
|
|
163
|
-
|
|
91
|
+
if (channel instanceof Master) return (channel as Master).AttachChannel(this);
|
|
164
92
|
|
|
165
|
-
if (
|
|
166
|
-
|
|
93
|
+
if (channel.id === this.id) return Debug.Error("Could not link channel to itself.", [
|
|
94
|
+
`This channel id: ${this.id}`
|
|
167
95
|
]);
|
|
168
96
|
|
|
169
|
-
this.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
public AddEffect(effect: Effector): void {
|
|
173
|
-
|
|
174
|
-
if (!this.parentialContext) return Debug.Error("Could not add effect on channel, because the parential context is undefined.", [
|
|
175
|
-
`Channel ID: ${this.id}`,
|
|
176
|
-
`Effect ID: ${effect.id}`,
|
|
177
|
-
`Effect name: ${effect.constructor.name}`
|
|
178
|
-
])
|
|
179
|
-
|
|
180
|
-
if (this.effects.includes(effect)) return Debug.Error("Could not add effect because it is already part of this channel", [
|
|
181
|
-
"Call .RemoveEffect([effect Effector]) before adding effect."
|
|
97
|
+
if (!this.isInitialized() || !channel.isInitialized()) return Debug.Error("Could not link channels because one (or both) channels are not initialized.", [
|
|
98
|
+
`This channel id: ${this.id} initialized: ${this.isInitialized()}`,
|
|
99
|
+
`Target channel id: ${channel.id} initialized: ${channel.isInitialized()}`
|
|
182
100
|
]);
|
|
183
101
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
public AttachEffect(effect: Effector): void {
|
|
191
|
-
return this.AddEffect(effect);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
public RemoveEffect(effect: Effector): void {
|
|
102
|
+
if (this.context !== channel.context) return Debug.Error("Could not link channels because they do not share the same AudioContext.", [
|
|
103
|
+
`This channel context: ${this.context ? "set" : "null"}`,
|
|
104
|
+
`Target channel context: ${channel.context ? "set" : "null"}`
|
|
105
|
+
]);
|
|
195
106
|
|
|
196
|
-
if (
|
|
197
|
-
|
|
107
|
+
if (this.sends.includes(channel)) return Debug.Error("Could not link channels, because the given channel is already linked with this one.", [
|
|
108
|
+
`This channel id: ${this.id}`,
|
|
109
|
+
`Target channel id: ${channel.id}`
|
|
198
110
|
]);
|
|
199
111
|
|
|
200
|
-
|
|
112
|
+
if (channel.isReachable(this)) return Debug.Error("Could not link channels because it would create a feedback loop.", [
|
|
113
|
+
`This channel id: ${this.id}`,
|
|
114
|
+
`Target channel id: ${channel.id}`
|
|
115
|
+
]);
|
|
201
116
|
|
|
202
|
-
this.
|
|
203
|
-
if (effect.id === _effect.id)
|
|
204
|
-
return self.effects.splice(index, 1);
|
|
205
|
-
});
|
|
117
|
+
(this.output as AudioNode).connect(channel.input as AudioNode);
|
|
206
118
|
|
|
207
|
-
this.
|
|
119
|
+
this.sends.push(channel);
|
|
208
120
|
}
|
|
209
121
|
|
|
210
|
-
public
|
|
211
|
-
return this.RemoveEffect(effect);
|
|
212
|
-
}
|
|
122
|
+
public Unsend(channel: Channel | Master) {
|
|
213
123
|
|
|
214
|
-
|
|
124
|
+
if(channel instanceof Master)
|
|
125
|
+
return (channel as Master).DetachChannel(this);
|
|
215
126
|
|
|
216
|
-
|
|
217
|
-
Debug.Error("Could not set FFT size on analyser because the analyser has not been defined.");
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
127
|
+
const idx: number = this.sends.indexOf(channel);
|
|
220
128
|
|
|
221
|
-
|
|
129
|
+
if (idx === -1) return;
|
|
222
130
|
|
|
223
|
-
this.
|
|
224
|
-
|
|
131
|
+
if (this.output && channel.input)
|
|
132
|
+
this.output.disconnect(channel.input);
|
|
225
133
|
|
|
226
|
-
|
|
134
|
+
this.sends.splice(idx, 1);
|
|
227
135
|
}
|
|
228
136
|
|
|
229
|
-
public
|
|
230
|
-
|
|
231
|
-
if (!this.analyserNode) {
|
|
232
|
-
Debug.Error("Could not get waveform float data, because the analyser has not been defined.");
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (this.analyserFloatArrayBuffer.length !== this.analyserNode.fftSize) {
|
|
237
|
-
this.analyserFloatArrayBuffer = new Float32Array(this.analyserNode.fftSize);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
this.analyserNode.getFloatTimeDomainData(this.analyserFloatArrayBuffer);
|
|
241
|
-
return this.analyserFloatArrayBuffer;
|
|
137
|
+
public HasAudioClipPlayer(): boolean {
|
|
138
|
+
return !!this.audioClipPlayer;
|
|
242
139
|
}
|
|
243
140
|
|
|
244
|
-
public
|
|
141
|
+
public UnsendToAllChannels() {
|
|
245
142
|
|
|
246
|
-
|
|
247
|
-
Debug.Error("Could not get waveform byte data, because the analyser has not been defined.");
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
143
|
+
for (var i: number = 0; i < this.sends.length; i++) {
|
|
250
144
|
|
|
251
|
-
|
|
252
|
-
|
|
145
|
+
this.Unsend(this.sends[i]);
|
|
146
|
+
i--;
|
|
253
147
|
}
|
|
254
|
-
|
|
255
|
-
this.analyserNode.getByteTimeDomainData(this.analyserByteArrayBuffer);
|
|
256
|
-
return this.analyserByteArrayBuffer;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
public SetAnalyserOptions(options: AnalyserOptions): Channel | null {
|
|
260
|
-
|
|
261
|
-
this.analyserOptions = { ...options };
|
|
262
|
-
|
|
263
|
-
if (!this.analyserNode) return null;
|
|
264
|
-
|
|
265
|
-
this.analyserNode.minDecibels = options.minDecibels ?? this.analyserNode.minDecibels;
|
|
266
|
-
this.analyserNode.maxDecibels = options.maxDecibels ?? this.analyserNode.maxDecibels;
|
|
267
|
-
this.analyserNode.fftSize = options.fftSize ?? 32;
|
|
268
|
-
this.analyserNode.smoothingTimeConstant = options.smoothingTimeConstant ?? this.analyserNode.smoothingTimeConstant;
|
|
269
|
-
|
|
270
|
-
this.analyserByteArrayBuffer = new Uint8Array(this.analyserNode.fftSize);
|
|
271
|
-
this.analyserFloatArrayBuffer = new Float32Array(this.analyserNode.fftSize);
|
|
272
|
-
|
|
273
|
-
return this;
|
|
274
148
|
}
|
|
275
149
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
public get volume(): number | null {
|
|
279
|
-
|
|
280
|
-
if (!this.gainNode) return null;
|
|
281
|
-
return this.gainNode.gain.value;
|
|
282
|
-
}
|
|
150
|
+
public AttachAudioClip(audioClip: AudioClip) {
|
|
283
151
|
|
|
284
|
-
|
|
152
|
+
if (!this.audioClipPlayer) return Debug.Error("Cannot not link AudioClip to this channel because this channel's AudioClipPlayer is undefined.");
|
|
285
153
|
|
|
286
|
-
|
|
287
|
-
return this.stereoPannerNode.pan.value;
|
|
154
|
+
this.audioClipPlayer.AttachAudioClip(audioClip);
|
|
288
155
|
}
|
|
289
|
-
}
|
|
156
|
+
}
|
|
@@ -3,7 +3,7 @@ import { v4 } from "uuid";
|
|
|
3
3
|
import { AudioDevice } from "./AudioDevice";
|
|
4
4
|
import { Debug } from "../../utilities/debugger";
|
|
5
5
|
import { LoadWebAssemblyModule } from "../../utilities/web-assembly";
|
|
6
|
-
import { ConstructProcessorWorklet,
|
|
6
|
+
import { ConstructProcessorWorklet, LoadWorkletOnAudioDevice } from "../../utilities/helpers";
|
|
7
7
|
|
|
8
8
|
import { ErrorCodes, WarningCodes } from "../../console-codes";
|
|
9
9
|
import { DspPipelineInitializationOptions } from "../../typings";
|
|
@@ -142,7 +142,7 @@ export class DspPipeline {
|
|
|
142
142
|
|
|
143
143
|
if (!defaultAudioDevice) return null;
|
|
144
144
|
|
|
145
|
-
await
|
|
145
|
+
await LoadWorkletOnAudioDevice(defaultAudioDevice, this.blobUrl);
|
|
146
146
|
|
|
147
147
|
return defaultAudioDevice;
|
|
148
148
|
}
|
|
@@ -24,9 +24,7 @@ export abstract class Effector {
|
|
|
24
24
|
private handleIncomingMessages(event: MessageEvent) {
|
|
25
25
|
|
|
26
26
|
const messageData = event.data as IncomingProcessorMessage;
|
|
27
|
-
|
|
28
|
-
console.log(messageData);
|
|
29
|
-
|
|
27
|
+
|
|
30
28
|
switch (messageData.type) {
|
|
31
29
|
case "message":
|
|
32
30
|
return this.events["incoming-processor-message"].forEach(ev => ev(messageData));
|
|
@@ -3,48 +3,60 @@ import { v4 } from "uuid";
|
|
|
3
3
|
import { Channel } from "./Channel";
|
|
4
4
|
|
|
5
5
|
import { Debug } from "../../utilities/debugger";
|
|
6
|
+
import { AudioClipPlayer } from "./AudioClipPlayer";
|
|
6
7
|
|
|
7
8
|
export class Master {
|
|
8
9
|
|
|
9
10
|
public id: string = v4();
|
|
10
11
|
public channels: Channel[] = [];
|
|
11
|
-
public context: AudioContext = new AudioContext();
|
|
12
12
|
|
|
13
|
-
public gainNode: GainNode =
|
|
14
|
-
public analyserNode: AnalyserNode =
|
|
13
|
+
public gainNode: GainNode | null = null;
|
|
14
|
+
public analyserNode: AnalyserNode | null = null;
|
|
15
|
+
|
|
16
|
+
public context: AudioContext | null = null;
|
|
17
|
+
|
|
18
|
+
constructor(context: AudioContext) {
|
|
19
|
+
|
|
20
|
+
this.context = context;
|
|
21
|
+
|
|
22
|
+
this.gainNode = new GainNode(context);
|
|
23
|
+
this.analyserNode = new AnalyserNode(context);
|
|
15
24
|
|
|
16
|
-
constructor() {
|
|
17
|
-
|
|
18
25
|
this.gainNode.connect(this.analyserNode);
|
|
19
26
|
this.analyserNode.connect(this.context.destination);
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
public AttachChannel(channel: Channel): void {
|
|
23
30
|
|
|
24
|
-
if(this.channels.includes(channel)) return Debug.Error("Could not attach the channel because it is already part of this master channel.",[
|
|
31
|
+
if (this.channels.includes(channel)) return Debug.Error("Could not attach the channel because it is already part of this master channel.", [
|
|
25
32
|
"Call .DetachChannel([channel Channel]) before attaching the channel."
|
|
26
33
|
]);
|
|
27
34
|
|
|
28
|
-
channel.InitializeChannelOnMasterAttachment(this);
|
|
29
|
-
|
|
30
35
|
this.channels.push(channel);
|
|
36
|
+
|
|
37
|
+
if (channel.output && this.gainNode)
|
|
38
|
+
channel.output.connect(this.gainNode);
|
|
39
|
+
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
public DetachChannel(channel: Channel): void {
|
|
35
44
|
|
|
36
|
-
if(!this.channels.includes(channel)) return Debug.Error("Could not detach the channel because it is not part of this master channel.", [
|
|
45
|
+
if (!this.channels.includes(channel)) return Debug.Error("Could not detach the channel because it is not part of this master channel.", [
|
|
37
46
|
"Call .AttachChannel([channel Channel]) before detaching the channel."
|
|
38
47
|
]);
|
|
39
48
|
|
|
49
|
+
if (channel.output && this.gainNode)
|
|
50
|
+
channel.output.disconnect(this.gainNode);
|
|
51
|
+
|
|
40
52
|
const self = this;
|
|
41
53
|
|
|
42
|
-
this.channels.forEach(function(_channel: Channel, index: number) {
|
|
43
|
-
if(channel.id === _channel.id) {
|
|
54
|
+
this.channels.forEach(function (_channel: Channel, index: number) {
|
|
55
|
+
if (channel.id === _channel.id) {
|
|
44
56
|
|
|
45
57
|
self.channels.splice(index, 1);
|
|
46
58
|
return;
|
|
47
59
|
}
|
|
48
60
|
});
|
|
49
61
|
}
|
|
50
|
-
}
|
|
62
|
+
}
|
package/lib/src/core/exports.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { Channel } from "./classes/Channel";
|
|
|
3
3
|
export { Effector } from "./classes/Effector";
|
|
4
4
|
export { Master } from "./classes/Master";
|
|
5
5
|
export { AudioClip } from "./classes/AudioClip";
|
|
6
|
-
export { DspPipeline } from "./classes/DpsPipeline";
|
|
6
|
+
export { DspPipeline } from "./classes/DpsPipeline";
|
|
7
|
+
export { AudioClipPlayer } from "./classes/AudioClipPlayer";
|
package/lib/src/index.ts
CHANGED
|
@@ -54,7 +54,8 @@ export {
|
|
|
54
54
|
Effector,
|
|
55
55
|
Master,
|
|
56
56
|
AudioClip,
|
|
57
|
-
DspPipeline
|
|
57
|
+
DspPipeline,
|
|
58
|
+
AudioClipPlayer
|
|
58
59
|
} from "./core/exports";
|
|
59
60
|
|
|
60
61
|
export {
|
|
@@ -65,7 +66,7 @@ export {
|
|
|
65
66
|
ResolveDefaultAudioOutputDevice,
|
|
66
67
|
LoadAudioSource,
|
|
67
68
|
LoadAudioSourceFromBlob,
|
|
68
|
-
|
|
69
|
+
LoadWorkletOnAudioDevice,
|
|
69
70
|
SendMessageToWorklet
|
|
70
71
|
} from "./utilities/helpers";
|
|
71
72
|
|
|
@@ -113,7 +113,7 @@ export async function ResolveDefaultAudioOutputDevice(init: DspPipelineInitializ
|
|
|
113
113
|
|
|
114
114
|
if (!defaultAudioDevice) return null;
|
|
115
115
|
|
|
116
|
-
await
|
|
116
|
+
await LoadWorkletOnAudioDevice(defaultAudioDevice, init.workletBlobUrl);
|
|
117
117
|
|
|
118
118
|
return defaultAudioDevice;
|
|
119
119
|
}
|
|
@@ -138,7 +138,7 @@ export async function ResolveDefaultAudioInputDevice(init: DspPipelineInitializa
|
|
|
138
138
|
|
|
139
139
|
if (!defaultAudioDevice) return null;
|
|
140
140
|
|
|
141
|
-
await
|
|
141
|
+
await LoadWorkletOnAudioDevice(defaultAudioDevice, init.workletBlobUrl);
|
|
142
142
|
|
|
143
143
|
return defaultAudioDevice;
|
|
144
144
|
}
|
|
@@ -213,10 +213,10 @@ export function ConstructProcessorWorklet(code: string): string {
|
|
|
213
213
|
return URL.createObjectURL(blob);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
export async function
|
|
217
|
-
Debug.Log("Loading worklet modules on master channel...", [`Channel ID: ${
|
|
216
|
+
export async function LoadWorkletOnAudioDevice(audioDevice: AudioDevice, workletBlobUrl: string) {
|
|
217
|
+
Debug.Log("Loading worklet modules on master channel...", [`Channel ID: ${audioDevice.id}`]);
|
|
218
218
|
|
|
219
|
-
const context: AudioContext =
|
|
219
|
+
const context: AudioContext = audioDevice.context,
|
|
220
220
|
start: number = Date.now();
|
|
221
221
|
|
|
222
222
|
await context.audioWorklet.addModule(workletBlobUrl);
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluex/fluexgl-dsp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "An open-source, web-based DSP library developed alongside FluexGL, designed for creating and manipulating sounds in various contexts.",
|
|
5
5
|
"main": "lib/dist/index.js",
|
|
6
6
|
"types": "lib/dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"typecheck": "tsc -p lib/tsconfig.json --noEmit",
|
|
9
9
|
"build": "tsc --build",
|
|
10
|
+
"watch": "tsc --watch",
|
|
10
11
|
"build-and-deploy": "node ./scripts/build-and-deploy.js"
|
|
11
12
|
},
|
|
12
13
|
"keywords": [
|