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.
- package/README.md +1 -1
- package/bin/cli.js +4 -29
- package/codec/analysis/bands.js +41 -22
- package/codec/analysis/blocks.js +1 -1
- package/codec/analysis/spectrum.js +8 -8
- package/codec/boundary/container.js +27 -5
- package/codec/core/constants.js +10 -1
- package/codec/core/tables.js +99 -1
- package/codec/dequantization.js +2 -2
- package/codec/index.js +4 -4
- package/codec/quantization/stage.js +6 -2
- package/codec/signal/fft.js +37 -38
- package/codec/signal/{spectrum.js → mdct.js} +37 -80
- package/codec/signal/order.js +16 -0
- package/codec/signal/qmf.js +539 -0
- package/codec/state.js +34 -14
- package/codec/synthesis/bands.js +36 -9
- package/codec/synthesis/pcm.js +35 -21
- package/dist/carta1-worker.min.js +1 -1
- package/dist/carta1-worker.min.js.map +1 -1
- package/dist/carta1.min.js +1 -1
- package/dist/carta1.min.js.map +1 -1
- package/package.json +1 -1
- package/codec/signal/bands.js +0 -108
|
@@ -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 {
|
|
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.
|
|
13
|
-
lowBand: new Float32Array(
|
|
14
|
-
midBand: new Float32Array(
|
|
15
|
-
|
|
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.
|
|
51
|
-
|
|
52
|
-
128: new Float32Array(
|
|
53
|
-
256: new Float32Array(
|
|
54
|
-
512: new Float32Array(
|
|
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
|
-
|
|
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
|
-
|
|
85
|
+
imaginary: new Float32Array(16),
|
|
66
86
|
},
|
|
67
87
|
64: {
|
|
68
88
|
real: new Float32Array(64),
|
|
69
|
-
|
|
89
|
+
imaginary: new Float32Array(64),
|
|
70
90
|
},
|
|
71
91
|
128: {
|
|
72
92
|
real: new Float32Array(128),
|
|
73
|
-
|
|
93
|
+
imaginary: new Float32Array(128),
|
|
74
94
|
},
|
|
75
95
|
}
|
|
76
96
|
|
package/codec/synthesis/bands.js
CHANGED
|
@@ -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
|
-
|
|
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 = [
|
|
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 =
|
|
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]
|