carta1 1.0.16 → 1.0.18
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/codec/core/constants.js +11 -0
- package/codec/pipeline/decoder.js +184 -71
- package/codec/pipeline/encoder.js +145 -53
- package/codec/transforms/fft.js +175 -89
- package/codec/utils.js +40 -0
- 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/transforms/fft.js
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Carta1 Audio Codec - Fast Fourier Transform
|
|
3
|
-
*
|
|
4
|
-
* This module implements a radix-2 Cooley-Tukey FFT algorithm for frequency domain
|
|
5
|
-
* analysis used in psychoacoustic modeling and transient detection.
|
|
3
|
+
* Optimized radix-2 Cooley-Tukey FFT implementation
|
|
6
4
|
*/
|
|
7
5
|
export class FFT {
|
|
8
|
-
/**
|
|
9
|
-
* @private
|
|
10
|
-
* Caches pre-computed tables for different FFT sizes.
|
|
11
|
-
* @type {Map<number, { bitRev: Uint32Array, cos: Float32Array, sin: Float32Array }>}
|
|
12
|
-
*/
|
|
13
6
|
static _cache = new Map()
|
|
14
7
|
|
|
15
8
|
/**
|
|
16
9
|
* Perform an in-place forward FFT on complex data.
|
|
17
|
-
* @param {Float32Array} real - Real part of the input/output data
|
|
18
|
-
* @param {Float32Array} imag - Imaginary part of the input/output data
|
|
10
|
+
* @param {Float32Array} real - Real part of the input/output data
|
|
11
|
+
* @param {Float32Array} imag - Imaginary part of the input/output data
|
|
19
12
|
*/
|
|
20
13
|
static fft(real, imag) {
|
|
21
14
|
const n = real.length
|
|
@@ -29,110 +22,203 @@ export class FFT {
|
|
|
29
22
|
|
|
30
23
|
let cache = FFT._cache.get(n)
|
|
31
24
|
if (!cache) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const bitRev = new Uint32Array(n)
|
|
35
|
-
for (let i = 1; i < n; i++) {
|
|
36
|
-
bitRev[i] = (bitRev[i >>> 1] >>> 1) | ((i & 1) << (log2n - 1))
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const cosTbl = new Float32Array(n / 2)
|
|
40
|
-
const sinTbl = new Float32Array(n / 2)
|
|
41
|
-
for (let i = 0; i < n / 2; i++) {
|
|
42
|
-
const angle = (-2 * Math.PI * i) / n
|
|
43
|
-
cosTbl[i] = Math.cos(angle)
|
|
44
|
-
sinTbl[i] = Math.sin(angle)
|
|
45
|
-
}
|
|
46
|
-
cache = { bitRev, cos: cosTbl, sin: sinTbl }
|
|
25
|
+
cache = FFT._buildCache(n)
|
|
47
26
|
FFT._cache.set(n, cache)
|
|
48
27
|
}
|
|
49
28
|
|
|
50
29
|
const { bitRev, cos: cosTbl, sin: sinTbl } = cache
|
|
51
30
|
|
|
31
|
+
// Bit-reversal permutation with improved memory access pattern
|
|
52
32
|
for (let i = 0; i < n; i++) {
|
|
53
33
|
const j = bitRev[i]
|
|
54
34
|
if (j > i) {
|
|
55
|
-
|
|
56
|
-
|
|
35
|
+
// Use temporary variables to reduce array access
|
|
36
|
+
const tempR = real[i]
|
|
37
|
+
const tempI = imag[i]
|
|
57
38
|
real[i] = real[j]
|
|
58
|
-
real[j] = temp
|
|
59
|
-
temp = imag[i]
|
|
60
39
|
imag[i] = imag[j]
|
|
61
|
-
|
|
40
|
+
real[j] = tempR
|
|
41
|
+
imag[j] = tempI
|
|
62
42
|
}
|
|
63
43
|
}
|
|
64
44
|
|
|
45
|
+
// Optimized butterfly computations for small strides
|
|
46
|
+
// Stride 2 - no twiddle factors needed
|
|
65
47
|
for (let i = 0; i < n; i += 2) {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
real[i] =
|
|
74
|
-
imag[i] =
|
|
75
|
-
real[i1] = evenReal - tReal
|
|
76
|
-
imag[i1] = evenImag - tImag
|
|
48
|
+
const evenR = real[i]
|
|
49
|
+
const evenI = imag[i]
|
|
50
|
+
const oddR = real[i + 1]
|
|
51
|
+
const oddI = imag[i + 1]
|
|
52
|
+
|
|
53
|
+
real[i] = evenR + oddR
|
|
54
|
+
imag[i] = evenI + oddI
|
|
55
|
+
real[i + 1] = evenR - oddR
|
|
56
|
+
imag[i + 1] = evenI - oddI
|
|
77
57
|
}
|
|
78
58
|
|
|
59
|
+
// Stride 4 - simple twiddle factors
|
|
79
60
|
if (n >= 4) {
|
|
80
61
|
for (let i = 0; i < n; i += 4) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
62
|
+
// First pair (W^0 = 1)
|
|
63
|
+
let evenR = real[i]
|
|
64
|
+
let evenI = imag[i]
|
|
65
|
+
let oddR = real[i + 2]
|
|
66
|
+
let oddI = imag[i + 2]
|
|
67
|
+
|
|
68
|
+
real[i] = evenR + oddR
|
|
69
|
+
imag[i] = evenI + oddI
|
|
70
|
+
real[i + 2] = evenR - oddR
|
|
71
|
+
imag[i + 2] = evenI - oddI
|
|
72
|
+
|
|
73
|
+
// Second pair (W^1 = -i)
|
|
74
|
+
evenR = real[i + 1]
|
|
75
|
+
evenI = imag[i + 1]
|
|
76
|
+
oddR = real[i + 3]
|
|
77
|
+
oddI = imag[i + 3]
|
|
78
|
+
|
|
79
|
+
const tR = oddI // Multiplication by -i
|
|
80
|
+
const tI = -oddR
|
|
81
|
+
|
|
82
|
+
real[i + 1] = evenR + tR
|
|
83
|
+
imag[i + 1] = evenI + tI
|
|
84
|
+
real[i + 3] = evenR - tR
|
|
85
|
+
imag[i + 3] = evenI - tI
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Stride 8 - unrolled for better performance
|
|
90
|
+
if (n >= 8) {
|
|
91
|
+
for (let i = 0; i < n; i += 8) {
|
|
92
|
+
// W^0 = 1
|
|
93
|
+
let idx0 = i
|
|
94
|
+
let idx4 = i + 4
|
|
95
|
+
let r0 = real[idx0],
|
|
96
|
+
i0 = imag[idx0]
|
|
97
|
+
let r4 = real[idx4],
|
|
98
|
+
i4 = imag[idx4]
|
|
99
|
+
real[idx0] = r0 + r4
|
|
100
|
+
imag[idx0] = i0 + i4
|
|
101
|
+
real[idx4] = r0 - r4
|
|
102
|
+
imag[idx4] = i0 - i4
|
|
103
|
+
|
|
104
|
+
// W^1 = cos(-π/4) - i*sin(-π/4)
|
|
105
|
+
let idx1 = i + 1
|
|
106
|
+
let idx5 = i + 5
|
|
107
|
+
r0 = real[idx1]
|
|
108
|
+
i0 = imag[idx1]
|
|
109
|
+
r4 = real[idx5]
|
|
110
|
+
i4 = imag[idx5]
|
|
111
|
+
const sqrt2_2 = 0.7071067811865476
|
|
112
|
+
let tR = sqrt2_2 * (r4 + i4)
|
|
113
|
+
let tI = sqrt2_2 * (i4 - r4)
|
|
114
|
+
real[idx1] = r0 + tR
|
|
115
|
+
imag[idx1] = i0 + tI
|
|
116
|
+
real[idx5] = r0 - tR
|
|
117
|
+
imag[idx5] = i0 - tI
|
|
118
|
+
|
|
119
|
+
// W^2 = -i
|
|
120
|
+
let idx2 = i + 2
|
|
121
|
+
let idx6 = i + 6
|
|
122
|
+
r0 = real[idx2]
|
|
123
|
+
i0 = imag[idx2]
|
|
124
|
+
r4 = real[idx6]
|
|
125
|
+
i4 = imag[idx6]
|
|
126
|
+
tR = i4
|
|
127
|
+
tI = -r4
|
|
128
|
+
real[idx2] = r0 + tR
|
|
129
|
+
imag[idx2] = i0 + tI
|
|
130
|
+
real[idx6] = r0 - tR
|
|
131
|
+
imag[idx6] = i0 - tI
|
|
132
|
+
|
|
133
|
+
// W^3 = cos(-3π/4) - i*sin(-3π/4)
|
|
134
|
+
let idx3 = i + 3
|
|
135
|
+
let idx7 = i + 7
|
|
136
|
+
r0 = real[idx3]
|
|
137
|
+
i0 = imag[idx3]
|
|
138
|
+
r4 = real[idx7]
|
|
139
|
+
i4 = imag[idx7]
|
|
140
|
+
tR = sqrt2_2 * (-r4 + i4)
|
|
141
|
+
tI = sqrt2_2 * (-r4 - i4)
|
|
142
|
+
real[idx3] = r0 + tR
|
|
143
|
+
imag[idx3] = i0 + tI
|
|
144
|
+
real[idx7] = r0 - tR
|
|
145
|
+
imag[idx7] = i0 - tI
|
|
106
146
|
}
|
|
107
147
|
}
|
|
108
148
|
|
|
109
|
-
|
|
149
|
+
// General Cooley-Tukey butterflies with optimized inner loop
|
|
150
|
+
for (let stride = 16; stride <= n; stride <<= 1) {
|
|
110
151
|
const halfStride = stride >>> 1
|
|
111
152
|
const step = n / stride
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
|
|
154
|
+
// Process each group
|
|
155
|
+
for (let grp = 0; grp < n; grp += stride) {
|
|
156
|
+
// First butterfly (W^0 = 1) - no multiplication needed
|
|
157
|
+
let evenIdx = grp
|
|
158
|
+
let oddIdx = grp + halfStride
|
|
159
|
+
const r0 = real[evenIdx]
|
|
160
|
+
const i0 = imag[evenIdx]
|
|
161
|
+
const r1 = real[oddIdx]
|
|
162
|
+
const i1 = imag[oddIdx]
|
|
163
|
+
real[evenIdx] = r0 + r1
|
|
164
|
+
imag[evenIdx] = i0 + i1
|
|
165
|
+
real[oddIdx] = r0 - r1
|
|
166
|
+
imag[oddIdx] = i0 - i1
|
|
167
|
+
|
|
168
|
+
// Remaining butterflies with twiddle factors
|
|
169
|
+
let twiddleIdx = step
|
|
170
|
+
for (let k = 1; k < halfStride; k++) {
|
|
171
|
+
evenIdx = grp + k
|
|
172
|
+
oddIdx = evenIdx + halfStride
|
|
173
|
+
|
|
174
|
+
const cosW = cosTbl[twiddleIdx]
|
|
175
|
+
const sinW = sinTbl[twiddleIdx]
|
|
176
|
+
|
|
177
|
+
const oddR = real[oddIdx]
|
|
178
|
+
const oddI = imag[oddIdx]
|
|
179
|
+
const tR = oddR * cosW - oddI * sinW
|
|
180
|
+
const tI = oddR * sinW + oddI * cosW
|
|
181
|
+
|
|
182
|
+
const evenR = real[evenIdx]
|
|
183
|
+
const evenI = imag[evenIdx]
|
|
184
|
+
|
|
185
|
+
real[evenIdx] = evenR + tR
|
|
186
|
+
imag[evenIdx] = evenI + tI
|
|
187
|
+
real[oddIdx] = evenR - tR
|
|
188
|
+
imag[oddIdx] = evenI - tI
|
|
189
|
+
|
|
190
|
+
twiddleIdx += step
|
|
134
191
|
}
|
|
135
192
|
}
|
|
136
193
|
}
|
|
137
194
|
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Build cache tables for FFT of size n
|
|
198
|
+
* @private
|
|
199
|
+
*/
|
|
200
|
+
static _buildCache(n) {
|
|
201
|
+
const log2n = Math.log2(n) | 0
|
|
202
|
+
|
|
203
|
+
// Optimized bit-reversal using iterative approach
|
|
204
|
+
const bitRev = new Uint32Array(n)
|
|
205
|
+
for (let i = 1; i < n; i++) {
|
|
206
|
+
bitRev[i] = (bitRev[i >>> 1] >>> 1) | ((i & 1) << (log2n - 1))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Pre-compute twiddle factors with better precision
|
|
210
|
+
const halfN = n >>> 1
|
|
211
|
+
const cosTbl = new Float32Array(halfN)
|
|
212
|
+
const sinTbl = new Float32Array(halfN)
|
|
213
|
+
|
|
214
|
+
// Use higher precision calculation then cast to Float32
|
|
215
|
+
const angleStep = (-2 * Math.PI) / n
|
|
216
|
+
for (let i = 0; i < halfN; i++) {
|
|
217
|
+
const angle = angleStep * i
|
|
218
|
+
cosTbl[i] = Math.fround(Math.cos(angle))
|
|
219
|
+
sinTbl[i] = Math.fround(Math.sin(angle))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { bitRev, cos: cosTbl, sin: sinTbl }
|
|
223
|
+
}
|
|
138
224
|
}
|
package/codec/utils.js
CHANGED
|
@@ -146,6 +146,46 @@ export function withStereo(monoTransform, ...args) {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Reverses the spectral order of coefficients for mid/high band processing
|
|
151
|
+
*
|
|
152
|
+
* Used in both MDCT and IMDCT stages to handle spectral reversal required
|
|
153
|
+
* by the ATRAC1 format for mid and high frequency bands.
|
|
154
|
+
*
|
|
155
|
+
* @param {Float32Array} spectrum - Input spectrum coefficients
|
|
156
|
+
* @param {Object} reversalBuffers - Pre-allocated buffers for different spectrum sizes
|
|
157
|
+
* @returns {Float32Array} Spectrum with reversed coefficient order
|
|
158
|
+
*/
|
|
159
|
+
export function reverseSpectrum(spectrum, reversalBuffers) {
|
|
160
|
+
const reversed = reversalBuffers[spectrum.length]
|
|
161
|
+
for (let i = 0; i < spectrum.length; i++) {
|
|
162
|
+
reversed[i] = spectrum[spectrum.length - 1 - i]
|
|
163
|
+
}
|
|
164
|
+
return reversed
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Calculates the starting offset for a frequency band in the coefficient array
|
|
169
|
+
*
|
|
170
|
+
* @param {number} bandIndex - Band index (0=low, 1=mid, 2=high)
|
|
171
|
+
* @returns {number} Starting offset in the 512-sample coefficient array
|
|
172
|
+
*/
|
|
173
|
+
export function calculateBandOffset(bandIndex) {
|
|
174
|
+
return bandIndex === 0 ? 0 : bandIndex === 1 ? 128 : 256
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Extracts coefficients for a specific frequency band from the full coefficient array
|
|
179
|
+
*
|
|
180
|
+
* @param {Float32Array} coefficients - Full 512-sample coefficient array
|
|
181
|
+
* @param {number} bandIndex - Band index (0=low, 1=mid, 2=high)
|
|
182
|
+
* @returns {Float32Array} Subarray containing coefficients for the specified band
|
|
183
|
+
*/
|
|
184
|
+
export function extractBandCoefficients(coefficients, bandIndex) {
|
|
185
|
+
const offsets = [0, 128, 256, 512]
|
|
186
|
+
return coefficients.subarray(offsets[bandIndex], offsets[bandIndex + 1])
|
|
187
|
+
}
|
|
188
|
+
|
|
149
189
|
// Stereo-adapted utility functions
|
|
150
190
|
export const withStereoFlushSamples = withStereo(withFlushSamples)
|
|
151
191
|
export const withStereoDelayCompensation = withStereo(withDelayCompensation)
|