@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
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # 🎚️ FluexGL DSP
2
+ An open-source, web-based DSP library developed alongside FluexGL, with the purpose of creating and manipulating sound in various contexts.
3
+
4
+ This open-source project is part of the Fluex ecosystem and is maintained by Rohan Kanhaisingh, the lead developer of this library.
5
+
6
+ **Note: this project is still under development and may not work yet.**
7
+
8
+ ## 🧩 About
9
+ FluexGL DSP is a modern, TypeScript-based Web Audio library for creating and manipulating sound. Inspired by and based on DAW-style workflows, it provides a flexible, object-oriented channel system with master and sub channels.
10
+
11
+ It integrates smoothly with FluexGL, making it ideal for use in games, interactive web experiences, and multimedia projects.
12
+
13
+ ## 📦 Installation
14
+
15
+ See down below for more details.
16
+
17
+ ```
18
+ $ npm install @fluexgl/dsp
19
+ ```
20
+
21
+ ## ⚡ Quick start
22
+
23
+ ```ts
24
+ import {
25
+ AudioDevice,
26
+ InitializeDspPipeline,
27
+ LoadAudioSource,
28
+ ResolveDefaultAudioOutputDevice,
29
+ Channel,
30
+ AudioSourceData,
31
+ AudioClip
32
+ } from "@fluexgl/dsp";
33
+
34
+ (async function() {
35
+
36
+ // Make sure that FluexGL can access audio devices.
37
+ const hasInitialized = await InitializeDspPipeline();
38
+
39
+ if(!hasInitialized) return null;
40
+
41
+ // Resolves the default audio output device.
42
+ const audioDevice: AudioDevice | null = await ResolveDefaultAudioOutputDevice({
43
+ pathToWasm: "/wasm/fluexgl-dsp-wasm_bg.wasm"
44
+ });
45
+
46
+ if(!audioDevice) return;
47
+
48
+ // Get the master channel from the default audio output device.
49
+ const masterChannel = audioDevice.GetMasterChannel();
50
+
51
+ // Create a new empty channel.
52
+ const channel = new Channel();
53
+
54
+ // Label the channel as 'BackgroundMusic'.
55
+ channel.SetLabel("BackgroundMusic");
56
+
57
+ // Attach the 'BackgroundMusic' channel, to the device's master channel.
58
+ masterChannel.AttachChannel(channel);
59
+
60
+ // Load the data from the audio source.
61
+ const audioSourceData: AudioSourceData | null = await LoadAudioSource("/assets/data/bruh.mp3");
62
+
63
+ if(!audioSourceData) return;
64
+
65
+ // Create a audio node based on the data.
66
+ const audioClip = new AudioClip(audioSourceData);
67
+
68
+ // Attach the audio node to the channel.
69
+ channel.AttachAudioClip(audioClip);
70
+
71
+ // Click event listener on window.
72
+ window.addEventListener("click", function() {
73
+
74
+ // Play the audio clip
75
+ audioClip.Play();
76
+ });
77
+ })();
78
+ ```
79
+
80
+ ## 💡 Effects, tools and more
81
+ FluexGL DSP provides built-in such as effects, tools, and utilities for advanced web audio processing, such as reverbs, delays, and stereo imaging effects.
82
+
83
+ The library also includes a powerful debugging system that allows developers to inspect and analyze the audio pipeline for easier troubleshooting and optimization.
84
+
85
+ ## Web Assembly
86
+
87
+ FluexGL DSP is partially made using Web Assembly, and Rust as programming language. In order to use FluexGL DSP, the wasm source code must be provided when initializing the DSP pipeline. The source code can be found at the [FluexGL DSP WebAssembly Github repository](https://github.com/rohankanhaisingh/FluexGL-DSP-WebAssembly).
@@ -0,0 +1,14 @@
1
+ export declare enum ErrorCodes {
2
+ NO_CONTEXT_PERMISSION = "FLUEXGL@AUDIO_ERROR_0001",
3
+ NO_FILE_TYPE_FOUND = "FLUEXGL@AUDIO_ERROR_0002",
4
+ PATH_TO_FILE_NOT_FOUND = "FLUEXGL@AUDIO_ERROR_0003",
5
+ SAME_MASTER_CHANNEL = "FLUEXGL@AUDIO_ERROR_0004",
6
+ WASM_MODULE_INITIALIZATION_FAILED = "FLUEXGL@AUDIO_ERROR_0005",
7
+ PATH_TO_WASM_FILE_NOT_FOUND = "FLUEXGL@AUDIO_ERROR_0006"
8
+ }
9
+ export declare enum WarningCodes {
10
+ NO_DEFAULT_AUDIO_DEVICE_FOUND = "FLUEXGL@AUDIO_WARNING_0001"
11
+ }
12
+ export declare enum SuccessCodes {
13
+ }
14
+ //# sourceMappingURL=console-codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-codes.d.ts","sourceRoot":"","sources":["../src/console-codes.ts"],"names":[],"mappings":"AAAA,oBAAY,UAAU;IAClB,qBAAqB,6BAA6B;IAClD,kBAAkB,6BAA6B;IAC/C,sBAAsB,6BAA6B;IACnD,mBAAmB,6BAA6B;IAChD,iCAAiC,6BAA6B;IAC9D,2BAA2B,6BAA6B;CAC3D;AAED,oBAAY,YAAY;IACpB,6BAA6B,+BAA+B;CAC/D;AAED,oBAAY,YAAY;CAEvB"}
@@ -0,0 +1,16 @@
1
+ export var ErrorCodes;
2
+ (function (ErrorCodes) {
3
+ ErrorCodes["NO_CONTEXT_PERMISSION"] = "FLUEXGL@AUDIO_ERROR_0001";
4
+ ErrorCodes["NO_FILE_TYPE_FOUND"] = "FLUEXGL@AUDIO_ERROR_0002";
5
+ ErrorCodes["PATH_TO_FILE_NOT_FOUND"] = "FLUEXGL@AUDIO_ERROR_0003";
6
+ ErrorCodes["SAME_MASTER_CHANNEL"] = "FLUEXGL@AUDIO_ERROR_0004";
7
+ ErrorCodes["WASM_MODULE_INITIALIZATION_FAILED"] = "FLUEXGL@AUDIO_ERROR_0005";
8
+ ErrorCodes["PATH_TO_WASM_FILE_NOT_FOUND"] = "FLUEXGL@AUDIO_ERROR_0006";
9
+ })(ErrorCodes || (ErrorCodes = {}));
10
+ export var WarningCodes;
11
+ (function (WarningCodes) {
12
+ WarningCodes["NO_DEFAULT_AUDIO_DEVICE_FOUND"] = "FLUEXGL@AUDIO_WARNING_0001";
13
+ })(WarningCodes || (WarningCodes = {}));
14
+ export var SuccessCodes;
15
+ (function (SuccessCodes) {
16
+ })(SuccessCodes || (SuccessCodes = {}));
@@ -0,0 +1,67 @@
1
+ import { AudioClipAnalyserProperty, AudioClipAnalyserType, AudioClipEventMap, AudioSourceData } from "../../typings";
2
+ import { Channel } from "./Channel";
3
+ export declare class AudioClip {
4
+ data: AudioSourceData;
5
+ id: string;
6
+ hasAttachedToChannel: boolean;
7
+ loop: boolean;
8
+ isPlaying: boolean;
9
+ startTime: number;
10
+ offsetAtStart: number;
11
+ progressUpdateSpeed: number;
12
+ gainNode: GainNode | null;
13
+ stereoPannerNode: StereoPannerNode | null;
14
+ parentialAudioContext: AudioContext | null;
15
+ parentialChannel: Channel | null;
16
+ preAnalyser: AnalyserNode | null;
17
+ postAnalyser: AnalyserNode | null;
18
+ preAnalyserEnabled: boolean;
19
+ postAnalyserEnabled: boolean;
20
+ preAnalyserOptions: AnalyserOptions;
21
+ postAnalyserOptions: AnalyserOptions;
22
+ preAnalyserFloatArrayBuffer: Float32Array;
23
+ postAnalyserFloatArrayBuffer: Float32Array;
24
+ preAnalyserByteArrayBuffer: Uint8Array;
25
+ postAnalyserByteArrayBuffer: Uint8Array;
26
+ private audioBufferSourceNodes;
27
+ private maxAudioBufferSourceNodes;
28
+ private progressInterval;
29
+ private events;
30
+ constructor(data: AudioSourceData);
31
+ private createBufferSource;
32
+ private connectSourcesTo;
33
+ private safeDisconnect;
34
+ private rebuildNodeChain;
35
+ InitializeAudioClipOnAttaching(channel: Channel): AudioClip | null;
36
+ Play(timestamp?: number, offset?: number): AudioClip | null;
37
+ Seek(seconds: number): AudioClip | null;
38
+ Stop(): AudioClip | null;
39
+ SetVolume(volume: number): AudioClip | void;
40
+ SetPanLevel(panLevel: number): AudioClip | void;
41
+ Loop(loop?: boolean): AudioClip;
42
+ SetMaxAudioBufferSourceNodes(value: number): AudioClip;
43
+ DisconnectAllAudioBufferSourceNodes(): boolean;
44
+ AddEventListener<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): () => void;
45
+ Once<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): () => void;
46
+ RemoveEventListener<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): AudioClip;
47
+ ClearEventListeners(event?: keyof AudioClipEventMap): AudioClip;
48
+ GetChannelData(channel?: number): Float32Array;
49
+ EnablePreAnalyser(): boolean;
50
+ DisablePreAnalyser(): boolean;
51
+ EnablePostAnalyser(): boolean;
52
+ DisablePostAnalyser(): boolean;
53
+ SetPreAnalyserOptions(options: AnalyserOptions): void;
54
+ SetPostAnalyserOptions(options: AnalyserOptions): void;
55
+ SetAnalyserOption(analyserType: AudioClipAnalyserType, property: AudioClipAnalyserProperty, value: number): boolean;
56
+ GetWaveformFloatData(analyserType: AudioClipAnalyserType): Float32Array | null;
57
+ GetWaveformByteData(analyserType: AudioClipAnalyserType): Uint8Array | null;
58
+ get currentPlaybackTime(): number;
59
+ get duration(): number;
60
+ get volume(): number;
61
+ get stereoPanning(): number;
62
+ get formattedDuration(): string;
63
+ get sampleRate(): number;
64
+ get numberOfChannels(): number;
65
+ get byteLength(): number;
66
+ }
67
+ //# sourceMappingURL=AudioClip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioClip.d.ts","sourceRoot":"","sources":["../../../src/core/classes/AudioClip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,410 @@
1
+ import { v4 } from "uuid";
2
+ import { format } from "date-fns";
3
+ import { Debug } from "../../utilities/debugger";
4
+ export class AudioClip {
5
+ data;
6
+ id = v4();
7
+ hasAttachedToChannel = false;
8
+ loop = false;
9
+ isPlaying = false;
10
+ startTime = 0;
11
+ offsetAtStart = 0;
12
+ progressUpdateSpeed = 20;
13
+ gainNode = null;
14
+ stereoPannerNode = null;
15
+ parentialAudioContext = null;
16
+ parentialChannel = null;
17
+ preAnalyser = null;
18
+ postAnalyser = null;
19
+ preAnalyserEnabled = false;
20
+ postAnalyserEnabled = false;
21
+ preAnalyserOptions = {};
22
+ postAnalyserOptions = {};
23
+ preAnalyserFloatArrayBuffer = new Float32Array();
24
+ postAnalyserFloatArrayBuffer = new Float32Array();
25
+ preAnalyserByteArrayBuffer = new Uint8Array();
26
+ postAnalyserByteArrayBuffer = new Uint8Array();
27
+ audioBufferSourceNodes = [];
28
+ maxAudioBufferSourceNodes = 1;
29
+ progressInterval = 0;
30
+ events = {
31
+ "progress": []
32
+ };
33
+ constructor(data) {
34
+ this.data = data;
35
+ }
36
+ // Private methods.
37
+ createBufferSource() {
38
+ if (!this.parentialAudioContext)
39
+ return null;
40
+ const context = this.parentialAudioContext;
41
+ const bufferSource = context.createBufferSource();
42
+ bufferSource.buffer = this.data.audioBuffer;
43
+ bufferSource.loop = this.loop;
44
+ return bufferSource;
45
+ }
46
+ connectSourcesTo(target) {
47
+ for (const src of this.audioBufferSourceNodes) {
48
+ src.disconnect();
49
+ src.connect(target);
50
+ }
51
+ }
52
+ safeDisconnect(node) {
53
+ node?.disconnect();
54
+ }
55
+ rebuildNodeChain() {
56
+ if (!this.parentialAudioContext || !this.gainNode || !this.stereoPannerNode || !this.parentialChannel || !this.parentialChannel.audioClipsInputGainNode) {
57
+ Debug.Error("rebuildNodeChain: missing context or core nodes (gain/panner).");
58
+ return false;
59
+ }
60
+ const destination = this.parentialChannel.audioClipsInputGainNode;
61
+ this.safeDisconnect(this.gainNode);
62
+ this.safeDisconnect(this.stereoPannerNode);
63
+ this.safeDisconnect(this.preAnalyser);
64
+ this.safeDisconnect(this.postAnalyser);
65
+ let entry = this.gainNode;
66
+ if (this.preAnalyserEnabled && this.preAnalyser) {
67
+ entry = this.preAnalyser;
68
+ this.preAnalyser.connect(this.gainNode);
69
+ }
70
+ this.gainNode.connect(this.stereoPannerNode);
71
+ if (this.postAnalyserEnabled && this.postAnalyser) {
72
+ this.stereoPannerNode.connect(this.postAnalyser);
73
+ this.postAnalyser.connect(destination);
74
+ }
75
+ else {
76
+ this.stereoPannerNode.connect(destination);
77
+ }
78
+ this.connectSourcesTo(entry);
79
+ return true;
80
+ }
81
+ // Public methods
82
+ InitializeAudioClipOnAttaching(channel) {
83
+ if (!channel.parentialContext || !channel.audioClipsInputGainNode) {
84
+ Debug.Error("Could not initialize audio clip on channel attachment, because channel it's master channel has not been defined.", [
85
+ `Call .AttachChannel([channel<"${channel.id}"> Channel]) on the master channel.`
86
+ ]);
87
+ return null;
88
+ }
89
+ this.gainNode = new GainNode(channel.parentialContext);
90
+ this.stereoPannerNode = new StereoPannerNode(channel.parentialContext);
91
+ this.parentialAudioContext = channel.parentialContext;
92
+ this.parentialChannel = channel;
93
+ this.hasAttachedToChannel = true;
94
+ return this;
95
+ }
96
+ Play(timestamp, offset) {
97
+ if (!this.hasAttachedToChannel || !this.parentialAudioContext || !this.parentialChannel) {
98
+ Debug.Error("Could not play the audio node because it is not attached to a channel", [
99
+ "Call 'AttachAudioClip([clip AudioClip])' on a channel, before playing this audio node."
100
+ ]);
101
+ return this;
102
+ }
103
+ const context = this.parentialAudioContext;
104
+ const self = this;
105
+ if (this.audioBufferSourceNodes.length > this.maxAudioBufferSourceNodes - 1)
106
+ return null;
107
+ const bufferSource = this.createBufferSource();
108
+ if (!bufferSource) {
109
+ Debug.Error("Something went wrong.");
110
+ return null;
111
+ }
112
+ const actualOffset = offset ?? this.offsetAtStart ?? 0;
113
+ this.startTime = context.currentTime;
114
+ this.offsetAtStart = actualOffset;
115
+ this.isPlaying = true;
116
+ if (this.progressInterval)
117
+ clearInterval(this.progressInterval);
118
+ if (this.isPlaying)
119
+ this.progressInterval = setInterval(function () {
120
+ if (!self.isPlaying)
121
+ return;
122
+ const current = self.offsetAtStart + (context.currentTime - self.startTime);
123
+ const date = new Date(current * 1000);
124
+ const formattedTime = format(date, "mm:ss");
125
+ const progressPayload = {
126
+ current: parseFloat(current.toFixed(2)),
127
+ startTime: self.startTime,
128
+ offset: self.offsetAtStart,
129
+ contextTimestamp: context.currentTime,
130
+ formatted: formattedTime
131
+ };
132
+ self.events.progress.forEach(function (cb) {
133
+ cb(progressPayload);
134
+ });
135
+ }, this.progressUpdateSpeed);
136
+ bufferSource.addEventListener("ended", function () {
137
+ const i = self.audioBufferSourceNodes.indexOf(bufferSource);
138
+ bufferSource.disconnect();
139
+ if (i === 0)
140
+ self.isPlaying = false;
141
+ if (i >= 0)
142
+ return self.audioBufferSourceNodes.splice(i, 1);
143
+ });
144
+ this.audioBufferSourceNodes.push(bufferSource);
145
+ this.rebuildNodeChain();
146
+ bufferSource.start(timestamp ?? this.startTime, actualOffset);
147
+ return this;
148
+ }
149
+ Seek(seconds) {
150
+ if (!this.parentialAudioContext || !this.hasAttachedToChannel) {
151
+ Debug.Error("Could not seek because the clip is not attached to a channel.", [
152
+ `Clip ID: ${this.id}`
153
+ ]);
154
+ return null;
155
+ }
156
+ const clamped = Math.max(0, Math.min(seconds, this.duration));
157
+ if (!this.isPlaying) {
158
+ this.offsetAtStart = clamped;
159
+ return this;
160
+ }
161
+ this.Stop();
162
+ this.Play(undefined, clamped);
163
+ return this;
164
+ }
165
+ Stop() {
166
+ if (!this.hasAttachedToChannel || !this.parentialAudioContext) {
167
+ Debug.Error("Could not stop the audio node because it is not attached to a channel", [
168
+ "Call 'AttachAudioClip([node AudioNode])' on a channel, before stopping this audio node."
169
+ ]);
170
+ return null;
171
+ }
172
+ this.audioBufferSourceNodes.forEach(function (node) {
173
+ node.stop();
174
+ node.disconnect();
175
+ });
176
+ this.audioBufferSourceNodes.length = 0;
177
+ this.isPlaying = false;
178
+ if (this.progressInterval) {
179
+ clearInterval(this.progressInterval);
180
+ this.progressInterval = null;
181
+ }
182
+ return this;
183
+ }
184
+ SetVolume(volume) {
185
+ if (!this.gainNode || !this.parentialAudioContext)
186
+ return Debug.Error("Something went wrong while setting the volume.", [
187
+ `Gain node on audio clip '${this.id}' is undefined.`
188
+ ]);
189
+ this.gainNode.gain.setValueAtTime(volume, this.parentialAudioContext.currentTime);
190
+ return this;
191
+ }
192
+ SetPanLevel(panLevel) {
193
+ if (!this.stereoPannerNode || !this.parentialAudioContext)
194
+ return Debug.Error("Something went wrong while setting the pan level", [
195
+ `Stereo panner node on audio clip '${this.id}' is undefined`
196
+ ]);
197
+ if (panLevel < -1 || panLevel > 1)
198
+ return Debug.Error("Could not set the pan level because it is not between -1 and 1.", [
199
+ "Provide this method with a value between -1 and 1"
200
+ ]);
201
+ this.stereoPannerNode.pan.setValueAtTime(panLevel, this.parentialAudioContext.currentTime);
202
+ return this;
203
+ }
204
+ Loop(loop) {
205
+ this.audioBufferSourceNodes.forEach(function (node) {
206
+ node.loop = loop ?? true;
207
+ });
208
+ this.loop = loop ?? true;
209
+ return this;
210
+ }
211
+ SetMaxAudioBufferSourceNodes(value) {
212
+ Debug.Warn("Changing the amount of buffer source nodes may cause some properties of this class instance to work inproperly.", [
213
+ "The default value is 1."
214
+ ]);
215
+ this.maxAudioBufferSourceNodes = value;
216
+ return this;
217
+ }
218
+ DisconnectAllAudioBufferSourceNodes() {
219
+ if (!this.parentialAudioContext) {
220
+ Debug.Error("Could not disconnect audio buffer source nodes, because the parential audio contex has not been found", [
221
+ `Clip ID: ${this.id}`,
222
+ `Parential channel id: ${this.parentialChannel ? this.parentialChannel.id : "none"}`
223
+ ]);
224
+ return false;
225
+ }
226
+ const contextCurrentTime = this.parentialAudioContext?.currentTime;
227
+ this.audioBufferSourceNodes.forEach(function (node) {
228
+ node.stop(contextCurrentTime);
229
+ node.disconnect();
230
+ });
231
+ return true;
232
+ }
233
+ AddEventListener(event, cb) {
234
+ this.events[event].push(cb);
235
+ return () => this.RemoveEventListener(event, cb);
236
+ }
237
+ Once(event, cb) {
238
+ const wrapper = ((...args) => {
239
+ // @ts-ignore
240
+ cb(...args);
241
+ this.RemoveEventListener(event, wrapper);
242
+ });
243
+ return this.AddEventListener(event, wrapper);
244
+ }
245
+ RemoveEventListener(event, cb) {
246
+ const arr = this.events[event];
247
+ for (let i = 0; i < arr.length; i++) {
248
+ if (arr[i] === cb) {
249
+ arr.splice(i, 1);
250
+ break;
251
+ }
252
+ }
253
+ return this;
254
+ }
255
+ ClearEventListeners(event) {
256
+ if (event) {
257
+ this.events[event].length = 0;
258
+ }
259
+ else {
260
+ Object.keys(this.events).forEach((k) => (this.events[k].length = 0));
261
+ }
262
+ return this;
263
+ }
264
+ GetChannelData(channel = 0) {
265
+ return this.data.audioBuffer.getChannelData(channel);
266
+ }
267
+ EnablePreAnalyser() {
268
+ if (!this.parentialAudioContext || !this.parentialChannel || !this.hasAttachedToChannel) {
269
+ Debug.Error("EnablePreAnalyser: clip niet aan channel gekoppeld.");
270
+ return false;
271
+ }
272
+ if (!this.preAnalyser)
273
+ this.preAnalyser = new AnalyserNode(this.parentialAudioContext, this.preAnalyserOptions);
274
+ this.preAnalyserFloatArrayBuffer = new Float32Array(this.preAnalyser.fftSize);
275
+ this.preAnalyserByteArrayBuffer = new Uint8Array(this.preAnalyser.fftSize);
276
+ this.preAnalyserEnabled = true;
277
+ return this.rebuildNodeChain();
278
+ }
279
+ DisablePreAnalyser() {
280
+ this.preAnalyserEnabled = false;
281
+ return this.rebuildNodeChain();
282
+ }
283
+ EnablePostAnalyser() {
284
+ if (!this.parentialAudioContext || !this.parentialChannel || !this.hasAttachedToChannel) {
285
+ Debug.Error("EnablePostAnalyser: clip niet aan channel gekoppeld.");
286
+ return false;
287
+ }
288
+ if (!this.postAnalyser)
289
+ this.postAnalyser = new AnalyserNode(this.parentialAudioContext, this.postAnalyserOptions);
290
+ this.postAnalyserFloatArrayBuffer = new Float32Array(this.postAnalyser.fftSize);
291
+ this.postAnalyserByteArrayBuffer = new Uint8Array(this.postAnalyser.fftSize);
292
+ this.postAnalyserEnabled = true;
293
+ return this.rebuildNodeChain();
294
+ }
295
+ DisablePostAnalyser() {
296
+ this.postAnalyserEnabled = false;
297
+ return this.rebuildNodeChain();
298
+ }
299
+ SetPreAnalyserOptions(options) {
300
+ this.preAnalyserOptions = { ...options };
301
+ if (!this.preAnalyser)
302
+ return;
303
+ this.preAnalyser.fftSize = options.fftSize ?? this.preAnalyser.fftSize;
304
+ this.preAnalyser.minDecibels = options.minDecibels ?? this.preAnalyser.minDecibels;
305
+ this.preAnalyser.maxDecibels = options.maxDecibels ?? this.preAnalyser.maxDecibels;
306
+ this.preAnalyser.smoothingTimeConstant = options.smoothingTimeConstant ?? this.preAnalyser.smoothingTimeConstant;
307
+ }
308
+ SetPostAnalyserOptions(options) {
309
+ this.postAnalyserOptions = { ...options };
310
+ if (!this.postAnalyser)
311
+ return;
312
+ this.postAnalyser.fftSize = options.fftSize ?? this.postAnalyser.fftSize;
313
+ this.postAnalyser.minDecibels = options.minDecibels ?? this.postAnalyser.minDecibels;
314
+ this.postAnalyser.maxDecibels = options.maxDecibels ?? this.postAnalyser.maxDecibels;
315
+ this.postAnalyser.smoothingTimeConstant = options.smoothingTimeConstant ?? this.postAnalyser.smoothingTimeConstant;
316
+ }
317
+ SetAnalyserOption(analyserType, property, value) {
318
+ const node = analyserType === "pre" ? this.preAnalyser : this.postAnalyser;
319
+ if (node)
320
+ switch (property) {
321
+ case "fftSize":
322
+ node.fftSize = value;
323
+ break;
324
+ case "minDecibels":
325
+ node.minDecibels = value;
326
+ break;
327
+ case "maxDecibels":
328
+ node.maxDecibels = value;
329
+ break;
330
+ case "smoothingTimeConstant":
331
+ node.smoothingTimeConstant = value;
332
+ break;
333
+ default: return false;
334
+ }
335
+ const opts = analyserType === "pre" ? this.preAnalyserOptions : this.postAnalyserOptions;
336
+ switch (property) {
337
+ case "fftSize":
338
+ opts.fftSize = value;
339
+ break;
340
+ case "minDecibels":
341
+ opts.minDecibels = value;
342
+ break;
343
+ case "maxDecibels":
344
+ opts.maxDecibels = value;
345
+ break;
346
+ case "smoothingTimeConstant":
347
+ opts.smoothingTimeConstant = value;
348
+ break;
349
+ default: return false;
350
+ }
351
+ return true;
352
+ }
353
+ GetWaveformFloatData(analyserType) {
354
+ if (analyserType === "pre" && this.preAnalyser) {
355
+ this.preAnalyser.getFloatTimeDomainData(this.preAnalyserFloatArrayBuffer);
356
+ return this.preAnalyserFloatArrayBuffer;
357
+ }
358
+ else if (analyserType === "post" && this.postAnalyser) {
359
+ this.postAnalyser.getFloatTimeDomainData(this.postAnalyserFloatArrayBuffer);
360
+ return this.postAnalyserFloatArrayBuffer;
361
+ }
362
+ return null;
363
+ }
364
+ GetWaveformByteData(analyserType) {
365
+ if (!this.preAnalyser || !this.postAnalyser) {
366
+ Debug.Error("Could not get byte waveform data because the pre analyser or post analyser has not been enabled.", [
367
+ "Call .EnablePreAnalyser() or .EnablePostAnalyser() before getting waveform data."
368
+ ]);
369
+ return null;
370
+ }
371
+ switch (analyserType) {
372
+ case "pre":
373
+ this.preAnalyser.getByteTimeDomainData(this.preAnalyserByteArrayBuffer);
374
+ return this.preAnalyserByteArrayBuffer;
375
+ case "post":
376
+ this.postAnalyser.getByteTimeDomainData(this.postAnalyserByteArrayBuffer);
377
+ return this.postAnalyserByteArrayBuffer;
378
+ default:
379
+ return null;
380
+ }
381
+ }
382
+ // Public getters and setters
383
+ get currentPlaybackTime() {
384
+ return (!this.isPlaying || !this.parentialAudioContext)
385
+ ? 0
386
+ : this.offsetAtStart + (this.parentialAudioContext.currentTime - this.startTime);
387
+ }
388
+ get duration() {
389
+ return this.data.audioBuffer.duration;
390
+ }
391
+ get volume() {
392
+ return this.gainNode?.gain.value ?? 0;
393
+ }
394
+ get stereoPanning() {
395
+ return this.stereoPannerNode?.pan.value ?? 1;
396
+ }
397
+ get formattedDuration() {
398
+ const date = new Date(this.duration * 1000);
399
+ return format(date, "mm:ss");
400
+ }
401
+ get sampleRate() {
402
+ return this.data.audioBuffer.sampleRate;
403
+ }
404
+ get numberOfChannels() {
405
+ return this.data.audioBuffer.numberOfChannels;
406
+ }
407
+ get byteLength() {
408
+ return this.data.arrayBuffer.byteLength;
409
+ }
410
+ }
@@ -0,0 +1,13 @@
1
+ import { Master } from "./Master";
2
+ export declare class AudioDevice {
3
+ deviceInfo: MediaDeviceInfo;
4
+ id: string;
5
+ timestamp: number;
6
+ masterChannel: Master;
7
+ masterChannels: Master[];
8
+ constructor(deviceInfo: MediaDeviceInfo);
9
+ GetMasterChannel(): Master;
10
+ SetMasterChannel(channel: Master): void;
11
+ CreateMasterChannel(): Master;
12
+ }
13
+ //# sourceMappingURL=AudioDevice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioDevice.d.ts","sourceRoot":"","sources":["../../../src/core/classes/AudioDevice.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKlC,qBAAa,WAAW;IAQD,UAAU,EAAE,eAAe;IANvC,EAAE,EAAE,MAAM,CAAQ;IAClB,SAAS,EAAE,MAAM,CAAc;IAE/B,aAAa,EAAE,MAAM,CAAgB;IACrC,cAAc,EAAE,MAAM,EAAE,CAAM;gBAElB,UAAU,EAAE,eAAe;IAEvC,gBAAgB,IAAI,MAAM;IAI1B,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAUvC,mBAAmB,IAAI,MAAM;CAOvC"}
@@ -0,0 +1,30 @@
1
+ import { v4 } from "uuid";
2
+ import { Master } from "./Master";
3
+ import { ErrorCodes } from "../../console-codes";
4
+ import { Debug } from "../../utilities/debugger";
5
+ export class AudioDevice {
6
+ deviceInfo;
7
+ id = v4();
8
+ timestamp = Date.now();
9
+ masterChannel = new Master();
10
+ masterChannels = [];
11
+ constructor(deviceInfo) {
12
+ this.deviceInfo = deviceInfo;
13
+ }
14
+ GetMasterChannel() {
15
+ return this.masterChannel;
16
+ }
17
+ SetMasterChannel(channel) {
18
+ if (channel.id === this.masterChannel.id)
19
+ return Debug.Error("The provided master channel is the same as the current channel.", [
20
+ "Provide this method with a different master channel.",
21
+ `Received master channel ID ${channel.id}.`
22
+ ], ErrorCodes.SAME_MASTER_CHANNEL);
23
+ this.masterChannel = channel;
24
+ }
25
+ CreateMasterChannel() {
26
+ const master = new Master();
27
+ this.masterChannels.push(master);
28
+ return master;
29
+ }
30
+ }