@fluex/fluexgl-dsp 0.4.1 → 0.4.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 (78) hide show
  1. package/README.md +16 -38
  2. package/lib/dist/core/classes/AudioClip.d.ts +3 -1
  3. package/lib/dist/core/classes/AudioClip.d.ts.map +1 -1
  4. package/lib/dist/core/classes/AudioClip.js +18 -5
  5. package/lib/dist/core/classes/AudioClipPlayer.d.ts +8 -2
  6. package/lib/dist/core/classes/AudioClipPlayer.d.ts.map +1 -1
  7. package/lib/dist/core/classes/AudioClipPlayer.js +48 -1
  8. package/lib/dist/core/classes/AudioDevice.d.ts +2 -0
  9. package/lib/dist/core/classes/AudioDevice.d.ts.map +1 -1
  10. package/lib/dist/core/classes/AudioDevice.js +4 -0
  11. package/lib/dist/core/classes/Channel.d.ts +10 -2
  12. package/lib/dist/core/classes/Channel.d.ts.map +1 -1
  13. package/lib/dist/core/classes/Channel.js +67 -0
  14. package/lib/dist/core/classes/DpsPipeline.d.ts +1 -1
  15. package/lib/dist/core/classes/DpsPipeline.d.ts.map +1 -1
  16. package/lib/dist/core/classes/DpsPipeline.js +7 -3
  17. package/lib/dist/core/classes/Effector.d.ts +2 -2
  18. package/lib/dist/core/classes/Effector.d.ts.map +1 -1
  19. package/lib/dist/core/classes/Effector.js +1 -1
  20. package/lib/dist/core/classes/Master.d.ts +12 -0
  21. package/lib/dist/core/classes/Master.d.ts.map +1 -1
  22. package/lib/dist/core/classes/Master.js +77 -7
  23. package/lib/dist/effects/classes/Analyser.d.ts +16 -0
  24. package/lib/dist/effects/classes/Analyser.d.ts.map +1 -0
  25. package/lib/dist/effects/classes/Analyser.js +62 -0
  26. package/lib/dist/effects/classes/Chorus.d.ts +4 -3
  27. package/lib/dist/effects/classes/Chorus.d.ts.map +1 -1
  28. package/lib/dist/effects/classes/Chorus.js +21 -12
  29. package/lib/dist/effects/classes/clips/HardClip.d.ts +4 -3
  30. package/lib/dist/effects/classes/clips/HardClip.d.ts.map +1 -1
  31. package/lib/dist/effects/classes/clips/HardClip.js +15 -9
  32. package/lib/dist/effects/classes/clips/SoftClip.d.ts +4 -3
  33. package/lib/dist/effects/classes/clips/SoftClip.d.ts.map +1 -1
  34. package/lib/dist/effects/classes/clips/SoftClip.js +15 -9
  35. package/lib/dist/effects/classes/filters/LowPassFilter.d.ts +4 -3
  36. package/lib/dist/effects/classes/filters/LowPassFilter.d.ts.map +1 -1
  37. package/lib/dist/effects/classes/filters/LowPassFilter.js +23 -14
  38. package/lib/dist/effects/exports.d.ts +1 -0
  39. package/lib/dist/effects/exports.d.ts.map +1 -1
  40. package/lib/dist/effects/exports.js +1 -0
  41. package/lib/dist/index.d.ts +5 -4
  42. package/lib/dist/index.d.ts.map +1 -1
  43. package/lib/dist/index.js +13 -18
  44. package/lib/dist/typings.d.ts +13 -11
  45. package/lib/dist/typings.d.ts.map +1 -1
  46. package/lib/dist/typings.js +5 -0
  47. package/lib/dist/utilities/debugger.js +6 -6
  48. package/lib/dist/utilities/helpers.d.ts +2 -0
  49. package/lib/dist/utilities/helpers.d.ts.map +1 -1
  50. package/lib/dist/utilities/helpers.js +35 -12
  51. package/lib/src/core/classes/AudioClip.ts +23 -7
  52. package/lib/src/core/classes/AudioClipPlayer.ts +67 -7
  53. package/lib/src/core/classes/AudioDevice.ts +5 -0
  54. package/lib/src/core/classes/Channel.ts +97 -4
  55. package/lib/src/core/classes/DpsPipeline.ts +7 -3
  56. package/lib/src/core/classes/Effector.ts +3 -2
  57. package/lib/src/core/classes/Master.ts +98 -11
  58. package/lib/src/effects/classes/Analyser.ts +84 -0
  59. package/lib/src/effects/classes/Chorus.ts +22 -13
  60. package/lib/src/effects/classes/clips/HardClip.ts +17 -10
  61. package/lib/src/effects/classes/clips/SoftClip.ts +17 -10
  62. package/lib/src/effects/classes/filters/LowPassFilter.ts +28 -15
  63. package/lib/src/effects/exports.ts +2 -1
  64. package/lib/src/index.ts +27 -29
  65. package/lib/src/typings.ts +14 -14
  66. package/lib/src/utilities/debugger.ts +6 -6
  67. package/lib/src/utilities/helpers.ts +38 -14
  68. package/package.json +1 -1
  69. package/lib/dist/effects/classes/Filter.d.ts +0 -3
  70. package/lib/dist/effects/classes/Filter.d.ts.map +0 -1
  71. package/lib/dist/effects/classes/Filter.js +0 -2
  72. package/lib/dist/effects/classes/SoftClip.d.ts +0 -10
  73. package/lib/dist/effects/classes/SoftClip.d.ts.map +0 -1
  74. package/lib/dist/effects/classes/SoftClip.js +0 -39
  75. package/lib/dist/test/linkable-channels.d.ts +0 -2
  76. package/lib/dist/test/linkable-channels.d.ts.map +0 -1
  77. package/lib/dist/test/linkable-channels.js +0 -16
  78. package/lib/src/core/functions/rebuild-effect-chain.ts +0 -5
