carta1 1.1.0 → 1.1.2
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 +23 -72
- package/codec/coding/quantization.js +9 -11
- package/codec/core/buffers.js +33 -44
- package/codec/core/constants.js +13 -84
- package/codec/io/processor.js +18 -18
- package/codec/pipeline/decoder.js +15 -15
- package/codec/pipeline/encoder.js +19 -19
- package/codec/transforms/fft.js +4 -4
- package/codec/transforms/mdct.js +19 -19
- package/codec/transforms/qmf.js +8 -8
- package/codec/utils.js +12 -12
- 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
package/codec/core/constants.js
CHANGED
|
@@ -38,7 +38,7 @@ export const BFU_AMOUNTS = new Int32Array([20, 28, 32, 36, 40, 44, 48, 52])
|
|
|
38
38
|
export const BFU_BAND_BOUNDARIES = new Int32Array([20, 36, 52])
|
|
39
39
|
|
|
40
40
|
// BFU center frequencies (Hz)
|
|
41
|
-
export const BFU_FREQUENCIES = new
|
|
41
|
+
export const BFU_FREQUENCIES = new Float64Array([
|
|
42
42
|
172.3, 516.8, 861.3, 1205.9, 1464.3, 1636.5, 1808.8, 1981.1, 2239.5, 2584.0,
|
|
43
43
|
2928.5, 3273.0, 3574.5, 3832.9, 4091.3, 4349.7, 4608.1, 4866.5, 5124.9,
|
|
44
44
|
5383.3, 5641.7, 5900.1, 6158.5, 6416.9, 6696.8, 6998.3, 7299.8, 7601.2,
|
|
@@ -69,7 +69,7 @@ export const MDCT_SIZE_LONG = 512
|
|
|
69
69
|
|
|
70
70
|
// Window functions
|
|
71
71
|
export const WINDOW_SHORT = (() => {
|
|
72
|
-
const table = new
|
|
72
|
+
const table = new Float64Array(32)
|
|
73
73
|
for (let i = 0; i < 32; i++) {
|
|
74
74
|
table[i] = Math.sin(((i + 0.5) * Math.PI) / 64)
|
|
75
75
|
}
|
|
@@ -82,7 +82,7 @@ export const QMF_DELAY = 46
|
|
|
82
82
|
export const QMF_HIGH_BAND_DELAY = 39
|
|
83
83
|
|
|
84
84
|
// QMF prototype filter coefficients
|
|
85
|
-
export const QMF_COEFFS = new
|
|
85
|
+
export const QMF_COEFFS = new Float64Array([
|
|
86
86
|
-0.00001461907, -0.00009205479, -0.000056157569, 0.00030117269, 0.0002422519,
|
|
87
87
|
-0.00085293897, -0.0005205574, 0.0020340169, 0.00078333891, -0.0042153862,
|
|
88
88
|
-0.00075614988, 0.0078402944, -0.000061169922, -0.01344162, 0.0024626821,
|
|
@@ -92,7 +92,7 @@ export const QMF_COEFFS = new Float32Array([
|
|
|
92
92
|
|
|
93
93
|
// QMF window function
|
|
94
94
|
export const QMF_WINDOW = (() => {
|
|
95
|
-
const window = new
|
|
95
|
+
const window = new Float64Array(QMF_TAPS)
|
|
96
96
|
for (let i = 0; i < 24; i++) {
|
|
97
97
|
window[i] = QMF_COEFFS[i] * 2.0
|
|
98
98
|
window[47 - i] = QMF_COEFFS[i] * 2.0
|
|
@@ -102,7 +102,7 @@ export const QMF_WINDOW = (() => {
|
|
|
102
102
|
|
|
103
103
|
// QMF even/odd taps for optimization
|
|
104
104
|
export const QMF_EVEN = (() => {
|
|
105
|
-
const even = new
|
|
105
|
+
const even = new Float64Array(24)
|
|
106
106
|
for (let i = 0; i < 24; i++) {
|
|
107
107
|
even[i] = QMF_WINDOW[i * 2]
|
|
108
108
|
}
|
|
@@ -110,47 +110,13 @@ export const QMF_EVEN = (() => {
|
|
|
110
110
|
})()
|
|
111
111
|
|
|
112
112
|
export const QMF_ODD = (() => {
|
|
113
|
-
const odd = new
|
|
113
|
+
const odd = new Float64Array(24)
|
|
114
114
|
for (let i = 0; i < 24; i++) {
|
|
115
115
|
odd[i] = QMF_WINDOW[i * 2 + 1]
|
|
116
116
|
}
|
|
117
117
|
return odd
|
|
118
118
|
})()
|
|
119
119
|
|
|
120
|
-
// Psychoacoustic model
|
|
121
|
-
export const BARK_SCALE = (() => {
|
|
122
|
-
const table = new Float32Array(NUM_BFUS)
|
|
123
|
-
for (let i = 0; i < NUM_BFUS; i++) {
|
|
124
|
-
const freq = BFU_FREQUENCIES[i]
|
|
125
|
-
table[i] =
|
|
126
|
-
13.0 * Math.atan(0.00076 * freq) + 3.5 * Math.atan((freq / 7500) ** 2)
|
|
127
|
-
}
|
|
128
|
-
return table
|
|
129
|
-
})()
|
|
130
|
-
|
|
131
|
-
export const SPREADING_MATRIX = (() => {
|
|
132
|
-
const matrix = new Float32Array(NUM_BFUS * NUM_BFUS)
|
|
133
|
-
for (let masker = 0; masker < NUM_BFUS; masker++) {
|
|
134
|
-
for (let masked = 0; masked < NUM_BFUS; masked++) {
|
|
135
|
-
const distance = BARK_SCALE[masker] - BARK_SCALE[masked]
|
|
136
|
-
let spreading = -100.0
|
|
137
|
-
|
|
138
|
-
if (distance >= -3.0 && distance < -1.0) {
|
|
139
|
-
spreading = 17.0 * (distance + 3.0) - 70.0
|
|
140
|
-
} else if (distance >= -1.0 && distance < 0) {
|
|
141
|
-
spreading = 36.0 * distance
|
|
142
|
-
} else if (distance >= 0 && distance < 1.0) {
|
|
143
|
-
spreading = -27.0 * distance
|
|
144
|
-
} else if (distance >= 1.0 && distance < 8.0) {
|
|
145
|
-
spreading = -(distance - 1.0) * 4.0 - 27.0
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
matrix[masker * NUM_BFUS + masked] = spreading
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return matrix
|
|
152
|
-
})()
|
|
153
|
-
|
|
154
120
|
// FFT sizes for transient detection
|
|
155
121
|
export const FFT_SIZE_LOW = 128
|
|
156
122
|
export const FFT_SIZE_MID = 128
|
|
@@ -167,9 +133,6 @@ export const MDCT_SHORT_BLOCK_SIZE = 32
|
|
|
167
133
|
export const MDCT_OVERLAP_SIZE = 32
|
|
168
134
|
export const MDCT_TAIL_WINDOW_SIZE = 16
|
|
169
135
|
|
|
170
|
-
// Quantization
|
|
171
|
-
export const QUANTIZATION_SIGN_BIT_SHIFT = 1
|
|
172
|
-
|
|
173
136
|
// WAV format
|
|
174
137
|
export const WAV_HEADER_SIZE = 44
|
|
175
138
|
export const WAV_BYTES_PER_SAMPLE = 2
|
|
@@ -184,59 +147,25 @@ export const FRAME_WORD_LENGTH_BITS = 4
|
|
|
184
147
|
export const FRAME_SCALE_FACTOR_BITS = 6
|
|
185
148
|
|
|
186
149
|
// Quantization
|
|
150
|
+
export const QUANTIZATION_SIGN_BIT_SHIFT = 1
|
|
187
151
|
export const MAX_WORD_LENGTH_INDEX = 15
|
|
188
152
|
export const WORD_LENGTH_BITS = new Int32Array([
|
|
189
153
|
0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
190
154
|
])
|
|
191
|
-
export const QUANT_DISTORTION_TABLE = (() => {
|
|
192
|
-
const table = new Float32Array(MAX_WORD_LENGTH_INDEX + 1)
|
|
193
|
-
table[0] = 1.0
|
|
194
|
-
for (let wl = 1; wl <= MAX_WORD_LENGTH_INDEX; wl++) {
|
|
195
|
-
const bitsPerSample = WORD_LENGTH_BITS[wl]
|
|
196
|
-
const quantRange = (1 << (bitsPerSample - 1)) - 1
|
|
197
|
-
table[wl] = 1.0 / (12.0 * quantRange * quantRange)
|
|
198
|
-
}
|
|
199
|
-
return table
|
|
200
|
-
})()
|
|
201
|
-
export const DISTORTION_DELTA_DB = (() => {
|
|
202
|
-
const table = new Float32Array(MAX_WORD_LENGTH_INDEX)
|
|
203
|
-
for (let i = 0; i < MAX_WORD_LENGTH_INDEX; i++) {
|
|
204
|
-
table[i] =
|
|
205
|
-
10 *
|
|
206
|
-
Math.log10(
|
|
207
|
-
QUANT_DISTORTION_TABLE[i + 1] / (QUANT_DISTORTION_TABLE[i] || 1.0)
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
return table
|
|
211
|
-
})()
|
|
212
155
|
export const SCALE_FACTORS = (() => {
|
|
213
|
-
const table = new
|
|
156
|
+
const table = new Float64Array(64)
|
|
214
157
|
for (let i = 0; i < 64; i++) {
|
|
215
158
|
table[i] = Math.pow(2.0, i / 3.0 - 21)
|
|
216
159
|
}
|
|
217
160
|
return table
|
|
218
161
|
})()
|
|
219
162
|
|
|
220
|
-
//
|
|
221
|
-
export const
|
|
222
|
-
export const LP_DISTORTION_CONSTANT = 1 / 80 // For L4 (p=4), it's 1/80.
|
|
223
|
-
export const { POW2_NEG_P, POW2_POS_P, SF_POW_P } = (() => {
|
|
224
|
-
// Calculate the 2^{-p*b} table
|
|
163
|
+
// Bit allocation
|
|
164
|
+
export const INV_POWER_OF_TWO = (() => {
|
|
225
165
|
const maxBits = WORD_LENGTH_BITS[MAX_WORD_LENGTH_INDEX]
|
|
226
|
-
const
|
|
166
|
+
const table = new Float64Array(maxBits + 1)
|
|
227
167
|
for (let b = 0; b <= maxBits; b++) {
|
|
228
|
-
|
|
168
|
+
table[b] = Math.pow(2, -b)
|
|
229
169
|
}
|
|
230
|
-
|
|
231
|
-
// Calculate the 2^{p} constant for the D(0) case
|
|
232
|
-
const pow2_pos_p = Math.pow(2, LP_DISTORTION_P)
|
|
233
|
-
|
|
234
|
-
// Calculate the (scale_factor)^p table
|
|
235
|
-
const sfPowP = new Float64Array(SCALE_FACTORS.length)
|
|
236
|
-
for (let i = 0; i < SCALE_FACTORS.length; i++) {
|
|
237
|
-
sfPowP[i] = Math.pow(SCALE_FACTORS[i], LP_DISTORTION_P)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Return all computed tables in a single object
|
|
241
|
-
return { POW2_NEG_P: pow2_neg_p, POW2_POS_P: pow2_pos_p, SF_POW_P: sfPowP }
|
|
170
|
+
return table
|
|
242
171
|
})()
|
package/codec/io/processor.js
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
export class AudioProcessor {
|
|
44
44
|
/**
|
|
45
45
|
* Encodes a stream of audio frames to ATRAC1 format
|
|
46
|
-
* @param {AsyncIterable<
|
|
46
|
+
* @param {AsyncIterable<Float64Array>|AsyncIterable<[Float64Array, Float64Array]>} audioFrames - Audio frame stream
|
|
47
47
|
* @param {Object} [options={}] - Encoding options
|
|
48
48
|
* @param {number} [options.channelCount=1] - Number of audio channels (1 or 2)
|
|
49
49
|
* @param {Function} [options.onProgress] - Progress callback function
|
|
@@ -73,7 +73,7 @@ export class AudioProcessor {
|
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Internal method for encoding mono audio streams
|
|
76
|
-
* @param {AsyncIterable<
|
|
76
|
+
* @param {AsyncIterable<Float64Array>} audioFrames - Mono audio frame stream
|
|
77
77
|
* @param {Function} onProgress - Progress callback function
|
|
78
78
|
* @param {EncoderOptions} encoderOptions - Encoder configuration
|
|
79
79
|
* @returns {AsyncGenerator<Object>} Stream of encoded frame data
|
|
@@ -95,7 +95,7 @@ export class AudioProcessor {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Internal method for encoding stereo audio streams
|
|
98
|
-
* @param {AsyncIterable<[
|
|
98
|
+
* @param {AsyncIterable<[Float64Array, Float64Array]>} audioFrames - Stereo audio frame stream
|
|
99
99
|
* @param {Function} onProgress - Progress callback function
|
|
100
100
|
* @param {EncoderOptions} encoderOptions - Encoder configuration
|
|
101
101
|
* @returns {AsyncGenerator<Object>} Stream of encoded frame data (left then right)
|
|
@@ -128,7 +128,7 @@ export class AudioProcessor {
|
|
|
128
128
|
* @param {Object} [options={}] - Decoding options
|
|
129
129
|
* @param {number} [options.channelCount=1] - Number of audio channels (1 or 2)
|
|
130
130
|
* @param {Function} [options.onProgress] - Progress callback function
|
|
131
|
-
* @returns {AsyncGenerator<
|
|
131
|
+
* @returns {AsyncGenerator<Float64Array>|AsyncGenerator<[Float64Array, Float64Array]>} Stream of decoded PCM frames
|
|
132
132
|
* @throws {Error} If unsupported channel count is provided
|
|
133
133
|
*/
|
|
134
134
|
static async *decodeStream(encodedFrames, options = {}) {
|
|
@@ -147,7 +147,7 @@ export class AudioProcessor {
|
|
|
147
147
|
* Internal method for decoding mono audio streams
|
|
148
148
|
* @param {AsyncIterable<Object>} encodedFrames - Mono encoded frame stream
|
|
149
149
|
* @param {Function} onProgress - Progress callback function
|
|
150
|
-
* @returns {AsyncGenerator<
|
|
150
|
+
* @returns {AsyncGenerator<Float64Array>} Stream of decoded mono PCM frames
|
|
151
151
|
* @private
|
|
152
152
|
*/
|
|
153
153
|
static async *_decodeMonoStream(encodedFrames, onProgress) {
|
|
@@ -174,7 +174,7 @@ export class AudioProcessor {
|
|
|
174
174
|
* Internal method for decoding stereo audio streams
|
|
175
175
|
* @param {AsyncIterable<Object>} encodedFrames - Stereo encoded frame stream (interleaved left/right)
|
|
176
176
|
* @param {Function} onProgress - Progress callback function
|
|
177
|
-
* @returns {AsyncGenerator<[
|
|
177
|
+
* @returns {AsyncGenerator<[Float64Array, Float64Array]>} Stream of decoded stereo PCM frame pairs
|
|
178
178
|
* @private
|
|
179
179
|
*/
|
|
180
180
|
static async *_decodeStereoStream(encodedFrames, onProgress) {
|
|
@@ -225,9 +225,9 @@ export class AudioProcessor {
|
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
227
|
* Converts channel buffer arrays to frame-based stream format
|
|
228
|
-
* @param {
|
|
228
|
+
* @param {Float64Array[]} buffers - Array of channel buffers (1 for mono, 2 for stereo)
|
|
229
229
|
* @param {number} [frameSize=SAMPLES_PER_FRAME] - Frame size in samples
|
|
230
|
-
* @returns {Generator<
|
|
230
|
+
* @returns {Generator<Float64Array>|Generator<[Float64Array, Float64Array]>} Stream of audio frames
|
|
231
231
|
* @throws {Error} If unsupported channel count is provided
|
|
232
232
|
*/
|
|
233
233
|
static *frameBufferToFrames(buffers, frameSize = SAMPLES_PER_FRAME) {
|
|
@@ -236,7 +236,7 @@ export class AudioProcessor {
|
|
|
236
236
|
if (channelCount === 1) {
|
|
237
237
|
const [left] = buffers
|
|
238
238
|
for (let i = 0; i < left.length; i += frameSize) {
|
|
239
|
-
const frame = new
|
|
239
|
+
const frame = new Float64Array(frameSize)
|
|
240
240
|
const end = Math.min(i + frameSize, left.length)
|
|
241
241
|
for (let j = 0; j < end - i; j++) {
|
|
242
242
|
frame[j] = left[i + j]
|
|
@@ -248,8 +248,8 @@ export class AudioProcessor {
|
|
|
248
248
|
const maxLength = Math.max(left.length, right.length)
|
|
249
249
|
|
|
250
250
|
for (let i = 0; i < maxLength; i += frameSize) {
|
|
251
|
-
const leftFrame = new
|
|
252
|
-
const rightFrame = new
|
|
251
|
+
const leftFrame = new Float64Array(frameSize)
|
|
252
|
+
const rightFrame = new Float64Array(frameSize)
|
|
253
253
|
|
|
254
254
|
const end = Math.min(i + frameSize, maxLength)
|
|
255
255
|
|
|
@@ -327,7 +327,7 @@ export class AudioProcessor {
|
|
|
327
327
|
|
|
328
328
|
/**
|
|
329
329
|
* Creates a WAV file blob from PCM audio frames
|
|
330
|
-
* @param {
|
|
330
|
+
* @param {Float64Array[]|Float64Array|[Float64Array, Float64Array][]} pcmFrames - PCM audio frames
|
|
331
331
|
* @param {number} [channelCount=1] - Number of audio channels
|
|
332
332
|
* @param {number} [sampleRate=SAMPLE_RATE] - Audio sample rate
|
|
333
333
|
* @returns {Blob} WAV file blob
|
|
@@ -347,7 +347,7 @@ export class AudioProcessor {
|
|
|
347
347
|
|
|
348
348
|
/**
|
|
349
349
|
* Creates a mono WAV file blob from PCM frames
|
|
350
|
-
* @param {
|
|
350
|
+
* @param {Float64Array[]} frames - Array of mono PCM frames
|
|
351
351
|
* @param {number} sampleRate - Audio sample rate
|
|
352
352
|
* @returns {Blob} Mono WAV file blob
|
|
353
353
|
* @private
|
|
@@ -383,7 +383,7 @@ export class AudioProcessor {
|
|
|
383
383
|
|
|
384
384
|
/**
|
|
385
385
|
* Creates a stereo WAV file blob from PCM frame pairs
|
|
386
|
-
* @param {[
|
|
386
|
+
* @param {[Float64Array, Float64Array][]} framePairs - Array of stereo PCM frame pairs
|
|
387
387
|
* @param {number} sampleRate - Audio sample rate
|
|
388
388
|
* @returns {Blob} Stereo WAV file blob
|
|
389
389
|
* @private
|
|
@@ -524,9 +524,9 @@ export class AudioProcessor {
|
|
|
524
524
|
|
|
525
525
|
/**
|
|
526
526
|
* Assembles PCM frames into a continuous buffer
|
|
527
|
-
* @param {
|
|
527
|
+
* @param {Float64Array[]|[Float64Array, Float64Array][]} pcmFrames - Array of PCM frames
|
|
528
528
|
* @param {number} channelCount - Number of audio channels
|
|
529
|
-
* @returns {
|
|
529
|
+
* @returns {Float64Array} Continuous PCM buffer (interleaved for stereo)
|
|
530
530
|
* @throws {Error} If unsupported channel count is provided
|
|
531
531
|
*/
|
|
532
532
|
static assemblePcmFrames(pcmFrames, channelCount) {
|
|
@@ -535,7 +535,7 @@ export class AudioProcessor {
|
|
|
535
535
|
(sum, frame) => sum + frame.length,
|
|
536
536
|
0
|
|
537
537
|
)
|
|
538
|
-
const pcm = new
|
|
538
|
+
const pcm = new Float64Array(totalSamples)
|
|
539
539
|
let offset = 0
|
|
540
540
|
for (const frame of pcmFrames) {
|
|
541
541
|
pcm.set(frame, offset)
|
|
@@ -547,7 +547,7 @@ export class AudioProcessor {
|
|
|
547
547
|
(sum, [left, right]) => sum + Math.max(left.length, right.length),
|
|
548
548
|
0
|
|
549
549
|
)
|
|
550
|
-
const pcm = new
|
|
550
|
+
const pcm = new Float64Array(totalSamples * 2)
|
|
551
551
|
|
|
552
552
|
let pcmOffset = 0
|
|
553
553
|
for (const [leftFrame, rightFrame] of pcmFrames) {
|
|
@@ -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 {Float64Array} 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 Float64Array(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 {Float64Array} coeffs
|
|
127
127
|
* @param {number} bandIndex
|
|
128
128
|
* @param {number} blockMode
|
|
129
|
-
* @param {
|
|
129
|
+
* @param {Float64Array} overlapBuffer
|
|
130
130
|
* @param {Object} bufferPool
|
|
131
131
|
* @param {Object} config
|
|
132
|
-
* @returns {
|
|
132
|
+
* @returns {Float64Array}
|
|
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 {Float64Array} coeffs
|
|
168
168
|
* @param {number} bandIndex
|
|
169
|
-
* @param {
|
|
169
|
+
* @param {Float64Array} overlapBuffer
|
|
170
170
|
* @param {Object} bufferPool
|
|
171
171
|
* @param {Object} config
|
|
172
172
|
* @param {Object} transformFunc
|
|
173
|
-
* @returns {
|
|
173
|
+
* @returns {Float64Array}
|
|
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 {Float64Array} coeffs
|
|
238
238
|
* @param {number} bandIndex
|
|
239
|
-
* @param {
|
|
239
|
+
* @param {Float64Array} overlapBuffer
|
|
240
240
|
* @param {Object} bufferPool
|
|
241
241
|
* @param {Object} config
|
|
242
|
-
* @returns {
|
|
242
|
+
* @returns {Float64Array}
|
|
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 {Float64Array} input.coefficients - MDCT coefficients (512 samples)
|
|
312
312
|
* @param {Array<number>} input.blockModes - Block modes for each band
|
|
313
|
-
* @returns {Array<
|
|
313
|
+
* @returns {Array<Float64Array>} Three reconstructed frequency bands [low, mid, high]
|
|
314
314
|
*/
|
|
315
315
|
return (input) => {
|
|
316
316
|
const { coefficients, blockModes } = input
|
|
@@ -354,8 +354,8 @@ 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<Float64Array>} bands - Three frequency bands [low, mid, high]
|
|
358
|
+
* @returns {Float64Array} Reconstructed PCM samples
|
|
359
359
|
*/
|
|
360
360
|
return (bands) => {
|
|
361
361
|
// Apply high band delay compensation
|
|
@@ -62,9 +62,9 @@ export function qmfAnalysisStage(context) {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Process PCM samples through QMF analysis
|
|
65
|
-
* @param {
|
|
65
|
+
* @param {Float64Array} pcmSamples - Input PCM samples
|
|
66
66
|
* @returns {Object} Analysis result containing frequency bands
|
|
67
|
-
* @returns {Array<
|
|
67
|
+
* @returns {Array<Float64Array>} 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<Float64Array>} input.bands - Three frequency bands [low, mid, high]
|
|
122
122
|
* @returns {Object} Block selection results
|
|
123
|
-
* @returns {Array<
|
|
123
|
+
* @returns {Array<Float64Array>} 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 {Float64Array} samples
|
|
174
174
|
* @param {number} bandIndex
|
|
175
175
|
* @param {number} blockMode
|
|
176
|
-
* @param {
|
|
176
|
+
* @param {Float64Array} overlapBuffer
|
|
177
177
|
* @param {Object} bufferPool
|
|
178
|
-
* @returns {
|
|
178
|
+
* @returns {Float64Array}
|
|
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 {Float64Array} samples
|
|
214
214
|
* @param {number} bandIndex
|
|
215
215
|
* @param {Object} config
|
|
216
216
|
* @param {Object} transformFunc
|
|
217
|
-
* @param {
|
|
217
|
+
* @param {Float64Array} overlapBuffer
|
|
218
218
|
* @param {Object} bufferPool
|
|
219
|
-
* @returns {
|
|
219
|
+
* @returns {Float64Array}
|
|
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 {Float64Array} samples
|
|
256
256
|
* @param {number} bandIndex
|
|
257
257
|
* @param {Object} config
|
|
258
|
-
* @param {
|
|
258
|
+
* @param {Float64Array} overlapBuffer
|
|
259
259
|
* @param {Object} bufferPool
|
|
260
|
-
* @returns {
|
|
260
|
+
* @returns {Float64Array}
|
|
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 Float64Array(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<Float64Array>} 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<Float64Array>} returns.bands - Original frequency bands
|
|
319
|
+
* @returns {Float64Array} 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 Float64Array(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 {Float64Array} 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
|
@@ -7,8 +7,8 @@ export class FFT {
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Perform an in-place forward FFT on complex data.
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
10
|
+
* @param {Float64Array} real - Real part of the input/output data
|
|
11
|
+
* @param {Float64Array} imag - Imaginary part of the input/output data
|
|
12
12
|
*/
|
|
13
13
|
static fft(real, imag) {
|
|
14
14
|
const n = real.length
|
|
@@ -123,8 +123,8 @@ export class FFT {
|
|
|
123
123
|
bitRev[i] = (bitRev[i >>> 1] >>> 1) | ((i & 1) << (log2n - 1))
|
|
124
124
|
}
|
|
125
125
|
const halfN = n >>> 1
|
|
126
|
-
const cosTbl = new
|
|
127
|
-
const sinTbl = new
|
|
126
|
+
const cosTbl = new Float64Array(halfN)
|
|
127
|
+
const sinTbl = new Float64Array(halfN)
|
|
128
128
|
const ang = (-2 * Math.PI) / n
|
|
129
129
|
for (let i = 0; i < halfN; i++) {
|
|
130
130
|
const a = ang * i
|
package/codec/transforms/mdct.js
CHANGED
|
@@ -28,7 +28,7 @@ class MDCTBase {
|
|
|
28
28
|
const omega = (2.0 * Math.PI) / size
|
|
29
29
|
const scaleRoot = Math.sqrt(scale / size)
|
|
30
30
|
|
|
31
|
-
this.sinCosTable = new
|
|
31
|
+
this.sinCosTable = new Float64Array(this.halfSize)
|
|
32
32
|
for (let i = 0; i < this.quarterSize; i++) {
|
|
33
33
|
const angle = omega * i + alpha
|
|
34
34
|
this.sinCosTable[i * 2] = scaleRoot * Math.cos(angle)
|
|
@@ -47,9 +47,9 @@ export class MDCT extends MDCTBase {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Perform forward MDCT transform
|
|
50
|
-
* @param {
|
|
50
|
+
* @param {Float64Array} input - Time-domain input samples
|
|
51
51
|
* @param {Object} mdctBuffers - Optional work buffers for FFT
|
|
52
|
-
* @returns {
|
|
52
|
+
* @returns {Float64Array} 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 Float64Array(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 {Float64Array} input - Frequency-domain MDCT coefficients
|
|
136
136
|
* @param {Object} mdctBuffers - Optional work buffers for FFT
|
|
137
|
-
* @returns {
|
|
137
|
+
* @returns {Float64Array} Time-domain output samples
|
|
138
138
|
*/
|
|
139
139
|
transform(input, mdctBuffers = null) {
|
|
140
140
|
const buffers =
|
|
@@ -160,19 +160,19 @@ export class IMDCT extends MDCTBase {
|
|
|
160
160
|
// Pre-FFT processing
|
|
161
161
|
for (let i = 0; i < this.fftSize; i++) {
|
|
162
162
|
const i2 = i * 2
|
|
163
|
-
const r = input[i2]
|
|
164
|
-
const im = input[this.halfSize - 1 - i2]
|
|
163
|
+
const r = -input[i2]
|
|
164
|
+
const im = -input[this.halfSize - 1 - i2]
|
|
165
165
|
const c = this.sinCosTable[i2]
|
|
166
166
|
const s = this.sinCosTable[i2 + 1]
|
|
167
167
|
|
|
168
|
-
real[i] =
|
|
169
|
-
imag[i] =
|
|
168
|
+
real[i] = im * s + r * c
|
|
169
|
+
imag[i] = im * c - r * s
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
FFT.fft(real, imag)
|
|
173
173
|
|
|
174
174
|
// Post-FFT butterfly
|
|
175
|
-
const output = new
|
|
175
|
+
const output = new Float64Array(this.size)
|
|
176
176
|
|
|
177
177
|
for (let i = 0; i < this.fftSize / 2; i++) {
|
|
178
178
|
const i2 = i * 2
|
|
@@ -216,20 +216,20 @@ export const mdct64 = new MDCT(MDCT_SIZE_SHORT, 0.5)
|
|
|
216
216
|
export const mdct256 = new MDCT(MDCT_SIZE_MID, 0.5)
|
|
217
217
|
export const mdct512 = new MDCT(MDCT_SIZE_LONG, 1.0)
|
|
218
218
|
|
|
219
|
-
export const imdct64 = new IMDCT(MDCT_SIZE_SHORT, MDCT_SIZE_SHORT *
|
|
220
|
-
export const imdct256 = new IMDCT(MDCT_SIZE_MID, MDCT_SIZE_MID *
|
|
221
|
-
export const imdct512 = new IMDCT(MDCT_SIZE_LONG, MDCT_SIZE_LONG *
|
|
219
|
+
export const imdct64 = new IMDCT(MDCT_SIZE_SHORT, MDCT_SIZE_SHORT * 8)
|
|
220
|
+
export const imdct256 = new IMDCT(MDCT_SIZE_MID, MDCT_SIZE_MID * 8)
|
|
221
|
+
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 {Float64Array} prev - Previous block samples
|
|
226
|
+
* @param {Float64Array} curr - Current block samples
|
|
227
|
+
* @param {Float64Array} window - Window function coefficients
|
|
228
|
+
* @returns {Float64Array} 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 Float64Array(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 {Float64Array} input - Input audio samples
|
|
15
|
+
* @param {Float64Array} 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 Float64Array(outputLength)
|
|
30
|
+
const highBand = new Float64Array(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 {Float64Array} lowBand - Low frequency band samples
|
|
55
|
+
* @param {Float64Array} highBand - High frequency band samples
|
|
56
|
+
* @param {Float64Array} 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 Float64Array(outputLength)
|
|
86
86
|
|
|
87
87
|
// Convolution
|
|
88
88
|
for (let i = 0; i < subbandLength; i++) {
|