@fluex/fluexgl-dsp 0.1.2

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.
Files changed (138) hide show
  1. package/README.md +87 -0
  2. package/lib/dist/console-codes.d.ts +14 -0
  3. package/lib/dist/console-codes.d.ts.map +1 -0
  4. package/lib/dist/console-codes.js +16 -0
  5. package/lib/dist/core/classes/AudioClip.d.ts +67 -0
  6. package/lib/dist/core/classes/AudioClip.d.ts.map +1 -0
  7. package/lib/dist/core/classes/AudioClip.js +410 -0
  8. package/lib/dist/core/classes/AudioDevice.d.ts +13 -0
  9. package/lib/dist/core/classes/AudioDevice.d.ts.map +1 -0
  10. package/lib/dist/core/classes/AudioDevice.js +30 -0
  11. package/lib/dist/core/classes/Channel.d.ts +30 -0
  12. package/lib/dist/core/classes/Channel.d.ts.map +1 -0
  13. package/lib/dist/core/classes/Channel.js +144 -0
  14. package/lib/dist/core/classes/DpsPipeline.d.ts +60 -0
  15. package/lib/dist/core/classes/DpsPipeline.d.ts.map +1 -0
  16. package/lib/dist/core/classes/DpsPipeline.js +115 -0
  17. package/lib/dist/core/classes/Effector.d.ts +8 -0
  18. package/lib/dist/core/classes/Effector.d.ts.map +1 -0
  19. package/lib/dist/core/classes/Effector.js +7 -0
  20. package/lib/dist/core/classes/Master.d.ts +12 -0
  21. package/lib/dist/core/classes/Master.d.ts.map +1 -0
  22. package/lib/dist/core/classes/Master.js +35 -0
  23. package/lib/dist/core/exports.d.ts +7 -0
  24. package/lib/dist/core/exports.d.ts.map +1 -0
  25. package/lib/dist/core/exports.js +6 -0
  26. package/lib/dist/effects/classes/Chorus.d.ts +19 -0
  27. package/lib/dist/effects/classes/Chorus.d.ts.map +1 -0
  28. package/lib/dist/effects/classes/Chorus.js +83 -0
  29. package/lib/dist/effects/classes/Distortion.d.ts +3 -0
  30. package/lib/dist/effects/classes/Distortion.d.ts.map +1 -0
  31. package/lib/dist/effects/classes/Distortion.js +2 -0
  32. package/lib/dist/effects/classes/Equalizer.d.ts +3 -0
  33. package/lib/dist/effects/classes/Equalizer.d.ts.map +1 -0
  34. package/lib/dist/effects/classes/Equalizer.js +2 -0
  35. package/lib/dist/effects/classes/Filter.d.ts +3 -0
  36. package/lib/dist/effects/classes/Filter.d.ts.map +1 -0
  37. package/lib/dist/effects/classes/Filter.js +2 -0
  38. package/lib/dist/effects/classes/Limiter.d.ts +3 -0
  39. package/lib/dist/effects/classes/Limiter.d.ts.map +1 -0
  40. package/lib/dist/effects/classes/Limiter.js +2 -0
  41. package/lib/dist/effects/classes/Saturation.d.ts +3 -0
  42. package/lib/dist/effects/classes/Saturation.d.ts.map +1 -0
  43. package/lib/dist/effects/classes/Saturation.js +2 -0
  44. package/lib/dist/effects/classes/SoftClip.d.ts +8 -0
  45. package/lib/dist/effects/classes/SoftClip.d.ts.map +1 -0
  46. package/lib/dist/effects/classes/SoftClip.js +37 -0
  47. package/lib/dist/effects/classes/StereoPanner.d.ts +3 -0
  48. package/lib/dist/effects/classes/StereoPanner.d.ts.map +1 -0
  49. package/lib/dist/effects/classes/StereoPanner.js +2 -0
  50. package/lib/dist/effects/classes/compressors/Compressor.d.ts +3 -0
  51. package/lib/dist/effects/classes/compressors/Compressor.d.ts.map +1 -0
  52. package/lib/dist/effects/classes/compressors/Compressor.js +2 -0
  53. package/lib/dist/effects/classes/compressors/MultibandCompressor.d.ts +3 -0
  54. package/lib/dist/effects/classes/compressors/MultibandCompressor.d.ts.map +1 -0
  55. package/lib/dist/effects/classes/compressors/MultibandCompressor.js +2 -0
  56. package/lib/dist/effects/classes/delays/AdvancedDelay.d.ts +3 -0
  57. package/lib/dist/effects/classes/delays/AdvancedDelay.d.ts.map +1 -0
  58. package/lib/dist/effects/classes/delays/AdvancedDelay.js +2 -0
  59. package/lib/dist/effects/classes/delays/MonoDelay.d.ts +3 -0
  60. package/lib/dist/effects/classes/delays/MonoDelay.d.ts.map +1 -0
  61. package/lib/dist/effects/classes/delays/MonoDelay.js +2 -0
  62. package/lib/dist/effects/classes/delays/PingPongDelay.d.ts +3 -0
  63. package/lib/dist/effects/classes/delays/PingPongDelay.d.ts.map +1 -0
  64. package/lib/dist/effects/classes/delays/PingPongDelay.js +2 -0
  65. package/lib/dist/effects/classes/delays/StereoDelay.d.ts +3 -0
  66. package/lib/dist/effects/classes/delays/StereoDelay.d.ts.map +1 -0
  67. package/lib/dist/effects/classes/delays/StereoDelay.js +2 -0
  68. package/lib/dist/effects/classes/reverbs/ChamberReverb.d.ts +3 -0
  69. package/lib/dist/effects/classes/reverbs/ChamberReverb.d.ts.map +1 -0
  70. package/lib/dist/effects/classes/reverbs/ChamberReverb.js +2 -0
  71. package/lib/dist/effects/classes/reverbs/ConvolverReverb.d.ts +3 -0
  72. package/lib/dist/effects/classes/reverbs/ConvolverReverb.d.ts.map +1 -0
  73. package/lib/dist/effects/classes/reverbs/ConvolverReverb.js +2 -0
  74. package/lib/dist/effects/classes/reverbs/GenericReverb.d.ts +3 -0
  75. package/lib/dist/effects/classes/reverbs/GenericReverb.d.ts.map +1 -0
  76. package/lib/dist/effects/classes/reverbs/GenericReverb.js +2 -0
  77. package/lib/dist/effects/classes/reverbs/HallReverb.d.ts +3 -0
  78. package/lib/dist/effects/classes/reverbs/HallReverb.d.ts.map +1 -0
  79. package/lib/dist/effects/classes/reverbs/HallReverb.js +2 -0
  80. package/lib/dist/effects/classes/reverbs/RoomReverb.d.ts +3 -0
  81. package/lib/dist/effects/classes/reverbs/RoomReverb.d.ts.map +1 -0
  82. package/lib/dist/effects/classes/reverbs/RoomReverb.js +2 -0
  83. package/lib/dist/effects/exports.d.ts +20 -0
  84. package/lib/dist/effects/exports.d.ts.map +1 -0
  85. package/lib/dist/effects/exports.js +19 -0
  86. package/lib/dist/index.d.ts +13 -0
  87. package/lib/dist/index.d.ts.map +1 -0
  88. package/lib/dist/index.js +28 -0
  89. package/lib/dist/typings.d.ts +67 -0
  90. package/lib/dist/typings.d.ts.map +1 -0
  91. package/lib/dist/typings.js +1 -0
  92. package/lib/dist/utilities/constants.d.ts +2 -0
  93. package/lib/dist/utilities/constants.d.ts.map +1 -0
  94. package/lib/dist/utilities/constants.js +9 -0
  95. package/lib/dist/utilities/debugger.d.ts +8 -0
  96. package/lib/dist/utilities/debugger.d.ts.map +1 -0
  97. package/lib/dist/utilities/debugger.js +49 -0
  98. package/lib/dist/utilities/helpers.d.ts +41 -0
  99. package/lib/dist/utilities/helpers.d.ts.map +1 -0
  100. package/lib/dist/utilities/helpers.js +148 -0
  101. package/lib/dist/utilities/web-assembly.d.ts +4 -0
  102. package/lib/dist/utilities/web-assembly.d.ts.map +1 -0
  103. package/lib/dist/utilities/web-assembly.js +12 -0
  104. package/lib/src/console-codes.ts +16 -0
  105. package/lib/src/core/classes/AudioClip.ts +545 -0
  106. package/lib/src/core/classes/AudioDevice.ts +39 -0
  107. package/lib/src/core/classes/Channel.ts +199 -0
  108. package/lib/src/core/classes/DpsPipeline.ts +153 -0
  109. package/lib/src/core/classes/Effector.ts +11 -0
  110. package/lib/src/core/classes/Master.ts +50 -0
  111. package/lib/src/core/exports.ts +6 -0
  112. package/lib/src/effects/classes/Chorus.ts +109 -0
  113. package/lib/src/effects/classes/Distortion.ts +1 -0
  114. package/lib/src/effects/classes/Equalizer.ts +1 -0
  115. package/lib/src/effects/classes/Filter.ts +1 -0
  116. package/lib/src/effects/classes/Limiter.ts +1 -0
  117. package/lib/src/effects/classes/Saturation.ts +1 -0
  118. package/lib/src/effects/classes/SoftClip.ts +42 -0
  119. package/lib/src/effects/classes/StereoPanner.ts +1 -0
  120. package/lib/src/effects/classes/compressors/Compressor.ts +1 -0
  121. package/lib/src/effects/classes/compressors/MultibandCompressor.ts +1 -0
  122. package/lib/src/effects/classes/delays/AdvancedDelay.ts +1 -0
  123. package/lib/src/effects/classes/delays/MonoDelay.ts +1 -0
  124. package/lib/src/effects/classes/delays/PingPongDelay.ts +1 -0
  125. package/lib/src/effects/classes/delays/StereoDelay.ts +1 -0
  126. package/lib/src/effects/classes/reverbs/ChamberReverb.ts +1 -0
  127. package/lib/src/effects/classes/reverbs/ConvolverReverb.ts +1 -0
  128. package/lib/src/effects/classes/reverbs/GenericReverb.ts +1 -0
  129. package/lib/src/effects/classes/reverbs/HallReverb.ts +1 -0
  130. package/lib/src/effects/classes/reverbs/RoomReverb.ts +1 -0
  131. package/lib/src/effects/exports.ts +22 -0
  132. package/lib/src/index.ts +93 -0
  133. package/lib/src/typings.ts +79 -0
  134. package/lib/src/utilities/constants.ts +9 -0
  135. package/lib/src/utilities/debugger.ts +66 -0
  136. package/lib/src/utilities/helpers.ts +215 -0
  137. package/lib/src/utilities/web-assembly.ts +14 -0
  138. package/package.json +50 -0
