loudness-worklet 1.6.3 → 1.6.6

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 CHANGED
@@ -1,7 +1,8 @@
1
- # Loudness Audio Worklet Processor
1
+ # Loudness Worklet
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/loudness-worklet.svg)](https://www.npmjs.com/package/loudness-worklet)
4
4
  [![license](https://img.shields.io/github/license/lcweden/loudness-worklet.svg)](LICENSE)
5
+ [![demo](https://img.shields.io/badge/demo-Online-purple.svg)](https://lcweden.github.io/loudness-worklet/)
5
6
 
6
7
  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
8
 
@@ -9,10 +10,17 @@ A loudness meter for the `Web Audio API`, based on the [ITU-R BS.1770-5](https:/
9
10
 
10
11
  ## Features
11
12
 
12
- - **Loudness Measurement**: Compliant with the **ITU-R BS.1770-5** standard.
13
- - **Comprehensive Metrics**: Calculates Momentary, Short-term, and Integrated Loudness, as well as Loudness Range (LRA) and True-Peak levels.
14
- - **Flexible**: Works with live audio and pre-recorded files.
15
- - **Lightweight**: No external dependencies required.
13
+ - **Standard Compliant**: Strictly follows **ITU-R BS.1770-5** for accurate loudness measurement.
14
+ - **Comprehensive Metrics**: Calculates Momentary, Short-term, and Integrated Loudness, plus Loudness Range (LRA) and True-Peak levels.
15
+ - **Versatile Input**: Seamlessly supports both live audio streams ("Microphone/WebRTC") and offline file analysis.
16
+ - **Zero Dependencies**: Lightweight, pure AudioWorklet implementation requiring no external libraries.
17
+
18
+ ## Use Cases
19
+
20
+ - **Volume Normalization**: Dynamically analyze and harmonize volume levels across multi-source playlists directly in the browser, eliminating the need for server-side processing.
21
+ - **Pre-upload Validation**: Verify if audio files meet platform-specific loudness standards (e.g., -14 LUFS) locally before uploading.
22
+ - **Live Metering**: Build responsive, standard-compliant loudness meters for web-based recorders, conferencing tools, or broadcasting interfaces.
23
+ - **Web Audio Editors**: Integrate professional-grade loudness analysis into browser-based Digital Audio Workstations (DAWs).
16
24
 
17
25
  ## Installation
18
26
 
@@ -21,8 +29,8 @@ A loudness meter for the `Web Audio API`, based on the [ITU-R BS.1770-5](https:/
21
29
  Import directly in your code:
22
30
 
23
31
  ```javascript
24
- const module = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
25
- await audioContext.audioWorklet.addModule(module);
32
+ const moduleUrl = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
33
+ await audioContext.audioWorklet.addModule(moduleUrl);
26
34
  const worklet = new AudioWorkletNode(audioContext, "loudness-processor");
27
35
  ```
28
36
 
@@ -47,63 +55,18 @@ npm install loudness-worklet
47
55
  Use helper functions to create and load the worklet:
48
56
 
49
57
  ```javascript
50
- import { createLoudnessWorklet, LoudnessWorkletNode } from "loudness-worklet";
58
+ import { createLoudnessWorklet } from "loudness-worklet";
51
59
 
52
60
  const worklet = await createLoudnessWorklet(audioContext);
53
-
54
- // or
55
-
56
- await LoudnessWorkletNode.loadModule(audioContext);
57
- const worklet = new LoudnessWorkletNode(audioContext);
58
- ```
59
-
60
- ## Concepts
61
-
62
- ### Contexts
63
-
64
- Provide the execution environment for audio processing.
65
-
66
- #### AudioContext
67
-
68
- `AudioContext` is used for real-time audio processing, such as live audio input from a microphone or media stream.
69
-
70
- #### OfflineAudioContext
71
-
72
- `OfflineAudioContext` is used for processing audio data offline, allowing for rendering and analysis without requiring real-time playback.
73
-
74
- ### Nodes
75
-
76
- 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:
77
-
78
- #### AudioBufferSourceNode
79
-
80
- `AudioBufferSourceNode` is used to play audio data stored in an `AudioBuffer`, typically for pre-recorded audio files.
81
-
82
- ```javascript
83
- const audioContext = new AudioContext();
84
- const arrayBuffer = await file.arrayBuffer();
85
- const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
86
- const bufferSource = new AudioBufferSourceNode(audioContext, { buffer: audioBuffer });
87
61
  ```
88
62
 
89
- #### MediaStreamAudioSourceNode
90
-
91
- `MediaStreamAudioSourceNode` is used to play audio from a `MediaStream`, such as a live microphone input or a video element.
63
+ or
92
64
 
93
65
  ```javascript
94
- const audioContext = new AudioContext();
95
- const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
96
- const mediaStreamSource = new MediaStreamAudioSourceNode(audioContext, { mediaStream });
97
- ```
98
-
99
- #### MediaElementAudioSourceNode
100
-
101
- `MediaElementAudioSourceNode` is used to play audio from an HTML `<audio>` or `<video>` element.
66
+ import { LoudnessWorkletNode } from "loudness-worklet";
102
67
 
103
- ```javascript
104
- const audioContext = new AudioContext();
105
- const mediaElement = document.querySelector("audio");
106
- const elementSource = new MediaElementAudioSourceNode(audioContext, { mediaElement });
68
+ await LoudnessWorkletNode.loadModule(audioContext);
69
+ const worklet = new LoudnessWorkletNode(audioContext);
107
70
  ```
108
71
 
109
72
  ## Quick Start
@@ -119,24 +82,24 @@ This example shows the easiest way to get started with the Loudness Audio Workle
119
82
  <button>Share Screen</button>
120
83
  <pre></pre>
121
84
  <script>
122
- const module = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
85
+ const moduleUrl = "https://lcweden.github.io/loudness-worklet/loudness.worklet.js";
123
86
  const button = document.querySelector("button");
124
87
  const pre = document.querySelector("pre");
125
88
 
126
89
  button.onclick = async () => {
127
- // Get the screen stream with audio, for example a youtube tab
90
+ // Get the screen stream with audio, for example a YouTube tab
128
91
  const mediaStream = await navigator.mediaDevices.getDisplayMedia({ audio: true });
129
92
  const context = new AudioContext();
130
93
 
131
94
  // Load the loudness worklet processor
132
- await context.audioWorklet.addModule(module);
95
+ await context.audioWorklet.addModule(moduleUrl);
133
96
 
134
97
  // Create the audio node from the stream
135
98
  const source = new MediaStreamAudioSourceNode(context, { mediaStream });
136
99
  // Create the loudness worklet node
137
100
  const worklet = new AudioWorkletNode(context, "loudness-processor", {
138
101
  processorOptions: {
139
- interval: 0.1, // every 0.1s a message will be sent
102
+ interval: 0.02, // every 0.02s a message will be sent
140
103
  capacity: 600 // 1 minute of history can be stored
141
104
  }
142
105
  });
@@ -204,6 +167,55 @@ source.connect(worklet);
204
167
  // Optionally connect to destination for monitoring (echo)
205
168
  ```
206
169
 
170
+ ## Concepts
171
+
172
+ ### Contexts
173
+
174
+ Provide the execution environment for audio processing.
175
+
176
+ #### AudioContext
177
+
178
+ `AudioContext` is used for real-time audio processing, such as live audio input from a microphone or media stream.
179
+
180
+ #### OfflineAudioContext
181
+
182
+ `OfflineAudioContext` is used for processing audio data offline, allowing for rendering and analysis without requiring real-time playback.
183
+
184
+ ### Nodes
185
+
186
+ 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:
187
+
188
+ #### AudioBufferSourceNode
189
+
190
+ `AudioBufferSourceNode` is used to play audio data stored in an `AudioBuffer`, typically for pre-recorded audio files.
191
+
192
+ ```javascript
193
+ const audioContext = new AudioContext();
194
+ const arrayBuffer = await file.arrayBuffer();
195
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
196
+ const bufferSource = new AudioBufferSourceNode(audioContext, { buffer: audioBuffer });
197
+ ```
198
+
199
+ #### MediaStreamAudioSourceNode
200
+
201
+ `MediaStreamAudioSourceNode` is used to play audio from a `MediaStream`, such as a live microphone input or a video element.
202
+
203
+ ```javascript
204
+ const audioContext = new AudioContext();
205
+ const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
206
+ const mediaStreamSource = new MediaStreamAudioSourceNode(audioContext, { mediaStream });
207
+ ```
208
+
209
+ #### MediaElementAudioSourceNode
210
+
211
+ `MediaElementAudioSourceNode` is used to play audio from an HTML `<audio>` or `<video>` element.
212
+
213
+ ```javascript
214
+ const audioContext = new AudioContext();
215
+ const mediaElement = document.querySelector("audio");
216
+ const elementSource = new MediaElementAudioSourceNode(audioContext, { mediaElement });
217
+ ```
218
+
207
219
  ## API
208
220
 
209
221
  ### Options
@@ -214,11 +226,8 @@ The `AudioWorkletNode` constructor accepts the following options:
214
226
 
215
227
  | Option | Type | Required | Default | Description |
216
228
  | ------------------------- | ---------- | -------- | ------- | ---------------------------------------------------------------------------------------------------- |
217
- | numberOfInputs | `number` | `N` | `1` | Number of input channels. |
218
- | numberOfOutputs | `number` | `N` | `1` | Number of output channels. |
219
- | outputChannelCount | `number[]` | `N` | - | Determined at runtime automatically. |
220
- | processorOptions.interval | `number` | `N` | `null` | Message interval in seconds. |
221
- | processorOptions.capacity | `number` | `N` | `null` | Maximum seconds of history to keep. If set to `null`, the processor will not limit the history size. |
229
+ | processorOptions.interval | `number` | `No` | `0.02` | Message interval in seconds. |
230
+ | processorOptions.capacity | `number` | `No` | `0` | Maximum seconds of history to keep. If set to `0`, the processor will not limit the history size. |
222
231
 
223
232
  #### Example
224
233
 
@@ -227,19 +236,16 @@ Most of the time, you only need to set `processorOptions`.
227
236
  ```javascript
228
237
  const { numberOfChannels, length, sampleRate } = audioBuffer;
229
238
  const worklet = new AudioWorkletNode(context, "loudness-processor", {
230
- numberOfInputs: 1,
231
- numberOfOutputs: 1,
232
- outputChannelCount: [numberOfChannels], // Unnecessary
233
239
  processorOptions: {
234
240
  capacity: length / sampleRate,
235
- interval: 0.1
241
+ interval: 0.02
236
242
  }
237
243
  });
238
244
  ```
239
245
 
240
246
  ### Message Format
241
247
 
242
- Measurement results are sent back to the main thread via `port.onmessage` with the following format:
248
+ Measurement results are sent back to the main thread via `port.onmessage` as a `LoudnessSnapshot` object:
243
249
 
244
250
  ```typescript
245
251
  type LoudnessMeasurements = {
@@ -269,7 +275,7 @@ type LoudnessSnapshot = {
269
275
  | `maximumMomentaryLoudness` | `LUFS`/`LKFS` |
270
276
  | `maximumShortTermLoudness` | `LUFS`/`LKFS` |
271
277
  | `maximumTruePeakLevel` | `dBTP` |
272
- | `loudnessRange` | `LU` |
278
+ | `loudnessRange` | `LU` |
273
279
 
274
280
  ### Supported Channels
275
281
 
@@ -284,7 +290,7 @@ The following coefficients are used for the K-weighting filter:
284
290
 
285
291
  | | highshelf | highpass |
286
292
  | --- | ----------------- | ----------------- |
287
- | a1 | -1.69065929318241 | 1.99004745483398 |
293
+ | a1 | -1.69065929318241 | -1.99004745483398 |
288
294
  | a2 | 0.73248077421585 | 0.99007225036621 |
289
295
  | b0 | 1.53512485958697 | 1.0 |
290
296
  | b1 | -2.69169618940638 | -2.0 |
@@ -294,25 +300,24 @@ The following FIR filter coefficients are used for true-peak measurement:
294
300
 
295
301
  | Phase 0 | Phase 1 | Phase 2 | Phase 3 |
296
302
  | ---------------- | ---------------- | ---------------- | ---------------- |
297
- | 0.0017089843750 | 0.0291748046875 | 0.0189208984375 | 0.0083007812500 |
303
+ | 0.0017089843750 | -0.0291748046875 | -0.0189208984375 | -0.0083007812500 |
298
304
  | 0.0109863281250 | 0.0292968750000 | 0.0330810546875 | 0.0148925781250 |
299
- | 0.0196533203125 | 0.0517578125000 | 0.0582275390625 | 0.0266113281250 |
305
+ | -0.0196533203125 | -0.0517578125000 | -0.0582275390625 | -0.0266113281250 |
300
306
  | 0.0332031250000 | 0.0891113281250 | 0.1015625000000 | 0.0476074218750 |
301
- | 0.0594482421875 | 0.1665039062500 | 0.2003173828125 | 0.1022949218750 |
307
+ | -0.0594482421875 | -0.1665039062500 | -0.2003173828125 | -0.1022949218750 |
302
308
  | 0.1373291015625 | 0.4650878906250 | 0.7797851562500 | 0.9721679687500 |
303
309
  | 0.9721679687500 | 0.7797851562500 | 0.4650878906250 | 0.1373291015625 |
304
- | 0.1022949218750 | 0.2003173828125 | 0.1665039062500 | 0.0594482421875 |
310
+ | -0.1022949218750 | -0.2003173828125 | -0.1665039062500 | -0.0594482421875 |
305
311
  | 0.0476074218750 | 0.1015625000000 | 0.0891113281250 | 0.0332031250000 |
306
- | 0.0266113281250 | 0.0582275390625 | 0.0517578125000 | 0.0196533203125 |
312
+ | -0.0266113281250 | -0.0582275390625 | -0.0517578125000 | -0.0196533203125 |
307
313
  | 0.0148925781250 | 0.0330810546875 | 0.0292968750000 | 0.0109863281250 |
308
- | 0.0083007812500 | 0.0189208984375 | 0.0291748046875 | 0.0017089843750 |
314
+ | -0.0083007812500 | -0.0189208984375 | -0.0291748046875 | 0.0017089843750 |
309
315
 
310
316
  ## Validation
311
317
 
312
318
  ### ITU-R BS.2217
313
319
 
314
- The [ITU-R BS.2217](https://www.itu.int/pub/R-REP-BS.2217) test suite provides a table of compliance test files and related information for verifying that a meter
315
- meets the specifications within Recommendation [ITU-R BS.1770](https://www.itu.int/rec/R-REC-BS.1770).
320
+ Code correctness is verified against the official **[ITU-R BS.2217](https://www.itu.int/pub/R-REP-BS.2217)** compliance test suite, ensuring strict adherence to the **[ITU-R BS.1770](https://www.itu.int/rec/R-REC-BS.1770)** specification.
316
321
 
317
322
  | file | measurement | channels | |
318
323
  | ------------------------------------ | ----------- | -------- | ------------------ |
@@ -371,33 +376,33 @@ meets the specifications within Recommendation [ITU-R BS.1770](https://www.itu.i
371
376
 
372
377
  ### EBU TECH 3341 Minimum requirements test signals
373
378
 
374
- [EBU TECH 3341](https://tech.ebu.ch/publications/tech3341) defines minimum requirements and corresponding test signals for verifying momentary, short-term, and integrated loudness accuracy, gating behavior, and true-peak measurement.
379
+ Validated against **[EBU TECH 3341](https://tech.ebu.ch/publications/tech3341)** minimum requirements for loudness metering, including gating behavior, time scales, and true-peak accuracy.
375
380
 
376
381
  | file | expected response and accepted tolerances | |
377
382
  | ------------------------------------ | ----------------------------------------------------------- | ------------------ |
378
- | seq-3341-1-16bit | M, S, I = 23.0 ±0.1 LUFS | :white_check_mark: |
379
- | seq-3341-2-16bit | M, S, I = 33.0 ±0.1 LUFS | :white_check_mark: |
380
- | seq-3341-3-16bit-v02 | I = 23.0 ±0.1 LUFS | :white_check_mark: |
381
- | seq-3341-4-16bit-v02 | I = 23.0 ±0.1 LUFS | :white_check_mark: |
382
- | seq-3341-5-16bit-v02 | I = 23.0 ±0.1 LUFS | :white_check_mark: |
383
- | seq-3341-6-6channels-WAVEEX-16bit | I = 23.0 ±0.1 LUFS | :white_check_mark: |
384
- | seq-3341-7_seq-3342-5-24bit | I = 23.0 ±0.1 LUFS | :white_check_mark: |
385
- | seq-3341-2011-8_seq-3342-6-24bit-v02 | I = 23.0 ±0.1 LUFS | :white_check_mark: |
386
- | seq-3341-9-24bit | S = 23.0 ±0.1 LUFS, constant after 3 s | :white_check_mark: |
387
- | seq-3341-10-\*-24bit | Max S = 23.0 ±0.1 LUFS, for each segment | :white_check_mark: |
388
- | seq-3341-11-24bit | Max S = 38.0, 37.0, …, 19.0 ±0.1 LUFS, successive values | :white_check_mark: |
389
- | seq-3341-12-24bit | M = 23.0 ±0.1 LUFS, constant after 1 s | :white_check_mark: |
390
- | seq-3341-13-\*-24bit | Max M = 23.0 ±0.1 LUFS, for each segment | :white_check_mark: |
391
- | seq-3341-14-24bit | Max M = 38.0, …, 19.0 ±0.1 LUFS, successive values | :white_check_mark: |
392
- | seq-3341-15-24bit | Max true-peak = 6.0 +0.2/−0.4 dBTP | :white_check_mark: |
393
- | seq-3341-16-24bit | Max true-peak = 6.0 +0.2/−0.4 dBTP | :white_check_mark: |
394
- | seq-3341-17-24bit | Max true-peak = 6.0 +0.2/−0.4 dBTP | :white_check_mark: |
395
- | seq-3341-18-24bit | Max true-peak = 6.0 +0.2/−0.4 dBTP | :white_check_mark: |
396
- | seq-3341-19-24bit | Max true-peak = +3.0 +0.2/−0.4 dBTP | :white_check_mark: |
397
- | seq-3341-20-24bit | Max true-peak = 0.0 +0.2/−0.4 dBTP | :white_check_mark: |
398
- | seq-3341-21-24bit | Max true-peak = 0.0 +0.2/−0.4 dBTP | :white_check_mark: |
399
- | seq-3341-22-24bit | Max true-peak = 0.0 +0.2/−0.4 dBTP | :white_check_mark: |
400
- | seq-3341-23-24bit | Max true-peak = 0.0 +0.2/−0.4 dBTP | :white_check_mark: |
383
+ | seq-3341-1-16bit | M, S, I = -23.0 ±0.1 LUFS | :white_check_mark: |
384
+ | seq-3341-2-16bit | M, S, I = -33.0 ±0.1 LUFS | :white_check_mark: |
385
+ | seq-3341-3-16bit-v02 | I = -23.0 ±0.1 LUFS | :white_check_mark: |
386
+ | seq-3341-4-16bit-v02 | I = -23.0 ±0.1 LUFS | :white_check_mark: |
387
+ | seq-3341-5-16bit-v02 | I = -23.0 ±0.1 LUFS | :white_check_mark: |
388
+ | seq-3341-6-6channels-WAVEEX-16bit | I = -23.0 ±0.1 LUFS | :white_check_mark: |
389
+ | seq-3341-7_seq-3342-5-24bit | I = -23.0 ±0.1 LUFS | :white_check_mark: |
390
+ | seq-3341-2011-8_seq-3342-6-24bit-v02 | I = -23.0 ±0.1 LUFS | :white_check_mark: |
391
+ | seq-3341-9-24bit | S = -23.0 ±0.1 LUFS, constant after 3 s | :white_check_mark: |
392
+ | seq-3341-10-\*-24bit | Max S = -23.0 ±0.1 LUFS, for each segment | :white_check_mark: |
393
+ | seq-3341-11-24bit | Max S = -38.0, -37.0, …, -19.0 ±0.1 LUFS, successive values | :white_check_mark: |
394
+ | seq-3341-12-24bit | M = -23.0 ±0.1 LUFS, constant after 1 s | :white_check_mark: |
395
+ | seq-3341-13-\*-24bit | Max M = -23.0 ±0.1 LUFS, for each segment | :white_check_mark: |
396
+ | seq-3341-14-24bit | Max M = -38.0, …, -19.0 ±0.1 LUFS, successive values | :white_check_mark: |
397
+ | seq-3341-15-24bit | Max true-peak = -6.0 +0.2/-0.4 dBTP | :white_check_mark: |
398
+ | seq-3341-16-24bit | Max true-peak = -6.0 +0.2/-0.4 dBTP | :white_check_mark: |
399
+ | seq-3341-17-24bit | Max true-peak = -6.0 +0.2/-0.4 dBTP | :white_check_mark: |
400
+ | seq-3341-18-24bit | Max true-peak = -6.0 +0.2/-0.4 dBTP | :white_check_mark: |
401
+ | seq-3341-19-24bit | Max true-peak = +3.0 +0.2/-0.4 dBTP | :white_check_mark: |
402
+ | seq-3341-20-24bit | Max true-peak = 0.0 +0.2/-0.4 dBTP | :white_check_mark: |
403
+ | seq-3341-21-24bit | Max true-peak = 0.0 +0.2/-0.4 dBTP | :white_check_mark: |
404
+ | seq-3341-22-24bit | Max true-peak = 0.0 +0.2/-0.4 dBTP | :white_check_mark: |
405
+ | seq-3341-23-24bit | Max true-peak = 0.0 +0.2/-0.4 dBTP | :white_check_mark: |
401
406
 
402
407
  ### EBU TECH 3342 Minimum requirements test signals
403
408
 
package/package.json CHANGED
@@ -1,24 +1,13 @@
1
1
  {
2
2
  "name": "loudness-worklet",
3
- "version": "1.6.3",
3
+ "version": "1.6.6",
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",
7
- "web-audio-api",
8
- "audio-meter",
9
7
  "audio-worklet",
10
8
  "loudness",
11
- "loudness-meter",
12
- "browser-audio",
13
9
  "ebu-r128",
14
- "itu-r-bs1770",
15
- "audio-processing",
16
- "audio-analysis",
17
- "true-peak",
18
- "lufs",
19
- "lra",
20
- "javascript",
21
- "typescript"
10
+ "itu-r-bs1770"
22
11
  ],
23
12
  "type": "module",
24
13
  "main": "./packages/lib/dist/index.js",
@@ -50,7 +39,10 @@
50
39
  "lint": "biome lint --write"
51
40
  },
52
41
  "devDependencies": {
53
- "@biomejs/biome": "2.3.8",
54
- "@types/node": "^24.10.1"
42
+ "@biomejs/biome": "2.3.13",
43
+ "@types/node": "^24.10.1",
44
+ "typescript": "^5.9.3",
45
+ "vite": "^7.3.1",
46
+ "vite-plugin-dts": "^4.5.4"
55
47
  }
56
48
  }
@@ -1,10 +1,10 @@
1
- const i = `const x = {
1
+ const i = `const C = {
2
2
  highshelf: {
3
3
  a: [-1.69065929318241, 0.73248077421585],
4
4
  b: [1.53512485958697, -2.69169618940638, 1.19839281085285]
5
5
  },
6
6
  highpass: { a: [-1.99004745483398, 0.99007225036621], b: [1, -2, 1] }
7
- }, C = {
7
+ }, x = {
8
8
  lowpass: {
9
9
  phase0: [
10
10
  0.001708984375,
@@ -96,8 +96,8 @@ const i = `const x = {
96
96
  1,
97
97
  1
98
98
  ]
99
- }, P = 0.4, M = 0.1, H = 3, O = 0.1, W = 0.1, k = 0.95, V = 12.04, U = -70, Y = -10, z = -70, K = -20;
100
- class D {
99
+ }, P = 0.4, O = 0.1, H = 3, G = 0.1, W = 0.1, k = 0.95, M = 12.04, U = -70, Y = -10, z = -70, j = -20;
100
+ class V {
101
101
  #t = new Float32Array(2);
102
102
  #e = new Float32Array(3);
103
103
  #s = new Float32Array(2);
@@ -155,9 +155,9 @@ class N {
155
155
  process(e) {
156
156
  this.#e[this.#s] = e, this.#s = (this.#s + 1) % this.#e.length;
157
157
  let i = 0;
158
- for (let g = 0; g < this.#t.length; g++) {
159
- const s = (this.#s - 1 - g + this.#e.length) % this.#e.length;
160
- i += this.#t[g] * this.#e[s];
158
+ for (let u = 0; u < this.#t.length; u++) {
159
+ const s = (this.#s - 1 - u + this.#e.length) % this.#e.length;
160
+ i += this.#t[u] * this.#e[s];
161
161
  }
162
162
  return i;
163
163
  }
@@ -217,12 +217,12 @@ class I {
217
217
  slice(e, i) {
218
218
  if (e >= i)
219
219
  return [];
220
- const g = [];
220
+ const u = [];
221
221
  for (let s = Math.max(0, e); s < Math.min(this.#i, i); s++) {
222
222
  const n = (this.#s + s) % this.#e;
223
- g.push(this.#t[n]);
223
+ u.push(this.#t[n]);
224
224
  }
225
- return g;
225
+ return u;
226
226
  }
227
227
  /**
228
228
  * Adds an item to the buffer and
@@ -265,11 +265,11 @@ class I {
265
265
  }
266
266
  }
267
267
  }
268
- class j extends AudioWorkletProcessor {
268
+ class K extends AudioWorkletProcessor {
269
269
  capacity;
270
270
  interval;
271
271
  previousTime = 0;
272
- attenuation = 10 ** (-V / 20);
272
+ attenuation = 10 ** (-M / 20);
273
273
  measurements = [];
274
274
  kWeightingFilters = [];
275
275
  overSamplingFilters = [];
@@ -287,11 +287,20 @@ class j extends AudioWorkletProcessor {
287
287
  sTraceDirtyFlags = [];
288
288
  constructor(e) {
289
289
  super();
290
- const { numberOfInputs: i = 1, processorOptions: g } = e, { capacity: s, interval: n } = g;
291
- this.capacity = s || 0, this.interval = n || 0;
292
- for (let h = 0; h < i; h++) {
293
- const F = Math.round(sampleRate * P), E = Math.round(sampleRate * H), L = Math.ceil(this.capacity / M), _ = Math.ceil(this.capacity / O);
294
- this.mEnergySums[h] = 0, this.mSampleAccumulators[h] = 0, this.mEnergyBuffers[h] = new I(F), this.mTraces[h] = this.capacity ? new I(L) : [], this.sEnergySums[h] = 0, this.sSampleAccumulators[h] = 0, this.sEnergyBuffers[h] = new I(E), this.sTraces[h] = this.capacity ? new I(_) : [], this.measurements[h] = {
290
+ const { numberOfInputs: i = 1, processorOptions: u } = e ?? {};
291
+ if (typeof i != "number" || !Number.isInteger(i) || i < 1)
292
+ throw new Error("numberOfInputs must be a positive integer.");
293
+ if (u && typeof u != "object")
294
+ throw new Error("processorOptions must be an object.");
295
+ const { capacity: s, interval: n } = u ?? {};
296
+ if (s !== void 0 && (typeof s != "number" || !Number.isFinite(s) || s < 0))
297
+ throw new Error("Capacity must be a non-negative finite number.");
298
+ if (n !== void 0 && (typeof n != "number" || !Number.isFinite(n) || n < 0))
299
+ throw new Error("Interval must be a non-negative finite number.");
300
+ this.capacity = s || 0, this.interval = n ?? 0.02;
301
+ for (let a = 0; a < i; a++) {
302
+ const F = Math.round(sampleRate * P), E = Math.round(sampleRate * H), L = Math.ceil(this.capacity / O), v = Math.ceil(this.capacity / G);
303
+ this.mEnergySums[a] = 0, this.mSampleAccumulators[a] = 0, this.mEnergyBuffers[a] = new I(F), this.mTraces[a] = this.capacity ? new I(L) : [], this.sEnergySums[a] = 0, this.sSampleAccumulators[a] = 0, this.sEnergyBuffers[a] = new I(E), this.sTraces[a] = this.capacity ? new I(v) : [], this.measurements[a] = {
295
304
  momentaryLoudness: Number.NEGATIVE_INFINITY,
296
305
  shortTermLoudness: Number.NEGATIVE_INFINITY,
297
306
  integratedLoudness: Number.NEGATIVE_INFINITY,
@@ -302,67 +311,67 @@ class j extends AudioWorkletProcessor {
302
311
  };
303
312
  }
304
313
  }
305
- process(e, i, g) {
314
+ process(e, i, u) {
306
315
  for (let s = 0; s < e.length; s++) {
307
316
  if (!e[s].length)
308
317
  continue;
309
- const n = e[s].length, h = e[s][0].length, F = sampleRate >= 96e3 ? 2 : 4, E = B[n], L = this.mEnergyBuffers[s].capacity, _ = this.sEnergyBuffers[s].capacity;
318
+ const n = e[s].length, a = e[s][0].length, F = sampleRate >= 96e3 ? 2 : 4, E = B[n], L = this.mEnergyBuffers[s].capacity, v = this.sEnergyBuffers[s].capacity;
310
319
  if (!this.kWeightingFilters[s] || this.kWeightingFilters[s].length !== n) {
311
- const { highshelf: o, highpass: t } = x;
320
+ const { highshelf: o, highpass: t } = C;
312
321
  this.kWeightingFilters[s] = this.kWeightingFilters[s] || [];
313
- for (let a = 0; a < n; a++)
314
- this.kWeightingFilters[s][a] = [
315
- new D(o.a, o.b),
316
- new D(t.a, t.b)
322
+ for (let h = 0; h < n; h++)
323
+ this.kWeightingFilters[s][h] = [
324
+ new V(o.a, o.b),
325
+ new V(t.a, t.b)
317
326
  ];
318
327
  }
319
328
  if (!this.overSamplingFilters[s] || this.overSamplingFilters[s].length !== n) {
320
- const { lowpass: o } = C, { phase0: t, phase1: a, phase2: f, phase3: u } = o;
329
+ const { lowpass: o } = x, { phase0: t, phase1: h, phase2: f, phase3: m } = o;
321
330
  this.overSamplingFilters[s] = this.overSamplingFilters[s] || [];
322
331
  for (let l = 0; l < n; l++)
323
332
  this.overSamplingFilters[s][l] = [
324
333
  new N(t),
325
- new N(a),
334
+ new N(h),
326
335
  new N(f),
327
- new N(u)
336
+ new N(m)
328
337
  ];
329
338
  }
330
- for (let o = 0; o < h; o++) {
339
+ for (let o = 0; o < a; o++) {
331
340
  let t = 0;
332
- for (let u = 0; u < n; u++) {
333
- const l = e[s][u][o], [c, r] = this.kWeightingFilters[s][u], y = c.process(l), T = r.process(y), S = T * T, m = E ? E[u] ?? 1 : 1;
334
- t += S * m;
335
- const d = l * this.attenuation;
336
- let p = 0;
337
- for (let v = 0; v < F; v++) {
338
- const w = this.overSamplingFilters[s][u][v], G = Math.abs(w.process(d));
339
- p < G && (p = G);
341
+ for (let m = 0; m < n; m++) {
342
+ const l = e[s][m][o], [c, r] = this.kWeightingFilters[s][m], y = c.process(l), T = r.process(y), S = T * T, g = E ? E[m] ?? 1 : 1;
343
+ t += S * g;
344
+ const p = l * this.attenuation;
345
+ let d = 0;
346
+ for (let A = 0; A < F; A++) {
347
+ const D = this.overSamplingFilters[s][m][A], w = Math.abs(D.process(p));
348
+ d < w && (d = w);
340
349
  }
341
- this.overSampledValues[s] !== void 0 ? p > this.overSampledValues[s] && (this.overSampledValues[s] = p, this.overSampledValueDirtyFlags[s] = !0) : (this.overSampledValues[s] = p, this.overSampledValueDirtyFlags[s] = !0);
350
+ this.overSampledValues[s] !== void 0 ? d > this.overSampledValues[s] && (this.overSampledValues[s] = d, this.overSampledValueDirtyFlags[s] = !0) : (this.overSampledValues[s] = d, this.overSampledValueDirtyFlags[s] = !0);
342
351
  }
343
- const a = this.mEnergyBuffers[s].evict(t) ?? 0;
344
- this.mEnergySums[s] += t - a;
352
+ const h = this.mEnergyBuffers[s].evict(t) ?? 0;
353
+ this.mEnergySums[s] += t - h;
345
354
  const f = this.sEnergyBuffers[s].evict(t) ?? 0;
346
355
  if (this.sEnergySums[s] += t - f, this.mEnergyBuffers[s].isFull()) {
347
- const u = this.mEnergySums[s] / L, l = this.energyToLoudness(u), c = this.measurements[s].maximumMomentaryLoudness, r = Math.max(l, c);
356
+ const m = this.mEnergySums[s] / L, l = this.energyToLoudness(m), c = this.measurements[s].maximumMomentaryLoudness, r = Math.max(l, c);
348
357
  this.measurements[s].momentaryLoudness = l, this.measurements[s].maximumMomentaryLoudness = r;
349
358
  }
350
359
  }
351
- this.mSampleAccumulators[s] += h, this.sSampleAccumulators[s] += h;
352
- const R = Math.round(sampleRate * M), b = Math.round(sampleRate * O);
353
- for (; this.mSampleAccumulators[s] >= R; ) {
360
+ this.mSampleAccumulators[s] += a, this.sSampleAccumulators[s] += a;
361
+ const _ = Math.round(sampleRate * O), R = Math.round(sampleRate * G);
362
+ for (; this.mSampleAccumulators[s] >= _; ) {
354
363
  if (this.mEnergyBuffers[s].isFull()) {
355
364
  const o = this.mEnergySums[s] / L, t = this.energyToLoudness(o);
356
365
  this.mTraces[s].push(t), this.mTraceDirtyFlags[s] = !0;
357
366
  }
358
- this.mSampleAccumulators[s] -= R;
367
+ this.mSampleAccumulators[s] -= _;
359
368
  }
360
- for (; this.sSampleAccumulators[s] >= b; ) {
369
+ for (; this.sSampleAccumulators[s] >= R; ) {
361
370
  if (this.sEnergyBuffers[s].isFull()) {
362
- const o = this.sEnergySums[s] / _, t = this.energyToLoudness(o), a = this.measurements[s].maximumShortTermLoudness, f = Math.max(t, a);
371
+ const o = this.sEnergySums[s] / v, t = this.energyToLoudness(o), h = this.measurements[s].maximumShortTermLoudness, f = Math.max(t, h);
363
372
  this.measurements[s].shortTermLoudness = t, this.measurements[s].maximumShortTermLoudness = f, this.sTraces[s].push(t), this.sTraceDirtyFlags[s] = !0;
364
373
  }
365
- this.sSampleAccumulators[s] -= b;
374
+ this.sSampleAccumulators[s] -= R;
366
375
  }
367
376
  if (this.mTraces[s].length > 2 && this.mTraceDirtyFlags[s]) {
368
377
  const o = [];
@@ -372,19 +381,19 @@ class j extends AudioWorkletProcessor {
372
381
  const t = [];
373
382
  for (const r of o)
374
383
  t.push(this.loudnessToEnergy(r));
375
- let a = 0;
384
+ let h = 0;
376
385
  for (const r of t)
377
- a += r;
378
- const f = a / t.length, l = this.energyToLoudness(f) + Y, c = [];
386
+ h += r;
387
+ const f = h / t.length, l = this.energyToLoudness(f) + Y, c = [];
379
388
  for (const r of o)
380
389
  r > l && c.push(r);
381
390
  if (c.length > 2) {
382
391
  const r = [];
383
- for (const m of c)
384
- r.push(this.loudnessToEnergy(m));
392
+ for (const g of c)
393
+ r.push(this.loudnessToEnergy(g));
385
394
  let y = 0;
386
- for (const m of r)
387
- y += m;
395
+ for (const g of r)
396
+ y += g;
388
397
  const T = y / r.length, S = this.energyToLoudness(T);
389
398
  this.measurements[s].integratedLoudness = S;
390
399
  }
@@ -399,23 +408,23 @@ class j extends AudioWorkletProcessor {
399
408
  const t = [];
400
409
  for (const r of o)
401
410
  t.push(this.loudnessToEnergy(r));
402
- let a = 0;
411
+ let h = 0;
403
412
  for (const r of t)
404
- a += r;
405
- const f = a / t.length, l = this.energyToLoudness(f) + K, c = [];
413
+ h += r;
414
+ const f = h / t.length, l = this.energyToLoudness(f) + j, c = [];
406
415
  for (const r of o)
407
416
  r > l && c.push(r);
408
417
  if (c.length > 2) {
409
- const r = c.sort((m, d) => m - d), [y, T] = [
418
+ const r = c.sort((g, p) => g - p), [y, T] = [
410
419
  W,
411
420
  k
412
- ].map((m) => {
413
- const d = Math.floor(
414
- m * (r.length - 1)
415
- ), p = Math.ceil(
416
- m * (r.length - 1)
421
+ ].map((g) => {
422
+ const p = Math.floor(
423
+ g * (r.length - 1)
424
+ ), d = Math.ceil(
425
+ g * (r.length - 1)
417
426
  );
418
- return p === d ? r[d] : r[d] + (r[p] - r[d]) * (m * (r.length - 1) - d);
427
+ return d === p ? r[p] : r[p] + (r[d] - r[p]) * (g * (r.length - 1) - p);
419
428
  }), S = T - y;
420
429
  this.measurements[s].loudnessRange = S;
421
430
  }
@@ -425,9 +434,9 @@ class j extends AudioWorkletProcessor {
425
434
  }
426
435
  if (currentTime - this.previousTime >= Number(this.interval)) {
427
436
  for (let n = 0; n < this.measurements.length; n++) {
428
- const h = this.overSampledValues[n];
437
+ const a = this.overSampledValues[n];
429
438
  if (this.overSampledValueDirtyFlags[n]) {
430
- const E = 20 * Math.log10(h) + V;
439
+ const E = 20 * Math.log10(a) + M;
431
440
  this.measurements[n].maximumTruePeakLevel = E, this.overSampledValueDirtyFlags[n] = !1;
432
441
  }
433
442
  }
@@ -450,9 +459,9 @@ class j extends AudioWorkletProcessor {
450
459
  return 10 ** ((e + 0.691) / 10);
451
460
  }
452
461
  }
453
- registerProcessor("loudness-processor", j);
462
+ registerProcessor("loudness-processor", K);
454
463
  `, t = "loudness-processor";
455
- class a extends AudioWorkletNode {
464
+ class o extends AudioWorkletNode {
456
465
  constructor(n, s) {
457
466
  super(n, t, s);
458
467
  }
@@ -460,7 +469,7 @@ class a extends AudioWorkletNode {
460
469
  return r(n);
461
470
  }
462
471
  }
463
- async function h(e, n) {
472
+ async function a(e, n) {
464
473
  return await r(e), new AudioWorkletNode(e, t, n);
465
474
  }
466
475
  async function r(e) {
@@ -472,6 +481,6 @@ async function r(e) {
472
481
  }
473
482
  }
474
483
  export {
475
- a as LoudnessWorkletNode,
476
- h as createLoudnessWorklet
484
+ o as LoudnessWorkletNode,
485
+ a as createLoudnessWorklet
477
486
  };