@fluex/fluexgl-dsp 0.4.7 → 0.4.8
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/README.md +7 -7
- package/lib/dist/console-codes.d.ts +2 -1
- package/lib/dist/console-codes.d.ts.map +1 -1
- package/lib/dist/console-codes.js +1 -0
- package/lib/dist/core/classes/AudioClip.d.ts +26 -17
- package/lib/dist/core/classes/AudioClip.d.ts.map +1 -1
- package/lib/dist/core/classes/AudioClip.js +124 -51
- package/lib/dist/core/classes/AudioClipPlayer.d.ts +8 -8
- package/lib/dist/core/classes/AudioClipPlayer.d.ts.map +1 -1
- package/lib/dist/core/classes/AudioClipPlayer.js +22 -24
- package/lib/dist/core/classes/AudioDevice.d.ts +46 -5
- package/lib/dist/core/classes/AudioDevice.d.ts.map +1 -1
- package/lib/dist/core/classes/AudioDevice.js +47 -6
- package/lib/dist/core/classes/Channel.d.ts +20 -20
- package/lib/dist/core/classes/Channel.d.ts.map +1 -1
- package/lib/dist/core/classes/Channel.js +42 -42
- package/lib/dist/core/classes/DpsPipeline.d.ts +6 -6
- package/lib/dist/core/classes/DpsPipeline.d.ts.map +1 -1
- package/lib/dist/core/classes/DpsPipeline.js +18 -18
- package/lib/dist/core/classes/Effector.d.ts +5 -5
- package/lib/dist/core/classes/Effector.d.ts.map +1 -1
- package/lib/dist/core/classes/Effector.js +8 -8
- package/lib/dist/core/classes/Master.d.ts +6 -6
- package/lib/dist/core/classes/Master.d.ts.map +1 -1
- package/lib/dist/core/classes/Master.js +18 -18
- package/lib/dist/effects/classes/Analyser.d.ts +6 -6
- package/lib/dist/effects/classes/Analyser.d.ts.map +1 -1
- package/lib/dist/effects/classes/Analyser.js +7 -7
- package/lib/dist/effects/classes/Chorus.d.ts +7 -7
- package/lib/dist/effects/classes/Chorus.d.ts.map +1 -1
- package/lib/dist/effects/classes/Chorus.js +25 -25
- package/lib/dist/effects/classes/Distortion.d.ts +1 -1
- package/lib/dist/effects/classes/Distortion.d.ts.map +1 -1
- package/lib/dist/effects/classes/Distortion.js +1 -1
- package/lib/dist/effects/classes/Reverb.d.ts +32 -0
- package/lib/dist/effects/classes/Reverb.d.ts.map +1 -0
- package/lib/dist/effects/classes/Reverb.js +61 -0
- package/lib/dist/effects/classes/clips/HardClip.d.ts +5 -5
- package/lib/dist/effects/classes/clips/HardClip.d.ts.map +1 -1
- package/lib/dist/effects/classes/clips/HardClip.js +14 -14
- package/lib/dist/effects/classes/clips/SoftClip.d.ts +4 -4
- package/lib/dist/effects/classes/clips/SoftClip.d.ts.map +1 -1
- package/lib/dist/effects/classes/clips/SoftClip.js +13 -13
- package/lib/dist/effects/classes/filters/HighPassFilter.d.ts +5 -5
- package/lib/dist/effects/classes/filters/HighPassFilter.d.ts.map +1 -1
- package/lib/dist/effects/classes/filters/HighPassFilter.js +16 -16
- package/lib/dist/effects/classes/filters/LowPassFilter.d.ts +5 -5
- package/lib/dist/effects/classes/filters/LowPassFilter.d.ts.map +1 -1
- package/lib/dist/effects/classes/filters/LowPassFilter.js +17 -17
- package/lib/dist/effects/classes/filters/NotchFilter.d.ts +5 -5
- package/lib/dist/effects/classes/filters/NotchFilter.d.ts.map +1 -1
- package/lib/dist/effects/classes/filters/NotchFilter.js +16 -16
- package/lib/dist/effects/exports.d.ts +1 -5
- package/lib/dist/effects/exports.d.ts.map +1 -1
- package/lib/dist/effects/exports.js +1 -5
- 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/typings.d.ts +11 -0
- package/lib/dist/typings.d.ts.map +1 -1
- package/lib/dist/utilities/debugger.d.ts +4 -4
- package/lib/dist/utilities/debugger.js +8 -8
- package/lib/dist/utilities/helpers.d.ts +13 -13
- package/lib/dist/utilities/helpers.d.ts.map +1 -1
- package/lib/dist/utilities/helpers.js +45 -59
- package/lib/dist/utilities/web-assembly.js +1 -1
- package/lib/src/console-codes.ts +2 -1
- package/lib/src/core/classes/AudioClip.ts +154 -60
- package/lib/src/core/classes/AudioClipPlayer.ts +22 -24
- package/lib/src/core/classes/AudioDevice.ts +49 -6
- package/lib/src/core/classes/Channel.ts +42 -42
- package/lib/src/core/classes/DpsPipeline.ts +19 -19
- package/lib/src/core/classes/Effector.ts +9 -9
- package/lib/src/core/classes/Master.ts +18 -18
- package/lib/src/effects/classes/Analyser.ts +7 -7
- package/lib/src/effects/classes/Chorus.ts +25 -25
- package/lib/src/effects/classes/Distortion.ts +1 -1
- package/lib/src/effects/classes/Reverb.ts +81 -0
- package/lib/src/effects/classes/clips/HardClip.ts +14 -14
- package/lib/src/effects/classes/clips/SoftClip.ts +13 -13
- package/lib/src/effects/classes/filters/HighPassFilter.ts +16 -16
- package/lib/src/effects/classes/filters/LowPassFilter.ts +17 -17
- package/lib/src/effects/classes/filters/NotchFilter.ts +16 -16
- package/lib/src/effects/exports.ts +1 -6
- package/lib/src/index.ts +11 -15
- package/lib/src/typings.ts +13 -0
- package/lib/src/utilities/debugger.ts +4 -4
- package/lib/src/utilities/helpers.ts +47 -60
- package/lib/src/utilities/web-assembly.ts +1 -1
- package/package.json +3 -2
- package/lib/src/effects/classes/reverbs/ChamberReverb.ts +0 -1
- package/lib/src/effects/classes/reverbs/ConvolverReverb.ts +0 -1
- package/lib/src/effects/classes/reverbs/GenericReverb.ts +0 -1
- package/lib/src/effects/classes/reverbs/HallReverb.ts +0 -1
- package/lib/src/effects/classes/reverbs/RoomReverb.ts +0 -1
|
@@ -10,30 +10,71 @@ export class AudioDevice {
|
|
|
10
10
|
context = new AudioContext();
|
|
11
11
|
masterChannel = new Master(this.context);
|
|
12
12
|
masterChannels = [];
|
|
13
|
+
sampleRate = this.context.sampleRate;
|
|
14
|
+
baseLatency = this.context.baseLatency;
|
|
15
|
+
outputLatency = this.context.outputLatency;
|
|
16
|
+
state = this.context.state;
|
|
17
|
+
currentTime = this.context.currentTime;
|
|
18
|
+
maximumFrequency = this.context.sampleRate / 2;
|
|
13
19
|
constructor(deviceInfo) {
|
|
14
20
|
this.deviceInfo = deviceInfo;
|
|
15
21
|
}
|
|
16
22
|
;
|
|
17
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Returns the master channel associated with this audio device.
|
|
25
|
+
* The master channel is the main output channel for this audio device, and is used to control the overall volume and other properties of the audio output.
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
getMasterChannel() {
|
|
18
29
|
return this.masterChannel;
|
|
19
30
|
}
|
|
20
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Sets the master channel for this audio device.
|
|
33
|
+
* The master channel is normally automatically created when the audio device is initialized, but this method allows for the master channel to be changed if needed.
|
|
34
|
+
* @param channel
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
setMasterChannel(channel) {
|
|
21
38
|
if (channel.id === this.masterChannel.id)
|
|
22
|
-
return Debug.
|
|
39
|
+
return Debug.error("The provided master channel is the same as the current channel.", [
|
|
23
40
|
"Provide this method with a different master channel.",
|
|
24
41
|
`Received master channel ID ${channel.id}.`
|
|
25
42
|
], ErrorCodes.SAME_MASTER_CHANNEL);
|
|
26
43
|
this.masterChannel = channel;
|
|
27
44
|
}
|
|
28
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new master channel based on the this audio device's audio context, and adds it to the master channels array.
|
|
47
|
+
* Normally, an audio device should only have one master channel, but this method allows for multiple master channels to be created and used if needed.
|
|
48
|
+
* @returns
|
|
49
|
+
*/
|
|
50
|
+
createMasterChannel() {
|
|
29
51
|
const master = new Master(this.context);
|
|
30
52
|
this.masterChannels.push(master);
|
|
31
53
|
return master;
|
|
32
54
|
}
|
|
33
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Returns the audio context associated with this audio device.
|
|
57
|
+
* This method is provided for ease of use, and is a wrapper for the context property.
|
|
58
|
+
*
|
|
59
|
+
* The audio context can vary between audio devices, and is used to create channels and master channels that are associated with this audio device.
|
|
60
|
+
*/
|
|
61
|
+
getContext() {
|
|
34
62
|
return this.context;
|
|
35
63
|
}
|
|
36
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Creates a new channel based on the this audio device's audio context.
|
|
66
|
+
* This method is a wrapper for the Channel constructor, and is provided for ease of use.
|
|
67
|
+
* In case you want to create a channel with a different audio context, you can use the Channel constructor directly.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```
|
|
71
|
+
* const myChannel: Channel = audioDevice.createChannel("My Channel");
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @param label
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
createChannel(label) {
|
|
37
78
|
return new Channel(this.context, label);
|
|
38
79
|
}
|
|
39
80
|
}
|
|
@@ -16,7 +16,7 @@ export declare class Channel {
|
|
|
16
16
|
sends: Channel[];
|
|
17
17
|
audioClipPlayer: AudioClipPlayer | null;
|
|
18
18
|
constructor(context: AudioContext, label?: string);
|
|
19
|
-
private
|
|
19
|
+
private rebuildEffectChainInternal;
|
|
20
20
|
private disconnectAudioNodes;
|
|
21
21
|
private isInitialized;
|
|
22
22
|
private isReachable;
|
|
@@ -26,24 +26,24 @@ export declare class Channel {
|
|
|
26
26
|
* work properly.
|
|
27
27
|
* @returns
|
|
28
28
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
rebuildEffectChain(): void;
|
|
30
|
+
addEffect(effect: Effector): Channel;
|
|
31
|
+
attachEffect(effect: Effector): Channel;
|
|
32
|
+
removeEffect(effect: Effector): void;
|
|
33
|
+
removeAllEffects(): void;
|
|
34
|
+
detachEffect(effect: Effector): void;
|
|
35
|
+
detachAllEffects(): void;
|
|
36
|
+
send(channel: Channel | Master): void;
|
|
37
|
+
unsend(channel: Channel | Master): void;
|
|
38
|
+
hasAudioClipPlayer(): boolean;
|
|
39
|
+
unsendToAllChannels(): void;
|
|
40
|
+
attachAudioClip(audioClip: AudioClip): Channel;
|
|
41
|
+
volume(volume?: number): number;
|
|
42
|
+
pan(pan?: number): number;
|
|
43
|
+
getEffectsByLabel(label: string): Effector[];
|
|
44
|
+
getFirstEffectByLabel(label: string): Effector | null;
|
|
45
|
+
getEffectById(id: string): Effector[];
|
|
46
|
+
getFirstEffectById(id: string): Effector | null;
|
|
47
|
+
moveEffectToIndex(effect: Effector, index: number | ArrayPosition): void;
|
|
48
48
|
}
|
|
49
49
|
//# sourceMappingURL=Channel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Channel.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Channel.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,qBAAa,OAAO;IAET,EAAE,EAAE,MAAM,CAAQ;IAClB,KAAK,EAAE,MAAM,CAAa;IAE1B,KAAK,EAAE,SAAS,GAAG,IAAI,CAAQ;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACjD,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACjC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAQ;IAEhC,OAAO,EAAE,QAAQ,EAAE,CAAM;IACzB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IAEpC,KAAK,EAAE,OAAO,EAAE,CAAM;IACtB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;
|
|
1
|
+
{"version":3,"file":"Channel.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Channel.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,qBAAa,OAAO;IAET,EAAE,EAAE,MAAM,CAAQ;IAClB,KAAK,EAAE,MAAM,CAAa;IAE1B,KAAK,EAAE,SAAS,GAAG,IAAI,CAAQ;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACjD,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACjC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAQ;IAEhC,OAAO,EAAE,QAAQ,EAAE,CAAM;IACzB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IAEpC,KAAK,EAAE,OAAO,EAAE,CAAM;IACtB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;IAEtD,YAAY,OAAO,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,MAAM,EAoBhD;IAED,OAAO,CAAC,0BAA0B;IAsClC,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAwBnB;;;;;OAKG;IACI,kBAAkB,SAExB;IAEM,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAY1C;IAEM,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAE7C;IAEM,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAmB1C;IAEM,gBAAgB,SAItB;IAEM,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAE1C;IAEM,gBAAgB,SAEtB;IAEM,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,QAgCpC;IAEM,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,QAatC;IAEM,kBAAkB,IAAI,OAAO,CAEnC;IAEM,mBAAmB,SAKzB;IAEM,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAMpD;IAEM,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrC;IAEM,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAO/B;IAEM,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAElD;IAEM,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAI3D;IAEM,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,EAAE,CAE3C;IAEM,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAIrD;IAEM,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAkE9E;CACJ"}
|
|
@@ -29,11 +29,11 @@ export class Channel {
|
|
|
29
29
|
this.stereoPannerNode.connect(this.analyserNode);
|
|
30
30
|
this.analyserNode.connect(this.gainNode);
|
|
31
31
|
this.gainNode.connect(this.output);
|
|
32
|
-
this.audioClipPlayer.
|
|
32
|
+
this.audioClipPlayer.send(this);
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
rebuildEffectChainInternal() {
|
|
35
35
|
if (!this.input || !this.stereoPannerNode) {
|
|
36
|
-
Debug.
|
|
36
|
+
Debug.error("Could not rebuild effect chain because one or more required audio nodes are undefined.", [
|
|
37
37
|
`Channel id: ${this.id}.`,
|
|
38
38
|
`Input defined: ${!!this.input}.`,
|
|
39
39
|
`StereoPannerNode defined: ${!!this.stereoPannerNode}.`
|
|
@@ -99,26 +99,26 @@ export class Channel {
|
|
|
99
99
|
* work properly.
|
|
100
100
|
* @returns
|
|
101
101
|
*/
|
|
102
|
-
|
|
103
|
-
return this.
|
|
102
|
+
rebuildEffectChain() {
|
|
103
|
+
return this.rebuildEffectChainInternal();
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
addEffect(effect) {
|
|
106
106
|
if (!this.context)
|
|
107
107
|
throw new Error(`Could not add effect (${effect.id}), to channel (${this.id}), because the channel's AudioContext is undefined.`);
|
|
108
108
|
if (this.effects.includes(effect))
|
|
109
109
|
throw new Error(`Could not add effect (${effect.id}), to channel (${this.id}), because it has already been added to the channel.`);
|
|
110
110
|
this.effects.push(effect);
|
|
111
|
-
effect.
|
|
112
|
-
this.
|
|
111
|
+
effect.initializeOnAttachment(this.context);
|
|
112
|
+
this.rebuildEffectChainInternal();
|
|
113
113
|
return this;
|
|
114
114
|
}
|
|
115
|
-
|
|
116
|
-
return this.
|
|
115
|
+
attachEffect(effect) {
|
|
116
|
+
return this.addEffect(effect);
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
removeEffect(effect) {
|
|
119
119
|
if (!this.effects.includes(effect)) {
|
|
120
|
-
Debug.
|
|
121
|
-
"Call .
|
|
120
|
+
Debug.error("Could not remove effect, because it is not part of this channel.", [
|
|
121
|
+
"Call .addEffect([effect Effector]) before removing effect."
|
|
122
122
|
], ErrorCodes.EFFECT_NOT_FOUND);
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
@@ -128,52 +128,52 @@ export class Channel {
|
|
|
128
128
|
self.effects.splice(index, 1);
|
|
129
129
|
});
|
|
130
130
|
effect.audioWorkletNode?.disconnect();
|
|
131
|
-
this.
|
|
131
|
+
this.rebuildEffectChainInternal();
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
removeAllEffects() {
|
|
134
134
|
for (const effect of this.effects) {
|
|
135
|
-
this.
|
|
135
|
+
this.removeEffect(effect);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
return this.
|
|
138
|
+
detachEffect(effect) {
|
|
139
|
+
return this.removeEffect(effect);
|
|
140
140
|
}
|
|
141
|
-
|
|
142
|
-
return this.
|
|
141
|
+
detachAllEffects() {
|
|
142
|
+
return this.removeAllEffects();
|
|
143
143
|
}
|
|
144
|
-
|
|
144
|
+
send(channel) {
|
|
145
145
|
if (channel instanceof Master)
|
|
146
|
-
return channel.
|
|
146
|
+
return channel.attachChannel(this);
|
|
147
147
|
if (channel.id === this.id)
|
|
148
|
-
return Debug.
|
|
148
|
+
return Debug.error("Could not link channel to itself.", [
|
|
149
149
|
`This channel id: ${this.id}`
|
|
150
150
|
], ErrorCodes.CHANNEL_CANNOT_LINK_TO_ITSELF);
|
|
151
151
|
if (!this.isInitialized() || !channel.isInitialized())
|
|
152
|
-
return Debug.
|
|
152
|
+
return Debug.error("Could not link channels because one (or both) channels are not initialized.", [
|
|
153
153
|
`This channel id: ${this.id} initialized: ${this.isInitialized()}`,
|
|
154
154
|
`Target channel id: ${channel.id} initialized: ${channel.isInitialized()}`
|
|
155
155
|
], ErrorCodes.CHANNEL_NOT_INITIALIZED);
|
|
156
156
|
if (this.context !== channel.context)
|
|
157
|
-
return Debug.
|
|
157
|
+
return Debug.error("Could not link channels because they do not share the same AudioContext.", [
|
|
158
158
|
`This channel context: ${this.context ? "set" : "null"}`,
|
|
159
159
|
`Target channel context: ${channel.context ? "set" : "null"}`
|
|
160
160
|
], ErrorCodes.CHANNEL_NOT_SAME_AUDIO_CONTEXT);
|
|
161
161
|
if (this.sends.includes(channel))
|
|
162
|
-
return Debug.
|
|
162
|
+
return Debug.error("Could not link channels, because the given channel is already linked with this one.", [
|
|
163
163
|
`This channel id: ${this.id}`,
|
|
164
164
|
`Target channel id: ${channel.id}`
|
|
165
165
|
], ErrorCodes.CHANNEL_ALREADY_LINKED);
|
|
166
166
|
if (channel.isReachable(this))
|
|
167
|
-
return Debug.
|
|
167
|
+
return Debug.error("Could not link channels because it would create a feedback loop.", [
|
|
168
168
|
`This channel id: ${this.id}`,
|
|
169
169
|
`Target channel id: ${channel.id}`
|
|
170
170
|
], ErrorCodes.CHANNEL_POSSIBLE_FEEDBACK_LOOP);
|
|
171
171
|
this.output.connect(channel.input);
|
|
172
172
|
this.sends.push(channel);
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
unsend(channel) {
|
|
175
175
|
if (channel instanceof Master)
|
|
176
|
-
return channel.
|
|
176
|
+
return channel.detachChannel(this);
|
|
177
177
|
const idx = this.sends.indexOf(channel);
|
|
178
178
|
if (idx === -1)
|
|
179
179
|
return;
|
|
@@ -181,22 +181,22 @@ export class Channel {
|
|
|
181
181
|
this.output.disconnect(channel.input);
|
|
182
182
|
this.sends.splice(idx, 1);
|
|
183
183
|
}
|
|
184
|
-
|
|
184
|
+
hasAudioClipPlayer() {
|
|
185
185
|
return !!this.audioClipPlayer;
|
|
186
186
|
}
|
|
187
|
-
|
|
187
|
+
unsendToAllChannels() {
|
|
188
188
|
for (var i = 0; i < this.sends.length; i++) {
|
|
189
|
-
this.
|
|
189
|
+
this.unsend(this.sends[i]);
|
|
190
190
|
i--;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
|
|
193
|
+
attachAudioClip(audioClip) {
|
|
194
194
|
if (!this.audioClipPlayer)
|
|
195
195
|
throw Error("Cannot not link AudioClip to this channel because this channel's AudioClipPlayer is undefined.");
|
|
196
|
-
this.audioClipPlayer.
|
|
196
|
+
this.audioClipPlayer.attachAudioClip(audioClip);
|
|
197
197
|
return this;
|
|
198
198
|
}
|
|
199
|
-
|
|
199
|
+
volume(volume) {
|
|
200
200
|
if (!this.context)
|
|
201
201
|
throw new Error("Could not set volume on channel, because it's context is undefined.");
|
|
202
202
|
if (!this.gainNode)
|
|
@@ -204,7 +204,7 @@ export class Channel {
|
|
|
204
204
|
volume && this.gainNode.gain.setValueAtTime(volume, this.context.currentTime);
|
|
205
205
|
return volume ?? this.gainNode.gain.value;
|
|
206
206
|
}
|
|
207
|
-
|
|
207
|
+
pan(pan) {
|
|
208
208
|
if (!this.context)
|
|
209
209
|
throw new Error("Could not set pan on channel, because it's context is undefined.");
|
|
210
210
|
if (!this.stereoPannerNode)
|
|
@@ -212,21 +212,21 @@ export class Channel {
|
|
|
212
212
|
pan && this.stereoPannerNode.pan.setValueAtTime(pan, this.context.currentTime);
|
|
213
213
|
return pan ?? this.stereoPannerNode.pan.value;
|
|
214
214
|
}
|
|
215
|
-
|
|
215
|
+
getEffectsByLabel(label) {
|
|
216
216
|
return this.effects.filter(effect => effect.label === label);
|
|
217
217
|
}
|
|
218
|
-
|
|
218
|
+
getFirstEffectByLabel(label) {
|
|
219
219
|
const filteredEffects = this.effects.filter(effect => effect.label === label);
|
|
220
220
|
return filteredEffects.length !== 0 ? filteredEffects[0] : null;
|
|
221
221
|
}
|
|
222
|
-
|
|
222
|
+
getEffectById(id) {
|
|
223
223
|
return this.effects.filter(effect => effect.id === id);
|
|
224
224
|
}
|
|
225
|
-
|
|
225
|
+
getFirstEffectById(id) {
|
|
226
226
|
const filteredEffects = this.effects.filter(effect => effect.id === id);
|
|
227
227
|
return filteredEffects.length !== 0 ? filteredEffects[0] : null;
|
|
228
228
|
}
|
|
229
|
-
|
|
229
|
+
moveEffectToIndex(effect, index) {
|
|
230
230
|
let fromIndex = -1, matches = 0, i = 0;
|
|
231
231
|
for (i = 0; i < this.effects.length; i++) {
|
|
232
232
|
if (this.effects[i].id === effect.id) {
|
|
@@ -275,6 +275,6 @@ export class Channel {
|
|
|
275
275
|
this.effects.splice(toIndex, 0, item);
|
|
276
276
|
// Initializing the effect is unnessecary because it should have been intialized already
|
|
277
277
|
// otherwise it could not been found. Rebuilding the effect chain is nessecary though.
|
|
278
|
-
this.
|
|
278
|
+
this.rebuildEffectChainInternal();
|
|
279
279
|
}
|
|
280
280
|
}
|
|
@@ -34,11 +34,11 @@ export declare class DspPipeline {
|
|
|
34
34
|
constructor({ pathToWasm, pathToWorklet, options }: DspPipelineInitializationOptions);
|
|
35
35
|
/**
|
|
36
36
|
* Initializes the DSP pipeline.
|
|
37
|
-
* This is a convenience wrapper around {@link
|
|
37
|
+
* This is a convenience wrapper around {@link initializeDpsPipeline}.
|
|
38
38
|
*
|
|
39
39
|
* @returns A promise resolving to `true` if the initialization succeeded, otherwise `false`.
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
init(): Promise<boolean>;
|
|
42
42
|
/**
|
|
43
43
|
* Performs the full initialization of the DSP pipeline.
|
|
44
44
|
* Loads the WebAssembly module, constructs the AudioWorklet blob URL,
|
|
@@ -46,15 +46,15 @@ export declare class DspPipeline {
|
|
|
46
46
|
*
|
|
47
47
|
* @returns A promise resolving to `true` when initialization completes successfully, otherwise `false`.
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
initializeDpsPipeline(): Promise<boolean>;
|
|
50
50
|
/**
|
|
51
|
-
* Resolves and initializes the system
|
|
51
|
+
* Resolves and initializes the system’s default audio output device.
|
|
52
52
|
* This method can only be called after the DSP pipeline has been initialized.
|
|
53
53
|
*
|
|
54
54
|
* @returns A promise that resolves with the default {@link AudioDevice} instance,
|
|
55
55
|
* or `null` if no default device was found or initialization failed.
|
|
56
56
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
resolveDefaultAudioOutputDevice(): Promise<AudioDevice | null>;
|
|
58
|
+
tellMeWhatTheFuckThisWholeLibraryActuallyDoes(): string;
|
|
59
59
|
}
|
|
60
60
|
//# sourceMappingURL=DpsPipeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DpsPipeline.d.ts","sourceRoot":"","sources":["../../../src/core/classes/DpsPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAM5C,OAAO,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AAGjE;;;GAGG;AACH,qBAAa,WAAW;IAEpB;;OAEG;IACI,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC;;OAEG;IACI,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;OAEG;IACI,EAAE,EAAE,MAAM,CAAQ;IAEzB;;OAEG;IACI,cAAc,EAAE,OAAO,CAAS;IAEvC;;OAEG;IACH,OAAO,CAAC,OAAO,CAAuB;IAEtC;;;;;OAKG;
|
|
1
|
+
{"version":3,"file":"DpsPipeline.d.ts","sourceRoot":"","sources":["../../../src/core/classes/DpsPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAM5C,OAAO,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AAGjE;;;GAGG;AACH,qBAAa,WAAW;IAEpB;;OAEG;IACI,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC;;OAEG;IACI,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;OAEG;IACI,EAAE,EAAE,MAAM,CAAQ;IAEzB;;OAEG;IACI,cAAc,EAAE,OAAO,CAAS;IAEvC;;OAEG;IACH,OAAO,CAAC,OAAO,CAAuB;IAEtC;;;;;OAKG;IACH,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,gCAAgC,EAMnF;IAED;;;;;OAKG;IACU,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAEpC;IAED;;;;;;OAMG;IACU,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAwCrD;IAED;;;;;;OAMG;IACU,+BAA+B,gCA6B3C;IAEM,6CAA6C,WAEnD;CACJ"}
|
|
@@ -2,7 +2,7 @@ import { v4 } from "uuid";
|
|
|
2
2
|
import { AudioDevice } from "./AudioDevice";
|
|
3
3
|
import { Debug } from "../../utilities/debugger";
|
|
4
4
|
import { LoadWebAssemblyModule } from "../../utilities/web-assembly";
|
|
5
|
-
import {
|
|
5
|
+
import { constructProcessorWorklet, loadWorkletOnAudioDevice } from "../../utilities/helpers";
|
|
6
6
|
import { ErrorCodes, WarningCodes } from "../../console-codes";
|
|
7
7
|
import { DSP } from "../../index";
|
|
8
8
|
/**
|
|
@@ -45,12 +45,12 @@ export class DspPipeline {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Initializes the DSP pipeline.
|
|
48
|
-
* This is a convenience wrapper around {@link
|
|
48
|
+
* This is a convenience wrapper around {@link initializeDpsPipeline}.
|
|
49
49
|
*
|
|
50
50
|
* @returns A promise resolving to `true` if the initialization succeeded, otherwise `false`.
|
|
51
51
|
*/
|
|
52
|
-
async
|
|
53
|
-
return await this.
|
|
52
|
+
async init() {
|
|
53
|
+
return await this.initializeDpsPipeline();
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Performs the full initialization of the DSP pipeline.
|
|
@@ -59,45 +59,45 @@ export class DspPipeline {
|
|
|
59
59
|
*
|
|
60
60
|
* @returns A promise resolving to `true` when initialization completes successfully, otherwise `false`.
|
|
61
61
|
*/
|
|
62
|
-
async
|
|
63
|
-
Debug.
|
|
62
|
+
async initializeDpsPipeline() {
|
|
63
|
+
Debug.log("Attempting to initialize DSP pipeline...");
|
|
64
64
|
const start = Date.now();
|
|
65
65
|
if (!this.pathToWasm || !this.pathToWorklet) {
|
|
66
|
-
Debug.
|
|
66
|
+
Debug.error("Could not initialize DSP pipeline, because the path to the WASM, or the worklet file has not been defined.");
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
try {
|
|
70
70
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
71
|
-
Debug.
|
|
71
|
+
Debug.log(`Found ${stream.getTracks().length} media stream tracks.`);
|
|
72
72
|
// Stop all tracks immediately after verification
|
|
73
73
|
stream.getTracks().forEach((track) => {
|
|
74
74
|
track.stop();
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
catch (err) {
|
|
78
|
-
Debug.
|
|
78
|
+
Debug.error("Permission to access media devices has not been granted.", ["Make sure the user has granted FluentGL permission to access media devices."], ErrorCodes.NO_CONTEXT_PERMISSION);
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
81
|
await LoadWebAssemblyModule(this.pathToWasm);
|
|
82
82
|
const workletFileRequest = await fetch(this.pathToWorklet);
|
|
83
83
|
const textContent = await workletFileRequest.text();
|
|
84
|
-
this.blobUrl =
|
|
84
|
+
this.blobUrl = constructProcessorWorklet(textContent);
|
|
85
85
|
const end = Date.now();
|
|
86
86
|
const difference = end - start;
|
|
87
|
-
Debug.
|
|
87
|
+
Debug.success(`Successfully initialized DSP pipeline within ${difference}ms.`);
|
|
88
88
|
return (this.hasInitialized = true);
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
* Resolves and initializes the system
|
|
91
|
+
* Resolves and initializes the system’s default audio output device.
|
|
92
92
|
* This method can only be called after the DSP pipeline has been initialized.
|
|
93
93
|
*
|
|
94
94
|
* @returns A promise that resolves with the default {@link AudioDevice} instance,
|
|
95
95
|
* or `null` if no default device was found or initialization failed.
|
|
96
96
|
*/
|
|
97
|
-
async
|
|
98
|
-
Debug.
|
|
97
|
+
async resolveDefaultAudioOutputDevice() {
|
|
98
|
+
Debug.log("Attempting to resolve default audio output device...");
|
|
99
99
|
if (!this.hasInitialized || !this.blobUrl) {
|
|
100
|
-
Debug.
|
|
100
|
+
Debug.error("Could not resolve default audio output device, because the required blob URL has not been created.", ["Call await .initializeDpsPipeline() before resolving default audio output device."]);
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
103
|
const audioDeviceInfos = [];
|
|
@@ -106,14 +106,14 @@ export class DspPipeline {
|
|
|
106
106
|
(device.kind === "audiooutput" && device.deviceId === "default") &&
|
|
107
107
|
audioDeviceInfos.push(device);
|
|
108
108
|
devices.length === 0 &&
|
|
109
|
-
Debug.
|
|
109
|
+
Debug.warn("No default audio device found.", [], WarningCodes.NO_DEFAULT_AUDIO_DEVICE_FOUND);
|
|
110
110
|
const defaultAudioDevice = devices.length === 0 ? null : new AudioDevice(audioDeviceInfos[0]);
|
|
111
111
|
if (!defaultAudioDevice)
|
|
112
112
|
return null;
|
|
113
|
-
await
|
|
113
|
+
await loadWorkletOnAudioDevice(defaultAudioDevice, this.blobUrl);
|
|
114
114
|
return defaultAudioDevice;
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
tellMeWhatTheFuckThisWholeLibraryActuallyDoes() {
|
|
117
117
|
return `I wanted to create a web based game and implement advanced audio, but javascript is slow so that's why this library uses Web Assembly. You dont have to use it, unless you know what the fuck you're doing. If you do, god bless you.`;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -9,10 +9,10 @@ export declare abstract class Effector {
|
|
|
9
9
|
private events;
|
|
10
10
|
private handleIncomingMessages;
|
|
11
11
|
protected registerMessageEventListener(node: AudioWorkletNode): void;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
abstract
|
|
12
|
+
addEventListener<K extends keyof EffectorEventMap>(event: K, cb: EffectorEventMap[K]): () => void;
|
|
13
|
+
once<K extends keyof EffectorEventMap>(event: K, cb: EffectorEventMap[K]): () => void;
|
|
14
|
+
removeEventListener<K extends keyof EffectorEventMap>(event: K, cb: EffectorEventMap[K]): Effector;
|
|
15
|
+
clearEventListeners(event?: keyof EffectorEventMap): Effector;
|
|
16
|
+
abstract initializeOnAttachment(context: AudioContext): Promise<void>;
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=Effector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Effector.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Effector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGhG,8BAAsB,QAAQ;IAEnB,EAAE,EAAE,MAAM,CAAQ;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC5B,IAAI,EAAE,MAAM,CAAc;IAE1B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACjD,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IAE3C,OAAO,CAAC,iCAAiC,CAAkB;IAE3D,OAAO,CAAC,MAAM,CAMb;IAED,OAAO,CAAC,sBAAsB;IAgB9B,SAAS,CAAC,4BAA4B,CAAC,IAAI,EAAE,gBAAgB;
|
|
1
|
+
{"version":3,"file":"Effector.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Effector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGhG,8BAAsB,QAAQ;IAEnB,EAAE,EAAE,MAAM,CAAQ;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC5B,IAAI,EAAE,MAAM,CAAc;IAE1B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACjD,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IAE3C,OAAO,CAAC,iCAAiC,CAAkB;IAE3D,OAAO,CAAC,MAAM,CAMb;IAED,OAAO,CAAC,sBAAsB;IAgB9B,SAAS,CAAC,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,QAM5D;IAEM,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAKvG;IAEM,IAAI,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAW3F;IAEM,mBAAmB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAWxG;IAEM,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,gBAAgB,GAAG,QAAQ,CASnE;IAED,SAAgB,sBAAsB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF"}
|
|
@@ -32,21 +32,21 @@ export class Effector {
|
|
|
32
32
|
return;
|
|
33
33
|
this.hasRegisteredMessageEventListener = true;
|
|
34
34
|
node.port.onmessage = this.handleIncomingMessages.bind(this);
|
|
35
|
-
Debug.
|
|
35
|
+
Debug.log(`Succesfully registered event listener on effector ${this.name} (${this.label}) [${this.id}]`);
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
addEventListener(event, cb) {
|
|
38
38
|
this.events[event].push(cb);
|
|
39
|
-
return () => this.
|
|
39
|
+
return () => this.removeEventListener(event, cb);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
once(event, cb) {
|
|
42
42
|
const wrapper = ((...args) => {
|
|
43
43
|
// @ts-ignore
|
|
44
44
|
cb(...args);
|
|
45
|
-
this.
|
|
45
|
+
this.removeEventListener(event, wrapper);
|
|
46
46
|
});
|
|
47
|
-
return this.
|
|
47
|
+
return this.addEventListener(event, wrapper);
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
removeEventListener(event, cb) {
|
|
50
50
|
const arr = this.events[event];
|
|
51
51
|
for (let i = 0; i < arr.length; i++) {
|
|
52
52
|
if (arr[i] === cb) {
|
|
@@ -56,7 +56,7 @@ export class Effector {
|
|
|
56
56
|
}
|
|
57
57
|
return this;
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
clearEventListeners(event) {
|
|
60
60
|
if (event) {
|
|
61
61
|
this.events[event].length = 0;
|
|
62
62
|
}
|
|
@@ -14,11 +14,11 @@ export declare class Master {
|
|
|
14
14
|
constructor(context: AudioContext);
|
|
15
15
|
private disconnectAudioNodes;
|
|
16
16
|
private rebuildEffectChain;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
attachEffect(effect: Effector): void;
|
|
18
|
+
detachEffect(effect: Effector): void;
|
|
19
|
+
attachChannel(channel: Channel): void;
|
|
20
|
+
detachChannel(channel: Channel): void;
|
|
21
|
+
hasAudioClipPlayer(): boolean;
|
|
22
|
+
attachAudioClip(audioClip: AudioClip): void;
|
|
23
23
|
}
|
|
24
24
|
//# sourceMappingURL=Master.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Master.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Master.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,qBAAa,MAAM;IAER,EAAE,EAAE,MAAM,CAAQ;IAElB,QAAQ,EAAE,OAAO,EAAE,CAAM;IACzB,OAAO,EAAE,QAAQ,EAAE,CAAM;IAEzB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAC9B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IACpC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;
|
|
1
|
+
{"version":3,"file":"Master.d.ts","sourceRoot":"","sources":["../../../src/core/classes/Master.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,qBAAa,MAAM;IAER,EAAE,EAAE,MAAM,CAAQ;IAElB,QAAQ,EAAE,OAAO,EAAE,CAAM;IACzB,OAAO,EAAE,QAAQ,EAAE,CAAM;IAEzB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAC9B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAQ;IACpC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;IAEtD,YAAY,OAAO,EAAE,YAAY,EAiBhC;IAED,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,kBAAkB;IAuBnB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAW1C;IAEM,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAgB1C;IAEM,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAY3C;IAEM,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB3C;IAEM,kBAAkB,IAAI,OAAO,CAEnC;IAEM,eAAe,CAAC,SAAS,EAAE,SAAS,QAK1C;CACJ"}
|