carta1 1.2.0 → 1.4.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.
@@ -0,0 +1,539 @@
1
+ /** Configurable polyphase quadrature-mirror analysis and synthesis. */
2
+
3
+ import {
4
+ QMF_ANALYSIS_MODULATION_SCALE,
5
+ QMF_ANALYSIS_START_SAMPLE,
6
+ QMF_ANALYSIS_STEP_SAMPLES,
7
+ QMF_ANALYSIS_TERMS,
8
+ QMF_ANALYSIS_WINDOW_SAMPLES,
9
+ QMF_BANDS,
10
+ QMF_SYNTHESIS_DELAY_ROWS,
11
+ QMF_SYNTHESIS_TERMS,
12
+ } from '../core/constants.js'
13
+ import {
14
+ QMF_ANALYSIS_HALF_BUTTERFLY_SCALES,
15
+ QMF_ANALYSIS_ODD_PI_OVER_16_COSINES,
16
+ QMF_ANALYSIS_ODD_PI_OVER_32_COSINES,
17
+ QMF_ANALYSIS_ODD_PI_OVER_64_COSINES,
18
+ QMF_ANALYSIS_PI_OVER_8_BUTTERFLY_SCALES,
19
+ QMF_ANALYSIS_FILTER_COEFFICIENTS,
20
+ QMF_ANALYSIS_MODULATION_COEFFICIENTS,
21
+ QMF_ANALYSIS_SAMPLE_OFFSETS,
22
+ QMF_SYNTHESIS_DELAY_COMPONENTS,
23
+ QMF_SYNTHESIS_DELAY_ROW_OFFSETS,
24
+ QMF_SYNTHESIS_FILTER_COEFFICIENTS,
25
+ QMF_SYNTHESIS_MODULATION_COEFFICIENTS,
26
+ } from '../core/tables.js'
27
+
28
+ const float32Round = Math.fround
29
+
30
+ /**
31
+ * Preserve the reference spill order of the native sixteen-band polyphase sum.
32
+ * Other supported widths use the dense configurable path below.
33
+ *
34
+ * @param {Float32Array} input
35
+ * @param {number} inputOffset
36
+ * @param {Float32Array} accumulator
37
+ * @param {Float64Array} extended
38
+ */
39
+ function filter16Band(input, inputOffset, accumulator, extended) {
40
+ const c = QMF_ANALYSIS_FILTER_COEFFICIENTS
41
+
42
+ for (let component = 0; component < 16; component++) {
43
+ const coefficientOffset = component * QMF_ANALYSIS_TERMS
44
+ const firstSample = component < 8 ? component : component + 8
45
+ const secondSample = component < 8 ? 15 - component : 39 - component
46
+ const firstProduct = input[inputOffset + firstSample] * c[coefficientOffset]
47
+ accumulator[component] = float32Round(
48
+ input[inputOffset + secondSample] * c[coefficientOffset + 1] +
49
+ (component % 4 === 0 ? float32Round(firstProduct) : firstProduct)
50
+ )
51
+ }
52
+
53
+ let extended7 = accumulator[7]
54
+ let extended8 = accumulator[8]
55
+ let extended10 = accumulator[10]
56
+ let extended11 = accumulator[11]
57
+ let extended12 = accumulator[12]
58
+ let extended13 = accumulator[13]
59
+ let extended14 = accumulator[14]
60
+ let extended15 = accumulator[15]
61
+
62
+ for (let segment = 1; segment < 12; segment++) {
63
+ const inputBase = inputOffset + segment * 32
64
+ const term = segment * 2
65
+
66
+ const product0 = float32Round(input[inputBase] * c[term])
67
+ accumulator[0] = float32Round(
68
+ accumulator[0] + product0 + input[inputBase + 15] * c[term + 1]
69
+ )
70
+ accumulator[1] = float32Round(
71
+ input[inputBase + 1] * c[QMF_ANALYSIS_TERMS + term] +
72
+ float32Round(
73
+ input[inputBase + 14] * c[QMF_ANALYSIS_TERMS + term + 1] +
74
+ accumulator[1]
75
+ )
76
+ )
77
+ const product2 = float32Round(
78
+ input[inputBase + 2] * c[QMF_ANALYSIS_TERMS * 2 + term]
79
+ )
80
+ accumulator[2] = float32Round(
81
+ accumulator[2] +
82
+ product2 +
83
+ input[inputBase + 13] * c[QMF_ANALYSIS_TERMS * 2 + term + 1]
84
+ )
85
+ accumulator[3] = float32Round(
86
+ accumulator[3] +
87
+ input[inputBase + 3] * c[QMF_ANALYSIS_TERMS * 3 + term] +
88
+ input[inputBase + 12] * c[QMF_ANALYSIS_TERMS * 3 + term + 1]
89
+ )
90
+ const product4 = float32Round(
91
+ input[inputBase + 4] * c[QMF_ANALYSIS_TERMS * 4 + term]
92
+ )
93
+ const product4Sum =
94
+ product4 + input[inputBase + 11] * c[QMF_ANALYSIS_TERMS * 4 + term + 1]
95
+ accumulator[4] = float32Round(accumulator[4] + product4Sum)
96
+ accumulator[5] = float32Round(
97
+ input[inputBase + 5] * c[QMF_ANALYSIS_TERMS * 5 + term] +
98
+ float32Round(
99
+ input[inputBase + 10] * c[QMF_ANALYSIS_TERMS * 5 + term + 1] +
100
+ accumulator[5]
101
+ )
102
+ )
103
+ const product6 = float32Round(
104
+ input[inputBase + 6] * c[QMF_ANALYSIS_TERMS * 6 + term]
105
+ )
106
+ accumulator[6] = float32Round(
107
+ accumulator[6] +
108
+ product6 +
109
+ input[inputBase + 9] * c[QMF_ANALYSIS_TERMS * 6 + term + 1]
110
+ )
111
+ extended7 =
112
+ accumulator[7] +
113
+ (input[inputBase + 7] * c[QMF_ANALYSIS_TERMS * 7 + term] +
114
+ input[inputBase + 8] * c[QMF_ANALYSIS_TERMS * 7 + term + 1])
115
+ accumulator[7] = float32Round(extended7)
116
+
117
+ extended8 =
118
+ input[inputBase + 31] * c[QMF_ANALYSIS_TERMS * 8 + term + 1] +
119
+ input[inputBase + 16] * c[QMF_ANALYSIS_TERMS * 8 + term] +
120
+ accumulator[8]
121
+ accumulator[8] = float32Round(extended8)
122
+ accumulator[9] = float32Round(
123
+ input[inputBase + 17] * c[QMF_ANALYSIS_TERMS * 9 + term] +
124
+ float32Round(
125
+ input[inputBase + 30] * c[QMF_ANALYSIS_TERMS * 9 + term + 1] +
126
+ accumulator[9]
127
+ )
128
+ )
129
+ extended10 =
130
+ accumulator[10] +
131
+ (input[inputBase + 18] * c[QMF_ANALYSIS_TERMS * 10 + term] +
132
+ input[inputBase + 29] * c[QMF_ANALYSIS_TERMS * 10 + term + 1])
133
+ accumulator[10] = float32Round(extended10)
134
+ extended11 =
135
+ accumulator[11] +
136
+ (input[inputBase + 19] * c[QMF_ANALYSIS_TERMS * 11 + term] +
137
+ input[inputBase + 28] * c[QMF_ANALYSIS_TERMS * 11 + term + 1])
138
+ accumulator[11] = float32Round(extended11)
139
+ extended12 =
140
+ accumulator[12] +
141
+ (input[inputBase + 20] * c[QMF_ANALYSIS_TERMS * 12 + term] +
142
+ input[inputBase + 27] * c[QMF_ANALYSIS_TERMS * 12 + term + 1])
143
+ accumulator[12] = float32Round(extended12)
144
+ extended13 =
145
+ accumulator[13] +
146
+ (input[inputBase + 21] * c[QMF_ANALYSIS_TERMS * 13 + term] +
147
+ input[inputBase + 26] * c[QMF_ANALYSIS_TERMS * 13 + term + 1])
148
+ accumulator[13] = float32Round(extended13)
149
+ extended14 =
150
+ accumulator[14] +
151
+ (input[inputBase + 22] * c[QMF_ANALYSIS_TERMS * 14 + term] +
152
+ input[inputBase + 25] * c[QMF_ANALYSIS_TERMS * 14 + term + 1])
153
+ accumulator[14] = float32Round(extended14)
154
+ const product15 =
155
+ input[inputBase + 24] * c[QMF_ANALYSIS_TERMS * 15 + term + 1] +
156
+ input[inputBase + 23] * c[QMF_ANALYSIS_TERMS * 15 + term]
157
+ extended15 = accumulator[15] + product15
158
+ accumulator[15] = float32Round(extended15)
159
+ }
160
+
161
+ extended[7] = extended7
162
+ extended[10] = extended10
163
+ extended[11] = extended11
164
+ extended[12] = extended12
165
+ extended[13] = extended13
166
+ extended[14] = extended14
167
+ extended[15] = extended15
168
+ }
169
+
170
+ /**
171
+ * Preserve the reference factorization and spill order of 16-band modulation.
172
+ *
173
+ * @param {Float32Array} polyphase
174
+ * @param {Float64Array} extended
175
+ * @param {Float32Array} output
176
+ */
177
+ function modulate16Band(polyphase, extended, output) {
178
+ const half = QMF_ANALYSIS_HALF_BUTTERFLY_SCALES
179
+ const cos32 = QMF_ANALYSIS_ODD_PI_OVER_32_COSINES
180
+ const cos16 = QMF_ANALYSIS_ODD_PI_OVER_16_COSINES
181
+ const cos64 = QMF_ANALYSIS_ODD_PI_OVER_64_COSINES
182
+ const pi8 = QMF_ANALYSIS_PI_OVER_8_BUTTERFLY_SCALES
183
+
184
+ const twoCosPi8 = pi8[0]
185
+ const cosPi4 = half[0]
186
+ const twoCosPi32 = cos32[0]
187
+ const twoCosPi16 = cos16[0]
188
+
189
+ const rotated7Extended = extended[7] * cos64[0]
190
+ const rotated0Extended = polyphase[0] * cos64[7]
191
+ const rotated13Extended = extended[13] * cos64[13]
192
+ const rotated7 = float32Round(rotated7Extended)
193
+ const rotated5 = float32Round(polyphase[5] * cos64[2])
194
+ const rotated6Extended = polyphase[6] * cos64[1]
195
+ const rotated6 = float32Round(rotated6Extended)
196
+ const rotated4Extended = polyphase[4] * cos64[3]
197
+ const rotated4 = float32Round(rotated4Extended)
198
+ const rotated3 = float32Round(polyphase[3] * cos64[4])
199
+ const rotated3Extended = rotated3
200
+ const rotated2 = float32Round(polyphase[2] * cos64[5])
201
+ const rotated0 = float32Round(rotated0Extended)
202
+ const rotated1Extended = polyphase[1] * cos64[6]
203
+ const rotated8Extended = polyphase[8] * cos64[8]
204
+ const rotated8 = float32Round(rotated8Extended)
205
+ const rotated14Extended = extended[14] * cos64[14]
206
+ const rotated14 = float32Round(rotated14Extended)
207
+ const rotated15Extended = extended[15] * cos64[15]
208
+ const rotated9Extended = polyphase[9] * cos64[9]
209
+ const rotated9 = float32Round(rotated9Extended)
210
+ const rotated13 = float32Round(rotated13Extended)
211
+ const rotated10Extended = extended[10] * cos64[10]
212
+ const rotated10 = float32Round(rotated10Extended)
213
+ const rotated11Extended = extended[11] * cos64[11]
214
+ const rotated11 = float32Round(rotated11Extended)
215
+ const rotated12Extended = extended[12] * cos64[12]
216
+
217
+ const sum0Extended = rotated0 + rotated8 + (rotated7 + rotated15Extended)
218
+ const mix0Term0Extended = (rotated7 - rotated15Extended) * twoCosPi32
219
+ const mix0Term1Extended = (rotated0 - rotated8) * cos32[7]
220
+ const mix0Extended = mix0Term0Extended + mix0Term1Extended
221
+ const mix0 = float32Round(mix0Extended)
222
+ const mixASum0Extended = rotated7 + rotated15Extended
223
+ const mixASum1Extended = rotated0 + rotated8
224
+ const mixAExtended = twoCosPi16 * (mixASum0Extended - mixASum1Extended)
225
+ const mix1 = float32Round(
226
+ twoCosPi16 * (mix0Term0Extended - mix0Term1Extended)
227
+ )
228
+
229
+ const sum1Extended = rotated14 + rotated1Extended + (rotated6 + rotated9)
230
+ const sum1Float32 = float32Round(sum1Extended)
231
+ const mix2Term0Extended = (rotated1Extended - rotated9) * cos32[6]
232
+ const mixBInnerExtended = rotated6 + rotated14 - rotated1Extended - rotated9
233
+ const mixBInnerFloat32 = float32Round(mixBInnerExtended)
234
+ const mixBExtended = cos16[1] * mixBInnerFloat32
235
+ const mixB = float32Round(mixBExtended)
236
+ const mix2Term1Extended = float32Round(rotated6 - rotated14) * cos32[1]
237
+ const mix2 = float32Round(mix2Term1Extended + mix2Term0Extended)
238
+ const mix3 = float32Round(cos16[1] * (mix2Term1Extended - mix2Term0Extended))
239
+
240
+ const sum2Pair0Extended = rotated5 + rotated13
241
+ const sum2Pair1Extended = rotated2 + rotated10
242
+ const sum2Extended = sum2Pair0Extended + sum2Pair1Extended
243
+ const sum2Float32 = float32Round(sum2Extended)
244
+ const mixCInnerExtended = sum2Pair0Extended - rotated2 - rotated10
245
+ const mixCInnerFloat32 = float32Round(mixCInnerExtended)
246
+ const mixCExtended = cos16[2] * mixCInnerFloat32
247
+ const mixC = float32Round(mixCExtended)
248
+ const mix4Term0Extended = (rotated5 - rotated13) * cos32[2]
249
+ const mix4Term1Extended = (rotated2 - rotated10) * cos32[5]
250
+ const mix4 = float32Round(mix4Term0Extended + mix4Term1Extended)
251
+ const mix5 = float32Round(cos16[2] * (mix4Term0Extended - mix4Term1Extended))
252
+
253
+ const sum3Extended =
254
+ rotated3 + rotated4 + rotated11Extended + rotated12Extended
255
+ const mix6Term0Extended = (rotated4 - rotated12Extended) * cos32[3]
256
+ const mixDInnerExtended =
257
+ rotated4 + rotated12Extended - rotated3Extended - rotated11Extended
258
+ const mixDExtended = cos16[3] * mixDInnerExtended
259
+ const mix6Term1Extended = (rotated3Extended - rotated11) * cos32[4]
260
+ const mix6 = float32Round(mix6Term0Extended + mix6Term1Extended)
261
+ const mix6Term0SpilledExtended = float32Round(mix6Term0Extended)
262
+ const mix7 = float32Round(
263
+ cos16[3] * (mix6Term0SpilledExtended - mix6Term1Extended)
264
+ )
265
+
266
+ const sumAllExtended = sum0Extended + sum1Float32 + sum2Float32 + sum3Extended
267
+ const out0Extended = sumAllExtended * 0.5
268
+ output[0] = float32Round(out0Extended)
269
+ const mid0Extended =
270
+ cosPi4 * (sum0Extended - sum1Float32 - sum2Float32 + sum3Extended)
271
+ const mid0 = float32Round(mid0Extended)
272
+ const diff03Extended = (sum0Extended - sum3Extended) * twoCosPi8
273
+ const diff12Extended = float32Round(sum1Float32 - sum2Float32) * pi8[1]
274
+ const diff12 = float32Round(diff12Extended)
275
+ const mid1Extended = half[1] * diff03Extended - half[2] * diff12
276
+ const mid1 = float32Round(mid1Extended)
277
+ const mid2Extended = (mixAExtended - mixDExtended) * pi8[2]
278
+ const mid2 = float32Round(mid2Extended)
279
+ const mid3Extended = (mixB - mixC) * pi8[3]
280
+ const avg2Pair0Extended = mixB + mixC
281
+ const avg2Pair1Extended = mixAExtended + mixDExtended
282
+ const avg2Extended = (avg2Pair0Extended + avg2Pair1Extended) * 0.5
283
+ const mid4Extended = (mid2 + mid3Extended) * 0.5 - avg2Extended
284
+ const mid4 = float32Round(mid4Extended)
285
+ const mid5Extended =
286
+ cosPi4 * (mixAExtended - mixB - mixC + mixDExtended) - mid4Extended
287
+ const mid6Extended = half[1] * mid2 - half[2] * mid3Extended - mid5Extended
288
+ const mid6 = float32Round(mid6Extended)
289
+
290
+ const avg1Extended = (mix0 + mix2 + mix4 + mix6) * 0.5
291
+ const out1Extended = avg1Extended - output[0]
292
+ output[1] = float32Round(out1Extended)
293
+ const out2Extended = avg2Extended - out1Extended
294
+ output[2] = float32Round(out2Extended)
295
+ const mid7Extended = cosPi4 * (mix0 - mix2 - mix4 + mix6)
296
+ const mid7 = float32Round(mid7Extended)
297
+ const mid8Extended = twoCosPi8 * (mix0 - mix6)
298
+ const mid8 = float32Round(mid8Extended)
299
+ const mid9 = float32Round(pi8[1] * float32Round(mix2 - mix4))
300
+ const mid10Extended = half[1] * mid8 - half[2] * mid9
301
+ const mid10 = float32Round(mid10Extended)
302
+ const avg3Extended = (mix1 + mix3 + mix5 + mix7) * 0.5 - avg1Extended
303
+ const out3Extended = avg3Extended - out2Extended
304
+ output[3] = float32Round(out3Extended)
305
+ const mid11Extended = (mid8 + mid9) * 0.5 - avg3Extended
306
+ const mid11 = float32Round(mid11Extended)
307
+ const out4Extended = (diff03Extended + diff12) * 0.5 - out3Extended
308
+ output[4] = float32Round(out4Extended)
309
+ const mid12Extended = (mix1 - mix7) * pi8[2]
310
+ const mid13Extended = (mix3 - mix5) * pi8[3]
311
+ const out5Extended = mid11Extended - out4Extended
312
+ output[5] = float32Round(out5Extended)
313
+ const out6Extended = mid4 - out5Extended
314
+ output[6] = float32Round(out6Extended)
315
+ const mid14Extended =
316
+ mid12Extended + mid13Extended - mix1 - mix3 - mix5 - mix7
317
+ const mid14HalfExtended = mid14Extended * 0.5
318
+ const mid15Extended = cosPi4 * (mix1 - mix3 - mix5 + mix7) - mid14HalfExtended
319
+ const mid16Extended =
320
+ half[1] * mid12Extended - half[2] * mid13Extended - mid15Extended
321
+ const mid17Extended = mid14HalfExtended - mid11
322
+ const out7Extended = mid17Extended - output[6]
323
+ output[7] = float32Round(out7Extended)
324
+ const mid18Extended = mid7 - mid17Extended
325
+ const out8Extended = mid0 - out7Extended
326
+ output[8] = float32Round(out8Extended)
327
+ const out9Extended = mid18Extended - out8Extended
328
+ output[9] = float32Round(out9Extended)
329
+ const mid19Extended = mid15Extended - mid18Extended
330
+ const out10Extended = mid5Extended - out9Extended
331
+ output[10] = float32Round(out10Extended)
332
+ const out11Extended = mid19Extended - out10Extended
333
+ output[11] = float32Round(out11Extended)
334
+ const mid20Extended = mid10 - mid19Extended
335
+ const out12Extended = mid1 - out11Extended
336
+ output[12] = float32Round(out12Extended)
337
+ const out13Extended = mid20Extended - out12Extended
338
+ output[13] = float32Round(out13Extended)
339
+ const mid21Extended = mid16Extended - mid20Extended
340
+ const out14Extended = mid6 - out13Extended
341
+ output[14] = float32Round(out14Extended)
342
+ const out15Extended = mid21Extended - out14Extended
343
+ output[15] = float32Round(out15Extended)
344
+ }
345
+
346
+ /**
347
+ * Transform one complete time window into caller-owned QMF subband rows.
348
+ *
349
+ * @param {Float32Array} input Current samples preceded by the required history.
350
+ * @param {Float32Array[]} outputBands Caller-owned chronological subband rows.
351
+ * @param {QmfScratch} scratch Reusable component and modulation storage.
352
+ * @returns {Float32Array[]} The completed subband rows.
353
+ */
354
+ export function analyzeQmf(input, outputBands, scratch) {
355
+ if (
356
+ !(input instanceof Float32Array) ||
357
+ outputBands?.length !== QMF_BANDS ||
358
+ outputBands.some(
359
+ (band) =>
360
+ !(band instanceof Float32Array) || band.length !== outputBands[0].length
361
+ ) ||
362
+ !scratch ||
363
+ scratch.polyphase?.length !== QMF_BANDS ||
364
+ scratch.extended?.length !== QMF_BANDS ||
365
+ scratch.bands?.length !== QMF_BANDS
366
+ ) {
367
+ throw new RangeError('QMF analysis geometry is invalid')
368
+ }
369
+
370
+ const sampleCount = outputBands[0].length
371
+ const requiredSamples =
372
+ QMF_ANALYSIS_START_SAMPLE +
373
+ Math.max(0, sampleCount - 1) * QMF_ANALYSIS_STEP_SAMPLES +
374
+ QMF_ANALYSIS_WINDOW_SAMPLES
375
+ if (input.length < requiredSamples) {
376
+ throw new RangeError('QMF analysis window is incomplete')
377
+ }
378
+
379
+ const { polyphase, extended, bands } = scratch
380
+
381
+ for (let sample = 0; sample < sampleCount; sample++) {
382
+ const inputOffset =
383
+ QMF_ANALYSIS_START_SAMPLE + sample * QMF_ANALYSIS_STEP_SAMPLES
384
+ if (QMF_BANDS === 2) {
385
+ for (let component = 0; component < QMF_BANDS; component++) {
386
+ const termOffset = component * QMF_ANALYSIS_TERMS
387
+ let sum = 0
388
+ for (let term = 0; term < QMF_ANALYSIS_TERMS; term++) {
389
+ const index = termOffset + term
390
+ sum +=
391
+ input[inputOffset + QMF_ANALYSIS_SAMPLE_OFFSETS[index]] *
392
+ QMF_ANALYSIS_FILTER_COEFFICIENTS[index]
393
+ }
394
+ extended[component] = sum
395
+ }
396
+ for (let band = 0; band < QMF_BANDS; band++) {
397
+ const coefficientOffset = band * QMF_BANDS
398
+ let sum = 0
399
+ for (let component = 0; component < QMF_BANDS; component++) {
400
+ sum +=
401
+ extended[component] *
402
+ QMF_ANALYSIS_MODULATION_COEFFICIENTS[coefficientOffset + component]
403
+ }
404
+ outputBands[band][sample] = sum * QMF_ANALYSIS_MODULATION_SCALE
405
+ }
406
+ continue
407
+ }
408
+ if (QMF_BANDS === 16) {
409
+ filter16Band(input, inputOffset, polyphase, extended)
410
+ modulate16Band(polyphase, extended, bands)
411
+ for (let band = 0; band < QMF_BANDS; band++) {
412
+ outputBands[band][sample] = bands[band]
413
+ }
414
+ continue
415
+ }
416
+
417
+ for (let component = 0; component < QMF_BANDS; component++) {
418
+ const termOffset = component * QMF_ANALYSIS_TERMS
419
+ let sum = 0
420
+ for (let term = 0; term < QMF_ANALYSIS_TERMS; term++) {
421
+ const index = termOffset + term
422
+ sum +=
423
+ input[inputOffset + QMF_ANALYSIS_SAMPLE_OFFSETS[index]] *
424
+ QMF_ANALYSIS_FILTER_COEFFICIENTS[index]
425
+ }
426
+ polyphase[component] = sum
427
+ }
428
+
429
+ for (let band = 0; band < QMF_BANDS; band++) {
430
+ const coefficientOffset = band * QMF_BANDS
431
+ let sum = 0
432
+ for (let component = 0; component < QMF_BANDS; component++) {
433
+ sum +=
434
+ polyphase[component] *
435
+ QMF_ANALYSIS_MODULATION_COEFFICIENTS[coefficientOffset + component]
436
+ }
437
+ bands[band] = sum * QMF_ANALYSIS_MODULATION_SCALE
438
+ outputBands[band][sample] = bands[band]
439
+ }
440
+ }
441
+ return outputBands
442
+ }
443
+
444
+ /**
445
+ * Fold caller-owned QMF subband rows through one circular synthesis delay.
446
+ *
447
+ * @param {Float32Array[]} inputBands Chronological subband rows.
448
+ * @param {Float32Array} delay Caller-owned component delay ring.
449
+ * @param {number} delayRow Current delay-ring row.
450
+ * @param {Float32Array} output Caller-owned interleaved PCM output.
451
+ * @param {QmfScratch} scratch Reusable component and modulation storage.
452
+ * @returns {number} Next delay-ring row.
453
+ */
454
+ export function synthesizeQmf(inputBands, delay, delayRow, output, scratch) {
455
+ if (
456
+ inputBands?.length !== QMF_BANDS ||
457
+ inputBands.some(
458
+ (band) =>
459
+ !(band instanceof Float32Array) || band.length !== inputBands[0].length
460
+ ) ||
461
+ !(delay instanceof Float32Array) ||
462
+ delay.length !== QMF_SYNTHESIS_DELAY_ROWS * QMF_BANDS ||
463
+ !Number.isInteger(delayRow) ||
464
+ delayRow < 0 ||
465
+ delayRow >= QMF_SYNTHESIS_DELAY_ROWS ||
466
+ !(output instanceof Float32Array) ||
467
+ output.length < inputBands[0].length * QMF_BANDS ||
468
+ !scratch ||
469
+ scratch.polyphase?.length !== QMF_BANDS ||
470
+ scratch.bands?.length !== QMF_BANDS
471
+ ) {
472
+ throw new RangeError('QMF synthesis geometry is invalid')
473
+ }
474
+
475
+ const { polyphase, bands } = scratch
476
+ const sampleCount = inputBands[0].length
477
+
478
+ for (let sample = 0; sample < sampleCount; sample++) {
479
+ for (let band = 0; band < QMF_BANDS; band++) {
480
+ bands[band] = inputBands[band][sample]
481
+ }
482
+
483
+ const delayOffset = delayRow * QMF_BANDS
484
+ for (let component = 0; component < QMF_BANDS; component++) {
485
+ const coefficientOffset = component * QMF_BANDS
486
+ let sum = 0
487
+ for (let band = 0; band < QMF_BANDS; band++) {
488
+ sum +=
489
+ QMF_SYNTHESIS_MODULATION_COEFFICIENTS[coefficientOffset + band] *
490
+ bands[band]
491
+ }
492
+ polyphase[component] = sum
493
+ delay[delayOffset + component] = polyphase[component]
494
+ }
495
+
496
+ const outputOffset = sample * QMF_BANDS
497
+ for (let outputSample = 0; outputSample < QMF_BANDS; outputSample++) {
498
+ const termOffset = outputSample * QMF_SYNTHESIS_TERMS
499
+ let sum = 0
500
+ if (QMF_BANDS === 16) {
501
+ for (let term = 0; term < QMF_SYNTHESIS_TERMS; term += 2) {
502
+ const first = termOffset + term
503
+ const second = first + 1
504
+ const firstRow =
505
+ (delayRow + QMF_SYNTHESIS_DELAY_ROW_OFFSETS[first]) %
506
+ QMF_SYNTHESIS_DELAY_ROWS
507
+ const secondRow =
508
+ (delayRow + QMF_SYNTHESIS_DELAY_ROW_OFFSETS[second]) %
509
+ QMF_SYNTHESIS_DELAY_ROWS
510
+ sum = float32Round(
511
+ delay[
512
+ firstRow * QMF_BANDS + QMF_SYNTHESIS_DELAY_COMPONENTS[first]
513
+ ] *
514
+ QMF_SYNTHESIS_FILTER_COEFFICIENTS[first] +
515
+ delay[
516
+ secondRow * QMF_BANDS + QMF_SYNTHESIS_DELAY_COMPONENTS[second]
517
+ ] *
518
+ QMF_SYNTHESIS_FILTER_COEFFICIENTS[second] +
519
+ sum
520
+ )
521
+ }
522
+ output[outputOffset + outputSample] = sum
523
+ continue
524
+ }
525
+ for (let term = 0; term < QMF_SYNTHESIS_TERMS; term++) {
526
+ const index = termOffset + term
527
+ const row =
528
+ (delayRow + QMF_SYNTHESIS_DELAY_ROW_OFFSETS[index]) %
529
+ QMF_SYNTHESIS_DELAY_ROWS
530
+ sum +=
531
+ delay[row * QMF_BANDS + QMF_SYNTHESIS_DELAY_COMPONENTS[index]] *
532
+ QMF_SYNTHESIS_FILTER_COEFFICIENTS[index]
533
+ }
534
+ output[outputOffset + outputSample] = sum
535
+ }
536
+ delayRow = delayRow === 0 ? QMF_SYNTHESIS_DELAY_ROWS - 1 : delayRow - 1
537
+ }
538
+ return delayRow
539
+ }
package/codec/state.js CHANGED
@@ -2,18 +2,35 @@
2
2
  * Carta1 stream state and reusable scratch.