@@ -0,0 +1,199 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { Effector } from "./Effector";
4
+ import { AudioClip } from "./AudioClip";
5
+ import { Master } from "./Master";
6
+
7
+ import { ChannelOptions } from "../../typings";
8
+ import { Debug } from "../../utilities/debugger";
9
+
10
+ export class Channel {
11
+
12
+ public id: string = v4();
13
+ public effects: Effector[] = [];
14
+ public label: string | null;
15
+
16
+ public parentialContext: AudioContext | null = null;
17
+ public parentialMasterChannel: Master | null = null;
18
+
19
+ public audioClips: AudioClip[] = [];
20
+
21
+ public gainNode: GainNode | null = null;
22
+ public stereoPannerNode: StereoPannerNode | null = null;
23
+
24
+ public audioClipsInputGainNode: GainNode | null = null;
25
+
26
+ constructor(public options: Partial<ChannelOptions> = { maxAudioNodes: 8, maxEffects: 8 }) {
27
+
28
+ this.label = options.label ?? null;
29
+ }
30
+
31
+ private rebuildEffectChain() {
32
+ Debug.Log("Attempting to rebuild effect chain.");
33
+
34
+ if (!this.audioClipsInputGainNode || !this.gainNode) return Debug.Error("Could not rebuild effect chain, because one or more gain nodes on this channel are undefined.", [
35
+ `Channel id: ${this.id}.`,
36
+ `Current amount of effects: ${this.effects.length}.`
37
+ ]);
38
+
39
+ this.audioClipsInputGainNode.disconnect();
40
+
41
+ for (const effect of this.effects) {
42
+ effect.audioWorkletNode?.disconnect();
43
+ }
44
+
45
+ const activeEffects = this.effects.filter(e => !!e.audioWorkletNode /* && e.enabled !== false */);
46
+
47
+ if (activeEffects.length === 0) {
48
+
49
+ this.audioClipsInputGainNode.connect(this.gainNode);
50
+ } else {
51
+
52
+ const firstEffectGain = activeEffects[0].audioWorkletNode!;
53
+ this.audioClipsInputGainNode.connect(firstEffectGain);
54
+
55
+ for (let i = 0; i < activeEffects.length - 1; i++) {
56
+
57
+ const current = activeEffects[i].audioWorkletNode!;
58
+ const next = activeEffects[i + 1].audioWorkletNode!;
59
+
60
+ current.connect(next);
61
+ }
62
+
63
+ const lastEffectGain = activeEffects[activeEffects.length - 1].audioWorkletNode!;
64
+
65
+ lastEffectGain.connect(this.gainNode);
66
+ }
67
+
68
+ Debug.Success("Successfully rebuilt effect chain.", [
69
+ `Channel id: ${this.id}.`,
70
+ `Current amount of effects: ${this.effects.length}`
71
+ ]);
72
+ }
73
+
74
+ public InitializeChannelOnMasterAttachment(master: Master) {
75
+
76
+ this.parentialMasterChannel = master;
77
+ this.parentialContext = master.context;
78
+
79
+ this.gainNode = new GainNode(this.parentialContext);
80
+ this.stereoPannerNode = new StereoPannerNode(this.parentialContext);
81
+
82
+ this.audioClipsInputGainNode = new GainNode(this.parentialContext);
83
+
84
+ this.audioClipsInputGainNode.connect(this.stereoPannerNode);
85
+ this.stereoPannerNode.connect(this.gainNode);
86
+ this.gainNode.connect(this.parentialMasterChannel.gainNode);
87
+ }
88
+
89
+ public SetLabel(label: string): void {
90
+
91
+ this.options.label = label;
92
+ this.label = label;
93
+ }
94
+
95
+ public ClearLabel(): void {
96
+
97
+ this.options.label = "";
98
+ this.label = null;
99
+ }
100
+
101
+ public AttachAudioClip(clip: AudioClip) {
102
+
103
+ if (this.audioClips.includes(clip)) return Debug.Error("Could not attach audio clip because it is already part of this channel", [
104
+ "Call .DetachAudioClip([clip AudioClip]) before attaching audio clip."
105
+ ]);
106
+
107
+ clip.InitializeAudioClipOnAttaching(this);
108
+ this.audioClips.push(clip);
109
+ }
110
+
111
+ public DetachAudioClip(clip: AudioClip) {
112
+
113
+ if (!this.audioClips.includes(clip)) return Debug.Error("Could not detach audio clip, because it is not part of this channel.", [
114
+ "Call .AttachAudioClip([clip AudioClip]) before deattaching audio clip."
115
+ ]);
116
+
117
+ const self: Channel = this;
118
+
119
+ clip.parentialAudioContext = null;
120
+ clip.parentialChannel = null;
121
+ clip.hasAttachedToChannel = false;
122
+
123
+ clip.stereoPannerNode?.disconnect();
124
+ clip.gainNode?.disconnect();
125
+
126
+ clip.DisconnectAllAudioBufferSourceNodes();
127
+
128
+ this.audioClips.forEach(function (_clip: AudioClip, index: number) {
129
+ if (clip.id === _clip.id)
130
+ return self.audioClips.splice(index, 1);
131
+ });
132
+ }
133
+
134
+ public SetVolume(volume: number): void {
135
+
136
+ if (!this.gainNode) return Debug.Error("Could not set channel volume because the channel is not attached to a master channel.", [
137
+ "Attach the channel to a master channel before setting the volume."
138
+ ]);
139
+
140
+ this.gainNode.gain.setValueAtTime(volume, this.parentialContext!.currentTime);
141
+ }
142
+
143
+ public SetPanLevel(pan: number): void {
144
+
145
+ if (!this.stereoPannerNode) return Debug.Error("Could not set channel pan level because the channel is not attached to a master channel.", [
146
+ "Attach the channel to a master channel before setting the pan level."
147
+ ]);
148
+
149
+ this.stereoPannerNode.pan.setValueAtTime(pan, this.parentialContext!.currentTime);
150
+ }
151
+
152
+ public AddEffect(effect: Effector): void {
153
+
154
+ if (!this.parentialContext) return Debug.Error("Could not add effect on channel, because the parential context is undefined.", [
155
+ `Channel ID: ${this.id}`,
156
+ `Effect ID: ${effect.id}`,
157
+ `Effect name: ${effect.constructor.name}`
158
+ ])
159
+
160
+ if (this.effects.includes(effect)) return Debug.Error("Could not add effect because it is already part of this channel", [
161
+ "Call .RemoveEffect([effect Effector]) before adding effect."
162
+ ]);
163
+
164
+ effect.InitializeOnAttachment(this.parentialContext);
165
+
166
+ this.effects.push(effect);
167
+ this.rebuildEffectChain();
168
+ }
169
+
170
+ public RemoveEffect(effect: Effector): void {
171
+
172
+ if (!this.effects.includes(effect)) return Debug.Error("Could not remove effect, because it is not part of this channel.", [
173
+ "Call .AddEffect([effect Effector]) before removing effect."
174
+ ]);
175
+
176
+ const self: Channel = this;
177
+
178
+ this.effects.forEach(function (_effect: Effector, index: number) {
179
+ if (effect.id === _effect.id)
180
+ return self.effects.splice(index, 1);
181
+ });
182
+
183
+ this.rebuildEffectChain();
184
+ }
185
+
186
+ // Public getters and setters
187
+
188
+ public get volume(): number | null {
189
+
190
+ if (!this.gainNode) return null;
191
+ return this.gainNode.gain.value;
192
+ }
193
+
194
+ public get panLevel(): number | null {
195
+
196
+ if (!this.stereoPannerNode) return null;
197
+ return this.stereoPannerNode.pan.value;
198
+ }
199
+ }
@@ -0,0 +1,153 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { AudioDevice } from "./AudioDevice";
4
+ import { Debug } from "../../utilities/debugger";
5
+ import { LoadWebAssemblyModule } from "../../utilities/web-assembly";
6
+ import { ConstructProcessorWorklet, LoadWorkletOnMasterChannel } from "../../utilities/helpers";
7
+
8
+ import { ErrorCodes, WarningCodes } from "../../console-codes";
9
+ import { DspPipelineInitializationOptions } from "../../typings";
10
+
11
+ /**
12
+ * Represents the digital signal processing (DSP) pipeline responsible for initializing
13
+ * WebAssembly modules, AudioWorklet processors, and resolving audio output devices.
14
+ */
15
+ export class DspPipeline {
16
+
17
+ /**
18
+ * The path to the WebAssembly module file.
19
+ */
20
+ public pathToWasm: string | null = null;
21
+
22
+ /**
23
+ * The path to the AudioWorklet processor script.
24
+ */
25
+ public pathToWorklet: string | null = null;
26
+
27
+ /**
28
+ * Unique identifier of this DSP pipeline instance.
29
+ */
30
+ public id: string = v4();
31
+
32
+ /**
33
+ * Indicates whether the DSP pipeline has been successfully initialized.
34
+ */
35
+ public hasInitialized: boolean = false;
36
+
37
+ /**
38
+ * The generated blob URL containing the AudioWorklet processor code.
39
+ */
40
+ private blobUrl: string | null = null;
41
+
42
+ /**
43
+ * Creates a new instance of the {@link DspPipeline} class.
44
+ *
45
+ * @param options - Initialization options for the DSP pipeline.
46
+ * Must include valid paths to the WebAssembly and AudioWorklet files.
47
+ */
48
+ constructor(options: DspPipelineInitializationOptions) {
49
+ this.pathToWasm = options.pathToWasm;
50
+ this.pathToWorklet = options.pathToWorklet;
51
+ }
52
+
53
+ /**
54
+ * Initializes the DSP pipeline.
55
+ * This is a convenience wrapper around {@link InitializeDpsPipeline}.
56
+ *
57
+ * @returns A promise resolving to `true` if the initialization succeeded, otherwise `false`.
58
+ */
59
+ public async Init(): Promise<boolean> {
60
+ return await this.InitializeDpsPipeline();
61
+ }
62
+
63
+ /**
64
+ * Performs the full initialization of the DSP pipeline.
65
+ * Loads the WebAssembly module, constructs the AudioWorklet blob URL,
66
+ * and ensures audio access permissions are granted.
67
+ *
68
+ * @returns A promise resolving to `true` when initialization completes successfully, otherwise `false`.
69
+ */
70
+ public async InitializeDpsPipeline(): Promise<boolean> {
71
+ Debug.Log("Attempting to initialize DSP pipeline...");
72
+
73
+ const start: number = Date.now();
74
+
75
+ if (!this.pathToWasm || !this.pathToWorklet) {
76
+ Debug.Error("Could not initialize DSP pipeline, because the path to the WASM, or the worklet file has not been defined.");
77
+ return false;
78
+ }
79
+
80
+ try {
81
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
82
+ Debug.Log(`Found ${stream.getTracks().length} media stream tracks.`);
83
+
84
+ // Stop all tracks immediately after verification
85
+ stream.getTracks().forEach((track: MediaStreamTrack) => {
86
+ track.stop();
87
+ });
88
+ } catch (err) {
89
+ Debug.Error(
90
+ "Permission to access media devices has not been granted.",
91
+ ["Make sure the user has granted FluentGL permission to access media devices."],
92
+ ErrorCodes.NO_CONTEXT_PERMISSION
93
+ );
94
+ return false;
95
+ }
96
+
97
+ await LoadWebAssemblyModule(this.pathToWasm);
98
+
99
+ const workletFileRequest = await fetch(this.pathToWorklet);
100
+ const textContent: string = await workletFileRequest.text();
101
+
102
+ this.blobUrl = ConstructProcessorWorklet(textContent);
103
+
104
+ const end: number = Date.now();
105
+ const difference: number = end - start;
106
+
107
+ Debug.Success(`Successfully initialized DSP pipeline within ${difference}ms.`);
108
+
109
+ return (this.hasInitialized = true);
110
+ }
111
+
112
+ /**
113
+ * Resolves and initializes the system’s default audio output device.
114
+ * This method can only be called after the DSP pipeline has been initialized.
115
+ *
116
+ * @returns A promise that resolves with the default {@link AudioDevice} instance,
117
+ * or `null` if no default device was found or initialization failed.
118
+ */
119
+ public async ResolveDefaultAudioOutputDevice() {
120
+ Debug.Log("Attempting to resolve default audio output device...");
121
+
122
+ if (!this.hasInitialized || !this.blobUrl) {
123
+ Debug.Error(
124
+ "Could not resolve default audio output device, because the required blob URL has not been created.",
125
+ ["Call await .InitializeDpsPipeline() before resolving default audio output device."]
126
+ );
127
+ return null;
128
+ }
129
+
130
+ const audioDeviceInfos: MediaDeviceInfo[] = [];
131
+ const devices = await navigator.mediaDevices.enumerateDevices();
132
+
133
+ for (let device of devices)
134
+ (device.kind === "audiooutput" && device.deviceId === "default") &&
135
+ audioDeviceInfos.push(device);
136
+
137
+ devices.length === 0 &&
138
+ Debug.Warn("No default audio device found.", [], WarningCodes.NO_DEFAULT_AUDIO_DEVICE_FOUND);
139
+
140
+ const defaultAudioDevice =
141
+ devices.length === 0 ? null : new AudioDevice(audioDeviceInfos[0]);
142
+
143
+ if (!defaultAudioDevice) return null;
144
+
145
+ await LoadWorkletOnMasterChannel(defaultAudioDevice.masterChannel, this.blobUrl);
146
+
147
+ return defaultAudioDevice;
148
+ }
149
+
150
+ public TellMeWhatTheFuckThisWholeLibraryActuallyDoes() {
151
+ 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.`
152
+ }
153
+ }
@@ -0,0 +1,11 @@
1
+ import { v4 } from "uuid";
2
+
3
+ export abstract class Effector {
4
+
5
+ public id: string = v4();
6
+ public label: string | null = null;
7
+ public audioWorkletNode: AudioWorkletNode | null = null;
8
+ public parentialContext: AudioContext | null = null;
9
+
10
+ public abstract InitializeOnAttachment(parentialContext: AudioContext): Promise<void>;
11
+ }
@@ -0,0 +1,50 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { Channel } from "./Channel";
4
+
5
+ import { Debug } from "../../utilities/debugger";
6
+
7
+ export class Master {
8
+
9
+ public id: string = v4();
10
+ public channels: Channel[] = [];
11
+ public context: AudioContext = new AudioContext();
12
+
13
+ public gainNode: GainNode = this.context.createGain();
14
+ public analyserNode: AnalyserNode = this.context.createAnalyser();
15
+
16
+ constructor() {
17
+
18
+ this.gainNode.connect(this.analyserNode);
19
+ this.analyserNode.connect(this.context.destination);
20
+ }
21
+
22
+ public AttachChannel(channel: Channel): void {
23
+
24
+ if(this.channels.includes(channel)) return Debug.Error("Could not attach the channel because it is already part of this master channel.",[
25
+ "Call .DetachChannel([channel Channel]) before attaching the channel."
26
+ ]);
27
+
28
+ channel.InitializeChannelOnMasterAttachment(this);
29
+
30
+ this.channels.push(channel);
31
+ return;
32
+ }
33
+
34
+ public DetachChannel(channel: Channel): void {
35
+
36
+ if(!this.channels.includes(channel)) return Debug.Error("Could not detach the channel because it is not part of this master channel.", [
37
+ "Call .AttachChannel([channel Channel]) before detaching the channel."
38
+ ]);
39
+
40
+ const self = this;
41
+
42
+ this.channels.forEach(function(_channel: Channel, index: number) {
43
+ if(channel.id === _channel.id) {
44
+
45
+ self.channels.splice(index, 1);
46
+ return;
47
+ }
48
+ });
49
+ }
50
+ }
@@ -0,0 +1,6 @@
1
+ export { AudioDevice } from "./classes/AudioDevice";
2
+ export { Channel } from "./classes/Channel";
3
+ export { Effector } from "./classes/Effector";
4
+ export { Master } from "./classes/Master";
5
+ export { AudioClip } from "./classes/AudioClip";
6
+ export { DspPipeline } from "./classes/DpsPipeline";
@@ -0,0 +1,109 @@
1
+ import { compiledWebAssemblyModule } from "../../utilities/web-assembly";
2
+ import { Effector } from "../../core/classes/Effector";
3
+ import { Debug } from "../../utilities/debugger";
4
+ import { ChorusEffectOptions } from "../../typings";
5
+
6
+ export class Chorus extends Effector {
7
+
8
+ public baseDelayMs: number = 15;
9
+ public depthMs: number = 8;
10
+ public rateHz: number = 1.5;
11
+ public mix: number = 0.5;
12
+ public feedback: number = 0.2;
13
+
14
+ public options: ChorusEffectOptions = {};
15
+
16
+ private sampleRate: number = 0;
17
+
18
+ constructor(options?: ChorusEffectOptions) {
19
+ super();
20
+
21
+ this.options = {
22
+ baseDelayMs: this.baseDelayMs,
23
+ depthMs: this.depthMs,
24
+ rateHz: this.rateHz,
25
+ mix: this.mix,
26
+ feedback: this.mix,
27
+ ...options
28
+ }
29
+ }
30
+
31
+ public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
32
+
33
+ if (!compiledWebAssemblyModule) return Debug.Error("Could not initialize effector because the nessecary WASM file has not been specified.", [
34
+ `Effector type: Chorus`
35
+ ]);
36
+
37
+ this.sampleRate = parentialContext.sampleRate;
38
+
39
+ this.audioWorkletNode = new AudioWorkletNode(parentialContext, "ChorusProcessor", {
40
+ numberOfInputs: 1,
41
+ numberOfOutputs: 1,
42
+ outputChannelCount: [2],
43
+ parameterData: {
44
+ ...this.options,
45
+ sampleRate: this.sampleRate
46
+ },
47
+ processorOptions: {
48
+ module: compiledWebAssemblyModule
49
+ }
50
+ });
51
+
52
+ this.parentialContext = parentialContext;
53
+ }
54
+
55
+ public SetBaseDelayMs(value: number) {
56
+
57
+ this.baseDelayMs = value;
58
+ this.options.baseDelayMs = value;
59
+
60
+ this.audioWorkletNode?.port.postMessage({
61
+ type: "set-base-delay-ms",
62
+ value
63
+ });
64
+ }
65
+
66
+ public SetDepthMs(value: number) {
67
+
68
+ this.depthMs = value;
69
+ this.options.depthMs = value;
70
+
71
+ this.audioWorkletNode?.port.postMessage({
72
+ type: "set-depth-ms",
73
+ value
74
+ });
75
+ }
76
+
77
+ public SetRateHz(value: number) {
78
+
79
+ this.rateHz = value;
80
+ this.options.rateHz = value;
81
+
82
+ this.audioWorkletNode?.port.postMessage({
83
+ type: "set-rate-hz",
84
+ value
85
+ });
86
+ }
87
+
88
+ public SetMix(value: number) {
89
+
90
+ this.mix = value;
91
+ this.options.mix = value;
92
+
93
+ this.audioWorkletNode?.port.postMessage({
94
+ type: "set-mix",
95
+ value
96
+ });
97
+ }
98
+
99
+ public SetFeedback(value: number) {
100
+
101
+ this.feedback = value;
102
+ this.options.feedback = value;
103
+
104
+ this.audioWorkletNode?.port.postMessage({
105
+ type: "set-mix",
106
+ value
107
+ });
108
+ }
109
+ }
@@ -0,0 +1 @@
1
+ export class Distortion {}
@@ -0,0 +1 @@
1
+ export class Equalizer {}
@@ -0,0 +1 @@
1
+ export class Filter {}
@@ -0,0 +1 @@
1
+ export class Limiter {}
@@ -0,0 +1 @@
1
+ export class Saturation {}
@@ -0,0 +1,42 @@
1
+ import { compiledWebAssemblyModule } from "../../utilities/web-assembly";
2
+ import { Effector } from "../../core/classes/Effector";
3
+ import { Debug } from "../../utilities/debugger";
4
+
5
+ export class SoftClip extends Effector {
6
+
7
+ constructor(public drive: number = 1) {
8
+ super();
9
+ }
10
+
11
+ public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
12
+
13
+ if(!compiledWebAssemblyModule) return Debug.Error("Could not initialize effector because the nessecary WASM file has not been specified.", [
14
+ `Effector type: SoftClip`
15
+ ]);
16
+
17
+ this.audioWorkletNode = new AudioWorkletNode(parentialContext, "SoftClipProcessor", {
18
+ numberOfInputs: 1,
19
+ numberOfOutputs: 1,
20
+ outputChannelCount: [2],
21
+ parameterData: {
22
+ drive: this.drive
23
+ },
24
+ processorOptions: {
25
+ module: compiledWebAssemblyModule
26
+ }
27
+ });
28
+
29
+ this.parentialContext = parentialContext;
30
+ }
31
+
32
+ public SetDrive(drive: number): SoftClip {
33
+
34
+ if(this.audioWorkletNode) this.audioWorkletNode.port.postMessage({
35
+ type: "set-drive",
36
+ value: drive
37
+ });
38
+
39
+ this.drive = drive;
40
+ return this;
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ export class StereoPanner {}
@@ -0,0 +1 @@
1
+ export class Compressor {}
@@ -0,0 +1 @@
1
+ export class MultibandCompressor {}
@@ -0,0 +1 @@
1
+ export class AdvancedDelay {}
@@ -0,0 +1 @@
1
+ export class MonoDelay {}
@@ -0,0 +1 @@
1
+ export class PingPongDelay {}
@@ -0,0 +1 @@
1
+ export class StereoDelay {}
@@ -0,0 +1 @@
1
+ export class ChamberReverb {}
@@ -0,0 +1 @@
1
+ export class ConvolverReverb {}
@@ -0,0 +1 @@
1
+ export class GenericReverb {}
@@ -0,0 +1 @@
1
+ export class HallReverb {}
@@ -0,0 +1 @@
1
+ export class RoomReverb {}
@@ -0,0 +1,22 @@
1
+ export { Chorus } from "./classes/Chorus";
2
+ export { Distortion } from "./classes/Distortion";
3
+ export { Equalizer } from "./classes/Equalizer";
4
+ export { Filter } from "./classes/Filter";
5
+ export { Limiter } from "./classes/Limiter";
6
+ export { Saturation } from "./classes/Saturation";
7
+ export { StereoPanner } from "./classes/StereoPanner";
8
+ export { SoftClip } from "./classes/SoftClip";
9
+
10
+ export { Compressor } from "./classes/compressors/Compressor";
11
+ export { MultibandCompressor } from "./classes/compressors/MultibandCompressor";
12
+
13
+ export { AdvancedDelay } from "./classes/delays/AdvancedDelay";
14
+ export { MonoDelay } from "./classes/delays/MonoDelay";
15
+ export { PingPongDelay } from "./classes/delays/PingPongDelay";
16
+ export { StereoDelay } from "./classes/delays/StereoDelay";
17
+
18
+ export { ChamberReverb } from "./classes/reverbs/ChamberReverb";
19
+ export { ConvolverReverb } from "./classes/reverbs/ConvolverReverb";
20
+ export { GenericReverb } from "./classes/reverbs/GenericReverb";
21
+ export { HallReverb } from "./classes/reverbs/HallReverb";
22
+ export { RoomReverb } from "./classes/reverbs/RoomReverb";