@@ -0,0 +1,84 @@
1
+ import { Effector } from "../../core/exports";
2
+
3
+ type AnalyserOptionKeys = keyof Omit<AnalyserOptions, "channelCount" | "channelCountMode" | "channelInterpretation">;
4
+
5
+ export class Analyser extends Effector {
6
+
7
+ public label: string | null = "Analyser";
8
+ public name: string = "Analyser";
9
+
10
+ public analyserNode: AnalyserNode | null = null;
11
+
12
+ public float32ArrayBuffer = new Float32Array();
13
+ public uint8ArrayBuffer = new Uint8Array();
14
+
15
+ private options: AnalyserOptions = {
16
+ fftSize: 32,
17
+ smoothingTimeConstant: 0.8,
18
+ minDecibels: -90,
19
+ maxDecibels: -10,
20
+ };
21
+
22
+ constructor(options?: Partial<AnalyserOptions>) {
23
+ super();
24
+
25
+ if (options) this.SetOptions(options);
26
+ }
27
+
28
+ public async InitializeOnAttachment(context: AudioContext): Promise<void> {
29
+
30
+ this.context = context;
31
+ this.analyserNode = new AnalyserNode(context, this.options);
32
+
33
+ this.audioWorkletNode = this.analyserNode as unknown as AudioWorkletNode;
34
+ this.allocateBuffers();
35
+ }
36
+
37
+ public SetOptions(options: Partial<AnalyserOptions>): void {
38
+
39
+ (Object.keys(options) as AnalyserOptionKeys[]).forEach((key) => {
40
+ const value = options[key];
41
+
42
+ if (typeof value === "undefined") return;
43
+
44
+ if (key === "fftSize" && value) {
45
+ const allowed = [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768];
46
+ if (!allowed.includes(value)) return;
47
+ }
48
+
49
+ (this.options as any)[key] = value;
50
+
51
+ if (this.analyserNode) {
52
+ (this.analyserNode as any)[key] = value;
53
+ }
54
+ });
55
+
56
+ if (options.fftSize && this.analyserNode) {
57
+ this.allocateBuffers();
58
+ }
59
+ }
60
+
61
+ public GetWaveformFloatData(): Float32Array | null {
62
+
63
+ if (!this.analyserNode) return null;
64
+
65
+ this.analyserNode.getFloatTimeDomainData(this.float32ArrayBuffer);
66
+ return this.float32ArrayBuffer;
67
+ }
68
+
69
+ public GetWaveformByteData(): Uint8Array | null {
70
+
71
+ if (!this.analyserNode) return null;
72
+
73
+ this.analyserNode.getByteTimeDomainData(this.uint8ArrayBuffer);
74
+ return this.uint8ArrayBuffer;
75
+ }
76
+
77
+ private allocateBuffers(): void {
78
+
79
+ if (!this.analyserNode) return;
80
+
81
+ this.float32ArrayBuffer = new Float32Array(this.analyserNode.fftSize);
82
+ this.uint8ArrayBuffer = new Uint8Array(this.analyserNode.fftSize);
83
+ }
84
+ }
@@ -1,6 +1,6 @@
1
- import { SendMessageToWorklet, CreateAudioWorkletNode } from "../../utilities/helpers";
1
+ import { CoerceFiniteNumber, SendMessageToWorklet, CreateAudioWorkletNode } from "../../utilities/helpers";
2
2
  import { Effector } from "../../core/classes/Effector";
