loudness-worklet 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -80
- package/package.json +17 -33
- package/packages/lib/dist/index.d.ts +34 -0
- package/{dist → packages/lib/dist}/index.js +271 -187
- package/dist/index.d.ts +0 -15
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Loudness Audio Worklet Processor
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/loudness-worklet)
|
|
4
|
-
[](LICENSE)
|
|
5
5
|
|
|
6
6
|
A loudness meter for the `Web Audio API`, based on the [ITU-R BS.1770-5](https://www.itu.int/rec/R-REC-BS.1770) standard and implemented as an AudioWorkletProcessor.
|
|
7
7
|
|
|
8
|
-
[](https://lcweden.github.io/loudness-worklet/)
|
|
9
9
|
|
|
10
|
-
<p align="center"><a href="https://lcweden.github.io/loudness-
|
|
10
|
+
<p align="center"><a href="https://lcweden.github.io/loudness-worklet/">Demo</a></p>
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
@@ -23,17 +23,19 @@ A loudness meter for the `Web Audio API`, based on the [ITU-R BS.1770-5](https:/
|
|
|
23
23
|
Import directly in your code:
|
|
24
24
|
|
|
25
25
|
```javascript
|
|
26
|
-
const module =
|
|
27
|
-
audioContext.audioWorklet.addModule(module);
|
|
26
|
+
const module = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
|
|
27
|
+
await audioContext.audioWorklet.addModule(module);
|
|
28
|
+
const worklet = new AudioWorkletNode(audioContext, "loudness-processor");
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
### Download
|
|
31
32
|
|
|
32
|
-
1. Download the pre-built file: [loudness.worklet.js](https://lcweden.github.io/loudness-
|
|
33
|
+
1. Download the pre-built file: [loudness.worklet.js](https://lcweden.github.io/loudness-worklet/loudness.worklet.js).
|
|
33
34
|
2. Place `loudness.worklet.js` in your project directory (e.g., `/public/`).
|
|
34
35
|
|
|
35
36
|
```javascript
|
|
36
|
-
audioContext.audioWorklet.addModule("loudness.worklet.js");
|
|
37
|
+
await audioContext.audioWorklet.addModule("loudness.worklet.js");
|
|
38
|
+
const worklet = new AudioWorkletNode(audioContext, "loudness-processor");
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
### NPM
|
|
@@ -49,23 +51,61 @@ Use helper functions to create and load the worklet:
|
|
|
49
51
|
```javascript
|
|
50
52
|
import { createLoudnessWorklet, LoudnessWorkletNode } from "loudness-worklet";
|
|
51
53
|
|
|
52
|
-
const worklet = await createLoudnessWorklet(audioContext
|
|
53
|
-
processorOptions: {
|
|
54
|
-
interval: 0.1,
|
|
55
|
-
capacity: 600
|
|
56
|
-
}
|
|
57
|
-
});
|
|
54
|
+
const worklet = await createLoudnessWorklet(audioContext);
|
|
58
55
|
|
|
59
56
|
// or
|
|
60
57
|
|
|
61
58
|
await LoudnessWorkletNode.loadModule(audioContext);
|
|
59
|
+
const worklet = new LoudnessWorkletNode(audioContext);
|
|
60
|
+
```
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
## Concepts
|
|
63
|
+
|
|
64
|
+
### Contexts
|
|
65
|
+
|
|
66
|
+
Provide the execution environment for audio processing.
|
|
67
|
+
|
|
68
|
+
#### AudioContext
|
|
69
|
+
|
|
70
|
+
`AudioContext` is used for real-time audio processing, such as live audio input from a microphone or media stream.
|
|
71
|
+
|
|
72
|
+
#### OfflineAudioContext
|
|
73
|
+
|
|
74
|
+
`OfflineAudioContext` is used for processing audio data offline, allowing for rendering and analysis without requiring real-time playback.
|
|
75
|
+
|
|
76
|
+
### Nodes
|
|
77
|
+
|
|
78
|
+
Nodes are the building blocks of an audio graph, representing audio sources, processing modules, and destinations. The following nodes are commonly used as a source input:
|
|
79
|
+
|
|
80
|
+
#### AudioBufferSourceNode
|
|
81
|
+
|
|
82
|
+
`AudioBufferSourceNode` is used to play audio data stored in an `AudioBuffer`, typically for pre-recorded audio files.
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
const audioContext = new AudioContext();
|
|
86
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
87
|
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
88
|
+
const bufferSource = new AudioBufferSourceNode(audioContext, { buffer: audioBuffer });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### MediaStreamAudioSourceNode
|
|
92
|
+
|
|
93
|
+
`MediaStreamAudioSourceNode` is used to play audio from a `MediaStream`, such as a live microphone input or a video element.
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const audioContext = new AudioContext();
|
|
97
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
98
|
+
const mediaStreamSource = new MediaStreamAudioSourceNode(audioContext, { mediaStream });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### MediaElementAudioSourceNode
|
|
102
|
+
|
|
103
|
+
`MediaElementAudioSourceNode` is used to play audio from an HTML `<audio>` or `<video>` element.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const audioContext = new AudioContext();
|
|
107
|
+
const mediaElement = document.querySelector("audio");
|
|
108
|
+
const elementSource = new MediaElementAudioSourceNode(audioContext, { mediaElement });
|
|
69
109
|
```
|
|
70
110
|
|
|
71
111
|
## Quick Start
|
|
@@ -75,13 +115,13 @@ const worklet = new LoudnessWorkletNode(audioContext, {
|
|
|
75
115
|
This example shows the easiest way to get started with the Loudness Audio Worklet Processor.
|
|
76
116
|
|
|
77
117
|
```html
|
|
78
|
-
<!
|
|
118
|
+
<!DOCTYPE html>
|
|
79
119
|
<html>
|
|
80
120
|
<body>
|
|
81
121
|
<button>Share Screen</button>
|
|
82
122
|
<pre></pre>
|
|
83
123
|
<script>
|
|
84
|
-
const
|
|
124
|
+
const module = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
|
|
85
125
|
const button = document.querySelector("button");
|
|
86
126
|
const pre = document.querySelector("pre");
|
|
87
127
|
|
|
@@ -91,15 +131,15 @@ This example shows the easiest way to get started with the Loudness Audio Workle
|
|
|
91
131
|
const context = new AudioContext();
|
|
92
132
|
|
|
93
133
|
// Load the loudness worklet processor
|
|
94
|
-
await context.audioWorklet.addModule(
|
|
134
|
+
await context.audioWorklet.addModule(module);
|
|
95
135
|
|
|
96
136
|
// Create the audio node from the stream
|
|
97
137
|
const source = new MediaStreamAudioSourceNode(context, { mediaStream });
|
|
98
138
|
// Create the loudness worklet node
|
|
99
139
|
const worklet = new AudioWorkletNode(context, "loudness-processor", {
|
|
100
140
|
processorOptions: {
|
|
101
|
-
interval: 0.1,
|
|
102
|
-
capacity: 600 //
|
|
141
|
+
interval: 0.1, // every 0.1s a message will be sent
|
|
142
|
+
capacity: 600 // 1 minute of history can be stored
|
|
103
143
|
}
|
|
104
144
|
});
|
|
105
145
|
|
|
@@ -117,65 +157,53 @@ This example shows the easiest way to get started with the Loudness Audio Workle
|
|
|
117
157
|
|
|
118
158
|
### File-based measurement
|
|
119
159
|
|
|
120
|
-
|
|
160
|
+
You can measure the loudness of audio files using `OfflineAudioContext`.
|
|
121
161
|
|
|
122
162
|
```javascript
|
|
123
|
-
|
|
124
|
-
const audioBuffer = await new AudioContext().decodeAudioData(arrayBuffer);
|
|
163
|
+
import { LoudnessWorkletNode } from "loudness-worklet";
|
|
125
164
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// capacity: 600
|
|
142
|
-
// }
|
|
143
|
-
// });
|
|
144
|
-
|
|
145
|
-
worklet.port.onmessage = (event) => {
|
|
146
|
-
console.log("Loudness Data:", event.data);
|
|
147
|
-
};
|
|
165
|
+
const input = document.querySelector("input");
|
|
166
|
+
|
|
167
|
+
input.addEventListener("change", async (event) => {
|
|
168
|
+
const file = event.target.files[0];
|
|
169
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
170
|
+
const audioBuffer = await new AudioContext().decodeAudioData(arrayBuffer);
|
|
171
|
+
const { numberOfChannels, length, sampleRate } = audioBuffer;
|
|
172
|
+
const context = new OfflineAudioContext(numberOfChannels, length, sampleRate);
|
|
173
|
+
|
|
174
|
+
await LoudnessWorkletNode.loadModule(context);
|
|
175
|
+
|
|
176
|
+
const source = new AudioBufferSourceNode(context, { buffer: audioBuffer });
|
|
177
|
+
const worklet = new LoudnessWorkletNode(context);
|
|
178
|
+
|
|
179
|
+
worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);
|
|
148
180
|
|
|
149
|
-
source.connect(worklet).connect(context.destination);
|
|
150
|
-
source.start();
|
|
181
|
+
source.connect(worklet).connect(context.destination);
|
|
182
|
+
source.start();
|
|
151
183
|
|
|
152
|
-
context.startRendering();
|
|
184
|
+
await context.startRendering();
|
|
185
|
+
});
|
|
153
186
|
```
|
|
154
187
|
|
|
155
188
|
### Live-based measurement
|
|
156
189
|
|
|
157
|
-
Supports
|
|
190
|
+
Supports all kinds of audio input.
|
|
158
191
|
|
|
159
192
|
```javascript
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
await context.audioWorklet.addModule("loudness.worklet.js");
|
|
193
|
+
import { createLoudnessWorklet } from "loudness-worklet";
|
|
163
194
|
|
|
164
|
-
const
|
|
165
|
-
const {
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
processorOptions: {
|
|
170
|
-
capacity: 600 // Seconds of history to keep, prevent memory overflow
|
|
171
|
-
}
|
|
195
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
196
|
+
const context = new AudioContext({ sampleRate: 48000 });
|
|
197
|
+
const source = context.createMediaStreamSource(mediaStream);
|
|
198
|
+
const worklet = await createLoudnessWorklet(context, {
|
|
199
|
+
processorOptions: { interval: 1, capacity: 600 }
|
|
172
200
|
});
|
|
173
201
|
|
|
174
|
-
worklet.port.onmessage = (event) =>
|
|
175
|
-
|
|
176
|
-
};
|
|
202
|
+
worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);
|
|
203
|
+
source.connect(worklet);
|
|
177
204
|
|
|
178
|
-
|
|
205
|
+
// worklet.connect(context.destination);
|
|
206
|
+
// Optionally connect to destination for monitoring (echo)
|
|
179
207
|
```
|
|
180
208
|
|
|
181
209
|
## API
|
|
@@ -196,6 +224,8 @@ The `AudioWorkletNode` constructor accepts the following options:
|
|
|
196
224
|
|
|
197
225
|
#### Example
|
|
198
226
|
|
|
227
|
+
Most of the time, you only need to set `processorOptions`.
|
|
228
|
+
|
|
199
229
|
```javascript
|
|
200
230
|
const { numberOfChannels, length, sampleRate } = audioBuffer;
|
|
201
231
|
const worklet = new AudioWorkletNode(context, "loudness-processor", {
|
|
@@ -214,20 +244,20 @@ const worklet = new AudioWorkletNode(context, "loudness-processor", {
|
|
|
214
244
|
Measurement results are sent back to the main thread via `port.onmessage` with the following format:
|
|
215
245
|
|
|
216
246
|
```typescript
|
|
217
|
-
type
|
|
247
|
+
type LoudnessMeasurements = {
|
|
248
|
+
momentaryLoudness: number;
|
|
249
|
+
shortTermLoudness: number;
|
|
250
|
+
integratedLoudness: number;
|
|
251
|
+
maximumMomentaryLoudness: number;
|
|
252
|
+
maximumShortTermLoudness: number;
|
|
253
|
+
maximumTruePeakLevel: number;
|
|
254
|
+
loudnessRange: number;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
type LoudnessSnapshot = {
|
|
218
258
|
currentFrame: number;
|
|
219
259
|
currentTime: number;
|
|
220
|
-
|
|
221
|
-
{
|
|
222
|
-
momentaryLoudness: number;
|
|
223
|
-
shortTermLoudness: number;
|
|
224
|
-
integratedLoudness: number;
|
|
225
|
-
maximumMomentaryLoudness: number;
|
|
226
|
-
maximumShortTermLoudness: number;
|
|
227
|
-
maximumTruePeakLevel: number;
|
|
228
|
-
loudnessRange: number;
|
|
229
|
-
}
|
|
230
|
-
];
|
|
260
|
+
currentMeasurements: LoudnessMeasurements[];
|
|
231
261
|
};
|
|
232
262
|
```
|
|
233
263
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loudness-worklet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A lightweight and efficient AudioWorklet for real-time loudness measurement in the browser, compliant with the ITU-R BS.1770-5 standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-audio",
|
|
@@ -18,55 +18,39 @@
|
|
|
18
18
|
"lufs",
|
|
19
19
|
"lra",
|
|
20
20
|
"javascript",
|
|
21
|
-
"typescript"
|
|
22
|
-
"lightweight"
|
|
21
|
+
"typescript"
|
|
23
22
|
],
|
|
24
23
|
"type": "module",
|
|
25
|
-
"
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
24
|
+
"main": "./packages/lib/dist/index.js",
|
|
25
|
+
"types": "./packages/lib/dist/index.d.ts",
|
|
27
26
|
"exports": {
|
|
28
27
|
".": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
28
|
+
"types": "./packages/lib/dist/index.d.ts",
|
|
29
|
+
"default": "./packages/lib/dist/index.js"
|
|
31
30
|
}
|
|
32
31
|
},
|
|
33
32
|
"files": [
|
|
34
|
-
"dist"
|
|
33
|
+
"packages/lib/dist"
|
|
34
|
+
],
|
|
35
|
+
"workspaces": [
|
|
36
|
+
"packages/*"
|
|
35
37
|
],
|
|
36
38
|
"license": "MIT",
|
|
37
39
|
"author": "lcweden",
|
|
38
|
-
"homepage": "https://lcweden.github.io/loudness-
|
|
40
|
+
"homepage": "https://lcweden.github.io/loudness-worklet/",
|
|
39
41
|
"repository": {
|
|
40
42
|
"type": "git",
|
|
41
|
-
"url": "https://github.com/lcweden/loudness-
|
|
43
|
+
"url": "https://github.com/lcweden/loudness-worklet.git"
|
|
42
44
|
},
|
|
43
45
|
"bugs": {
|
|
44
|
-
"url": "https://github.com/lcweden/loudness-
|
|
46
|
+
"url": "https://github.com/lcweden/loudness-worklet/issues"
|
|
45
47
|
},
|
|
46
48
|
"scripts": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"test": "vitest",
|
|
50
|
-
"build": "vite build --mode static && vite build --mode demo",
|
|
51
|
-
"lib": "vite build --mode static && vite build --mode lib && tsc -p tsconfig.lib.json",
|
|
52
|
-
"prepare": "npm run lib",
|
|
53
|
-
"preview": "vite preview --mode demo",
|
|
54
|
-
"format": "prettier --write ."
|
|
49
|
+
"format": "biome format --write",
|
|
50
|
+
"lint": "biome lint --write"
|
|
55
51
|
},
|
|
56
52
|
"devDependencies": {
|
|
57
|
-
"@
|
|
58
|
-
"@
|
|
59
|
-
"@types/audioworklet": "^0.0.85",
|
|
60
|
-
"@types/node": "^22.14.1",
|
|
61
|
-
"daisyui": "^5.0.28",
|
|
62
|
-
"echarts": "^6.0.0",
|
|
63
|
-
"prettier": "^3.5.3",
|
|
64
|
-
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
65
|
-
"solid-js": "^1.9.5",
|
|
66
|
-
"tailwindcss": "^4.1.4",
|
|
67
|
-
"typescript": "~5.7.2",
|
|
68
|
-
"vite": "^6.3.1",
|
|
69
|
-
"vite-plugin-solid": "^2.11.6",
|
|
70
|
-
"vitest": "^3.2.2"
|
|
53
|
+
"@biomejs/biome": "2.3.8",
|
|
54
|
+
"@types/node": "^24.10.1"
|
|
71
55
|
}
|
|
72
56
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare function createLoudnessWorklet(context: BaseAudioContext, options?: LoudnessWorkletProcessorOptions): Promise<AudioWorkletNode>;
|
|
2
|
+
|
|
3
|
+
export declare type LoudnessMeasurements = {
|
|
4
|
+
momentaryLoudness: number;
|
|
5
|
+
shortTermLoudness: number;
|
|
6
|
+
integratedLoudness: number;
|
|
7
|
+
maximumMomentaryLoudness: number;
|
|
8
|
+
maximumShortTermLoudness: number;
|
|
9
|
+
maximumTruePeakLevel: number;
|
|
10
|
+
loudnessRange: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export declare type LoudnessSnapshot = {
|
|
14
|
+
currentFrame: number;
|
|
15
|
+
currentTime: number;
|
|
16
|
+
currentMeasurements: LoudnessMeasurements[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export declare class LoudnessWorkletNode extends AudioWorkletNode {
|
|
20
|
+
constructor(context: BaseAudioContext, options?: LoudnessWorkletProcessorOptions);
|
|
21
|
+
static loadModule(context: BaseAudioContext): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export declare interface LoudnessWorkletProcessorOptions {
|
|
25
|
+
numberOfInputs?: AudioWorkletNodeOptions["numberOfInputs"];
|
|
26
|
+
numberOfOutputs?: AudioWorkletNodeOptions["numberOfOutputs"];
|
|
27
|
+
outputChannelCount?: AudioWorkletNodeOptions["outputChannelCount"];
|
|
28
|
+
processorOptions?: {
|
|
29
|
+
interval?: number;
|
|
30
|
+
capacity?: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { }
|
|
@@ -1,142 +1,10 @@
|
|
|
1
|
-
const i =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @author lcweden
|
|
7
|
-
* @license MIT
|
|
8
|
-
* @see https://github.com/lcweden/loudness-audio-worklet-processor.git
|
|
9
|
-
* @date 2025-10-07T13:01:56.024Z
|
|
10
|
-
*/\r
|
|
11
|
-
\r
|
|
12
|
-
class O {
|
|
13
|
-
#t = new Float32Array(2);
|
|
14
|
-
#e = new Float32Array(3);
|
|
15
|
-
#s = new Float32Array(2);
|
|
16
|
-
#r = new Float32Array(2);
|
|
17
|
-
/**
|
|
18
|
-
* Creates a new BiquadraticFilter with given coefficients.
|
|
19
|
-
* @param { number[] } a - Feedback coefficients [a1, a2]
|
|
20
|
-
* @param { number[] } b - Feedforward coefficients [b0, b1, b2]
|
|
21
|
-
*/
|
|
22
|
-
constructor(e, r) {
|
|
23
|
-
this.reset(), this.set(e, r);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Processes a single input sample and returns the filtered output.
|
|
27
|
-
* @param { number } input - The input sample.
|
|
28
|
-
* @returns { number } - The filtered output sample.
|
|
29
|
-
*/
|
|
30
|
-
process(e) {
|
|
31
|
-
const r = this.#e[0] * e + this.#e[1] * this.#s[0] + this.#e[2] * this.#s[1] - this.#t[0] * this.#r[0] - this.#t[1] * this.#r[1];
|
|
32
|
-
return this.#s[1] = this.#s[0], this.#s[0] = e, this.#r[1] = this.#r[0], this.#r[0] = r, r;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Sets new filter coefficients.
|
|
36
|
-
* @param { number[] } a - Feedback coefficients [a1, a2]
|
|
37
|
-
* @param { number[] } b - Feedforward coefficients [b0, b1, b2]
|
|
38
|
-
* @returns { void }
|
|
39
|
-
*/
|
|
40
|
-
set(e, r) {
|
|
41
|
-
this.#t.set((e.length = 2, e)), this.#e.set((r.length = 3, r));
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Resets the filter state.
|
|
45
|
-
* @returns { void }
|
|
46
|
-
*/
|
|
47
|
-
reset() {
|
|
48
|
-
this.#s.fill(0), this.#r.fill(0);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
class d {
|
|
52
|
-
#t;
|
|
53
|
-
#e;
|
|
54
|
-
#s = 0;
|
|
55
|
-
#r = 0;
|
|
56
|
-
#n = 0;
|
|
57
|
-
/**
|
|
58
|
-
* Creates a new CircularBuffer with given capacity.
|
|
59
|
-
* @param { number } capacity - The maximum number of items the buffer can hold.
|
|
60
|
-
*/
|
|
61
|
-
constructor(e) {
|
|
62
|
-
this.#e = e || 0, this.#t = new Array(e);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Adds an item to the buffer.
|
|
66
|
-
* @param item - The item to add to the buffer.
|
|
67
|
-
* @returns { void }
|
|
68
|
-
*/
|
|
69
|
-
push(e) {
|
|
70
|
-
this.#t[this.#r] = e, this.isFull() ? this.#s = (this.#s + 1) % this.#e : this.#n++, this.#r = (this.#r + 1) % this.#e;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Removes and returns the oldest item from the buffer.
|
|
74
|
-
* @returns { T | undefined }
|
|
75
|
-
*/
|
|
76
|
-
pop() {
|
|
77
|
-
if (this.isEmpty())
|
|
78
|
-
return;
|
|
79
|
-
const e = this.#t[this.#s];
|
|
80
|
-
return this.#s = (this.#s + 1) % this.#e, this.#n--, e;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Returns the oldest item from the buffer without removing it.
|
|
84
|
-
* @returns { T | undefined }
|
|
85
|
-
*/
|
|
86
|
-
peek() {
|
|
87
|
-
if (!this.isEmpty())
|
|
88
|
-
return this.#t[this.#s];
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Returns a slice of the buffer contents.
|
|
92
|
-
* @param { number } start - The starting index of the slice (inclusive).
|
|
93
|
-
* @param { number } end - The ending index of the slice (exclusive).
|
|
94
|
-
* @returns { T[] }
|
|
95
|
-
*/
|
|
96
|
-
slice(e = 0, r = this.#n) {
|
|
97
|
-
if (e < 0 && (e = 0), r > this.#n && (r = this.#n), e >= r)
|
|
98
|
-
return [];
|
|
99
|
-
const n = [];
|
|
100
|
-
for (let s = e; s < r; s++) {
|
|
101
|
-
const u = (this.#s + s) % this.#e;
|
|
102
|
-
n.push(this.#t[u]);
|
|
103
|
-
}
|
|
104
|
-
return n;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Checks if the buffer is empty.
|
|
108
|
-
* @returns { boolean }
|
|
109
|
-
*/
|
|
110
|
-
isEmpty() {
|
|
111
|
-
return this.#n === 0;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Checks if the buffer is full.
|
|
115
|
-
* @returns { boolean }
|
|
116
|
-
*/
|
|
117
|
-
isFull() {
|
|
118
|
-
return this.#n === this.#e;
|
|
119
|
-
}
|
|
120
|
-
/** @type { number } */
|
|
121
|
-
get length() {
|
|
122
|
-
return this.#n;
|
|
123
|
-
}
|
|
124
|
-
/** @type { number } */
|
|
125
|
-
get capacity() {
|
|
126
|
-
return this.#e;
|
|
127
|
-
}
|
|
128
|
-
/** @type { IterableIterator<T> } */
|
|
129
|
-
*[Symbol.iterator]() {
|
|
130
|
-
for (let e = 0; e < this.#n; e++) {
|
|
131
|
-
const r = (this.#s + e) % this.#e;
|
|
132
|
-
yield this.#t[r];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const L = {
|
|
137
|
-
highshelf: { a: [-1.69065929318241, 0.73248077421585], b: [1.53512485958697, -2.69169618940638, 1.19839281085285] },
|
|
1
|
+
const i = `const d = {
|
|
2
|
+
highshelf: {
|
|
3
|
+
a: [-1.69065929318241, 0.73248077421585],
|
|
4
|
+
b: [1.53512485958697, -2.69169618940638, 1.19839281085285]
|
|
5
|
+
},
|
|
138
6
|
highpass: { a: [-1.99004745483398, 0.99007225036621], b: [1, -2, 1] }
|
|
139
|
-
},
|
|
7
|
+
}, L = {
|
|
140
8
|
lowpass: {
|
|
141
9
|
phase0: [
|
|
142
10
|
0.001708984375,
|
|
@@ -195,12 +63,32 @@ const L = {
|
|
|
195
63
|
0.001708984375
|
|
196
64
|
]
|
|
197
65
|
}
|
|
198
|
-
},
|
|
66
|
+
}, O = {
|
|
199
67
|
1: { mono: 1 },
|
|
200
68
|
2: { L: 1, R: 1 },
|
|
201
69
|
6: { L: 1, R: 1, C: 1, LFE: 0, Ls: 1.41, Rs: 1.41 },
|
|
202
|
-
8: {
|
|
203
|
-
|
|
70
|
+
8: {
|
|
71
|
+
L: 1,
|
|
72
|
+
R: 1,
|
|
73
|
+
C: 1,
|
|
74
|
+
LFE: 0,
|
|
75
|
+
Lss: 1.41,
|
|
76
|
+
Rss: 1.41,
|
|
77
|
+
Lrs: 1,
|
|
78
|
+
Rrs: 1
|
|
79
|
+
},
|
|
80
|
+
10: {
|
|
81
|
+
L: 1,
|
|
82
|
+
R: 1,
|
|
83
|
+
C: 1,
|
|
84
|
+
LFE: 0,
|
|
85
|
+
Ls: 1.41,
|
|
86
|
+
Rs: 1.41,
|
|
87
|
+
Tfl: 1,
|
|
88
|
+
Tfr: 1,
|
|
89
|
+
Tbl: 1,
|
|
90
|
+
Tbr: 1
|
|
91
|
+
},
|
|
204
92
|
12: {
|
|
205
93
|
L: 1,
|
|
206
94
|
R: 1,
|
|
@@ -241,8 +129,47 @@ const L = {
|
|
|
241
129
|
BtFL: 1,
|
|
242
130
|
BtFR: 1
|
|
243
131
|
}
|
|
244
|
-
}, U = 0.4,
|
|
245
|
-
class
|
|
132
|
+
}, U = 0.4, G = 0.1, Y = 3, B = 0.1, j = 0.1, q = 0.95, C = 12.04, z = -70, K = -10, J = -70, Q = -20;
|
|
133
|
+
class x {
|
|
134
|
+
#t = new Float32Array(2);
|
|
135
|
+
#e = new Float32Array(3);
|
|
136
|
+
#s = new Float32Array(2);
|
|
137
|
+
#r = new Float32Array(2);
|
|
138
|
+
/**
|
|
139
|
+
* Creates a new BiquadraticFilter with given coefficients.
|
|
140
|
+
* @param { number[] } a - Feedback coefficients [a1, a2]
|
|
141
|
+
* @param { number[] } b - Feedforward coefficients [b0, b1, b2]
|
|
142
|
+
*/
|
|
143
|
+
constructor(e, r) {
|
|
144
|
+
this.reset(), this.set(e, r);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Processes a single input sample and returns the filtered output.
|
|
148
|
+
* @param { number } input - The input sample.
|
|
149
|
+
* @returns { number } - The filtered output sample.
|
|
150
|
+
*/
|
|
151
|
+
process(e) {
|
|
152
|
+
const r = this.#e[0] * e + this.#e[1] * this.#s[0] + this.#e[2] * this.#s[1] - this.#t[0] * this.#r[0] - this.#t[1] * this.#r[1];
|
|
153
|
+
return this.#s[1] = this.#s[0], this.#s[0] = e, this.#r[1] = this.#r[0], this.#r[0] = r, r;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sets new filter coefficients.
|
|
157
|
+
* @param { number[] } a - Feedback coefficients [a1, a2]
|
|
158
|
+
* @param { number[] } b - Feedforward coefficients [b0, b1, b2]
|
|
159
|
+
* @returns { void }
|
|
160
|
+
*/
|
|
161
|
+
set(e, r) {
|
|
162
|
+
e.length = 2, this.#t.set(e), r.length = 3, this.#e.set(r);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resets the filter state.
|
|
166
|
+
* @returns { void }
|
|
167
|
+
*/
|
|
168
|
+
reset() {
|
|
169
|
+
this.#s.fill(0), this.#r.fill(0);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
class R {
|
|
246
173
|
#t;
|
|
247
174
|
#e;
|
|
248
175
|
/**
|
|
@@ -272,11 +199,96 @@ class F {
|
|
|
272
199
|
this.#e.fill(0);
|
|
273
200
|
}
|
|
274
201
|
}
|
|
202
|
+
class F {
|
|
203
|
+
#t;
|
|
204
|
+
#e;
|
|
205
|
+
#s;
|
|
206
|
+
#r;
|
|
207
|
+
#n;
|
|
208
|
+
/**
|
|
209
|
+
* Creates a new CircularBuffer with given capacity.
|
|
210
|
+
* @param { number } capacity - The maximum number of items the buffer can hold.
|
|
211
|
+
*/
|
|
212
|
+
constructor(e) {
|
|
213
|
+
this.#e = e || 0, this.#t = new Array(e), this.#s = 0, this.#r = 0, this.#n = 0;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Adds an item to the buffer.
|
|
217
|
+
* @param item - The item to add to the buffer.
|
|
218
|
+
* @returns { void }
|
|
219
|
+
*/
|
|
220
|
+
push(e) {
|
|
221
|
+
this.#t[this.#r] = e, this.isFull() ? this.#s = (this.#s + 1) % this.#e : this.#n++, this.#r = (this.#r + 1) % this.#e;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Removes and returns the oldest item from the buffer.
|
|
225
|
+
* @returns { T | undefined }
|
|
226
|
+
*/
|
|
227
|
+
pop() {
|
|
228
|
+
if (this.isEmpty())
|
|
229
|
+
return;
|
|
230
|
+
const e = this.#t[this.#s];
|
|
231
|
+
return this.#t[this.#s] = void 0, this.#s = (this.#s + 1) % this.#e, this.#n--, e;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Returns the oldest item from the buffer without removing it.
|
|
235
|
+
* @returns { T | undefined }
|
|
236
|
+
*/
|
|
237
|
+
peek() {
|
|
238
|
+
if (!this.isEmpty())
|
|
239
|
+
return this.#t[this.#s];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Returns a slice of the buffer contents.
|
|
243
|
+
* @param { number } start - The starting index of the slice (inclusive).
|
|
244
|
+
* @param { number } end - The ending index of the slice (exclusive).
|
|
245
|
+
* @returns { T[] }
|
|
246
|
+
*/
|
|
247
|
+
slice(e, r) {
|
|
248
|
+
if (e >= r)
|
|
249
|
+
return [];
|
|
250
|
+
const n = [];
|
|
251
|
+
for (let s = Math.max(0, e); s < Math.min(this.#n, r); s++) {
|
|
252
|
+
const u = (this.#s + s) % this.#e;
|
|
253
|
+
n.push(this.#t[u]);
|
|
254
|
+
}
|
|
255
|
+
return n;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Checks if the buffer is empty.
|
|
259
|
+
* @returns { boolean }
|
|
260
|
+
*/
|
|
261
|
+
isEmpty() {
|
|
262
|
+
return this.#n === 0;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Checks if the buffer is full.
|
|
266
|
+
* @returns { boolean }
|
|
267
|
+
*/
|
|
268
|
+
isFull() {
|
|
269
|
+
return this.#n === this.#e;
|
|
270
|
+
}
|
|
271
|
+
/** @type { number } */
|
|
272
|
+
get length() {
|
|
273
|
+
return this.#n;
|
|
274
|
+
}
|
|
275
|
+
/** @type { number } */
|
|
276
|
+
get capacity() {
|
|
277
|
+
return this.#e;
|
|
278
|
+
}
|
|
279
|
+
/** @type { IterableIterator<T> } */
|
|
280
|
+
*[Symbol.iterator]() {
|
|
281
|
+
for (let e = 0; e < this.#n; e++) {
|
|
282
|
+
const r = (this.#s + e) % this.#e;
|
|
283
|
+
yield this.#t[r];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
275
287
|
class X extends AudioWorkletProcessor {
|
|
276
288
|
capacity = null;
|
|
277
289
|
interval = null;
|
|
278
290
|
lastTime = 0;
|
|
279
|
-
|
|
291
|
+
measurements = [];
|
|
280
292
|
kWeightingFilters = [];
|
|
281
293
|
truePeakFilters = [];
|
|
282
294
|
momentaryEnergyBuffers = [];
|
|
@@ -295,7 +307,15 @@ class X extends AudioWorkletProcessor {
|
|
|
295
307
|
this.capacity = s ?? null, this.interval = u ?? null;
|
|
296
308
|
}
|
|
297
309
|
for (let s = 0; s < r; s++)
|
|
298
|
-
this.momentaryEnergyRunningSums[s] = 0, this.momentarySampleAccumulators[s] = 0, this.momentaryEnergyBuffers[s] = new
|
|
310
|
+
this.momentaryEnergyRunningSums[s] = 0, this.momentarySampleAccumulators[s] = 0, this.momentaryEnergyBuffers[s] = new F(
|
|
311
|
+
Math.round(sampleRate * U)
|
|
312
|
+
), this.momentaryLoudnessHistories[s] = this.capacity ? new F(
|
|
313
|
+
Math.ceil(this.capacity / G)
|
|
314
|
+
) : [], this.shortTermEnergyRunningSums[s] = 0, this.shortTermSampleAccumulators[s] = 0, this.shortTermEnergyBuffers[s] = new F(
|
|
315
|
+
Math.round(sampleRate * Y)
|
|
316
|
+
), this.shortTermLoudnessHistories[s] = this.capacity ? new F(
|
|
317
|
+
Math.ceil(this.capacity / B)
|
|
318
|
+
) : [], this.measurements[s] = {
|
|
299
319
|
momentaryLoudness: Number.NEGATIVE_INFINITY,
|
|
300
320
|
shortTermLoudness: Number.NEGATIVE_INFINITY,
|
|
301
321
|
integratedLoudness: Number.NEGATIVE_INFINITY,
|
|
@@ -309,42 +329,69 @@ class X extends AudioWorkletProcessor {
|
|
|
309
329
|
const n = e.length;
|
|
310
330
|
for (let s = 0; s < n; s++) {
|
|
311
331
|
if (!e[s].length) continue;
|
|
312
|
-
const u = e[s], c = u.length, A = u[0].length, H = Object.values(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
const u = e[s], c = u.length, A = u[0].length, H = Object.values(
|
|
333
|
+
O[c] || O[1]
|
|
334
|
+
), v = 10 ** (-C / 20);
|
|
335
|
+
(!this.kWeightingFilters[s] || this.kWeightingFilters[s].length !== c) && (this.kWeightingFilters[s] = Array.from(
|
|
336
|
+
{ length: c },
|
|
337
|
+
() => [
|
|
338
|
+
new x(
|
|
339
|
+
d.highshelf.a,
|
|
340
|
+
d.highshelf.b
|
|
341
|
+
),
|
|
342
|
+
new x(
|
|
343
|
+
d.highpass.a,
|
|
344
|
+
d.highpass.b
|
|
345
|
+
)
|
|
346
|
+
]
|
|
347
|
+
)), (!this.truePeakFilters[s] || this.truePeakFilters[s].length !== c) && (this.truePeakFilters[s] = Array.from(
|
|
348
|
+
{ length: c },
|
|
349
|
+
() => [
|
|
350
|
+
new R(
|
|
351
|
+
L.lowpass.phase0
|
|
352
|
+
),
|
|
353
|
+
new R(
|
|
354
|
+
L.lowpass.phase1
|
|
355
|
+
),
|
|
356
|
+
new R(
|
|
357
|
+
L.lowpass.phase2
|
|
358
|
+
),
|
|
359
|
+
new R(
|
|
360
|
+
L.lowpass.phase3
|
|
361
|
+
)
|
|
362
|
+
]
|
|
363
|
+
));
|
|
322
364
|
for (let o = 0; o < A; o++) {
|
|
323
365
|
let i = 0;
|
|
324
366
|
for (let t = 0; t < c; t++) {
|
|
325
|
-
const h = u[t][o], [y, T] = this.kWeightingFilters[s][t], m = y.process(h), f = T.process(m) ** 2,
|
|
326
|
-
i += f *
|
|
327
|
-
const P = u[t][o] *
|
|
367
|
+
const h = u[t][o], [y, T] = this.kWeightingFilters[s][t], m = y.process(h), f = T.process(m) ** 2, w = H[t] ?? 1;
|
|
368
|
+
i += f * w;
|
|
369
|
+
const P = u[t][o] * v, k = sampleRate >= 96e3 ? 2 : 4, b = [];
|
|
328
370
|
for (let N = 0; N < k; N++) {
|
|
329
371
|
const D = this.truePeakFilters[s][t][N];
|
|
330
372
|
b.push(Math.abs(D.process(P)));
|
|
331
373
|
}
|
|
332
|
-
const W = 20 * Math.log10(Math.max(...b)) +
|
|
333
|
-
this.
|
|
374
|
+
const W = 20 * Math.log10(Math.max(...b)) + C, V = this.measurements[s].maximumTruePeakLevel;
|
|
375
|
+
this.measurements[s].maximumTruePeakLevel = Math.max(
|
|
376
|
+
V,
|
|
377
|
+
W
|
|
378
|
+
);
|
|
334
379
|
}
|
|
335
|
-
const g = i,
|
|
380
|
+
const g = i, E = this.momentaryEnergyBuffers[s].peek() ?? 0, I = this.momentaryEnergyBuffers[s].isFull() ? E : 0;
|
|
336
381
|
this.momentaryEnergyRunningSums[s] += g - I, this.momentaryEnergyBuffers[s].push(g);
|
|
337
|
-
const
|
|
382
|
+
const p = this.shortTermEnergyBuffers[s].peek() ?? 0, l = this.shortTermEnergyBuffers[s].isFull() ? p : 0;
|
|
338
383
|
if (this.shortTermEnergyRunningSums[s] += g - l, this.shortTermEnergyBuffers[s].push(g), this.momentaryEnergyBuffers[s].isFull()) {
|
|
339
384
|
const t = this.momentaryEnergyRunningSums[s] / this.momentaryEnergyBuffers[s].capacity, h = this.#s(t);
|
|
340
|
-
this.
|
|
341
|
-
this.
|
|
385
|
+
this.measurements[s].momentaryLoudness = h, this.measurements[s].maximumMomentaryLoudness = Math.max(
|
|
386
|
+
this.measurements[s].maximumMomentaryLoudness,
|
|
342
387
|
h
|
|
343
388
|
);
|
|
344
389
|
}
|
|
345
390
|
}
|
|
346
391
|
this.momentarySampleAccumulators[s] += A;
|
|
347
|
-
const _ = Math.round(
|
|
392
|
+
const _ = Math.round(
|
|
393
|
+
sampleRate * G
|
|
394
|
+
);
|
|
348
395
|
for (; this.momentarySampleAccumulators[s] >= _; ) {
|
|
349
396
|
if (this.momentaryEnergyBuffers[s].isFull()) {
|
|
350
397
|
const o = this.momentaryEnergyRunningSums[s] / this.momentaryEnergyBuffers[s].capacity, i = this.#s(o);
|
|
@@ -353,44 +400,77 @@ class X extends AudioWorkletProcessor {
|
|
|
353
400
|
this.momentarySampleAccumulators[s] -= _;
|
|
354
401
|
}
|
|
355
402
|
this.shortTermSampleAccumulators[s] += A;
|
|
356
|
-
const M = Math.round(
|
|
403
|
+
const M = Math.round(
|
|
404
|
+
sampleRate * B
|
|
405
|
+
);
|
|
357
406
|
for (; this.shortTermSampleAccumulators[s] >= M; ) {
|
|
358
407
|
if (this.shortTermEnergyBuffers[s].isFull()) {
|
|
359
408
|
const o = this.shortTermEnergyRunningSums[s] / this.shortTermEnergyBuffers[s].capacity, i = this.#s(o);
|
|
360
|
-
this.
|
|
361
|
-
this.
|
|
409
|
+
this.measurements[s].shortTermLoudness = i, this.measurements[s].maximumShortTermLoudness = Math.max(
|
|
410
|
+
this.measurements[s].maximumShortTermLoudness,
|
|
362
411
|
i
|
|
363
412
|
), this.shortTermLoudnessHistories[s].push(i);
|
|
364
413
|
}
|
|
365
414
|
this.shortTermSampleAccumulators[s] -= M;
|
|
366
415
|
}
|
|
367
416
|
if (this.momentaryLoudnessHistories[s].length > 2) {
|
|
368
|
-
const o = Array.from(
|
|
369
|
-
|
|
370
|
-
);
|
|
417
|
+
const o = Array.from(
|
|
418
|
+
this.momentaryLoudnessHistories[s]
|
|
419
|
+
).filter((i) => i > z);
|
|
371
420
|
if (o.length > 2) {
|
|
372
|
-
const i = o.map(
|
|
421
|
+
const i = o.map(
|
|
422
|
+
this.#r
|
|
423
|
+
), E = i.reduce(
|
|
424
|
+
(t, h) => t + h,
|
|
425
|
+
0
|
|
426
|
+
) / i.length, p = this.#s(
|
|
427
|
+
E
|
|
428
|
+
) + K, l = o.filter(
|
|
429
|
+
(t) => t > p
|
|
430
|
+
);
|
|
373
431
|
if (l.length > 2) {
|
|
374
|
-
const t = l.map(
|
|
375
|
-
|
|
432
|
+
const t = l.map(
|
|
433
|
+
this.#r
|
|
434
|
+
), y = t.reduce(
|
|
435
|
+
(m, a) => m + a,
|
|
436
|
+
0
|
|
437
|
+
) / t.length, T = this.#s(
|
|
438
|
+
y
|
|
439
|
+
);
|
|
440
|
+
this.measurements[s].integratedLoudness = T;
|
|
376
441
|
}
|
|
377
442
|
}
|
|
378
443
|
}
|
|
379
444
|
if (this.shortTermLoudnessHistories[s].length > 2) {
|
|
380
|
-
const o = Array.from(
|
|
381
|
-
|
|
382
|
-
);
|
|
445
|
+
const o = Array.from(
|
|
446
|
+
this.shortTermLoudnessHistories[s]
|
|
447
|
+
).filter((i) => i > J);
|
|
383
448
|
if (o.length > 2) {
|
|
384
|
-
const i = o.map(
|
|
449
|
+
const i = o.map(
|
|
450
|
+
this.#r
|
|
451
|
+
), E = i.reduce(
|
|
452
|
+
(t, h) => t + h,
|
|
453
|
+
0
|
|
454
|
+
) / i.length, p = this.#s(
|
|
455
|
+
E
|
|
456
|
+
) + Q, l = o.filter(
|
|
457
|
+
(t) => t > p
|
|
458
|
+
);
|
|
385
459
|
if (l.length > 2) {
|
|
386
|
-
const t = l.toSorted(
|
|
460
|
+
const t = l.toSorted(
|
|
461
|
+
(m, a) => m - a
|
|
462
|
+
), [h, y] = [
|
|
387
463
|
j,
|
|
388
464
|
q
|
|
389
465
|
].map((m) => {
|
|
390
|
-
const a = Math.floor(
|
|
466
|
+
const a = Math.floor(
|
|
467
|
+
m * (t.length - 1)
|
|
468
|
+
), f = Math.ceil(
|
|
469
|
+
m * (t.length - 1)
|
|
470
|
+
);
|
|
391
471
|
return f === a ? t[a] : t[a] + (t[f] - t[a]) * (m * (t.length - 1) - a);
|
|
392
472
|
}), T = y - h;
|
|
393
|
-
this.
|
|
473
|
+
this.measurements[s].loudnessRange = T;
|
|
394
474
|
}
|
|
395
475
|
}
|
|
396
476
|
}
|
|
@@ -399,7 +479,11 @@ class X extends AudioWorkletProcessor {
|
|
|
399
479
|
}
|
|
400
480
|
#t() {
|
|
401
481
|
if (currentTime - this.lastTime >= Number(this.interval)) {
|
|
402
|
-
const e = {
|
|
482
|
+
const e = {
|
|
483
|
+
currentFrame,
|
|
484
|
+
currentTime,
|
|
485
|
+
currentMeasurements: this.measurements
|
|
486
|
+
};
|
|
403
487
|
this.port.postMessage(e), this.lastTime = currentTime;
|
|
404
488
|
}
|
|
405
489
|
}
|
|
@@ -412,12 +496,12 @@ class X extends AudioWorkletProcessor {
|
|
|
412
496
|
return -0.691 + 10 * Math.log10(Math.max(e, Number.EPSILON));
|
|
413
497
|
}
|
|
414
498
|
#r(e) {
|
|
415
|
-
return
|
|
499
|
+
return 10 ** ((e + 0.691) / 10);
|
|
416
500
|
}
|
|
417
501
|
}
|
|
418
502
|
registerProcessor("loudness-processor", X);
|
|
419
503
|
`, t = "loudness-processor";
|
|
420
|
-
class
|
|
504
|
+
class h extends AudioWorkletNode {
|
|
421
505
|
constructor(n, s) {
|
|
422
506
|
super(n, t, s);
|
|
423
507
|
}
|
|
@@ -425,7 +509,7 @@ class o extends AudioWorkletNode {
|
|
|
425
509
|
return r(n);
|
|
426
510
|
}
|
|
427
511
|
}
|
|
428
|
-
async function
|
|
512
|
+
async function a(e, n) {
|
|
429
513
|
return await r(e), new AudioWorkletNode(e, t, n);
|
|
430
514
|
}
|
|
431
515
|
async function r(e) {
|
|
@@ -437,6 +521,6 @@ async function r(e) {
|
|
|
437
521
|
}
|
|
438
522
|
}
|
|
439
523
|
export {
|
|
440
|
-
|
|
441
|
-
|
|
524
|
+
h as LoudnessWorkletNode,
|
|
525
|
+
a as createLoudnessWorklet
|
|
442
526
|
};
|
package/dist/index.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
interface LoudnessWorkletProcessorOptions {
|
|
2
|
-
numberOfInputs?: AudioWorkletNodeOptions["numberOfInputs"];
|
|
3
|
-
numberOfOutputs?: AudioWorkletNodeOptions["numberOfOutputs"];
|
|
4
|
-
outputChannelCount?: AudioWorkletNodeOptions["outputChannelCount"];
|
|
5
|
-
processorOptions?: {
|
|
6
|
-
interval?: number;
|
|
7
|
-
capacity?: number;
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
declare class LoudnessWorkletNode extends AudioWorkletNode {
|
|
11
|
-
constructor(context: BaseAudioContext, options?: LoudnessWorkletProcessorOptions);
|
|
12
|
-
static loadModule(context: BaseAudioContext): Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
declare function createLoudnessWorklet(context: BaseAudioContext, options?: LoudnessWorkletProcessorOptions): Promise<AudioWorkletNode>;
|
|
15
|
-
export { createLoudnessWorklet, LoudnessWorkletNode };
|