@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
|
@@ -4,13 +4,13 @@ import { format } from "date-fns";
|
|
|
4
4
|
import { AudioClipAnalyserProperty, AudioClipAnalyserType, AudioClipEventMap, AudioClipEvents, AudioSourceData } from "../../typings";
|
|
5
5
|
import { Debug } from "../../utilities/debugger";
|
|
6
6
|
import { Channel } from "./Channel";
|
|
7
|
+
import { AudioClipPlayer } from "./AudioClipPlayer";
|
|
7
8
|
|
|
8
9
|
type ProgressPayload = Parameters<AudioClipEventMap["progress"]>[0];
|
|
9
10
|
|
|
10
11
|
export class AudioClip {
|
|
11
12
|
|
|
12
13
|
public id: string = v4();
|
|
13
|
-
public hasAttachedToChannel: boolean = false;
|
|
14
14
|
public label: string | null = null;
|
|
15
15
|
|
|
16
16
|
public loop: boolean = false;
|
|
@@ -23,23 +23,8 @@ export class AudioClip {
|
|
|
23
23
|
public gainNode: GainNode | null = null;
|
|
24
24
|
public stereoPannerNode: StereoPannerNode | null = null;
|
|
25
25
|
|
|
26
|
-
public
|
|
27
|
-
public
|
|
28
|
-
|
|
29
|
-
public preAnalyser: AnalyserNode | null = null;
|
|
30
|
-
public postAnalyser: AnalyserNode | null = null;
|
|
31
|
-
|
|
32
|
-
public preAnalyserEnabled: boolean = false;
|
|
33
|
-
public postAnalyserEnabled: boolean = false;
|
|
34
|
-
|
|
35
|
-
public preAnalyserOptions: AnalyserOptions = {};
|
|
36
|
-
public postAnalyserOptions: AnalyserOptions = {};
|
|
37
|
-
|
|
38
|
-
public preAnalyserFloatArrayBuffer = new Float32Array();
|
|
39
|
-
public postAnalyserFloatArrayBuffer = new Float32Array();
|
|
40
|
-
|
|
41
|
-
public preAnalyserByteArrayBuffer = new Uint8Array();
|
|
42
|
-
public postAnalyserByteArrayBuffer = new Uint8Array();
|
|
26
|
+
public context: AudioContext | null = null;
|
|
27
|
+
public audioClipPlayer: AudioClipPlayer | null = null;
|
|
43
28
|
|
|
44
29
|
private audioBufferSourceNodes: AudioBufferSourceNode[] = [];
|
|
45
30
|
private maxAudioBufferSourceNodes: number = 1;
|
|
@@ -52,13 +37,11 @@ export class AudioClip {
|
|
|
52
37
|
|
|
53
38
|
constructor(public data: AudioSourceData) { }
|
|
54
39
|
|
|
55
|
-
// Private methods.
|
|
56
|
-
|
|
57
40
|
private createBufferSource(): AudioBufferSourceNode | null {
|
|
58
41
|
|
|
59
|
-
if (!this.
|
|
42
|
+
if (!this.context) return null;
|
|
60
43
|
|
|
61
|
-
const context = this.
|
|
44
|
+
const context = this.context;
|
|
62
45
|
|
|
63
46
|
const bufferSource = context.createBufferSource();
|
|
64
47
|
bufferSource.buffer = this.data.audioBuffer;
|
|
@@ -78,74 +61,46 @@ export class AudioClip {
|
|
|
78
61
|
node?.disconnect();
|
|
79
62
|
}
|
|
80
63
|
|
|
81
|
-
private rebuildNodeChain() {
|
|
82
|
-
|
|
83
|
-
if (!this.parentialAudioContext || !this.gainNode || !this.stereoPannerNode || !this.parentialChannel || !this.parentialChannel.audioClipsInputGainNode) {
|
|
64
|
+
private rebuildNodeChain(): boolean {
|
|
84
65
|
|
|
85
|
-
|
|
66
|
+
if (!this.context || !this.gainNode || !this.stereoPannerNode || !this.audioClipPlayer || !this.audioClipPlayer.outputGainNode) {
|
|
67
|
+
Debug.Error("Failed to rebuild node chain, because some of the core AudioNodes are missing.");
|
|
86
68
|
return false;
|
|
87
69
|
}
|
|
88
70
|
|
|
89
|
-
const destination = this.
|
|
71
|
+
const destination = this.audioClipPlayer.outputGainNode;
|
|
90
72
|
|
|
91
73
|
this.safeDisconnect(this.gainNode);
|
|
92
74
|
this.safeDisconnect(this.stereoPannerNode);
|
|
93
|
-
this.safeDisconnect(this.preAnalyser);
|
|
94
|
-
this.safeDisconnect(this.postAnalyser);
|
|
95
|
-
|
|
96
|
-
let entry: AudioNode = this.gainNode;
|
|
97
|
-
|
|
98
|
-
if (this.preAnalyserEnabled && this.preAnalyser) {
|
|
99
|
-
|
|
100
|
-
entry = this.preAnalyser;
|
|
101
|
-
this.preAnalyser.connect(this.gainNode);
|
|
102
|
-
}
|
|
103
75
|
|
|
104
76
|
this.gainNode.connect(this.stereoPannerNode);
|
|
77
|
+
this.stereoPannerNode.connect(destination);
|
|
105
78
|
|
|
106
|
-
|
|
107
|
-
this.stereoPannerNode.connect(this.postAnalyser);
|
|
108
|
-
this.postAnalyser.connect(destination);
|
|
109
|
-
} else {
|
|
110
|
-
this.stereoPannerNode.connect(destination);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.connectSourcesTo(entry);
|
|
79
|
+
this.connectSourcesTo(this.gainNode);
|
|
114
80
|
return true;
|
|
115
81
|
}
|
|
116
82
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
public InitializeAudioClipOnAttaching(channel: Channel): AudioClip | null {
|
|
120
|
-
|
|
121
|
-
if (!channel.parentialContext || !channel.audioClipsInputGainNode) {
|
|
122
|
-
|
|
123
|
-
Debug.Error("Could not initialize audio clip on channel attachment, because channel it's master channel has not been defined.", [
|
|
124
|
-
`Call .AttachChannel([channel<"${channel.id}"> Channel]) on the master channel.`
|
|
125
|
-
]);
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
83
|
+
public Initialize(audioClipPlayer: AudioClipPlayer) {
|
|
128
84
|
|
|
129
|
-
|
|
130
|
-
|
|
85
|
+
if (!audioClipPlayer.context) return Debug.Error("Could not initialize AudioClip, because the AudioClipPlayer somehow has no audio context.", [
|
|
86
|
+
`AudioClipPlayer id: ${audioClipPlayer.id}`,
|
|
87
|
+
`AudioClip id: ${this.id}`
|
|
88
|
+
]);
|
|
131
89
|
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
134
|
-
this.hasAttachedToChannel = true;
|
|
90
|
+
this.audioClipPlayer = audioClipPlayer;
|
|
91
|
+
this.context = audioClipPlayer.context;
|
|
135
92
|
|
|
136
|
-
|
|
93
|
+
this.gainNode = new GainNode(this.context);
|
|
94
|
+
this.stereoPannerNode = new StereoPannerNode(this.context);
|
|
137
95
|
}
|
|
138
96
|
|
|
139
|
-
public Play(timestamp?: number, offset?: number)
|
|
97
|
+
public Play(timestamp?: number, offset?: number) {
|
|
140
98
|
|
|
141
|
-
if (!this.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
]);
|
|
145
|
-
return this;
|
|
146
|
-
}
|
|
99
|
+
if (!this.context) return Debug.Error("Could not play audio clip, because context (AudioContext) is undefined.", [
|
|
100
|
+
"Make sure to attach this AudioClip to an AudioClipPlayer instance, before calling .Play on this AudioClip."
|
|
101
|
+
]);
|
|
147
102
|
|
|
148
|
-
const context = this.
|
|
103
|
+
const context = this.context;
|
|
149
104
|
const self = this;
|
|
150
105
|
|
|
151
106
|
if (this.audioBufferSourceNodes.length > this.maxAudioBufferSourceNodes - 1) return null;
|
|
@@ -208,19 +163,19 @@ export class AudioClip {
|
|
|
208
163
|
return this;
|
|
209
164
|
}
|
|
210
165
|
|
|
211
|
-
public Seek(seconds: number)
|
|
166
|
+
public Seek(seconds: number) {
|
|
212
167
|
|
|
213
|
-
if (!this.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
168
|
+
if (!this.context) return Debug.Error("Could not seek because the context (AudioContext) of this AudioClip is undefined.", [
|
|
169
|
+
`Make sure to attach this AudioClip to an AudioClipPlayer instance before calling .Seek on this AudioClip.`,
|
|
170
|
+
`AudioClip id: ${this.id}`
|
|
171
|
+
]);
|
|
219
172
|
|
|
220
173
|
const clamped = Math.max(0, Math.min(seconds, this.duration));
|
|
221
174
|
|
|
222
175
|
if (!this.isPlaying) {
|
|
176
|
+
|
|
223
177
|
this.offsetAtStart = clamped;
|
|
178
|
+
|
|
224
179
|
return this;
|
|
225
180
|
}
|
|
226
181
|
|
|
@@ -230,20 +185,9 @@ export class AudioClip {
|
|
|
230
185
|
return this;
|
|
231
186
|
}
|
|
232
187
|
|
|
233
|
-
|
|
234
188
|
public Stop(): AudioClip | null {
|
|
235
189
|
|
|
236
|
-
if (!this.hasAttachedToChannel || !this.parentialAudioContext) {
|
|
237
|
-
|
|
238
|
-
Debug.Error("Could not stop the audio node because it is not attached to a channel", [
|
|
239
|
-
"Call 'AttachAudioClip([node AudioNode])' on a channel, before stopping this audio node."
|
|
240
|
-
]);
|
|
241
|
-
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
190
|
this.audioBufferSourceNodes.forEach(function (node: AudioBufferSourceNode) {
|
|
246
|
-
|
|
247
191
|
node.stop();
|
|
248
192
|
node.disconnect();
|
|
249
193
|
});
|
|
@@ -252,35 +196,43 @@ export class AudioClip {
|
|
|
252
196
|
this.isPlaying = false;
|
|
253
197
|
|
|
254
198
|
if (this.progressInterval) {
|
|
255
|
-
|
|
256
199
|
clearInterval(this.progressInterval);
|
|
257
200
|
this.progressInterval = null;
|
|
258
201
|
}
|
|
259
202
|
|
|
260
203
|
return this;
|
|
261
204
|
}
|
|
262
|
-
public SetVolume(volume: number): AudioClip | void {
|
|
263
205
|
|
|
264
|
-
|
|
265
|
-
|
|
206
|
+
public SetVolume(volume: number): AudioClip {
|
|
207
|
+
|
|
208
|
+
if (!this.context) Debug.Error("Could not set volume because the context (AudioContext) of this AudioClip is undefined.", [
|
|
209
|
+
"Make sure to attach this AudioClip to an AudioClipPlayer instance before calling .SetVolume() on this AudioClip.",
|
|
266
210
|
]);
|
|
267
211
|
|
|
268
|
-
this.gainNode.
|
|
212
|
+
if (!this.gainNode) Debug.Error("Could not set volume because the gainNode (GainNode) of this AudioClip is undefined.", [
|
|
213
|
+
"Make sure to attach this AudioClip to an AudioClipPlayer instance before calling .SetVolume() on this AudioClip.",
|
|
214
|
+
]);
|
|
269
215
|
|
|
216
|
+
this.gainNode?.gain.setValueAtTime(volume, this.context?.currentTime ?? 0);
|
|
270
217
|
return this;
|
|
271
218
|
}
|
|
272
219
|
|
|
273
|
-
public SetPanLevel(panLevel: number): AudioClip
|
|
220
|
+
public SetPanLevel(panLevel: number): AudioClip {
|
|
221
|
+
|
|
222
|
+
if (!this.context) Debug.Error("Could not set volume because the context (AudioContext) of this AudioClip is undefined.", [
|
|
223
|
+
"Make sure to attach this AudioClip to an AudioClipPlayer instance before calling .SetPanLevel() on this AudioClip.",
|
|
224
|
+
]);
|
|
274
225
|
|
|
275
|
-
if (!this.stereoPannerNode
|
|
276
|
-
|
|
226
|
+
if (!this.stereoPannerNode) Debug.Error("Could not set volume because the stereoPannerNode (StereoPannerNode) of this AudioClip is undefined.", [
|
|
227
|
+
"Make sure to attach this AudioClip to an AudioClipPlayer instance before calling .SetPanLevel() on this AudioClip.",
|
|
277
228
|
]);
|
|
278
229
|
|
|
279
|
-
if (panLevel < -1 || panLevel > 1)
|
|
280
|
-
|
|
230
|
+
if (panLevel < -1 || panLevel > 1) Debug.Error("Could not set the pan level because it is not between -1 and 1.", [
|
|
231
|
+
`Given value: ${panLevel}.`,
|
|
232
|
+
`Accepts a value between -1 and 1. Can be a floating number.`
|
|
281
233
|
]);
|
|
282
234
|
|
|
283
|
-
this.stereoPannerNode
|
|
235
|
+
this.stereoPannerNode?.pan.setValueAtTime(panLevel, this.context?.currentTime ?? 0);
|
|
284
236
|
return this;
|
|
285
237
|
}
|
|
286
238
|
|
|
@@ -306,9 +258,9 @@ export class AudioClip {
|
|
|
306
258
|
|
|
307
259
|
public DisconnectAllAudioBufferSourceNodes(): boolean {
|
|
308
260
|
|
|
309
|
-
if (!this.
|
|
261
|
+
if (!this.context) return false;
|
|
310
262
|
|
|
311
|
-
const contextCurrentTime: number = this.
|
|
263
|
+
const contextCurrentTime: number = this.context?.currentTime;
|
|
312
264
|
|
|
313
265
|
this.audioBufferSourceNodes.forEach(function (node: AudioBufferSourceNode) {
|
|
314
266
|
node.stop(contextCurrentTime);
|
|
@@ -321,7 +273,6 @@ export class AudioClip {
|
|
|
321
273
|
public AddEventListener<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): () => void {
|
|
322
274
|
|
|
323
275
|
this.events[event].push(cb);
|
|
324
|
-
|
|
325
276
|
return () => this.RemoveEventListener(event, cb);
|
|
326
277
|
}
|
|
327
278
|
|
|
@@ -331,7 +282,6 @@ export class AudioClip {
|
|
|
331
282
|
|
|
332
283
|
// @ts-ignore
|
|
333
284
|
cb(...args);
|
|
334
|
-
|
|
335
285
|
this.RemoveEventListener(event, wrapper as unknown as AudioClipEventMap[K]);
|
|
336
286
|
}) as unknown as AudioClipEventMap[K];
|
|
337
287
|
|
|
@@ -363,148 +313,24 @@ export class AudioClip {
|
|
|
363
313
|
}
|
|
364
314
|
|
|
365
315
|
public GetChannelData(channel: number = 0): Float32Array {
|
|
366
|
-
|
|
367
316
|
return this.data.audioBuffer.getChannelData(channel);
|
|
368
317
|
}
|
|
369
318
|
|
|
370
|
-
public
|
|
319
|
+
public Send(channel: Channel) {
|
|
371
320
|
|
|
372
|
-
if
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (!this.preAnalyser)
|
|
378
|
-
this.preAnalyser = new AnalyserNode(this.parentialAudioContext, this.preAnalyserOptions);
|
|
379
|
-
|
|
380
|
-
this.preAnalyserFloatArrayBuffer = new Float32Array(this.preAnalyser.fftSize);
|
|
381
|
-
this.preAnalyserByteArrayBuffer = new Uint8Array(this.preAnalyser.fftSize);
|
|
382
|
-
|
|
383
|
-
this.preAnalyserEnabled = true;
|
|
384
|
-
return this.rebuildNodeChain();
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
public DisablePreAnalyser() {
|
|
388
|
-
|
|
389
|
-
this.preAnalyserEnabled = false;
|
|
390
|
-
return this.rebuildNodeChain();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
public EnablePostAnalyser() {
|
|
394
|
-
|
|
395
|
-
if (!this.parentialAudioContext || !this.parentialChannel || !this.hasAttachedToChannel) {
|
|
396
|
-
|
|
397
|
-
Debug.Error("EnablePostAnalyser: clip niet aan channel gekoppeld.");
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (!this.postAnalyser)
|
|
402
|
-
this.postAnalyser = new AnalyserNode(this.parentialAudioContext, this.postAnalyserOptions);
|
|
403
|
-
|
|
404
|
-
this.postAnalyserFloatArrayBuffer = new Float32Array(this.postAnalyser.fftSize);
|
|
405
|
-
this.postAnalyserByteArrayBuffer = new Uint8Array(this.postAnalyser.fftSize);
|
|
406
|
-
|
|
407
|
-
this.postAnalyserEnabled = true;
|
|
408
|
-
return this.rebuildNodeChain();
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
public DisablePostAnalyser() {
|
|
412
|
-
|
|
413
|
-
this.postAnalyserEnabled = false;
|
|
414
|
-
return this.rebuildNodeChain();
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
public SetPreAnalyserOptions(options: AnalyserOptions) {
|
|
418
|
-
|
|
419
|
-
this.preAnalyserOptions = { ...options };
|
|
420
|
-
|
|
421
|
-
if (!this.preAnalyser) return;
|
|
422
|
-
|
|
423
|
-
this.preAnalyser.fftSize = options.fftSize ?? this.preAnalyser.fftSize;
|
|
424
|
-
this.preAnalyser.minDecibels = options.minDecibels ?? this.preAnalyser.minDecibels;
|
|
425
|
-
this.preAnalyser.maxDecibels = options.maxDecibels ?? this.preAnalyser.maxDecibels;
|
|
426
|
-
this.preAnalyser.smoothingTimeConstant = options.smoothingTimeConstant ?? this.preAnalyser.smoothingTimeConstant;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
public SetPostAnalyserOptions(options: AnalyserOptions) {
|
|
430
|
-
|
|
431
|
-
this.postAnalyserOptions = { ...options };
|
|
432
|
-
|
|
433
|
-
if (!this.postAnalyser) return;
|
|
434
|
-
|
|
435
|
-
this.postAnalyser.fftSize = options.fftSize ?? this.postAnalyser.fftSize;
|
|
436
|
-
this.postAnalyser.minDecibels = options.minDecibels ?? this.postAnalyser.minDecibels;
|
|
437
|
-
this.postAnalyser.maxDecibels = options.maxDecibels ?? this.postAnalyser.maxDecibels;
|
|
438
|
-
this.postAnalyser.smoothingTimeConstant = options.smoothingTimeConstant ?? this.postAnalyser.smoothingTimeConstant;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
public SetAnalyserOption(analyserType: AudioClipAnalyserType, property: AudioClipAnalyserProperty, value: number) {
|
|
442
|
-
|
|
443
|
-
const node = analyserType === "pre" ? this.preAnalyser : this.postAnalyser;
|
|
444
|
-
|
|
445
|
-
if (node) switch (property) {
|
|
446
|
-
case "fftSize": node.fftSize = value as AnalyserNode["fftSize"]; break;
|
|
447
|
-
case "minDecibels": node.minDecibels = value; break;
|
|
448
|
-
case "maxDecibels": node.maxDecibels = value; break;
|
|
449
|
-
case "smoothingTimeConstant": node.smoothingTimeConstant = value; break;
|
|
450
|
-
default: return false;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const opts = analyserType === "pre" ? this.preAnalyserOptions : this.postAnalyserOptions;
|
|
454
|
-
|
|
455
|
-
switch (property) {
|
|
456
|
-
case "fftSize": opts.fftSize = value as AnalyserNode["fftSize"]; break;
|
|
457
|
-
case "minDecibels": opts.minDecibels = value; break;
|
|
458
|
-
case "maxDecibels": opts.maxDecibels = value; break;
|
|
459
|
-
case "smoothingTimeConstant": opts.smoothingTimeConstant = value; break;
|
|
460
|
-
default: return false;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
public GetWaveformFloatData(analyserType: AudioClipAnalyserType): Float32Array | null {
|
|
467
|
-
|
|
468
|
-
if (analyserType === "pre" && this.preAnalyser) {
|
|
469
|
-
this.preAnalyser.getFloatTimeDomainData(this.preAnalyserFloatArrayBuffer);
|
|
470
|
-
return this.preAnalyserFloatArrayBuffer;
|
|
471
|
-
} else if (analyserType === "post" && this.postAnalyser) {
|
|
472
|
-
this.postAnalyser.getFloatTimeDomainData(this.postAnalyserFloatArrayBuffer);
|
|
473
|
-
return this.postAnalyserFloatArrayBuffer;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
public GetWaveformByteData(analyserType: AudioClipAnalyserType): Uint8Array | null {
|
|
480
|
-
|
|
481
|
-
if (!this.preAnalyser || !this.postAnalyser) {
|
|
482
|
-
|
|
483
|
-
Debug.Error("Could not get byte waveform data because the pre analyser or post analyser has not been enabled.", [
|
|
484
|
-
"Call .EnablePreAnalyser() or .EnablePostAnalyser() before getting waveform data."
|
|
485
|
-
]);
|
|
486
|
-
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
321
|
+
if(!channel.HasAudioClipPlayer()) return Debug.Error("Could not send AudioClip signal to channel, because the channel's AudioClipPlayer is undefined.", [
|
|
322
|
+
"Make sure to initialize the Channel.",
|
|
323
|
+
`Channel id: ${channel.id}.`,
|
|
324
|
+
`AudioClip id: ${this.id}.`
|
|
325
|
+
]);
|
|
489
326
|
|
|
490
|
-
|
|
491
|
-
case "pre":
|
|
492
|
-
this.preAnalyser.getByteTimeDomainData(this.preAnalyserByteArrayBuffer);
|
|
493
|
-
return this.preAnalyserByteArrayBuffer;
|
|
494
|
-
case "post":
|
|
495
|
-
this.postAnalyser.getByteTimeDomainData(this.postAnalyserByteArrayBuffer);
|
|
496
|
-
return this.postAnalyserByteArrayBuffer;
|
|
497
|
-
default:
|
|
498
|
-
return null;
|
|
499
|
-
}
|
|
327
|
+
channel.AttachAudioClip(this);
|
|
500
328
|
}
|
|
501
329
|
|
|
502
|
-
// Public getters and setters
|
|
503
|
-
|
|
504
330
|
public get currentPlaybackTime(): number {
|
|
505
|
-
return (!this.isPlaying || !this.
|
|
331
|
+
return (!this.isPlaying || !this.context)
|
|
506
332
|
? 0
|
|
507
|
-
: this.offsetAtStart + (this.
|
|
333
|
+
: this.offsetAtStart + (this.context.currentTime - this.startTime);
|
|
508
334
|
}
|
|
509
335
|
|
|
510
336
|
public get duration(): number {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { v4 } from "uuid";
|
|
2
|
+
|
|
3
|
+
import { AudioClip } from "./AudioClip";
|
|
4
|
+
import { Debug } from "../../utilities/debugger";
|
|
5
|
+
import { Master } from "./Master";
|
|
6
|
+
import { Channel } from "./Channel";
|
|
7
|
+
|
|
8
|
+
export class AudioClipPlayer {
|
|
9
|
+
|
|
10
|
+
public label: string = "AudioClipPlayer";
|
|
11
|
+
public id: string = v4();
|
|
12
|
+
|
|
13
|
+
public audioClips: AudioClip[] = [];
|
|
14
|
+
public outputGainNode: GainNode | null = null;
|
|
15
|
+
|
|
16
|
+
public context: AudioContext | null = null;
|
|
17
|
+
public channel: Channel | null = null;
|
|
18
|
+
|
|
19
|
+
constructor(context: AudioContext) {
|
|
20
|
+
this.context = context;
|
|
21
|
+
this.outputGainNode = new GainNode(context);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public AttachAudioClip(audioClip: AudioClip) {
|
|
25
|
+
|
|
26
|
+
if (this.audioClips.includes(audioClip)) return Debug.Error("Could not attach audio clip because it is already part of this channel", [
|
|
27
|
+
"Call .DetachAudioClip([clip AudioClip]) before attaching audio clip."
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
audioClip.Initialize(this);
|
|
31
|
+
this.audioClips.push(audioClip);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public Send(channel: Channel) {
|
|
35
|
+
|
|
36
|
+
if(!this.outputGainNode || !channel.input) return Debug.Error("Could not send AudioClipPlayer signal to a channel.");
|
|
37
|
+
|
|
38
|
+
this.channel = channel;
|
|
39
|
+
this.outputGainNode.connect(channel.input);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public SetLabel(label: string) {
|
|
43
|
+
this.label = label;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -10,20 +10,22 @@ export class AudioDevice {
|
|
|
10
10
|
public id: string = v4();
|
|
11
11
|
public timestamp: number = Date.now();
|
|
12
12
|
|
|
13
|
-
public
|
|
13
|
+
public context: AudioContext = new AudioContext();
|
|
14
|
+
|
|
15
|
+
public masterChannel: Master = new Master(this.context);
|
|
14
16
|
public masterChannels: Master[] = [];
|
|
15
17
|
|
|
16
|
-
constructor(public deviceInfo: MediaDeviceInfo) {}
|
|
18
|
+
constructor(public deviceInfo: MediaDeviceInfo) { }
|
|
17
19
|
|
|
18
20
|
public GetMasterChannel(): Master {
|
|
19
21
|
return this.masterChannel;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
public SetMasterChannel(channel: Master): void {
|
|
23
|
-
|
|
24
|
-
if(channel.id === this.masterChannel.id) return Debug.Error("The provided master channel is the same as the current channel.", [
|
|
25
|
+
|
|
26
|
+
if (channel.id === this.masterChannel.id) return Debug.Error("The provided master channel is the same as the current channel.", [
|
|
25
27
|
"Provide this method with a different master channel.",
|
|
26
|
-
`Received master channel ID ${channel.id}.`
|
|
28
|
+
`Received master channel ID ${channel.id}.`
|
|
27
29
|
], ErrorCodes.SAME_MASTER_CHANNEL);
|
|
28
30
|
|
|
29
31
|
this.masterChannel = channel;
|
|
@@ -31,9 +33,13 @@ export class AudioDevice {
|
|
|
31
33
|
|
|
32
34
|
public CreateMasterChannel(): Master {
|
|
33
35
|
|
|
34
|
-
const master = new Master();
|
|
36
|
+
const master = new Master(this.context);
|
|
35
37
|
|
|
36
38
|
this.masterChannels.push(master);
|
|
37
39
|
return master;
|
|
38
40
|
}
|
|
41
|
+
|
|
42
|
+
public GetContext(): AudioContext {
|
|
43
|
+
return this.context;
|
|
44
|
+
}
|
|
39
45
|
}
|