3
- import { ChorusEffectOptions, ChorusMessageCommandId, AudioWorkletProcessorNames } from "../../typings";
3
+ import { ChorusEffectOptions, ChorusMessageCommandId, AudioWorkletProcessorNames, StrictMode } from "../../typings";
4
4
 
5
5
  export class Chorus extends Effector {
6
6
 
@@ -12,21 +12,24 @@ export class Chorus extends Effector {
12
12
  public rateHz: number = 1.5;
13
13
  public mix: number = 0.5;
14
14
  public feedback: number = 0.2;
15
+ public strictMode: StrictMode = StrictMode.Disabled;
15
16
 
16
- constructor({ baseDelayMs, depthMs, rateHz, mix, feedback }: Partial<ChorusEffectOptions>) {
17
+ constructor({ baseDelayMs, depthMs, rateHz, mix, feedback, strictMode }: Partial<ChorusEffectOptions>) {
17
18
  super();
18
19
 
19
- this.baseDelayMs = baseDelayMs ?? this.baseDelayMs;
20
- this.depthMs = depthMs ?? this.depthMs;
21
- this.rateHz = rateHz ?? this.rateHz;
22
- this.mix = mix ?? this.mix;
23
- this.feedback = feedback ?? this.feedback;
24
- }
20
+ this.baseDelayMs = Math.max(0, CoerceFiniteNumber(baseDelayMs, this.baseDelayMs));
21
+ this.depthMs = Math.max(0, CoerceFiniteNumber(depthMs, this.depthMs));
22
+ this.rateHz = Math.max(0, CoerceFiniteNumber(rateHz, this.rateHz));
23
+ this.mix = Math.max(0, Math.min(1, CoerceFiniteNumber(mix, this.mix)));
24
+ this.feedback = Math.max(0, Math.min(1, CoerceFiniteNumber(feedback, this.feedback)));
25
25
 
26
- public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
26
+ const mode = CoerceFiniteNumber(strictMode, this.strictMode);
27
+ this.strictMode = mode === StrictMode.Enabled ? StrictMode.Enabled : StrictMode.Disabled;
28
+ }
27
29
 
28
- this.parentialContext = parentialContext;
29
- this.audioWorkletNode = CreateAudioWorkletNode<ChorusEffectOptions>(parentialContext, AudioWorkletProcessorNames.Chorus, this.ReturnOptionsAsObject());
30
+ public async InitializeOnAttachment(context: AudioContext): Promise<void> {
31
+ this.context = context;
32
+ this.audioWorkletNode = CreateAudioWorkletNode<ChorusEffectOptions>(context, AudioWorkletProcessorNames.Chorus, this.ReturnOptionsAsObject());
30
33
  }
31
34
 
32
35
  public ReturnOptionsAsObject(): ChorusEffectOptions {
@@ -36,31 +39,37 @@ export class Chorus extends Effector {
36
39
  rateHz: this.rateHz,
37
40
  mix: this.mix,
38
41
  feedback: this.feedback,
42
+ strictMode: this.strictMode
39
43
  }
40
44
  }
41
45
 
42
46
  public SetBaseDelayMs(value: number): boolean {
47
+ value = Math.max(0, CoerceFiniteNumber(value, this.baseDelayMs));
43
48
  this.baseDelayMs = value;
44
49
  return SendMessageToWorklet<ChorusMessageCommandId, number>(this.audioWorkletNode, ChorusMessageCommandId.SetBaseDelayMs, value);
45
50
  }
46
51
 
47
52
  public SetDepthMs(value: number) {
53
+ value = Math.max(0, CoerceFiniteNumber(value, this.depthMs));
48
54
  this.depthMs = value;
49
55
  return SendMessageToWorklet<ChorusMessageCommandId, number>(this.audioWorkletNode, ChorusMessageCommandId.SetDepthMs, value);
50
56
  }
51
57
 
52
58
  public SetRateHz(value: number) {
59
+ value = Math.max(0, CoerceFiniteNumber(value, this.rateHz));
53
60
  this.rateHz = value;
54
61
  return SendMessageToWorklet<ChorusMessageCommandId, number>(this.audioWorkletNode, ChorusMessageCommandId.SetRateHz, value);
55
62
  }
56
63
 
57
64
  public SetMix(value: number) {
65
+ value = Math.max(0, Math.min(1, CoerceFiniteNumber(value, this.mix)));
58
66
  this.mix = value;
59
67
  return SendMessageToWorklet<ChorusMessageCommandId, number>(this.audioWorkletNode, ChorusMessageCommandId.SetMix, value);
60
68
  }
61
69
 
62
70
  public SetFeedback(value: number) {
71
+ value = Math.max(0, Math.min(1, CoerceFiniteNumber(value, this.feedback)));
63
72
  this.feedback = value;
64
73
  return SendMessageToWorklet<ChorusMessageCommandId, number>(this.audioWorkletNode, ChorusMessageCommandId.SetFeedback, value);
65
74
  }
66
- }
75
+ }
@@ -1,6 +1,6 @@
1
1
  import { Effector } from "../../../core/classes/Effector";
