loudness-worklet 1.4.3 → 1.5.1

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 (3) hide show
  1. package/README.md +98 -68
  2. package/dist/index.js +55 -11
  3. package/package.json +5 -4
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Loudness Audio Worklet Processor
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/loudness-worklet.svg)](https://www.npmjs.com/package/loudness-worklet)
4
- [![license](https://img.shields.io/github/license/lcweden/loudness-audio-worklet-processor.svg)](LICENSE)
4
+ [![license](https://img.shields.io/github/license/lcweden/loudness-worklet.svg)](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
- [![screenshot](https://github.com/lcweden/loudness-audio-worklet-processor/blob/main/public/screenshots/meter.png)](https://lcweden.github.io/loudness-audio-worklet-processor/)
8
+ [![screenshot](https://github.com/lcweden/loudness-worklet/blob/main/public/screenshots/meter.png)](https://lcweden.github.io/loudness-worklet/)
9
9
 
10
- <p align="center"><a href="https://lcweden.github.io/loudness-audio-worklet-processor/">Demo</a></p>
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 = new URL("https://lcweden.github.io/loudness-audio-worklet-processor/loudness.worklet.js");
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-audio-worklet-processor/loudness.worklet.js).
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
- const worklet = new LoudnessWorkletNode(audioContext, {
64
- processorOptions: {
65
- interval: 0.1,
66
- capacity: 600
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
- <!doctype html>
118
+ <!DOCTYPE html>
79
119
  <html>
80
120
  <body>
81
121
  <button>Share Screen</button>
82
122
  <pre></pre>
83
123
  <script>
84
- const script = "https://lcweden.github.io/loudness-audio-worklet-processor/loudness.worklet.js";
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(script);
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 // it means 1 minute of history can be stored
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
- Suppose you already have an audio file (e.g., from an input[type="file"]):
160
+ You can measure the loudness of audio files using `OfflineAudioContext`.
121
161
 
122
162
  ```javascript
123
- const arrayBuffer = await file.arrayBuffer();
124
- const audioBuffer = await new AudioContext().decodeAudioData(arrayBuffer);
163
+ import { LoudnessWorkletNode } from "loudness-worklet";
125
164
 
126
- const { numberOfChannels, length, sampleRate } = audioBuffer;
127
- const context = new OfflineAudioContext(numberOfChannels, length, sampleRate);
128
-
129
- await context.audioWorklet.addModule("loudness.worklet.js");
130
-
131
- const source = new AudioBufferSourceNode(context, { buffer: audioBuffer });
132
- const worklet = new AudioWorkletNode(context, "loudness-processor");
133
-
134
- // Or using the helper function
135
- //
136
- // import { createLoudnessWorklet } from "loudness-worklet";
137
- //
138
- // const worklet = await createLoudnessWorklet(audioContext, {
139
- // processorOptions: {
140
- // interval: 0.1,
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);
148
178
 
149
- source.connect(worklet).connect(context.destination);
150
- source.start();
179
+ worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);
151
180
 
152
- context.startRendering();
181
+ source.connect(worklet).connect(context.destination);
182
+ source.start();
183
+
184
+ await context.startRendering();
185
+ });
153
186
  ```
154
187
 
155
188
  ### Live-based measurement
156
189
 
157
- Supports `MediaStream` or `MediaElement` sources:
190
+ Supports all kinds of audio input.
158
191
 
159
192
  ```javascript
160
- const context = new AudioContext({ sampleRate: 48000 });
193
+ import { createLoudnessWorklet } from "loudness-worklet";
161
194
 
162
- await context.audioWorklet.addModule("loudness.worklet.js");
163
-
164
- const audioTrack = mediaStream.getAudioTracks()[0];
165
- const { channelCount } = audioTrack.getSettings();
166
-
167
- const source = new MediaStreamAudioSourceNode(context, { mediaStream });
168
- const worklet = new AudioWorkletNode(context, "loudness-processor", {
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
- console.log("Loudness Data:", event.data);
176
- };
202
+ worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);
203
+ source.connect(worklet);
177
204
 
178
- source.connect(worklet).connect(context.destination);
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", {
package/dist/index.js CHANGED
@@ -2,11 +2,11 @@ const i = `/**
2
2
  * A lightweight and efficient AudioWorklet for real-time loudness measurement in the browser, compliant with the ITU-R BS.1770-5 standard.
3
3
  *
4
4
  * @file loudness.worklet.js
5
- * @version 1.4.3
5
+ * @version 1.5.1
6
6
  * @author lcweden
7
7
  * @license MIT
8
- * @see git+https://github.com/lcweden/loudness-audio-worklet-processor.git
9
- * @date 2025-09-25T18:58:20.105Z
8
+ * @see https://github.com/lcweden/loudness-worklet.git
9
+ * @date 2025-11-18T12:38:50.840Z
10
10
  */\r
11
11
  \r
12
12
  class O {
@@ -35,12 +35,14 @@ class O {
35
35
  * Sets new filter coefficients.
36
36
  * @param { number[] } a - Feedback coefficients [a1, a2]
37
37
  * @param { number[] } b - Feedforward coefficients [b0, b1, b2]
38
+ * @returns { void }
38
39
  */
39
40
  set(e, r) {
40
41
  this.#t.set((e.length = 2, e)), this.#e.set((r.length = 3, r));
41
42
  }
42
43
  /**
43
44
  * Resets the filter state.
45
+ * @returns { void }
44
46
  */
45
47
  reset() {
46
48
  this.#s.fill(0), this.#r.fill(0);
@@ -52,22 +54,45 @@ class d {
52
54
  #s = 0;
53
55
  #r = 0;
54
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
+ */
55
61
  constructor(e) {
56
62
  this.#e = e || 0, this.#t = new Array(e);
57
63
  }
64
+ /**
65
+ * Adds an item to the buffer.
66
+ * @param item - The item to add to the buffer.
67
+ * @returns { void }
68
+ */
58
69
  push(e) {
59
70
  this.#t[this.#r] = e, this.isFull() ? this.#s = (this.#s + 1) % this.#e : this.#n++, this.#r = (this.#r + 1) % this.#e;
60
71
  }
72
+ /**
73
+ * Removes and returns the oldest item from the buffer.
74
+ * @returns { T | undefined }
75
+ */
61
76
  pop() {
62
77
  if (this.isEmpty())
63
78
  return;
64
79
  const e = this.#t[this.#s];
65
80
  return this.#s = (this.#s + 1) % this.#e, this.#n--, e;
66
81
  }
82
+ /**
83
+ * Returns the oldest item from the buffer without removing it.
84
+ * @returns { T | undefined }
85
+ */
67
86
  peek() {
68
87
  if (!this.isEmpty())
69
88
  return this.#t[this.#s];
70
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
+ */
71
96
  slice(e = 0, r = this.#n) {
72
97
  if (e < 0 && (e = 0), r > this.#n && (r = this.#n), e >= r)
73
98
  return [];
@@ -78,18 +103,29 @@ class d {
78
103
  }
79
104
  return n;
80
105
  }
106
+ /**
107
+ * Checks if the buffer is empty.
108
+ * @returns { boolean }
109
+ */
81
110
  isEmpty() {
82
111
  return this.#n === 0;
83
112
  }
113
+ /**
114
+ * Checks if the buffer is full.
115
+ * @returns { boolean }
116
+ */
84
117
  isFull() {
85
118
  return this.#n === this.#e;
86
119
  }
120
+ /** @type { number } */
87
121
  get length() {
88
122
  return this.#n;
89
123
  }
124
+ /** @type { number } */
90
125
  get capacity() {
91
126
  return this.#e;
92
127
  }
128
+ /** @type { IterableIterator<T> } */
93
129
  *[Symbol.iterator]() {
94
130
  for (let e = 0; e < this.#n; e++) {
95
131
  const r = (this.#s + e) % this.#e;
@@ -209,6 +245,10 @@ const L = {
209
245
  class F {
210
246
  #t;
211
247
  #e;
248
+ /**
249
+ * Creates an instance of the filter.
250
+ * @param coefficients - The filter coefficients.
251
+ */
212
252
  constructor(e) {
213
253
  this.#t = e, this.#e = Array(e.length).fill(0);
214
254
  }
@@ -224,6 +264,10 @@ class F {
224
264
  return n;
225
265
  }
226
266
  }
267
+ /**
268
+ * Resets the filter state.
269
+ * @returns { void }
270
+ */
227
271
  reset() {
228
272
  this.#e.fill(0);
229
273
  }
@@ -374,22 +418,22 @@ class X extends AudioWorkletProcessor {
374
418
  registerProcessor("loudness-processor", X);
375
419
  `, t = "loudness-processor";
376
420
  class h extends AudioWorkletNode {
377
- constructor(n, e) {
378
- super(n, t, e);
421
+ constructor(n, s) {
422
+ super(n, t, s);
379
423
  }
380
424
  static async loadModule(n) {
381
425
  return r(n);
382
426
  }
383
427
  }
384
- async function o(s, n) {
385
- return await r(s), new AudioWorkletNode(s, t, n);
428
+ async function o(e, n) {
429
+ return await r(e), new AudioWorkletNode(e, t, n);
386
430
  }
387
- async function r(s) {
388
- const n = new Blob([i], { type: "application/javascript" }), e = URL.createObjectURL(n);
431
+ async function r(e) {
432
+ const n = new Blob([i], { type: "application/javascript" }), s = URL.createObjectURL(n);
389
433
  try {
390
- await s.audioWorklet.addModule(e);
434
+ await e.audioWorklet.addModule(s);
391
435
  } finally {
392
- URL.revokeObjectURL(e);
436
+ URL.revokeObjectURL(s);
393
437
  }
394
438
  }
395
439
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loudness-worklet",
3
- "version": "1.4.3",
3
+ "version": "1.5.1",
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",
@@ -35,12 +35,13 @@
35
35
  ],
36
36
  "license": "MIT",
37
37
  "author": "lcweden",
38
- "homepage": "https://lcweden.github.io/loudness-audio-worklet-processor/",
38
+ "homepage": "https://lcweden.github.io/loudness-worklet/",
39
39
  "repository": {
40
- "url": "git+https://github.com/lcweden/loudness-audio-worklet-processor.git"
40
+ "type": "git",
41
+ "url": "https://github.com/lcweden/loudness-worklet.git"
41
42
  },
42
43
  "bugs": {
43
- "url": "https://github.com/lcweden/loudness-audio-worklet-processor/issues"
44
+ "url": "https://github.com/lcweden/loudness-worklet/issues"
44
45
  },
45
46
  "scripts": {
46
47
  "dev": "vite --mode dev",