carta1 1.1.2 → 1.1.4
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 +10 -10
- package/codec/analysis/transient.js +13 -13
- package/codec/browser/interface.js +3 -3
- package/codec/browser/worker.js +1 -1
- package/codec/coding/bitallocation.js +3 -3
- package/codec/coding/quantization.js +7 -7
- package/codec/core/buffers.js +32 -32
- package/codec/core/constants.js +4 -15
- package/codec/io/processor.js +22 -30
- package/codec/pipeline/decoder.js +16 -16
- package/codec/pipeline/encoder.js +19 -19
- package/codec/transforms/fft.js +47 -114
- package/codec/transforms/mdct.js +11 -11
- package/codec/transforms/qmf.js +8 -8
- package/codec/utils.js +4 -125
- package/dist/carta1-worker-interface.min.js.map +1 -1
- 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
|
@@ -59,11 +59,11 @@ export function dequantizationStage() {
|
|
|
59
59
|
* @param {Array<Int32Array>} frameData.quantizedCoefficients - Quantized coefficient data
|
|
60
60
|
* @param {Array<number>} frameData.blockModes - Block modes for each band
|
|
61
61
|
* @returns {Object} Dequantization results
|
|
62
|
-
* @returns {
|
|
62
|
+
* @returns {Float32Array} returns.coefficients - Reconstructed MDCT coefficients (512 samples)
|
|
63
63
|
* @returns {Array<number>} returns.blockModes - Block modes for each band
|
|
64
64
|
*/
|
|
65
65
|
return (frameData) => {
|
|
66
|
-
const coefficients = new
|
|
66
|
+
const coefficients = new Float32Array(512)
|
|
67
67
|
const {
|
|
68
68
|
nBfu,
|
|
69
69
|
scaleFactorIndices,
|
|
@@ -123,13 +123,13 @@ export function imdctStage(context) {
|
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* Inverse transform a single frequency band.
|
|
126
|
-
* @param {
|
|
126
|
+
* @param {Float32Array} coeffs
|
|
127
127
|
* @param {number} bandIndex
|
|
128
128
|
* @param {number} blockMode
|
|
129
|
-
* @param {
|
|
129
|
+
* @param {Float32Array} overlapBuffer
|
|
130
130
|
* @param {Object} bufferPool
|
|
131
131
|
* @param {Object} config
|
|
132
|
-
* @returns {
|
|
132
|
+
* @returns {Float32Array}
|
|
133
133
|
*/
|
|
134
134
|
function transformBand(
|
|
135
135
|
coeffs,
|
|
@@ -164,13 +164,13 @@ export function imdctStage(context) {
|
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
166
|
* Inverse transform using long block.
|
|
167
|
-
* @param {
|
|
167
|
+
* @param {Float32Array} coeffs
|
|
168
168
|
* @param {number} bandIndex
|
|
169
|
-
* @param {
|
|
169
|
+
* @param {Float32Array} overlapBuffer
|
|
170
170
|
* @param {Object} bufferPool
|
|
171
171
|
* @param {Object} config
|
|
172
172
|
* @param {Object} transformFunc
|
|
173
|
-
* @returns {
|
|
173
|
+
* @returns {Float32Array}
|
|
174
174
|
*/
|
|
175
175
|
function inverseLongBlock(
|
|
176
176
|
coeffs,
|
|
@@ -234,12 +234,12 @@ export function imdctStage(context) {
|
|
|
234
234
|
|
|
235
235
|
/**
|
|
236
236
|
* Inverse transform using short blocks.
|
|
237
|
-
* @param {
|
|
237
|
+
* @param {Float32Array} coeffs
|
|
238
238
|
* @param {number} bandIndex
|
|
239
|
-
* @param {
|
|
239
|
+
* @param {Float32Array} overlapBuffer
|
|
240
240
|
* @param {Object} bufferPool
|
|
241
241
|
* @param {Object} config
|
|
242
|
-
* @returns {
|
|
242
|
+
* @returns {Float32Array}
|
|
243
243
|
*/
|
|
244
244
|
function inverseShortBlocks(
|
|
245
245
|
coeffs,
|
|
@@ -308,9 +308,9 @@ export function imdctStage(context) {
|
|
|
308
308
|
/**
|
|
309
309
|
* Transform coefficients back to time domain using inverse MDCT
|
|
310
310
|
* @param {Object} input - Dequantization results
|
|
311
|
-
* @param {
|
|
311
|
+
* @param {Float32Array} input.coefficients - MDCT coefficients (512 samples)
|
|
312
312
|
* @param {Array<number>} input.blockModes - Block modes for each band
|
|
313
|
-
* @returns {Array<
|
|
313
|
+
* @returns {Array<Float32Array>} Three reconstructed frequency bands [low, mid, high]
|
|
314
314
|
*/
|
|
315
315
|
return (input) => {
|
|
316
316
|
const { coefficients, blockModes } = input
|
|
@@ -354,14 +354,14 @@ export function qmfSynthesisStage(context) {
|
|
|
354
354
|
|
|
355
355
|
/**
|
|
356
356
|
* Reconstruct full-spectrum PCM samples from frequency bands
|
|
357
|
-
* @param {Array<
|
|
358
|
-
* @returns {
|
|
357
|
+
* @param {Array<Float32Array>} bands - Three frequency bands [low, mid, high]
|
|
358
|
+
* @returns {Float32Array} Reconstructed PCM samples
|
|
359
359
|
*/
|
|
360
360
|
return (bands) => {
|
|
361
361
|
// Apply high band delay compensation
|
|
362
362
|
const delayedHigh = bufferPool.qmfWorkBuffers.highBandDelay[bands[2].length]
|
|
363
363
|
delayedHigh.set(delays.highBand)
|
|
364
|
-
delayedHigh.set(bands[2])
|
|
364
|
+
delayedHigh.set(bands[2], delays.highBand.length)
|
|
365
365
|
|
|
366
366
|
const highBand = delayedHigh.slice(0, bands[0].length * 2)
|
|
367
367
|
delays.highBand = delayedHigh.slice(bands[0].length * 2)
|
|
@@ -62,9 +62,9 @@ export function qmfAnalysisStage(context) {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Process PCM samples through QMF analysis
|
|
65
|
-
* @param {
|
|
65
|
+
* @param {Float32Array} pcmSamples - Input PCM samples
|
|
66
66
|
* @returns {Object} Analysis result containing frequency bands
|
|
67
|
-
* @returns {Array<
|
|
67
|
+
* @returns {Array<Float32Array>} returns.bands - Three frequency bands [low, mid, high]
|
|
68
68
|
*/
|
|
69
69
|
return (pcmSamples) => {
|
|
70
70
|
const stage1 = qmfAnalysis(
|
|
@@ -118,9 +118,9 @@ export function blockSelectorStage(context) {
|
|
|
118
118
|
/**
|
|
119
119
|
* Analyze frequency bands and determine block modes
|
|
120
120
|
* @param {Object} input - QMF analysis results
|
|
121
|
-
* @param {Array<
|
|
121
|
+
* @param {Array<Float32Array>} input.bands - Three frequency bands [low, mid, high]
|
|
122
122
|
* @returns {Object} Block selection results
|
|
123
|
-
* @returns {Array<
|
|
123
|
+
* @returns {Array<Float32Array>} returns.bands - Original frequency bands
|
|
124
124
|
* @returns {Array<number>} returns.blockModes - Block modes for each band
|
|
125
125
|
*/
|
|
126
126
|
return (input) => {
|
|
@@ -170,12 +170,12 @@ export function mdctStage(context) {
|
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* Transform a single frequency band using MDCT.
|
|
173
|
-
* @param {
|
|
173
|
+
* @param {Float32Array} samples
|
|
174
174
|
* @param {number} bandIndex
|
|
175
175
|
* @param {number} blockMode
|
|
176
|
-
* @param {
|
|
176
|
+
* @param {Float32Array} overlapBuffer
|
|
177
177
|
* @param {Object} bufferPool
|
|
178
|
-
* @returns {
|
|
178
|
+
* @returns {Float32Array}
|
|
179
179
|
*/
|
|
180
180
|
function transformBand(
|
|
181
181
|
samples,
|
|
@@ -210,13 +210,13 @@ export function mdctStage(context) {
|
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
212
|
* Transform using long block (better frequency resolution).
|
|
213
|
-
* @param {
|
|
213
|
+
* @param {Float32Array} samples
|
|
214
214
|
* @param {number} bandIndex
|
|
215
215
|
* @param {Object} config
|
|
216
216
|
* @param {Object} transformFunc
|
|
217
|
-
* @param {
|
|
217
|
+
* @param {Float32Array} overlapBuffer
|
|
218
218
|
* @param {Object} bufferPool
|
|
219
|
-
* @returns {
|
|
219
|
+
* @returns {Float32Array}
|
|
220
220
|
*/
|
|
221
221
|
function transformLongBlock(
|
|
222
222
|
samples,
|
|
@@ -252,12 +252,12 @@ export function mdctStage(context) {
|
|
|
252
252
|
|
|
253
253
|
/**
|
|
254
254
|
* Transform using short blocks (better time resolution for transients).
|
|
255
|
-
* @param {
|
|
255
|
+
* @param {Float32Array} samples
|
|
256
256
|
* @param {number} bandIndex
|
|
257
257
|
* @param {Object} config
|
|
258
|
-
* @param {
|
|
258
|
+
* @param {Float32Array} overlapBuffer
|
|
259
259
|
* @param {Object} bufferPool
|
|
260
|
-
* @returns {
|
|
260
|
+
* @returns {Float32Array}
|
|
261
261
|
*/
|
|
262
262
|
function transformShortBlocks(
|
|
263
263
|
samples,
|
|
@@ -267,7 +267,7 @@ export function mdctStage(context) {
|
|
|
267
267
|
bufferPool
|
|
268
268
|
) {
|
|
269
269
|
const numBlocks = 1 << (config.size === 256 ? 3 : 2) // 8 for band 2, 4 for bands 0-1
|
|
270
|
-
const output = new
|
|
270
|
+
const output = new Float32Array(config.size)
|
|
271
271
|
|
|
272
272
|
for (let block = 0; block < numBlocks; block++) {
|
|
273
273
|
const blockStart = block * MDCT_SHORT_BLOCK_SIZE
|
|
@@ -311,18 +311,18 @@ export function mdctStage(context) {
|
|
|
311
311
|
/**
|
|
312
312
|
* Transform frequency bands using MDCT
|
|
313
313
|
* @param {Object} input - Block selection results
|
|
314
|
-
* @param {Array<
|
|
314
|
+
* @param {Array<Float32Array>} input.bands - Three frequency bands
|
|
315
315
|
* @param {Array<number>} input.blockModes - Block modes for each band
|
|
316
316
|
* @param {any} input.originalFrame - Original frame data (passed through)
|
|
317
317
|
* @returns {Object} MDCT transform results
|
|
318
|
-
* @returns {Array<
|
|
319
|
-
* @returns {
|
|
318
|
+
* @returns {Array<Float32Array>} returns.bands - Original frequency bands
|
|
319
|
+
* @returns {Float32Array} returns.coefficients - MDCT coefficients (512 samples)
|
|
320
320
|
* @returns {Array<number>} returns.blockModes - Block modes
|
|
321
321
|
* @returns {any} returns.originalFrame - Original frame data
|
|
322
322
|
*/
|
|
323
323
|
return (input) => {
|
|
324
324
|
const { bands, blockModes, originalFrame } = input
|
|
325
|
-
const coefficients = new
|
|
325
|
+
const coefficients = new Float32Array(512)
|
|
326
326
|
|
|
327
327
|
bands.forEach((bandSamples, bandIndex) => {
|
|
328
328
|
const transformed = transformBand(
|
|
@@ -359,7 +359,7 @@ export function quantizationStage() {
|
|
|
359
359
|
/**
|
|
360
360
|
* Perform RDO bit allocation and quantization
|
|
361
361
|
* @param {Object} input - MDCT transform results
|
|
362
|
-
* @param {
|
|
362
|
+
* @param {Float32Array} input.coefficients - MDCT coefficients
|
|
363
363
|
* @param {Array<number>} input.blockModes - Block modes
|
|
364
364
|
* @returns {Object} Quantization results ready for bitstream encoding
|
|
365
365
|
* @returns {number} returns.nBfu - Number of active BFUs
|
package/codec/transforms/fft.js
CHANGED
|
@@ -1,136 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Carta1 Audio Codec - Fast Fourier Transform
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* This module implements a radix-2 Cooley-Tukey FFT algorithm for frequency domain
|
|
5
|
+
* analysis used in psychoacoustic modeling and transient detection.
|
|
4
6
|
*/
|
|
5
|
-
export class FFT {
|
|
6
|
-
static _cache = new Map()
|
|
7
7
|
|
|
8
|
+
export class FFT {
|
|
8
9
|
/**
|
|
9
|
-
* Perform
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
10
|
+
* Perform in-place FFT on complex data
|
|
11
|
+
* @param {Float32Array} real - Real part of input/output data
|
|
12
|
+
* @param {Float32Array} imag - Imaginary part of input/output data
|
|
12
13
|
*/
|
|
13
14
|
static fft(real, imag) {
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
if ((n & (n - 1)) !== 0) throw new Error('Length must be power of 2.')
|
|
17
|
-
if (n <= 1) return
|
|
18
|
-
|
|
19
|
-
let cache = FFT._cache.get(n)
|
|
20
|
-
if (!cache) {
|
|
21
|
-
cache = FFT._buildCache(n)
|
|
22
|
-
FFT._cache.set(n, cache)
|
|
23
|
-
}
|
|
15
|
+
const size = real.length
|
|
16
|
+
if (size === 1) return
|
|
24
17
|
|
|
25
|
-
const
|
|
26
|
-
const cosTbl = cache.cos
|
|
27
|
-
const sinTbl = cache.sin
|
|
18
|
+
const bits = Math.log2(size)
|
|
28
19
|
|
|
29
20
|
// Bit reversal
|
|
30
|
-
for (let i = 0; i <
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
for (let i = 0; i < size; i++) {
|
|
22
|
+
let reversed = 0
|
|
23
|
+
let temp = i
|
|
24
|
+
for (let b = 0; b < bits; b++) {
|
|
25
|
+
reversed = (reversed << 1) | (temp & 1)
|
|
26
|
+
temp >>= 1
|
|
27
|
+
}
|
|
28
|
+
if (reversed > i) {
|
|
29
|
+
;[real[i], real[reversed]] = [real[reversed], real[i]]
|
|
30
|
+
;[imag[i], imag[reversed]] = [imag[reversed], imag[i]]
|
|
39
31
|
}
|
|
40
32
|
}
|
|
41
33
|
|
|
42
|
-
//
|
|
43
|
-
for (let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
real[i] = r0 + r1
|
|
49
|
-
imag[i] = i0 + i1
|
|
50
|
-
real[i + 1] = r0 - r1
|
|
51
|
-
imag[i + 1] = i0 - i1
|
|
52
|
-
}
|
|
34
|
+
// Cooley-Tukey decimation-in-time
|
|
35
|
+
for (let stride = 2; stride <= size; stride <<= 1) {
|
|
36
|
+
const halfStride = stride >> 1
|
|
37
|
+
const angle = (-2 * Math.PI) / stride
|
|
38
|
+
const wReal = Math.cos(angle)
|
|
39
|
+
const wImag = Math.sin(angle)
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
41
|
+
for (let start = 0; start < size; start += stride) {
|
|
42
|
+
let twiddleReal = 1
|
|
43
|
+
let twiddleImag = 0
|
|
65
44
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
77
|
-
}
|
|
78
|
-
}
|
|
45
|
+
for (let k = 0; k < halfStride; k++) {
|
|
46
|
+
const evenIndex = start + k
|
|
47
|
+
const oddIndex = evenIndex + halfStride
|
|
79
48
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
49
|
+
const evenReal = real[evenIndex]
|
|
50
|
+
const evenImag = imag[evenIndex]
|
|
51
|
+
const oddReal = real[oddIndex]
|
|
52
|
+
const oddImag = imag[oddIndex]
|
|
94
53
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
imag[base + k] = i1 + ti
|
|
107
|
-
real[base + k + halfStride] = r1 - tr
|
|
108
|
-
imag[base + k + halfStride] = i1 - ti
|
|
109
|
-
tw += tblStep
|
|
54
|
+
const tReal = oddReal * twiddleReal - oddImag * twiddleImag
|
|
55
|
+
const tImag = oddReal * twiddleImag + oddImag * twiddleReal
|
|
56
|
+
|
|
57
|
+
real[evenIndex] = evenReal + tReal
|
|
58
|
+
imag[evenIndex] = evenImag + tImag
|
|
59
|
+
real[oddIndex] = evenReal - tReal
|
|
60
|
+
imag[oddIndex] = evenImag - tImag
|
|
61
|
+
|
|
62
|
+
const nextReal = twiddleReal * wReal - twiddleImag * wImag
|
|
63
|
+
twiddleImag = twiddleReal * wImag + twiddleImag * wReal
|
|
64
|
+
twiddleReal = nextReal
|
|
110
65
|
}
|
|
111
66
|
}
|
|
112
67
|
}
|
|
113
68
|
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Build cache tables for FFT of size n
|
|
117
|
-
* @private
|
|
118
|
-
*/
|
|
119
|
-
static _buildCache(n) {
|
|
120
|
-
const bitRev = new Uint32Array(n)
|
|
121
|
-
const log2n = Math.log2(n) | 0
|
|
122
|
-
for (let i = 1; i < n; i++) {
|
|
123
|
-
bitRev[i] = (bitRev[i >>> 1] >>> 1) | ((i & 1) << (log2n - 1))
|
|
124
|
-
}
|
|
125
|
-
const halfN = n >>> 1
|
|
126
|
-
const cosTbl = new Float64Array(halfN)
|
|
127
|
-
const sinTbl = new Float64Array(halfN)
|
|
128
|
-
const ang = (-2 * Math.PI) / n
|
|
129
|
-
for (let i = 0; i < halfN; i++) {
|
|
130
|
-
const a = ang * i
|
|
131
|
-
cosTbl[i] = Math.cos(a)
|
|
132
|
-
sinTbl[i] = Math.sin(a)
|
|
133
|
-
}
|
|
134
|
-
return { bitRev, cos: cosTbl, sin: sinTbl }
|
|
135
|
-
}
|
|
136
69
|
}
|
package/codec/transforms/mdct.js
CHANGED
|
@@ -47,9 +47,9 @@ export class MDCT extends MDCTBase {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Perform forward MDCT transform
|
|
50
|
-
* @param {
|
|
50
|
+
* @param {Float32Array} input - Time-domain input samples
|
|
51
51
|
* @param {Object} mdctBuffers - Optional work buffers for FFT
|
|
52
|
-
* @returns {
|
|
52
|
+
* @returns {Float32Array} Frequency-domain MDCT coefficients
|
|
53
53
|
*/
|
|
54
54
|
transform(input, mdctBuffers = null) {
|
|
55
55
|
const buffers =
|
|
@@ -107,7 +107,7 @@ export class MDCT extends MDCTBase {
|
|
|
107
107
|
FFT.fft(real, imag)
|
|
108
108
|
|
|
109
109
|
// Post-FFT processing
|
|
110
|
-
const output = new
|
|
110
|
+
const output = new Float32Array(this.halfSize)
|
|
111
111
|
for (let i = 0; i < this.fftSize; i++) {
|
|
112
112
|
const c = this.sinCosTable[i * 2]
|
|
113
113
|
const s = this.sinCosTable[i * 2 + 1]
|
|
@@ -132,9 +132,9 @@ export class IMDCT extends MDCTBase {
|
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
134
|
* Perform inverse MDCT transform
|
|
135
|
-
* @param {
|
|
135
|
+
* @param {Float32Array} input - Frequency-domain MDCT coefficients
|
|
136
136
|
* @param {Object} mdctBuffers - Optional work buffers for FFT
|
|
137
|
-
* @returns {
|
|
137
|
+
* @returns {Float32Array} Time-domain output samples
|
|
138
138
|
*/
|
|
139
139
|
transform(input, mdctBuffers = null) {
|
|
140
140
|
const buffers =
|
|
@@ -172,7 +172,7 @@ export class IMDCT extends MDCTBase {
|
|
|
172
172
|
FFT.fft(real, imag)
|
|
173
173
|
|
|
174
174
|
// Post-FFT butterfly
|
|
175
|
-
const output = new
|
|
175
|
+
const output = new Float32Array(this.size)
|
|
176
176
|
|
|
177
177
|
for (let i = 0; i < this.fftSize / 2; i++) {
|
|
178
178
|
const i2 = i * 2
|
|
@@ -222,14 +222,14 @@ export const imdct512 = new IMDCT(MDCT_SIZE_LONG, MDCT_SIZE_LONG * 4)
|
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
224
|
* Perform overlap-add operation for MDCT reconstruction
|
|
225
|
-
* @param {
|
|
226
|
-
* @param {
|
|
227
|
-
* @param {
|
|
228
|
-
* @returns {
|
|
225
|
+
* @param {Float32Array} prev - Previous block samples
|
|
226
|
+
* @param {Float32Array} curr - Current block samples
|
|
227
|
+
* @param {Float32Array} window - Window function coefficients
|
|
228
|
+
* @returns {Float32Array} Overlap-added output samples
|
|
229
229
|
*/
|
|
230
230
|
export function overlapAdd(prev, curr, window) {
|
|
231
231
|
const size = prev.length
|
|
232
|
-
const output = new
|
|
232
|
+
const output = new Float32Array(size * 2)
|
|
233
233
|
|
|
234
234
|
for (let i = 0; i < size; i++) {
|
|
235
235
|
const w1 = window[i]
|
package/codec/transforms/qmf.js
CHANGED
|
@@ -11,8 +11,8 @@ import { throwError } from '../utils.js'
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Perform QMF analysis filtering to split input into low and high frequency bands
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
14
|
+
* @param {Float32Array} input - Input audio samples
|
|
15
|
+
* @param {Float32Array} delayLine - QMF delay line state
|
|
16
16
|
* @param {Object} qmfWorkBuffers - Optional work buffers for processing
|
|
17
17
|
* @returns {Object} Object containing lowBand, highBand, and newDelay
|
|
18
18
|
*/
|
|
@@ -26,8 +26,8 @@ export function qmfAnalysis(input, delayLine, qmfWorkBuffers = null) {
|
|
|
26
26
|
workBuffer.set(delayLine)
|
|
27
27
|
workBuffer.set(input, delayLine.length)
|
|
28
28
|
|
|
29
|
-
const lowBand = new
|
|
30
|
-
const highBand = new
|
|
29
|
+
const lowBand = new Float32Array(outputLength)
|
|
30
|
+
const highBand = new Float32Array(outputLength)
|
|
31
31
|
|
|
32
32
|
for (let i = 0; i < outputLength; i++) {
|
|
33
33
|
let evenSum = 0
|
|
@@ -51,9 +51,9 @@ export function qmfAnalysis(input, delayLine, qmfWorkBuffers = null) {
|
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Perform QMF synthesis filtering to reconstruct audio from low and high frequency bands
|
|
54
|
-
* @param {
|
|
55
|
-
* @param {
|
|
56
|
-
* @param {
|
|
54
|
+
* @param {Float32Array} lowBand - Low frequency band samples
|
|
55
|
+
* @param {Float32Array} highBand - High frequency band samples
|
|
56
|
+
* @param {Float32Array} delayLine - QMF delay line state
|
|
57
57
|
* @param {Object} qmfWorkBuffers - Optional work buffers for processing
|
|
58
58
|
* @returns {Object} Object containing output and newDelay
|
|
59
59
|
*/
|
|
@@ -82,7 +82,7 @@ export function qmfSynthesis(
|
|
|
82
82
|
workBuffer[offset + 1] = 0.5 * (low - high)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const output = new
|
|
85
|
+
const output = new Float32Array(outputLength)
|
|
86
86
|
|
|
87
87
|
// Convolution
|
|
88
88
|
for (let i = 0; i < subbandLength; i++) {
|
package/codec/utils.js
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* for delay compensation and frame padding.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { CODEC_DELAY, SAMPLES_PER_FRAME } from './core/constants.js'
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
10
|
* Throws an error with the given message
|
|
13
11
|
* @param {string} msg - Error message
|
|
@@ -31,130 +29,15 @@ export function pipe(context, ...stages) {
|
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
/**
|
|
35
|
-
* Pads audio frames and adds flush samples for proper codec delay handling
|
|
36
|
-
* @param {AsyncIterable<Float64Array>} iter - Input frame stream
|
|
37
|
-
* @param {number} [samplesToFlush=CODEC_DELAY] - Number of samples to flush
|
|
38
|
-
* @yields {Float64Array} Padded frames with flush samples
|
|
39
|
-
*/
|
|
40
|
-
export async function* withFlushSamples(iter, samplesToFlush = CODEC_DELAY) {
|
|
41
|
-
let lastFrameLength = SAMPLES_PER_FRAME
|
|
42
|
-
for await (const frame of iter) {
|
|
43
|
-
lastFrameLength = frame.length
|
|
44
|
-
if (lastFrameLength === SAMPLES_PER_FRAME) {
|
|
45
|
-
yield frame
|
|
46
|
-
} else {
|
|
47
|
-
// We need to pad
|
|
48
|
-
const paddedFrame = new Float64Array(SAMPLES_PER_FRAME)
|
|
49
|
-
paddedFrame.set(frame)
|
|
50
|
-
yield paddedFrame
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (SAMPLES_PER_FRAME - lastFrameLength < samplesToFlush) {
|
|
54
|
-
yield new Float64Array(SAMPLES_PER_FRAME)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Applies delay compensation to PCM frame stream by dropping initial samples
|
|
60
|
-
* @param {AsyncIterable<Float64Array>} pcmFrameStream - Input PCM frame stream
|
|
61
|
-
* @param {number} [samplesToDrop=CODEC_DELAY] - Number of samples to drop for delay compensation
|
|
62
|
-
* @yields {Float64Array} Delay-compensated PCM frames
|
|
63
|
-
*/
|
|
64
|
-
export async function* withDelayCompensation(
|
|
65
|
-
pcmFrameStream,
|
|
66
|
-
samplesToDrop = CODEC_DELAY
|
|
67
|
-
) {
|
|
68
|
-
let buffer = new Float64Array(0)
|
|
69
|
-
let droppedInitialSamples = false
|
|
70
|
-
|
|
71
|
-
for await (const frame of pcmFrameStream) {
|
|
72
|
-
// Drop samples from the very first frame only
|
|
73
|
-
if (!droppedInitialSamples) {
|
|
74
|
-
droppedInitialSamples = true
|
|
75
|
-
if (samplesToDrop >= frame.length) {
|
|
76
|
-
// Entire first frame is dropped
|
|
77
|
-
samplesToDrop -= frame.length
|
|
78
|
-
continue
|
|
79
|
-
}
|
|
80
|
-
// Keep only the portion after dropped samples
|
|
81
|
-
buffer = frame.slice(samplesToDrop)
|
|
82
|
-
samplesToDrop = 0
|
|
83
|
-
continue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Concatenate buffer with new frame
|
|
87
|
-
const combined = new Float64Array(buffer.length + frame.length)
|
|
88
|
-
combined.set(buffer)
|
|
89
|
-
combined.set(frame, buffer.length)
|
|
90
|
-
|
|
91
|
-
// Emit all complete frames
|
|
92
|
-
let offset = 0
|
|
93
|
-
while (offset + SAMPLES_PER_FRAME <= combined.length) {
|
|
94
|
-
yield combined.slice(offset, offset + SAMPLES_PER_FRAME)
|
|
95
|
-
offset += SAMPLES_PER_FRAME
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Buffer the remainder for next iteration
|
|
99
|
-
buffer = combined.slice(offset)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Emit final partial frame
|
|
103
|
-
if (buffer.length > 0) {
|
|
104
|
-
yield buffer
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Adapts a mono transform function to work with stereo audio streams
|
|
110
|
-
* @param {Function} monoTransform - Transform function for mono audio
|
|
111
|
-
* @param {...*} args - Additional arguments to pass to the transform
|
|
112
|
-
* @returns {Function} Stereo-adapted transform function
|
|
113
|
-
*/
|
|
114
|
-
export function withStereo(monoTransform, ...args) {
|
|
115
|
-
return async function* (stereoIter) {
|
|
116
|
-
// Buffer to store frames temporarily
|
|
117
|
-
const leftFrames = []
|
|
118
|
-
const rightFrames = []
|
|
119
|
-
|
|
120
|
-
// Collect all frames first (necessary for proper synchronization)
|
|
121
|
-
for await (const [left, right] of stereoIter) {
|
|
122
|
-
leftFrames.push(left)
|
|
123
|
-
rightFrames.push(right)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Create mono iterators
|
|
127
|
-
const leftIter = (async function* () {
|
|
128
|
-
for (const frame of leftFrames) yield frame
|
|
129
|
-
})()
|
|
130
|
-
|
|
131
|
-
const rightIter = (async function* () {
|
|
132
|
-
for (const frame of rightFrames) yield frame
|
|
133
|
-
})()
|
|
134
|
-
|
|
135
|
-
// Apply transform to each channel
|
|
136
|
-
const leftTransformed = monoTransform(leftIter, ...args)
|
|
137
|
-
const rightTransformed = monoTransform(rightIter, ...args)
|
|
138
|
-
|
|
139
|
-
// Zip back together
|
|
140
|
-
for await (const left of leftTransformed) {
|
|
141
|
-
const { value: right } = await rightTransformed.next()
|
|
142
|
-
if (right !== undefined) {
|
|
143
|
-
yield [left, right]
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
32
|
/**
|
|
150
33
|
* Reverses the spectral order of coefficients for mid/high band processing
|
|
151
34
|
*
|
|
152
35
|
* Used in both MDCT and IMDCT stages to handle spectral reversal required
|
|
153
36
|
* by the ATRAC1 format for mid and high frequency bands.
|
|
154
37
|
*
|
|
155
|
-
* @param {
|
|
38
|
+
* @param {Float32Array} spectrum - Input spectrum coefficients
|
|
156
39
|
* @param {Object} reversalBuffers - Pre-allocated buffers for different spectrum sizes
|
|
157
|
-
* @returns {
|
|
40
|
+
* @returns {Float32Array} Spectrum with reversed coefficient order
|
|
158
41
|
*/
|
|
159
42
|
export function reverseSpectrum(spectrum, reversalBuffers) {
|
|
160
43
|
const reversed = reversalBuffers[spectrum.length]
|
|
@@ -177,15 +60,11 @@ export function calculateBandOffset(bandIndex) {
|
|
|
177
60
|
/**
|
|
178
61
|
* Extracts coefficients for a specific frequency band from the full coefficient array
|
|
179
62
|
*
|
|
180
|
-
* @param {
|
|
63
|
+
* @param {Float32Array} coefficients - Full 512-sample coefficient array
|
|
181
64
|
* @param {number} bandIndex - Band index (0=low, 1=mid, 2=high)
|
|
182
|
-
* @returns {
|
|
65
|
+
* @returns {Float32Array} Subarray containing coefficients for the specified band
|
|
183
66
|
*/
|
|
184
67
|
export function extractBandCoefficients(coefficients, bandIndex) {
|
|
185
68
|
const offsets = [0, 128, 256, 512]
|
|
186
69
|
return coefficients.subarray(offsets[bandIndex], offsets[bandIndex + 1])
|
|
187
70
|
}
|
|
188
|
-
|
|
189
|
-
// Stereo-adapted utility functions
|
|
190
|
-
export const withStereoFlushSamples = withStereo(withFlushSamples)
|
|
191
|
-
export const withStereoDelayCompensation = withStereo(withDelayCompensation)
|