2
- import { CreateAudioWorkletNode, SendMessageToWorklet } from "../../../utilities/helpers";
3
- import { HardClipMessageCommandId, AudioWorkletProcessorNames, HardClipOptions } from "../../../typings";
2
+ import { CoerceFiniteNumber, CreateAudioWorkletNode, SendMessageToWorklet } from "../../../utilities/helpers";
3
+ import { HardClipMessageCommandId, AudioWorkletProcessorNames, HardClipOptions, StrictMode } from "../../../typings";
4
4
 
5
5
  export class HardClip extends Effector {
6
6
 
@@ -9,33 +9,40 @@ export class HardClip extends Effector {
9
9
 
10
10
  public drive: number = 1;
11
11
  public gain: number = 1;
12
+ public strictMode: StrictMode = StrictMode.Disabled;
12
13
 
13
- constructor({ drive, gain }: Partial<HardClipOptions>) {
14
+ constructor({ drive, gain, strictMode }: Partial<HardClipOptions>) {
14
15
  super();
15
16
 
16
- this.drive = drive ?? this.drive;
17
- this.gain = gain ?? this.gain;
17
+ this.drive = Math.max(0, CoerceFiniteNumber(drive, this.drive));
18
+ this.gain = Math.max(0, CoerceFiniteNumber(gain, this.gain));
19
+
20
+ const mode = CoerceFiniteNumber(strictMode, this.strictMode);
21
+ this.strictMode = mode === StrictMode.Enabled ? StrictMode.Enabled : StrictMode.Disabled;
18
22
  }
19
23
 
20
- public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
21
- this.parentialContext = parentialContext;
22
- this.audioWorkletNode = CreateAudioWorkletNode<HardClipOptions>(parentialContext, AudioWorkletProcessorNames.HardClip, this.ReturnOptionsAsObject());
24
+ public async InitializeOnAttachment(context: AudioContext): Promise<void> {
25
+ this.context = context;
26
+ this.audioWorkletNode = CreateAudioWorkletNode<HardClipOptions>(context, AudioWorkletProcessorNames.HardClip, this.ReturnOptionsAsObject());
23
27
  }
24
28
 
25
29
  public ReturnOptionsAsObject(): HardClipOptions {
26
30
  return {
27
31
  drive: this.drive,
28
- gain: this.gain
32
+ gain: this.gain,
33
+ strictMode: this.strictMode
29
34
  }
30
35
  }
31
36
 
32
37
  public SetDrive(drive: number): boolean {
38
+ drive = Math.max(0, CoerceFiniteNumber(drive, this.drive));
33
39
  this.drive = drive;
34
40
  return SendMessageToWorklet<HardClipMessageCommandId, number>(this.audioWorkletNode, HardClipMessageCommandId.SetDrive, drive);
35
41
  }
36
42
 
37
43
  public SetGain(gain: number): boolean{
44
+ gain = Math.max(0, CoerceFiniteNumber(gain, this.gain));
38
45
  this.gain = gain;
39
46
  return SendMessageToWorklet<HardClipMessageCommandId, number>(this.audioWorkletNode, HardClipMessageCommandId.SetGain, gain);
40
47
  }
41
- }
48
+ }
@@ -1,6 +1,6 @@
1
- import { CreateAudioWorkletNode, SendMessageToWorklet } from "../../../utilities/helpers";
1
+ import { CoerceFiniteNumber, CreateAudioWorkletNode, SendMessageToWorklet } from "../../../utilities/helpers";
2
2
  import { Effector } from "../../../core/classes/Effector";
3
- import { SoftClipOptions, AudioWorkletProcessorNames, SoftClipMessageCommandId } from "../../../typings";
3
+ import { SoftClipOptions, AudioWorkletProcessorNames, SoftClipMessageCommandId, StrictMode } from "../../../typings";
4
4
 
5
5
  export class SoftClip extends Effector {
6
6
 
@@ -9,33 +9,40 @@ export class SoftClip extends Effector {
9
9
 
10
10
  public drive: number = 1;
11
11
  public gain: number = 1;
12
+ public strictMode: StrictMode = StrictMode.Disabled;
12
13
 
13
- constructor({ drive, gain }: Partial<SoftClipOptions>) {
14
+ constructor({ drive, gain, strictMode }: Partial<SoftClipOptions>) {
14
15
  super();
15
16
 
16
- this.drive = drive ?? this.drive;
17
- this.gain = gain ?? this.gain;
17
+ this.drive = Math.max(0, CoerceFiniteNumber(drive, this.drive));
18
+ this.gain = Math.max(0, CoerceFiniteNumber(gain, this.gain));
19
+
20
+ const mode = CoerceFiniteNumber(strictMode, this.strictMode);
21
+ this.strictMode = mode === StrictMode.Enabled ? StrictMode.Enabled : StrictMode.Disabled;
18
22
  }
19
23
 
20
- public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
21
- this.parentialContext = parentialContext;
22
- this.audioWorkletNode = CreateAudioWorkletNode<SoftClipOptions>(parentialContext, AudioWorkletProcessorNames.SoftClip, this.ReturnOptionsAsObject());
24
+ public async InitializeOnAttachment(context: AudioContext): Promise<void> {
25
+ this.context = context;
26
+ this.audioWorkletNode = CreateAudioWorkletNode<SoftClipOptions>(context, AudioWorkletProcessorNames.SoftClip, this.ReturnOptionsAsObject());
23
27
  }
24
28
 
25
29
  public ReturnOptionsAsObject(): SoftClipOptions {
26
30
  return {
27
31
  drive: this.drive,
28
- gain: this.gain
32
+ gain: this.gain,
33
+ strictMode: this.strictMode
29
34
  }
30
35
  }
31
36
 
32
37
  public SetDrive(drive: number): boolean {
38
+ drive = Math.max(0, CoerceFiniteNumber(drive, this.drive));
33
39
  this.drive = drive;
34
40
  return SendMessageToWorklet<SoftClipMessageCommandId, number>(this.audioWorkletNode, SoftClipMessageCommandId.SetDrive, drive);
35
41
  }
36
42
 
37
43
  public SetGain(gain: number): boolean{
44
+ gain = Math.max(0, CoerceFiniteNumber(gain, this.gain));
38
45
  this.gain = gain;
39
46
  return SendMessageToWorklet<SoftClipMessageCommandId, number>(this.audioWorkletNode, SoftClipMessageCommandId.SetGain, gain);
40
47
  }
41
- }
48
+ }
@@ -1,8 +1,8 @@
1
1
  import { Effector } from "../../../core/classes/Effector";
2
2
 
3
- import { SendMessageToWorklet, CreateAudioWorkletNode } from "../../../utilities/helpers";
3
+ import { CoerceFiniteNumber, SendMessageToWorklet, CreateAudioWorkletNode } from "../../../utilities/helpers";
4
4
 
5
- import { LowPassFilterOptions, LowPassFilterMessageCommandId, AudioWorkletProcessorNames } from "../../../typings";
5
+ import { LowPassFilterOptions, LowPassFilterMessageCommandId, AudioWorkletProcessorNames, StrictMode } from "../../../typings";
6
6
 
7
7
  export class LowPassFilter extends Effector {
8
8
 
@@ -12,36 +12,44 @@ export class LowPassFilter extends Effector {
12
12
  public cutoff: number = 1000;
13
13
  public q: number = 0.7;
14
14
  public minFrequency: number = 10;
15
+ public strictMode: StrictMode = StrictMode.Disabled;
15
16
 
16
- constructor({ cutoff, minFrequency, q }: Partial<LowPassFilterOptions>) {
17
+ constructor({ cutoff, minFrequency, q, strictMode }: Partial<LowPassFilterOptions>) {
17
18
  super();
18
19
 
19
- this.cutoff = cutoff ?? this.cutoff;
20
- this.minFrequency = minFrequency ?? this.minFrequency;
21
- this.q = q ?? this.q;
20
+ this.cutoff = CoerceFiniteNumber(cutoff, this.cutoff);
21
+ this.minFrequency = CoerceFiniteNumber(minFrequency, this.minFrequency);
22
+ this.q = CoerceFiniteNumber(q, this.q);
23
+
24
+ const mode = CoerceFiniteNumber(strictMode, this.strictMode);
25
+ this.strictMode = mode === StrictMode.Enabled ? StrictMode.Enabled : StrictMode.Disabled;
22
26
  }
23
27
 
24
- public async InitializeOnAttachment(parentialContext: AudioContext): Promise<void> {
28
+ public async InitializeOnAttachment(context: AudioContext): Promise<void> {
25
29
 
26
- this.parentialContext = parentialContext;
27
- this.audioWorkletNode = CreateAudioWorkletNode(parentialContext, AudioWorkletProcessorNames.LowPassFilter, this.ReturnOptionsAsObject());
28
- this.registerMessageEventListener(this.audioWorkletNode as AudioWorkletNode);
30
+ this.context = context;
31
+ this.audioWorkletNode = CreateAudioWorkletNode(context, AudioWorkletProcessorNames.LowPassFilter, this.ReturnOptionsAsObject());
32
+ this.audioWorkletNode && this.registerMessageEventListener(this.audioWorkletNode);
29
33
  }
30
34
 
31
35
  public ReturnOptionsAsObject(): LowPassFilterOptions {
32
36
  return {
33
37
  cutoff: this.cutoff,
34
38
  minFrequency: this.minFrequency,
35
- q: this.q
39
+ q: this.q,
40
+ strictMode: this.strictMode
36
41
  }
37
42
  }
38
43
 
39
44
  public SetCutoff(cutoff: number = 1000): boolean {
40
45
 
41
- if (!this.parentialContext) return false;
46
+ if (!this.context) return false;
47
+
48
+ cutoff = CoerceFiniteNumber(cutoff, this.cutoff);
49
+ cutoff = Math.max(this.minFrequency, cutoff);
42
50
 
43
- if (cutoff >= this.parentialContext.sampleRate)
44
- cutoff = this.parentialContext.sampleRate;
51
+ if (cutoff >= this.context.sampleRate)
52
+ cutoff = this.context.sampleRate;
45
53
 
46
54
  this.cutoff = cutoff;
47
55
  return SendMessageToWorklet<LowPassFilterMessageCommandId, number>(this.audioWorkletNode, LowPassFilterMessageCommandId.SetCutoff, cutoff);
@@ -49,6 +57,8 @@ export class LowPassFilter extends Effector {
49
57
 
50
58
  public SetMinFrequency(minFrequency: number = 10): boolean {
51
59
 
60
+ minFrequency = CoerceFiniteNumber(minFrequency, this.minFrequency);
61
+
52
62
  if (minFrequency < 10)
53
63
  minFrequency = 10;
54
64
 
@@ -58,10 +68,13 @@ export class LowPassFilter extends Effector {
58
68
 
59
69
  public SetQ(q: number = 0.7): boolean {
60
70
 
71
+ q = CoerceFiniteNumber(q, this.q);
72
+ q = Math.max(0.0001, q);
73
+
61
74
  if(q > 4)
62
75
  q = 4;
63
76
 
64
77
  this.q = q;
65
78
  return SendMessageToWorklet<LowPassFilterMessageCommandId, number>(this.audioWorkletNode, LowPassFilterMessageCommandId.SetQ, q);
66
79
  }
67
- }
80
+ }
@@ -1,4 +1,5 @@
1
1
  export { Chorus } from "./classes/Chorus";
2
+ export { Analyser } from "./classes/Analyser";
2
3
  export { Distortion } from "./classes/Distortion";
3
4
  export { Equalizer } from "./classes/Equalizer";
4
5
  export { Limiter } from "./classes/Limiter";
@@ -21,4 +22,4 @@ export { GenericReverb } from "./classes/reverbs/GenericReverb";
21
22
  export { HallReverb } from "./classes/reverbs/HallReverb";
22
23
  export { RoomReverb } from "./classes/reverbs/RoomReverb";
23
24
 
24
- export { LowPassFilter } from "./classes/filters/LowPassFilter";
25
+ export { LowPassFilter } from "./classes/filters/LowPassFilter";
package/lib/src/index.ts CHANGED
@@ -3,30 +3,25 @@
3
3
  * A javascript digital audio processor library.
4
4
  */
5
5
 
6
- import { FluexGLAudioDescriptor } from "./typings";
6
+ import { DspOptions } from "./typings";
7
7
 
8
- export const FluexGLAudio: FluexGLAudioDescriptor = {
9
- name: "FluexGL Audio",
10
- author: "Rohan Kanhaisingh",
11
- version: "0.0.2",
12
- license: "MIT",
13
- repository: "https://github.com/rohankanhaisingh/FluexGL-Audio",
14
- options: {
15
- maxMasterChannels: 8,
16
- maxTotalChannels: 128,
17
- sampleRate: 44000,
18
- spatialization: "stereo",
19
- debugger: {
20
- showErrors: true,
21
- showInfo: true,
22
- showWarnings: true,
23
- breakOnError: true
24
- }
8
+ export const DSP: DspOptions = {
9
+ maxMasterChannels: 8,
10
+ maxTotalChannels: 128,
11
+ sampleRate: 44000,
12
+ spatialization: "stereo",
13
+ overrideMaxAudioBufferNodes: false,
14
+ debugger: {
15
+ showErrors: true,
16
+ showInfo: true,
17
+ showWarnings: true,
18
+ breakOnError: true
25
19
  }
26
20
  }
27
21
 
28
22
  export {
29
23
  Chorus,
24
+ Analyser,
30
25
  Distortion,
31
26
  Equalizer,
32
27
  Limiter,
@@ -78,10 +73,19 @@ export {
78
73
  hasInitializedWasm,
79
74
  } from "./utilities/web-assembly";
80
75
 
76
+ export {
77
+ StrictMode,
78
+ ProcessorIdentificationCodes,
79
+ HardClipMessageCommandId,
80
+ SoftClipMessageCommandId,
81
+ AudioWorkletProcessorNames,
82
+ ChorusMessageCommandId,
83
+ LowPassFilterMessageCommandId
84
+ } from "./typings";
85
+
81
86
  export type {
82
- FluexGLAudioDescriptor,
83
- FluexGLAudioDebuggerOptions,
84
- FluexGLAudioOptions,
87
+ DspDebuggerOptions,
88
+ DspOptions,
85
89
  LoadAudioSourceOptions,
86
90
  AudioSourceData,
87
91
  ChannelOptions,
@@ -95,17 +99,11 @@ export type {
95
99
  DspPipelineInitializationState,
96
100
  ChorusEffectOptions,
97
101
  LowPassFilterOptions,
98
- LowPassFilterMessageCommandId,
99
- ChorusMessageCommandId,
100
- AudioWorkletProcessorNames,
101
- SoftClipMessageCommandId,
102
102
  SoftClipOptions,
103
- HardClipMessageCommandId,
104
103
  HardClipOptions,
105
104
  EffectorEventMap,
106
105
  IncomingMessageType,
107
106
  IncomingProcessorMessage,
108
107
  ProcessorData,
109
- ProcessorIdentificationCodes,
110
- EffectorEvents
111
- } from "./typings";
108
+ EffectorEvents,
109
+ } from "./typings";
@@ -60,28 +60,25 @@ export enum ProcessorIdentificationCodes {
60
60
  UnknownProcessorCreationDate = "UNKNOWN_PROCESSOR_CREATION_DATE"
61
61
  }
62
62
 
63
- export interface FluexGLAudioDebuggerOptions {
63
+ export enum StrictMode {
64
+ Disabled = 0x00,
65
+ Enabled = 0x01
66
+ }
67
+
68
+ export interface DspDebuggerOptions {
64
69
  breakOnError: boolean;
65
70
  showInfo: boolean;
66
71
  showErrors: boolean;
67
72
  showWarnings: boolean;
68
73
  }
69
74
 
70
- export interface FluexGLAudioOptions {
75
+ export interface DspOptions {
71
76
  maxMasterChannels: number;
72
77
  maxTotalChannels: number;
73
78
  sampleRate: number;
74
79
  spatialization: ChannelSpatialization;
75
- debugger: FluexGLAudioDebuggerOptions;
76
- }
77
-
78
- export interface FluexGLAudioDescriptor {
79
- name: string;
80
- author: string;
81
- version: string;
82
- license: string;
83
- repository: string;
84
- options: FluexGLAudioOptions;
80
+ debugger: DspDebuggerOptions;
81
+ overrideMaxAudioBufferNodes: boolean;
85
82
  }
86
83
 
87
84
  export interface LoadAudioSourceOptions {
@@ -116,6 +113,7 @@ export interface AudioClipEventMap {
116
113
  export interface DspPipelineInitializationOptions {
117
114
  pathToWasm: string;
118
115
  pathToWorklet: string;
116
+ options?: Partial<DspOptions>;
119
117
  }
120
118
 
121
119
  export interface DspPipelineInitializationState {
@@ -123,30 +121,32 @@ export interface DspPipelineInitializationState {
123
121
  workletBlobUrl: string;
124
122
  }
125
123
 
126
- // ======== Effect options ========
127
-
128
124
  export interface ChorusEffectOptions {
129
125
  baseDelayMs: number;
130
126
  depthMs: number;
131
127
  rateHz: number;
132
128
  mix: number;
133
129
  feedback: number;
130
+ strictMode: StrictMode;
134
131
  }
135
132
 
136
133
  export interface LowPassFilterOptions {
137
134
  cutoff: number;
138
135
  minFrequency: number;
139
136
  q: number;
137
+ strictMode: StrictMode;
140
138
  }
141
139
 
142
140
  export interface SoftClipOptions {
143
141
  drive: number;
144
142
  gain: number;
143
+ strictMode: StrictMode;
145
144
  }
146
145
 
147
146
  export interface HardClipOptions {
148
147
  drive: number;
149
148
  gain: number;
149
+ strictMode: StrictMode;
150
150
  }
151
151
 
152
152
  export interface EffectorEventMap {
@@ -1,12 +1,12 @@
1
1
  import { ErrorCodes, WarningCodes } from "../console-codes";
2
2
 
3
- import { FluexGLAudio } from "../index";
3
+ import { DSP } from "../index";
4
4
 
5
5
  export namespace Debug {
6
6
 
7
7
  export function Log(message: string, additionalDetails?: string[]) {
8
8
 
9
- if (!FluexGLAudio.options.debugger.showInfo) return;
9
+ if (!DSP.debugger.showInfo) return;
10
10
 
11
11
  let outputString: string = `%c[INFO]: %c${message} %c`;
12
12
 
@@ -19,7 +19,7 @@ export namespace Debug {
19
19
 
20
20
  export function Success(message: string, additionalDetails?: string[]) {
21
21
 
22
- if (!FluexGLAudio.options.debugger.showInfo) return;
22
+ if (!DSP.debugger.showInfo) return;
23
23
 
24
24
  let outputString: string = `%c[SUCCESS]: %c${message} %c`;
25
25
 
@@ -32,7 +32,7 @@ export namespace Debug {
32
32
 
33
33
  export function Warn(message: string, additionalDetails?: string[], warningCode?: WarningCodes) {
34
34
 
35
- if (!FluexGLAudio.options.debugger.showWarnings) return;
35
+ if (!DSP.debugger.showWarnings) return;
36
36
 
37
37
  let outputString: string = `%c(${warningCode ? warningCode : "Unknown".toUpperCase()}) %c[WARNING]: %c${message} %c`;
38
38
 
@@ -45,7 +45,7 @@ export namespace Debug {
45
45
 
46
46
  export function Error(message: string, additionalDetails?: string[], errorCode?: ErrorCodes) {
47
47
 
48
- if (!FluexGLAudio.options.debugger.showErrors) return;
48
+ if (!DSP.debugger.showErrors) return;
49
49
 
50
50
  let outputString: string = `%c(${errorCode ? errorCode : "Unknown".toUpperCase()}) %c[ERROR]: %c${message} %c`;
51
51
 
@@ -55,7 +55,7 @@ export namespace Debug {
55
55
 
56
56
  console.log(outputString, "color: red", "color: red;", "color: white", "color: white; font-style: italic;");
57
57
 
58
- if (FluexGLAudio.options.debugger.breakOnError) {
58
+ if (DSP.debugger.breakOnError) {
59
59
 
60
60
  for (let char of message)
61
61
  outputString = outputString.replace("%c", "");
@@ -235,31 +235,55 @@ export function SendMessageToWorklet<T, K = any>(node: AudioWorkletNode | null,
235
235
 
236
236
  if (!node) return false;
237
237
 
238
+ if (typeof data === "number" && !Number.isFinite(data)) {
239
+ Debug.Warn("Refusing to send non-finite numeric value to AudioWorkletNode.", [
240
+ `Command: ${String(commandId)}`,
241
+ `Value: ${String(data)}`
242
+ ]);
243
+ return false;
244
+ }
245
+
238
246
  node.port.postMessage({ commandId, data });
239
247
 
240
248
  return true;
241
249
  }
242
250
 
251
+ export function IsFiniteNumber(value: unknown): value is number {
252
+ return typeof value === "number" && Number.isFinite(value);
253
+ }
254
+
255
+ export function CoerceFiniteNumber(value: unknown, fallback: number): number {
256
+ return IsFiniteNumber(value) ? value : fallback;
257
+ }
258
+
243
259
  export function CreateAudioWorkletNode<T = any>(context: AudioContext, name: AudioWorkletProcessorNames | string, data: T) {
244
260
 
245
261
  if (!compiledWebAssemblyModule) {
246
-
247
262
  Debug.Error("Failed to create audio worklet node, because the web assembly module has not been compiled properly.", [
248
263
  `Request audio worklet node: ${name}.`
249
264
  ]);
250
265
  return null;
251
266
  }
252
267
 
253
- return new AudioWorkletNode(context, name, {
254
- numberOfInputs: 1,
255
- numberOfOutputs: 1,
256
- outputChannelCount: [2],
257
- parameterData: {
258
- ...data,
259
- sampleRate: context.sampleRate
260
- },
261
- processorOptions: {
262
- module: compiledWebAssemblyModule
263
- }
264
- })
265
- }
268
+ try {
269
+ return new AudioWorkletNode(context, name, {
270
+ numberOfInputs: 1,
271
+ numberOfOutputs: 1,
272
+ outputChannelCount: [2],
273
+ parameterData: {
274
+ ...data,
275
+ sampleRate: context.sampleRate
276
+ },
277
+ processorOptions: {
278
+ module: compiledWebAssemblyModule
279
+ }
280
+ });
281
+ } catch (err) {
282
+ Debug.Error("Failed to create audio worklet node.", [
283
+ `Request audio worklet node: ${name}.`,
284
+ "Make sure you called InitializeDspPipeline/InitializeDpsPipeline and loaded the worklet module on the same AudioContext.",
285
+ String(err)
286
+ ]);
287
+ return null;
288
+ }
289
+ }