loudness-worklet 1.6.0 → 1.6.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.
package/README.md CHANGED
@@ -5,9 +5,7 @@
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-worklet/blob/main/public/screenshots/meter.png)](https://lcweden.github.io/loudness-worklet/)
9
-
10
- <p align="center"><a href="https://lcweden.github.io/loudness-worklet/">Demo</a></p>
8
+ [![screenshot](https://github.com/lcweden/loudness-worklet/blob/main/packages/web/public/screenshots/meter.png)](https://lcweden.github.io/loudness-worklet/)
11
9
 
12
10
  ## Features
13
11
 
@@ -271,7 +269,7 @@ type LoudnessSnapshot = {
271
269
  | `maximumMomentaryLoudness` | `LUFS`/`LKFS` |
272
270
  | `maximumShortTermLoudness` | `LUFS`/`LKFS` |
273
271
  | `maximumTruePeakLevel` | `dBTP` |
274
- | `loudnessRange` | `LRA` |
272
+ | `loudnessRange` | `LU` |
275
273
 
276
274
  ### Supported Channels
277
275
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loudness-worklet",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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",
@@ -10,6 +10,11 @@ export declare type LoudnessMeasurements = {
10
10
  loudnessRange: number;
11
11
  };
12
12
 
13
+ export declare type LoudnessProcessorOptions = {
14
+ capacity?: number;
15
+ interval?: number;
16
+ };
17
+
13
18
  export declare type LoudnessSnapshot = {
14
19
  currentFrame: number;
15
20
  currentTime: number;
@@ -25,10 +30,7 @@ export declare interface LoudnessWorkletProcessorOptions {
25
30
  numberOfInputs?: AudioWorkletNodeOptions["numberOfInputs"];
26
31
  numberOfOutputs?: AudioWorkletNodeOptions["numberOfOutputs"];
27
32
  outputChannelCount?: AudioWorkletNodeOptions["outputChannelCount"];
28
- processorOptions?: {
29
- interval?: number;
30
- capacity?: number;
31
- };
33
+ processorOptions?: LoudnessProcessorOptions;
32
34
  }
33
35
 
34
36
  export { }
@@ -1,10 +1,10 @@
1
- const i = `const d = {
1
+ const i = `const x = {
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
- }, L = {
7
+ }, C = {
8
8
  lowpass: {
9
9
  phase0: [
10
10
  0.001708984375,
@@ -63,74 +63,41 @@ const i = `const d = {
63
63
  0.001708984375
64
64
  ]
65
65
  }
66
- }, O = {
67
- 1: { mono: 1 },
68
- 2: { L: 1, R: 1 },
69
- 6: { L: 1, R: 1, C: 1, LFE: 0, Ls: 1.41, Rs: 1.41 },
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
- },
92
- 12: {
93
- L: 1,
94
- R: 1,
95
- C: 1,
96
- LFE: 0,
97
- Lss: 1.41,
98
- Rss: 1.41,
99
- Lrs: 1,
100
- Rrs: 1,
101
- Tfl: 1,
102
- Tfr: 1,
103
- Tbl: 1,
104
- Tbr: 1
105
- },
106
- 24: {
107
- FL: 1.41,
108
- FR: 1.41,
109
- FC: 1,
110
- LFE1: 0,
111
- BL: 1,
112
- BR: 1,
113
- FLc: 1,
114
- FRc: 1,
115
- BC: 1,
116
- LFE2: 0,
117
- SiL: 1.41,
118
- SiR: 1.41,
119
- TpFL: 1,
120
- TpFR: 1,
121
- TpFC: 1,
122
- TpC: 1,
123
- TpBL: 1,
124
- TpBR: 1,
125
- TpSiL: 1,
126
- TpSiR: 1,
127
- TpBC: 1,
128
- BtFC: 1,
129
- BtFL: 1,
130
- BtFR: 1
131
- }
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 {
66
+ }, B = {
67
+ 1: [1],
68
+ 2: [1, 1],
69
+ 6: [1, 1, 1, 0, 1.41, 1.41],
70
+ 8: [1, 1, 1, 0, 1.41, 1.41, 1, 1],
71
+ 10: [1, 1, 1, 0, 1.41, 1.41, 1, 1, 1, 1],
72
+ 12: [1, 1, 1, 0, 1.41, 1.41, 1, 1, 1, 1, 1],
73
+ 24: [
74
+ 1.41,
75
+ 1.41,
76
+ 1,
77
+ 0,
78
+ 1,
79
+ 1,
80
+ 1,
81
+ 1,
82
+ 1,
83
+ 0,
84
+ 1.41,
85
+ 1.41,
86
+ 1,
87
+ 1,
88
+ 1,
89
+ 1,
90
+ 1,
91
+ 1,
92
+ 1,
93
+ 1,
94
+ 1,
95
+ 1,
96
+ 1,
97
+ 1
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 {
134
101
  #t = new Float32Array(2);
135
102
  #e = new Float32Array(3);
136
103
  #s = new Float32Array(2);
@@ -140,8 +107,8 @@ class x {
140
107
  * @param { number[] } a - Feedback coefficients [a1, a2]
141
108
  * @param { number[] } b - Feedforward coefficients [b0, b1, b2]
142
109
  */
143
- constructor(e, r) {
144
- this.reset(), this.set(e, r);
110
+ constructor(e, i) {
111
+ this.reset(), this.set(e, i);
145
112
  }
146
113
  /**
147
114
  * Processes a single input sample and returns the filtered output.
@@ -149,8 +116,8 @@ class x {
149
116
  * @returns { number } - The filtered output sample.
150
117
  */
151
118
  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;
119
+ const i = 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];
120
+ return this.#s[1] = this.#s[0], this.#s[0] = e, this.#r[1] = this.#r[0], this.#r[0] = i, i;
154
121
  }
155
122
  /**
156
123
  * Sets new filter coefficients.
@@ -158,8 +125,8 @@ class x {
158
125
  * @param { number[] } b - Feedforward coefficients [b0, b1, b2]
159
126
  * @returns { void }
160
127
  */
161
- set(e, r) {
162
- e.length = 2, this.#t.set(e), r.length = 3, this.#e.set(r);
128
+ set(e, i) {
129
+ e.length = 2, this.#t.set(e), i.length = 3, this.#e.set(i);
163
130
  }
164
131
  /**
165
132
  * Resets the filter state.
@@ -169,56 +136,59 @@ class x {
169
136
  this.#s.fill(0), this.#r.fill(0);
170
137
  }
171
138
  }
172
- class R {
139
+ class N {
173
140
  #t;
174
141
  #e;
142
+ #s;
175
143
  /**
176
144
  * Creates an instance of the filter.
177
145
  * @param coefficients - The filter coefficients.
178
146
  */
179
147
  constructor(e) {
180
- this.#t = e, this.#e = Array(e.length).fill(0);
148
+ this.#t = e, this.#e = Array(e.length).fill(0), this.#s = 0;
181
149
  }
150
+ /**
151
+ * Processes a single input sample.
152
+ * @param {number} input - The input sample.
153
+ * @returns {number} - The filtered output sample.
154
+ */
182
155
  process(e) {
183
- if (Array.isArray(e))
184
- return e.map((n) => this.process(n));
185
- {
186
- const r = e;
187
- this.#e.pop(), this.#e.unshift(r);
188
- let n = 0;
189
- for (let s = 0; s < this.#t.length; s++)
190
- n += this.#t[s] * this.#e[s];
191
- return n;
156
+ this.#e[this.#s] = e, this.#s = (this.#s + 1) % this.#e.length;
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];
192
161
  }
162
+ return i;
193
163
  }
194
164
  /**
195
165
  * Resets the filter state.
196
166
  * @returns { void }
197
167
  */
198
168
  reset() {
199
- this.#e.fill(0);
169
+ this.#e.fill(0), this.#s = 0;
200
170
  }
201
171
  }
202
- class F {
172
+ class I {
203
173
  #t;
204
174
  #e;
205
175
  #s;
206
176
  #r;
207
- #n;
177
+ #i;
208
178
  /**
209
179
  * Creates a new CircularBuffer with given capacity.
210
180
  * @param { number } capacity - The maximum number of items the buffer can hold.
211
181
  */
212
182
  constructor(e) {
213
- this.#e = e || 0, this.#t = new Array(e), this.#s = 0, this.#r = 0, this.#n = 0;
183
+ this.#e = e || 0, this.#t = new Array(e), this.#s = 0, this.#r = 0, this.#i = 0;
214
184
  }
215
185
  /**
216
186
  * Adds an item to the buffer.
217
- * @param item - The item to add to the buffer.
187
+ * @param { T } item - The item to add to the buffer.
218
188
  * @returns { void }
219
189
  */
220
190
  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;
191
+ this.#t[this.#r] = e, this.isFull() ? this.#s = (this.#s + 1) % this.#e : this.#i++, this.#r = (this.#r + 1) % this.#e;
222
192
  }
223
193
  /**
224
194
  * Removes and returns the oldest item from the buffer.
@@ -228,7 +198,7 @@ class F {
228
198
  if (this.isEmpty())
229
199
  return;
230
200
  const e = this.#t[this.#s];
231
- return this.#t[this.#s] = void 0, this.#s = (this.#s + 1) % this.#e, this.#n--, e;
201
+ return this.#t[this.#s] = void 0, this.#s = (this.#s + 1) % this.#e, this.#i--, e;
232
202
  }
233
203
  /**
234
204
  * Returns the oldest item from the buffer without removing it.
@@ -244,33 +214,44 @@ class F {
244
214
  * @param { number } end - The ending index of the slice (exclusive).
245
215
  * @returns { T[] }
246
216
  */
247
- slice(e, r) {
248
- if (e >= r)
217
+ slice(e, i) {
218
+ if (e >= i)
249
219
  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]);
220
+ const g = [];
221
+ for (let s = Math.max(0, e); s < Math.min(this.#i, i); s++) {
222
+ const n = (this.#s + s) % this.#e;
223
+ g.push(this.#t[n]);
254
224
  }
255
- return n;
225
+ return g;
226
+ }
227
+ /**
228
+ * Adds an item to the buffer and
229
+ * returns undefined if the buffer is not full,
230
+ * otherwise returns the oldest item from the buffer without removing it.
231
+ *
232
+ * @param item
233
+ */
234
+ evict(e) {
235
+ const i = this.isFull() ? this.peek() : void 0;
236
+ return this.push(e), i;
256
237
  }
257
238
  /**
258
239
  * Checks if the buffer is empty.
259
240
  * @returns { boolean }
260
241
  */
261
242
  isEmpty() {
262
- return this.#n === 0;
243
+ return this.#i === 0;
263
244
  }
264
245
  /**
265
246
  * Checks if the buffer is full.
266
247
  * @returns { boolean }
267
248
  */
268
249
  isFull() {
269
- return this.#n === this.#e;
250
+ return this.#i === this.#e;
270
251
  }
271
252
  /** @type { number } */
272
253
  get length() {
273
- return this.#n;
254
+ return this.#i;
274
255
  }
275
256
  /** @type { number } */
276
257
  get capacity() {
@@ -278,44 +259,39 @@ class F {
278
259
  }
279
260
  /** @type { IterableIterator<T> } */
280
261
  *[Symbol.iterator]() {
281
- for (let e = 0; e < this.#n; e++) {
282
- const r = (this.#s + e) % this.#e;
283
- yield this.#t[r];
262
+ for (let e = 0; e < this.#i; e++) {
263
+ const i = (this.#s + e) % this.#e;
264
+ yield this.#t[i];
284
265
  }
285
266
  }
286
267
  }
287
- class X extends AudioWorkletProcessor {
288
- capacity = null;
289
- interval = null;
290
- lastTime = 0;
291
- measurements = [];
268
+ class j extends AudioWorkletProcessor {
269
+ capacity;
270
+ interval;
271
+ previousTime = 0;
272
+ attenuation = 10 ** (-V / 20);
273
+ measurements;
292
274
  kWeightingFilters = [];
293
- truePeakFilters = [];
294
- momentaryEnergyBuffers = [];
295
- momentaryEnergyRunningSums = [];
296
- momentarySampleAccumulators = [];
297
- momentaryLoudnessHistories = [];
298
- shortTermEnergyBuffers = [];
299
- shortTermEnergyRunningSums = [];
300
- shortTermLoudnessHistories = [];
301
- shortTermSampleAccumulators = [];
275
+ overSamplingFilters = [];
276
+ overSampledValues = [];
277
+ overSampledValueDirtyFlags = [];
278
+ mEnergyBuffers = [];
279
+ mEnergySums = [];
280
+ mSampleAccumulators = [];
281
+ mTraces = [];
282
+ mTraceDirtyFlags = [];
283
+ sEnergyBuffers = [];
284
+ sEnergySums = [];
285
+ sSampleAccumulators = [];
286
+ sTraces = [];
287
+ sTraceDirtyFlags = [];
302
288
  constructor(e) {
303
289
  super();
304
- const { numberOfInputs: r = 1, processorOptions: n } = e;
305
- if (n) {
306
- const { capacity: s, interval: u } = n;
307
- this.capacity = s ?? null, this.interval = u ?? null;
308
- }
309
- for (let s = 0; s < r; s++)
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] = {
290
+ const { numberOfInputs: i = 1, processorOptions: g } = e, { capacity: s, interval: n } = g;
291
+ this.capacity = s || 0, this.interval = n || 0, this.measurements = [];
292
+ for (let h = 0; h < i; h++) {
293
+ const F = Math.round(sampleRate * P), T = 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(T), this.sTraces[h] = this.capacity ? new I(_) : [], this.measurements[h] = {
319
295
  momentaryLoudness: Number.NEGATIVE_INFINITY,
320
296
  shortTermLoudness: Number.NEGATIVE_INFINITY,
321
297
  integratedLoudness: Number.NEGATIVE_INFINITY,
@@ -324,184 +300,159 @@ class X extends AudioWorkletProcessor {
324
300
  maximumTruePeakLevel: Number.NEGATIVE_INFINITY,
325
301
  loudnessRange: Number.NEGATIVE_INFINITY
326
302
  };
303
+ }
327
304
  }
328
- process(e, r) {
329
- const n = e.length;
330
- for (let s = 0; s < n; s++) {
331
- if (!e[s].length) continue;
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
- ));
364
- for (let o = 0; o < A; o++) {
365
- let i = 0;
366
- for (let t = 0; t < c; t++) {
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 = [];
370
- for (let N = 0; N < k; N++) {
371
- const D = this.truePeakFilters[s][t][N];
372
- b.push(Math.abs(D.process(P)));
305
+ process(e, i, g) {
306
+ for (let s = 0; s < e.length; s++) {
307
+ if (!e[s].length)
308
+ continue;
309
+ const n = e[s].length, h = e[s][0].length, F = sampleRate >= 96e3 ? 2 : 4, T = B[n], L = this.mEnergyBuffers[s].capacity, _ = this.sEnergyBuffers[s].capacity;
310
+ if (!this.kWeightingFilters[s] || this.kWeightingFilters[s].length !== n) {
311
+ const { highshelf: o, highpass: t } = x;
312
+ 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)
317
+ ];
318
+ }
319
+ if (!this.overSamplingFilters[s] || this.overSamplingFilters[s].length !== n) {
320
+ const { lowpass: o } = C, { phase0: t, phase1: a, phase2: f, phase3: u } = o;
321
+ this.overSamplingFilters[s] = this.overSamplingFilters[s] || [];
322
+ for (let l = 0; l < n; l++)
323
+ this.overSamplingFilters[s][l] = [
324
+ new N(t),
325
+ new N(a),
326
+ new N(f),
327
+ new N(u)
328
+ ];
329
+ }
330
+ for (let o = 0; o < h; o++) {
331
+ let t = 0;
332
+ for (let u = 0; u < n; u++) {
333
+ const l = e[s][u][o], [m, r] = this.kWeightingFilters[s][u], E = m.process(l), y = r.process(E), S = y * y, c = T[u] ?? 1;
334
+ t += S * c;
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);
373
340
  }
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
- );
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);
379
342
  }
380
- const g = i, E = this.momentaryEnergyBuffers[s].peek() ?? 0, I = this.momentaryEnergyBuffers[s].isFull() ? E : 0;
381
- this.momentaryEnergyRunningSums[s] += g - I, this.momentaryEnergyBuffers[s].push(g);
382
- const p = this.shortTermEnergyBuffers[s].peek() ?? 0, l = this.shortTermEnergyBuffers[s].isFull() ? p : 0;
383
- if (this.shortTermEnergyRunningSums[s] += g - l, this.shortTermEnergyBuffers[s].push(g), this.momentaryEnergyBuffers[s].isFull()) {
384
- const t = this.momentaryEnergyRunningSums[s] / this.momentaryEnergyBuffers[s].capacity, h = this.#s(t);
385
- this.measurements[s].momentaryLoudness = h, this.measurements[s].maximumMomentaryLoudness = Math.max(
386
- this.measurements[s].maximumMomentaryLoudness,
387
- h
388
- );
343
+ const a = this.mEnergyBuffers[s].evict(t) ?? 0;
344
+ this.mEnergySums[s] += t - a;
345
+ const f = this.sEnergyBuffers[s].evict(t) ?? 0;
346
+ if (this.sEnergySums[s] += t - f, this.mEnergyBuffers[s].isFull()) {
347
+ const u = this.mEnergySums[s] / L, l = this.energyToLoudness(u), m = this.measurements[s].maximumMomentaryLoudness, r = Math.max(l, m);
348
+ this.measurements[s].momentaryLoudness = l, this.measurements[s].maximumMomentaryLoudness = r;
389
349
  }
390
350
  }
391
- this.momentarySampleAccumulators[s] += A;
392
- const _ = Math.round(
393
- sampleRate * G
394
- );
395
- for (; this.momentarySampleAccumulators[s] >= _; ) {
396
- if (this.momentaryEnergyBuffers[s].isFull()) {
397
- const o = this.momentaryEnergyRunningSums[s] / this.momentaryEnergyBuffers[s].capacity, i = this.#s(o);
398
- this.momentaryLoudnessHistories[s].push(i);
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; ) {
354
+ if (this.mEnergyBuffers[s].isFull()) {
355
+ const o = this.mEnergySums[s] / L, t = this.energyToLoudness(o);
356
+ this.mTraces[s].push(t), this.mTraceDirtyFlags[s] = !0;
399
357
  }
400
- this.momentarySampleAccumulators[s] -= _;
358
+ this.mSampleAccumulators[s] -= R;
401
359
  }
402
- this.shortTermSampleAccumulators[s] += A;
403
- const M = Math.round(
404
- sampleRate * B
405
- );
406
- for (; this.shortTermSampleAccumulators[s] >= M; ) {
407
- if (this.shortTermEnergyBuffers[s].isFull()) {
408
- const o = this.shortTermEnergyRunningSums[s] / this.shortTermEnergyBuffers[s].capacity, i = this.#s(o);
409
- this.measurements[s].shortTermLoudness = i, this.measurements[s].maximumShortTermLoudness = Math.max(
410
- this.measurements[s].maximumShortTermLoudness,
411
- i
412
- ), this.shortTermLoudnessHistories[s].push(i);
360
+ for (; this.sSampleAccumulators[s] >= b; ) {
361
+ 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);
363
+ this.measurements[s].shortTermLoudness = t, this.measurements[s].maximumShortTermLoudness = f, this.sTraces[s].push(t), this.sTraceDirtyFlags[s] = !0;
413
364
  }
414
- this.shortTermSampleAccumulators[s] -= M;
365
+ this.sSampleAccumulators[s] -= b;
415
366
  }
416
- if (this.momentaryLoudnessHistories[s].length > 2) {
417
- const o = Array.from(
418
- this.momentaryLoudnessHistories[s]
419
- ).filter((i) => i > z);
367
+ if (this.mTraces[s].length > 2 && this.mTraceDirtyFlags[s]) {
368
+ const o = [];
369
+ for (const t of this.mTraces[s])
370
+ t > U && o.push(t);
420
371
  if (o.length > 2) {
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
- );
431
- if (l.length > 2) {
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;
372
+ const t = [];
373
+ for (const r of o)
374
+ t.push(this.loudnessToEnergy(r));
375
+ let a = 0;
376
+ for (const r of t)
377
+ a += r;
378
+ const f = a / t.length, l = this.energyToLoudness(f) + Y, m = [];
379
+ for (const r of o)
380
+ r > l && m.push(r);
381
+ if (m.length > 2) {
382
+ const r = [];
383
+ for (const c of m)
384
+ r.push(this.loudnessToEnergy(c));
385
+ let E = 0;
386
+ for (const c of r)
387
+ E += c;
388
+ const y = E / r.length, S = this.energyToLoudness(y);
389
+ this.measurements[s].integratedLoudness = S;
441
390
  }
442
391
  }
392
+ this.mTraceDirtyFlags[s] = !1;
443
393
  }
444
- if (this.shortTermLoudnessHistories[s].length > 2) {
445
- const o = Array.from(
446
- this.shortTermLoudnessHistories[s]
447
- ).filter((i) => i > J);
394
+ if (this.sTraces[s].length > 2 && this.sTraceDirtyFlags[s]) {
395
+ const o = [];
396
+ for (const t of this.sTraces[s])
397
+ t > z && o.push(t);
448
398
  if (o.length > 2) {
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
- );
459
- if (l.length > 2) {
460
- const t = l.toSorted(
461
- (m, a) => m - a
462
- ), [h, y] = [
463
- j,
464
- q
465
- ].map((m) => {
466
- const a = Math.floor(
467
- m * (t.length - 1)
468
- ), f = Math.ceil(
469
- m * (t.length - 1)
399
+ const t = [];
400
+ for (const r of o)
401
+ t.push(this.loudnessToEnergy(r));
402
+ let a = 0;
403
+ for (const r of t)
404
+ a += r;
405
+ const f = a / t.length, l = this.energyToLoudness(f) + K, m = [];
406
+ for (const r of o)
407
+ r > l && m.push(r);
408
+ if (m.length > 2) {
409
+ const r = m.sort((c, d) => c - d), [E, y] = [
410
+ W,
411
+ k
412
+ ].map((c) => {
413
+ const d = Math.floor(
414
+ c * (r.length - 1)
415
+ ), p = Math.ceil(
416
+ c * (r.length - 1)
470
417
  );
471
- return f === a ? t[a] : t[a] + (t[f] - t[a]) * (m * (t.length - 1) - a);
472
- }), T = y - h;
473
- this.measurements[s].loudnessRange = T;
418
+ return p === d ? r[d] : r[d] + (r[p] - r[d]) * (c * (r.length - 1) - d);
419
+ }), S = y - E;
420
+ this.measurements[s].loudnessRange = S;
474
421
  }
475
422
  }
423
+ this.sTraceDirtyFlags[s] = !1;
476
424
  }
477
425
  }
478
- return this.#e(e, r), this.#t(), !0;
479
- }
480
- #t() {
481
- if (currentTime - this.lastTime >= Number(this.interval)) {
482
- const e = {
426
+ if (currentTime - this.previousTime >= Number(this.interval)) {
427
+ for (let n = 0; n < this.measurements.length; n++) {
428
+ const h = this.overSampledValues[n];
429
+ if (this.overSampledValueDirtyFlags[n]) {
430
+ const T = 20 * Math.log10(h) + V;
431
+ this.measurements[n].maximumTruePeakLevel = T, this.overSampledValueDirtyFlags[n] = !1;
432
+ }
433
+ }
434
+ const s = {
483
435
  currentFrame,
484
436
  currentTime,
485
437
  currentMeasurements: this.measurements
486
438
  };
487
- this.port.postMessage(e), this.lastTime = currentTime;
439
+ this.port.postMessage(s), this.previousTime = currentTime;
488
440
  }
441
+ for (let s = 0; s < Math.min(e.length, i.length); s++)
442
+ for (let n = 0; n < Math.min(e[s].length, i[s].length); n++)
443
+ i[s][n].set(e[s][n]);
444
+ return !0;
489
445
  }
490
- #e(e, r) {
491
- for (let n = 0; n < Math.min(e.length, r.length); n++)
492
- for (let s = 0; s < Math.min(e[n].length, r[n].length); s++)
493
- r[n][s].set(e[n][s]);
494
- }
495
- #s(e) {
446
+ energyToLoudness(e) {
496
447
  return -0.691 + 10 * Math.log10(Math.max(e, Number.EPSILON));
497
448
  }
498
- #r(e) {
449
+ loudnessToEnergy(e) {
499
450
  return 10 ** ((e + 0.691) / 10);
500
451
  }
501
452
  }
502
- registerProcessor("loudness-processor", X);
453
+ registerProcessor("loudness-processor", j);
503
454
  `, t = "loudness-processor";
504
- class h extends AudioWorkletNode {
455
+ class a extends AudioWorkletNode {
505
456
  constructor(n, s) {
506
457
  super(n, t, s);
507
458
  }
@@ -509,7 +460,7 @@ class h extends AudioWorkletNode {
509
460
  return r(n);
510
461
  }
511
462
  }
512
- async function a(e, n) {
463
+ async function h(e, n) {
513
464
  return await r(e), new AudioWorkletNode(e, t, n);
514
465
  }
515
466
  async function r(e) {
@@ -521,6 +472,6 @@ async function r(e) {
521
472
  }
522
473
  }
523
474
  export {
524
- h as LoudnessWorkletNode,
525
- a as createLoudnessWorklet
475
+ a as LoudnessWorkletNode,
476
+ h as createLoudnessWorklet
526
477
  };