carta1 1.0.19 → 1.0.21

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/bin/cli.js CHANGED
@@ -552,7 +552,7 @@ async function dumpFile(inputFile, outputFile, options) {
552
552
  offset: offset,
553
553
  offsetHex: '0x' + offset.toString(16),
554
554
  nBfu: frame.nBfu,
555
- blockSizeMode: frame.blockSizeMode,
555
+ blockModes: frame.blockModes,
556
556
  scaleFactorIndices: Array.from(frame.scaleFactorIndices),
557
557
  wordLengthIndices: Array.from(frame.wordLengthIndices),
558
558
  hasNonZeroScaleFactors: frame.scaleFactorIndices.some((sf) => sf > 0),
@@ -286,7 +286,7 @@ export class AudioProcessor {
286
286
  static _createDummyFrame() {
287
287
  return {
288
288
  nBfu: 0,
289
- blockSizeMode: [0, 0, 0],
289
+ blockModes: [0, 0, 0],
290
290
  scaleFactorIndices: new Int32Array(0),
291
291
  wordLengthIndices: new Int32Array(0),
292
292
  quantizedCoefficients: [],
@@ -32,7 +32,7 @@ import { packBits, unpackBits, unpackSignedBits } from './bitstream.js'
32
32
  * Serializes encoded frame data into binary format
33
33
  * @param {Object} frameData - The encoded frame data to serialize
34
34
  * @param {number} frameData.nBfu - Number of block floating units
35
- * @param {number[]} frameData.blockSizeMode - Block size mode for each band [0-2]
35
+ * @param {number[]} frameData.blockModes - Block modes for each band [0-2]
36
36
  * @param {Int32Array} frameData.scaleFactorIndices - Scale factor indices for each BFU
37
37
  * @param {Int32Array} frameData.wordLengthIndices - Word length indices for each BFU
38
38
  * @param {Int32Array[]} frameData.quantizedCoefficients - Quantized spectral coefficients
@@ -45,9 +45,9 @@ export function serializeFrame(frameData) {
45
45
  // Pack header (16 bits)
46
46
  const bfuIndex = BFU_AMOUNTS.indexOf(frameData.nBfu)
47
47
  const header =
48
- ((2 - frameData.blockSizeMode[0]) << 14) |
49
- ((2 - frameData.blockSizeMode[1]) << 12) |
50
- ((3 - frameData.blockSizeMode[2]) << 10) |
48
+ ((2 - frameData.blockModes[0]) << 14) |
49
+ ((2 - frameData.blockModes[1]) << 12) |
50
+ ((3 - frameData.blockModes[2]) << 10) |
51
51
  (bfuIndex << 5)
52
52
 
53
53
  view.setUint16(0, header, false)
@@ -102,7 +102,7 @@ export function serializeFrame(frameData) {
102
102
  * @param {Uint8Array} buffer - Binary frame data buffer
103
103
  * @returns {Object} Deserialized frame data
104
104
  * @returns {number} returns.nBfu - Number of block floating units
105
- * @returns {number[]} returns.blockSizeMode - Block size mode for each band
105
+ * @returns {number[]} returns.blockModes - Block modes for each band
106
106
  * @returns {Int32Array} returns.scaleFactorIndices - Scale factor indices for each BFU
107
107
  * @returns {Int32Array} returns.wordLengthIndices - Word length indices for each BFU
108
108
  * @returns {Int32Array[]} returns.quantizedCoefficients - Quantized spectral coefficients
@@ -117,7 +117,7 @@ export function deserializeFrame(buffer) {
117
117
 
118
118
  // Unpack header
119
119
  const header = view.getUint16(0, false)
120
- const blockSizeMode = [
120
+ const blockModes = [
121
121
  2 - ((header >> 14) & 3),
122
122
  2 - ((header >> 12) & 3),
123
123
  3 - ((header >> 10) & 3),
@@ -171,7 +171,7 @@ export function deserializeFrame(buffer) {
171
171
  scaleFactorIndices,
172
172
  wordLengthIndices,
173
173
  quantizedCoefficients,
174
- blockSizeMode,
174
+ blockModes,
175
175
  }
176
176
  }
177
177
 
@@ -57,10 +57,10 @@ export function dequantizationStage() {
57
57
  * @param {Int32Array} frameData.scaleFactorIndices - Scale factor indices for each BFU
58
58
  * @param {Int32Array} frameData.wordLengthIndices - Word length indices for each BFU
59
59
  * @param {Array<Int32Array>} frameData.quantizedCoefficients - Quantized coefficient data
60
- * @param {Array<number>} frameData.blockSizeMode - Block size mode for each band
61
- * @returns {Array} Dequantization results [coefficients, blockModes]
62
- * @returns {Float32Array} returns[0] - Reconstructed MDCT coefficients (512 samples)
63
- * @returns {Array<number>} returns[1] - Block mode indices for each band
60
+ * @param {Array<number>} frameData.blockModes - Block modes for each band
61
+ * @returns {Object} Dequantization results
62
+ * @returns {Float32Array} returns.coefficients - Reconstructed MDCT coefficients (512 samples)
63
+ * @returns {Array<number>} returns.blockModes - Block modes for each band
64
64
  */
65
65
  return (frameData) => {
66
66
  const coefficients = new Float32Array(512)
@@ -69,7 +69,7 @@ export function dequantizationStage() {
69
69
  scaleFactorIndices,
70
70
  wordLengthIndices,
71
71
  quantizedCoefficients,
72
- blockSizeMode,
72
+ blockModes,
73
73
  } = frameData
74
74
 
75
75
  for (let bfu = 0; bfu < nBfu; bfu++) {
@@ -79,7 +79,7 @@ export function dequantizationStage() {
79
79
  if (bfu >= BFU_BAND_BOUNDARIES[0]) band = 1
80
80
  if (bfu >= BFU_BAND_BOUNDARIES[1]) band = 2
81
81
 
82
- const isLongBlock = blockSizeMode[band] === 0
82
+ const isLongBlock = blockModes[band] === 0
83
83
  const position = isLongBlock ? BFU_START_LONG[bfu] : BFU_START_SHORT[bfu]
84
84
 
85
85
  if (bitsPerSample > 0) {
@@ -93,7 +93,7 @@ export function dequantizationStage() {
93
93
  }
94
94
  }
95
95
 
96
- return [coefficients, blockSizeMode]
96
+ return { coefficients, blockModes }
97
97
  }
98
98
  }
99
99
 
@@ -287,13 +287,13 @@ export function imdctStage(context) {
287
287
 
288
288
  /**
289
289
  * Transform coefficients back to time domain using inverse MDCT
290
- * @param {Array} input - Dequantization results
291
- * @param {Float32Array} input[0] - MDCT coefficients (512 samples)
292
- * @param {Array<number>} input[1] - Block mode indices for each band
290
+ * @param {Object} input - Dequantization results
291
+ * @param {Float32Array} input.coefficients - MDCT coefficients (512 samples)
292
+ * @param {Array<number>} input.blockModes - Block modes for each band
293
293
  * @returns {Array<Float32Array>} Three reconstructed frequency bands [low, mid, high]
294
294
  */
295
295
  return (input) => {
296
- const [coefficients, blockModes] = input
296
+ const { coefficients, blockModes } = input
297
297
 
298
298
  return MDCT_BAND_CONFIGS.map((config, bandIndex) => {
299
299
  const bandCoeffs = extractBandCoefficients(coefficients, bandIndex)
@@ -126,7 +126,7 @@ export function blockSelectorStage(context) {
126
126
  * @param {Array<Float32Array>} input.bands - Three frequency bands [low, mid, high]
127
127
  * @returns {Object} Block selection results
128
128
  * @returns {Array<Float32Array>} returns.bands - Original frequency bands
129
- * @returns {Array<number>} returns.blockModes - Block mode indices for each band
129
+ * @returns {Array<number>} returns.blockModes - Block modes for each band
130
130
  */
131
131
  return (input) => {
132
132
  const { bands } = input
@@ -298,12 +298,12 @@ export function mdctStage(context) {
298
298
  * Transform frequency bands using MDCT
299
299
  * @param {Object} input - Block selection results
300
300
  * @param {Array<Float32Array>} input.bands - Three frequency bands
301
- * @param {Array<number>} input.blockModes - Block mode indices for each band
301
+ * @param {Array<number>} input.blockModes - Block modes for each band
302
302
  * @param {any} input.originalFrame - Original frame data (passed through)
303
303
  * @returns {Object} MDCT transform results
304
304
  * @returns {Array<Float32Array>} returns.bands - Original frequency bands
305
305
  * @returns {Float32Array} returns.coefficients - MDCT coefficients (512 samples)
306
- * @returns {Array<number>} returns.blockModes - Block mode indices
306
+ * @returns {Array<number>} returns.blockModes - Block modes
307
307
  * @returns {any} returns.originalFrame - Original frame data
308
308
  */
309
309
  return (input) => {
@@ -356,13 +356,13 @@ export function quantizationStage(context) {
356
356
  * Perform psychoacoustic analysis and quantization
357
357
  * @param {Object} input - MDCT transform results
358
358
  * @param {Float32Array} input.coefficients - MDCT coefficients
359
- * @param {Array<number>} input.blockModes - Block mode indices
359
+ * @param {Array<number>} input.blockModes - Block modes
360
360
  * @returns {Object} Quantization results ready for bitstream encoding
361
361
  * @returns {number} returns.nBfu - Number of active BFUs
362
362
  * @returns {Int32Array} returns.scaleFactorIndices - Scale factor indices for each BFU
363
363
  * @returns {Int32Array} returns.wordLengthIndices - Word length indices for each BFU
364
364
  * @returns {Array<Int32Array>} returns.quantizedCoefficients - Quantized coefficient data
365
- * @returns {Array<number>} returns.blockSizeMode - Block size mode for each band
365
+ * @returns {Array<number>} returns.blockModes - Block modes for each band
366
366
  */
367
367
  return (input) => {
368
368
  const { coefficients, blockModes } = input
@@ -399,7 +399,7 @@ export function quantizationStage(context) {
399
399
  scaleFactorIndices,
400
400
  wordLengthIndices: allocation.slice(0, selectedBfuCount),
401
401
  quantizedCoefficients,
402
- blockSizeMode: blockModes,
402
+ blockModes,
403
403
  }
404
404
  }
405
405
  }
@@ -12,12 +12,8 @@ export class FFT {
12
12
  */
13
13
  static fft(real, imag) {
14
14
  const n = real.length
15
- if (n !== imag.length) {
16
- throw new Error('Real and imaginary arrays must have the same length.')
17
- }
18
- if ((n & (n - 1)) !== 0) {
19
- throw new Error('Input size must be a power of 2.')
20
- }
15
+ if (n !== imag.length) throw new Error('Mismatched array lengths.')
16
+ if ((n & (n - 1)) !== 0) throw new Error('Length must be power of 2.')
21
17
  if (n <= 1) return
22
18
 
23
19
  let cache = FFT._cache.get(n)
@@ -26,168 +22,91 @@ export class FFT {
26
22
  FFT._cache.set(n, cache)
27
23
  }
28
24
 
29
- const { bitRev, cos: cosTbl, sin: sinTbl } = cache
25
+ const bitRev = cache.bitRev
26
+ const cosTbl = cache.cos
27
+ const sinTbl = cache.sin
30
28
 
31
- // Bit-reversal permutation with improved memory access pattern
29
+ // Bit reversal
32
30
  for (let i = 0; i < n; i++) {
33
31
  const j = bitRev[i]
34
32
  if (j > i) {
35
- // Use temporary variables to reduce array access
36
- const tempR = real[i]
37
- const tempI = imag[i]
33
+ let tr = real[i]
34
+ let ti = imag[i]
38
35
  real[i] = real[j]
39
36
  imag[i] = imag[j]
40
- real[j] = tempR
41
- imag[j] = tempI
37
+ real[j] = tr
38
+ imag[j] = ti
42
39
  }
43
40
  }
44
41
 
45
- // Optimized butterfly computations for small strides
46
- // Stride 2 - no twiddle factors needed
42
+ // First stage: stride=2
47
43
  for (let i = 0; i < n; i += 2) {
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
44
+ let r0 = real[i],
45
+ i0 = imag[i]
46
+ let r1 = real[i + 1],
47
+ i1 = imag[i + 1]
48
+ real[i] = r0 + r1
49
+ imag[i] = i0 + i1
50
+ real[i + 1] = r0 - r1
51
+ imag[i + 1] = i0 - i1
57
52
  }
58
53
 
59
- // Stride 4 - simple twiddle factors
54
+ // Second stage: stride=4
60
55
  if (n >= 4) {
61
56
  for (let i = 0; i < n; i += 4) {
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
57
+ let r0 = real[i],
58
+ i0 = imag[i]
59
+ let r2 = real[i + 2],
60
+ i2 = imag[i + 2]
61
+ real[i] = r0 + r2
62
+ imag[i] = i0 + i2
63
+ real[i + 2] = r0 - r2
64
+ imag[i + 2] = i0 - i2
65
+
66
+ r0 = real[i + 1]
67
+ i0 = imag[i + 1]
68
+ r2 = real[i + 3]
69
+ i2 = imag[i + 3]
70
+ // W^1 = -i
71
+ let tR = i2
72
+ let tI = -r2
73
+ real[i + 1] = r0 + tR
74
+ imag[i + 1] = i0 + tI
75
+ real[i + 3] = r0 - tR
76
+ imag[i + 3] = i0 - tI
146
77
  }
147
78
  }
148
79
 
149
- // General Cooley-Tukey butterflies with optimized inner loop
150
- for (let stride = 16; stride <= n; stride <<= 1) {
80
+ // Higher stages
81
+ for (let stride = 8; stride <= n; stride <<= 1) {
151
82
  const halfStride = stride >>> 1
152
- const step = n / stride
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
83
+ const tblStep = n / stride
84
+ for (let base = 0; base < n; base += stride) {
85
+ // k = 0
86
+ let r0 = real[base],
87
+ i0 = imag[base]
88
+ let r1 = real[base + halfStride],
89
+ i1 = imag[base + halfStride]
90
+ real[base] = r0 + r1
91
+ imag[base] = i0 + i1
92
+ real[base + halfStride] = r0 - r1
93
+ imag[base + halfStride] = i0 - i1
94
+
95
+ let tw = tblStep
170
96
  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
97
+ const c = cosTbl[tw]
98
+ const s = sinTbl[tw]
99
+ r1 = real[base + k]
100
+ i1 = imag[base + k]
101
+ let r2 = real[base + k + halfStride]
102
+ let i2 = imag[base + k + halfStride]
103
+ let tr = r2 * c - i2 * s
104
+ let ti = r2 * s + i2 * c
105
+ real[base + k] = r1 + tr
106
+ imag[base + k] = i1 + ti
107
+ real[base + k + halfStride] = r1 - tr
108
+ imag[base + k + halfStride] = i1 - ti
109
+ tw += tblStep
191
110
  }
192
111
  }
193
112
  }
@@ -198,27 +117,20 @@ export class FFT {
198
117
  * @private
199
118
  */
200
119
  static _buildCache(n) {
201
- const log2n = Math.log2(n) | 0
202
-
203
- // Optimized bit-reversal using iterative approach
204
120
  const bitRev = new Uint32Array(n)
121
+ const log2n = Math.log2(n) | 0
205
122
  for (let i = 1; i < n; i++) {
206
123
  bitRev[i] = (bitRev[i >>> 1] >>> 1) | ((i & 1) << (log2n - 1))
207
124
  }
208
-
209
- // Pre-compute twiddle factors with better precision
210
125
  const halfN = n >>> 1
211
126
  const cosTbl = new Float32Array(halfN)
212
127
  const sinTbl = new Float32Array(halfN)
213
-
214
- // Use higher precision calculation then cast to Float32
215
- const angleStep = (-2 * Math.PI) / n
128
+ const ang = (-2 * Math.PI) / n
216
129
  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))
130
+ const a = ang * i
131
+ cosTbl[i] = Math.cos(a)
132
+ sinTbl[i] = Math.sin(a)
220
133
  }
221
-
222
134
  return { bitRev, cos: cosTbl, sin: sinTbl }
223
135
  }
224
136
  }
@@ -52,9 +52,19 @@ export class MDCT extends MDCTBase {
52
52
  * @returns {Float32Array} Frequency-domain MDCT coefficients
53
53
  */
54
54
  transform(input, mdctBuffers = null) {
55
- const buffers = mdctBuffers?.[this.fftSize]
56
- const real = buffers?.real ?? throwError('mdct: real is required')
57
- const imag = buffers?.imag ?? throwError('mdct: imag is required')
55
+ const buffers =
56
+ mdctBuffers?.[this.fftSize] ??
57
+ throwError(`MDCT.transform: mdctBuffers[${this.fftSize}] is required`)
58
+ const real =
59
+ buffers?.real ??
60
+ throwError(
61
+ `MDCT.transform: mdctBuffers[${this.fftSize}].real is required`
62
+ )
63
+ const imag =
64
+ buffers?.imag ??
65
+ throwError(
66
+ `MDCT.transform: mdctBuffers[${this.fftSize}].imag is required`
67
+ )
58
68
 
59
69
  const n4 = this.quarterSize
60
70
  const n34 = 3 * n4
@@ -127,9 +137,19 @@ export class IMDCT extends MDCTBase {
127
137
  * @returns {Float32Array} Time-domain output samples
128
138
  */
129
139
  transform(input, mdctBuffers = null) {
130
- const buffers = mdctBuffers?.[this.fftSize]
131
- const real = buffers?.real ?? throwError('imdct: real is required')
132
- const imag = buffers?.imag ?? throwError('imdct: imag is required')
140
+ const buffers =
141
+ mdctBuffers?.[this.fftSize] ??
142
+ throwError(`IMDCT.transform: mdctBuffers[${this.fftSize}] is required`)
143
+ const real =
144
+ buffers?.real ??
145
+ throwError(
146
+ `IMDCT.transform: mdctBuffers[${this.fftSize}].real is required`
147
+ )
148
+ const imag =
149
+ buffers?.imag ??
150
+ throwError(
151
+ `IMDCT.transform: mdctBuffers[${this.fftSize}].imag is required`
152
+ )
133
153
 
134
154
  const n4 = this.quarterSize
135
155
  const n34 = 3 * n4
@@ -22,7 +22,7 @@ export function qmfAnalysis(input, delayLine, qmfWorkBuffers = null) {
22
22
 
23
23
  const workBuffer =
24
24
  qmfWorkBuffers?.delay?.[inputLength] ??
25
- throwError('qmfAnalysis: workBuffer is required')
25
+ throwError(`qmfAnalysis: qmfWorkBuffers.delay[${inputLength}] is required`)
26
26
  workBuffer.set(delayLine)
27
27
  workBuffer.set(input, delayLine.length)
28
28
 
@@ -68,7 +68,9 @@ export function qmfSynthesis(
68
68
 
69
69
  const workBuffer =
70
70
  qmfWorkBuffers?.delay?.[outputLength] ??
71
- throwError('qmfSynthesis: workBuffer is required')
71
+ throwError(
72
+ `qmfSynthesis: qmfWorkBuffers.delay[${outputLength}] is required`
73
+ )
72
74
  workBuffer.set(delayLine)
73
75
 
74
76
  // Upsample and merge