3
3
  */
4
4
 
5
- import { QMF_DELAY, QMF_HIGH_BAND_DELAY } from './core/constants.js'
5
+ import {
6
+ QMF_ANALYSIS_DELAY_SAMPLES,
7
+ QMF_BANDS,
8
+ QMF_HIGH_BAND_DELAY,
9
+ QMF_SYNTHESIS_DELAY_SAMPLES,
10
+ } from './core/constants.js'
11
+
12
+ /** Persistent delay state for one two-band QMF synthesis stage. */
13
+ class QmfSynthesisState {
14
+ constructor() {
15
+ this.delay = new Float32Array(QMF_SYNTHESIS_DELAY_SAMPLES)
16
+ this.delayRow = 0
17
+ }
18
+ }
6
19
 
7
20
  /**
8
21
  * Values with meaning across chronological frames.
9
22
  */
10
23
  class History {
11
24
  constructor() {
12
- this.qmfDelays = {
13
- lowBand: new Float32Array(QMF_DELAY),
14
- midBand: new Float32Array(QMF_DELAY),
15
- highBand: new Float32Array(QMF_HIGH_BAND_DELAY),
25
+ this.qmfAnalysisDelays = {
26
+ lowBand: new Float32Array(QMF_ANALYSIS_DELAY_SAMPLES),
27
+ midBand: new Float32Array(QMF_ANALYSIS_DELAY_SAMPLES),
28
+ }
29
+ this.qmfSynthesisStates = {
30
+ lowBand: new QmfSynthesisState(),
31
+ midBand: new QmfSynthesisState(),
16
32
  }
33
+ this.qmfHighBandDelay = new Float32Array(QMF_HIGH_BAND_DELAY)
17
34
 
18
35
  this.transientDetection = [
19
36
  new Float32Array(64),
@@ -47,30 +64,33 @@ class Scratch {
47
64
  512: new Float32Array(512),
48
65
  }
49
66
 
50
- this.qmfWorkBuffers = {
51
- delay: {
52
- 128: new Float32Array(QMF_DELAY + 128),
53
- 256: new Float32Array(QMF_DELAY + 256),
54
- 512: new Float32Array(QMF_DELAY + 512),
67
+ this.qmfScratch = {
68
+ analysisWindows: {
69
+ 128: new Float32Array(QMF_ANALYSIS_DELAY_SAMPLES + 128),
70
+ 256: new Float32Array(QMF_ANALYSIS_DELAY_SAMPLES + 256),
71
+ 512: new Float32Array(QMF_ANALYSIS_DELAY_SAMPLES + 512),
55
72
  },
56
- highBandDelay: {
73
+ highBandWindows: {
57
74
  128: new Float32Array(QMF_HIGH_BAND_DELAY + 128),
58
75
  256: new Float32Array(QMF_HIGH_BAND_DELAY + 256),
59
76
  },
77
+ polyphase: new Float32Array(QMF_BANDS),
78
+ extended: new Float64Array(QMF_BANDS),
79
+ bands: new Float32Array(QMF_BANDS),
60
80
  }
61
81
 
62
82
  this.mdctBuffers = {
63
83
  16: {
64
84
  real: new Float32Array(16),
65
- imag: new Float32Array(16),
85
+ imaginary: new Float32Array(16),
66
86
  },
67
87
  64: {
68
88
  real: new Float32Array(64),
69
- imag: new Float32Array(64),
89
+ imaginary: new Float32Array(64),
70
90
  },
71
91
  128: {
72
92
  real: new Float32Array(128),
73
- imag: new Float32Array(128),
93
+ imaginary: new Float32Array(128),
74
94
  },
75
95
  }
76
96
 
@@ -5,19 +5,43 @@
5
5
  import {
6
6
  MDCT_SHORT_BLOCK_SIZE,
7
7
  MDCT_SIZE_LONG,
8
+ MDCT_SIZE_MID,
9
+ MDCT_SIZE_SHORT,
8
10
  MDCT_TAIL_WINDOW_SIZE,
9
11
  } from '../core/constants.js'
10
12
  import { extractBandCoefficients } from '../core/geometry.js'
11
13
  import { MDCT_BAND_CONFIGS, WINDOW_SHORT } from '../core/tables.js'
12
- import {
13
- imdct64,
14
- imdct256,
15
- imdct512,
16
- overlapAdd,
17
- reverseSpectrum,
18
- } from '../signal/spectrum.js'
14
+ import { IMDCT } from '../signal/mdct.js'
15
+ import { reverseSpectrum } from '../signal/order.js'
19
16
  import { throwError } from '../utils.js'
20
17
 
18
+ const shortImdct = new IMDCT(MDCT_SIZE_SHORT, MDCT_SIZE_SHORT * 8)
19
+ const middleImdct = new IMDCT(MDCT_SIZE_MID, MDCT_SIZE_MID * 8)
20
+ const longImdct = new IMDCT(MDCT_SIZE_LONG, MDCT_SIZE_LONG * 4)
21
+
22
+ /**
23
+ * Combine adjacent inverse-transform blocks through the codec window.
24
+ *
25
+ * @param {Float32Array} previous Previous block samples.
26
+ * @param {Float32Array} current Current block samples.
27
+ * @param {Float32Array} window Window coefficients.
28
+ * @returns {Float32Array} Overlap-added samples.
29
+ */
30
+ export function overlapAdd(previous, current, window) {
31
+ const size = previous.length
32
+ const output = new Float32Array(size * 2)
33
+ for (let index = 0; index < size; index++) {
34
+ const headWindow = window[index]
35
+ const tailWindow = window[2 * size - 1 - index]
36
+ const previousSample = previous[index]
37
+ const currentSample = current[size - 1 - index]
38
+ output[index] = previousSample * tailWindow - currentSample * headWindow
39
+ output[2 * size - 1 - index] =
40
+ previousSample * headWindow + currentSample * tailWindow
41
+ }
42
+ return output
43
+ }
44
+
21
45
  /**
22
46
  * Synthesize time-domain bands from the ATRAC1 spectrum.
23
47
  *
@@ -30,7 +54,7 @@ export function synthesizeSpectrum(context) {
30
54
  context?.bufferPool ??
31
55
  throwError('synthesizeSpectrum: bufferPool is required')
32
56
  const overlapBuffers = bufferPool.imdctOverlap
33
- const transformFunctions = [imdct256, imdct256, imdct512]
57
+ const transformFunctions = [middleImdct, middleImdct, longImdct]
34
58
 
35
59
  /**
36
60
  * @param {Float32Array} coefficients
@@ -151,7 +175,10 @@ export function synthesizeSpectrum(context) {
151
175
  )
152
176
  }
153
177
 
154
- const inverse = imdct64.transform(blockSpectrum, bufferPool.mdctBuffers)
178
+ const inverse = shortImdct.transform(
179
+ blockSpectrum,
180
+ bufferPool.mdctBuffers
181
+ )
155
182
  const inverseStart = inverse.length / 4
156
183
  for (let i = 0; i < MDCT_SHORT_BLOCK_SIZE; i++) {
157
184
  inverseBuffer[start + i] = inverse[inverseStart + i]