carta1 1.1.1 → 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 +3 -3
- package/codec/coding/quantization.js +9 -11
- package/codec/core/buffers.js +32 -32
- package/codec/core/constants.js +9 -66
- 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/bin/cli.js
CHANGED
|
@@ -156,7 +156,7 @@ class ProgressTracker {
|
|
|
156
156
|
* Provides async iteration over WAV file frames for memory-efficient
|
|
157
157
|
* processing of large audio files. Automatically handles:
|
|
158
158
|
* - Multi-channel deinterleaving
|
|
159
|
-
* - Bit depth conversion to
|
|
159
|
+
* - Bit depth conversion to Float64
|
|
160
160
|
* - Frame padding for the last partial frame
|
|
161
161
|
*
|
|
162
162
|
* @class
|
|
@@ -225,9 +225,9 @@ class WavReader {
|
|
|
225
225
|
* Async iterator for streaming WAV file frames
|
|
226
226
|
*
|
|
227
227
|
* @param {number} [frameSize=SAMPLES_PER_FRAME] - Samples per frame
|
|
228
|
-
* @yields {
|
|
229
|
-
* - Mono:
|
|
230
|
-
* - Stereo: [left, right]
|
|
228
|
+
* @yields {Float64Array|Array<Float64Array>} Audio frame data
|
|
229
|
+
* - Mono: Float64Array of samples
|
|
230
|
+
* - Stereo: [left, right] Float64Arrays
|
|
231
231
|
*/
|
|
232
232
|
async *[Symbol.asyncIterator](frameSize = SAMPLES_PER_FRAME) {
|
|
233
233
|
const fileStream = fs.createReadStream(this.filePath)
|
|
@@ -305,23 +305,23 @@ class WavReader {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
/**
|
|
308
|
-
* Process raw PCM buffer into
|
|
308
|
+
* Process raw PCM buffer into float64 frame data
|
|
309
309
|
*
|
|
310
310
|
* @private
|
|
311
311
|
* @param {Buffer} frameBuffer - Raw PCM data
|
|
312
312
|
* @param {number} frameSize - Number of samples per channel
|
|
313
|
-
* @returns {
|
|
313
|
+
* @returns {Float64Array|Array<Float64Array>} Processed frame data
|
|
314
314
|
*/
|
|
315
315
|
_processFrameBuffer(frameBuffer, frameSize) {
|
|
316
316
|
if (this.channels === 1) {
|
|
317
|
-
const frame = new
|
|
317
|
+
const frame = new Float64Array(frameSize)
|
|
318
318
|
for (let i = 0; i < frameSize; i++) {
|
|
319
319
|
frame[i] = this._sampleToFloat(frameBuffer, i * (this.bitDepth / 8))
|
|
320
320
|
}
|
|
321
321
|
return frame
|
|
322
322
|
} else {
|
|
323
|
-
const left = new
|
|
324
|
-
const right = new
|
|
323
|
+
const left = new Float64Array(frameSize)
|
|
324
|
+
const right = new Float64Array(frameSize)
|
|
325
325
|
for (let i = 0; i < frameSize; i++) {
|
|
326
326
|
const offset = i * this.channels * (this.bitDepth / 8)
|
|
327
327
|
left[i] = this._sampleToFloat(frameBuffer, offset)
|
|
@@ -332,7 +332,7 @@ class WavReader {
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* Convert PCM sample to normalized
|
|
335
|
+
* Convert PCM sample to normalized Float64 value
|
|
336
336
|
*
|
|
337
337
|
* @private
|
|
338
338
|
* @param {Buffer} buffer - PCM data buffer
|
|
@@ -10,13 +10,13 @@ import { FFT } from '../transforms/fft.js'
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Perform FFT on time-domain samples and return magnitude spectrum
|
|
13
|
-
* @param {
|
|
13
|
+
* @param {Float64Array} samples - Time-domain samples
|
|
14
14
|
* @param {number} fftSize - FFT size (must be power of 2)
|
|
15
|
-
* @returns {
|
|
15
|
+
* @returns {Float64Array} Magnitude spectrum (positive frequencies only)
|
|
16
16
|
*/
|
|
17
17
|
export function performFFT(samples, fftSize) {
|
|
18
|
-
const real = new
|
|
19
|
-
const imag = new
|
|
18
|
+
const real = new Float64Array(fftSize)
|
|
19
|
+
const imag = new Float64Array(fftSize)
|
|
20
20
|
|
|
21
21
|
// Copy samples with zero-padding if necessary
|
|
22
22
|
const copyLen = Math.min(samples.length, fftSize)
|
|
@@ -26,7 +26,7 @@ export function performFFT(samples, fftSize) {
|
|
|
26
26
|
FFT.fft(real, imag)
|
|
27
27
|
|
|
28
28
|
// Calculate magnitude spectrum for positive frequencies
|
|
29
|
-
const magnitude = new
|
|
29
|
+
const magnitude = new Float64Array(fftSize / 2)
|
|
30
30
|
for (let i = 0; i < fftSize / 2; i++) {
|
|
31
31
|
magnitude[i] = Math.sqrt(real[i] * real[i] + imag[i] * imag[i])
|
|
32
32
|
}
|
|
@@ -36,8 +36,8 @@ export function performFFT(samples, fftSize) {
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Detect transients by analyzing spectral changes between frames
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {
|
|
39
|
+
* @param {Float64Array} currentCoeffs - Current frame coefficients
|
|
40
|
+
* @param {Float64Array} prevCoeffs - Previous frame coefficients
|
|
41
41
|
* @param {number} threshold - Detection threshold
|
|
42
42
|
* @returns {boolean} True if transient detected
|
|
43
43
|
*/
|
|
@@ -56,8 +56,8 @@ export function detectTransient(currentCoeffs, prevCoeffs, threshold) {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Calculate all spectral features for transient detection
|
|
59
|
-
* @param {
|
|
60
|
-
* @param {
|
|
59
|
+
* @param {Float64Array} currentCoeffs - Current frame coefficients
|
|
60
|
+
* @param {Float64Array} prevCoeffs - Previous frame coefficients
|
|
61
61
|
* @returns {Object} Computed spectral features
|
|
62
62
|
*/
|
|
63
63
|
function calculateSpectralFeatures(currentCoeffs, prevCoeffs) {
|
|
@@ -114,7 +114,7 @@ function calculateSpectralFlux(currentCoeffs, prevCoeffs) {
|
|
|
114
114
|
/**
|
|
115
115
|
* Calculate spectral flatness (geometric mean / arithmetic mean)
|
|
116
116
|
* Measures how noise-like vs tonal the spectrum is
|
|
117
|
-
* @param {
|
|
117
|
+
* @param {Float64Array} coeffs - Spectral coefficients
|
|
118
118
|
* @returns {number} Flatness measure (0 = tonal, 1 = noise-like)
|
|
119
119
|
*/
|
|
120
120
|
function calculateSpectralFlatness(coeffs) {
|
|
@@ -143,7 +143,7 @@ function calculateSpectralFlatness(coeffs) {
|
|
|
143
143
|
/**
|
|
144
144
|
* Calculate ratio of high-frequency to total energy
|
|
145
145
|
* High values indicate bright/harsh sounds that are perceptually important
|
|
146
|
-
* @param {
|
|
146
|
+
* @param {Float64Array} coeffs - Spectral coefficients
|
|
147
147
|
* @returns {number} High-frequency energy ratio (0-1)
|
|
148
148
|
*/
|
|
149
149
|
function calculateHighFrequencyRatio(coeffs) {
|
|
@@ -165,8 +165,8 @@ function calculateHighFrequencyRatio(coeffs) {
|
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
167
|
* Calculate energy change between frames in dB
|
|
168
|
-
* @param {
|
|
169
|
-
* @param {
|
|
168
|
+
* @param {Float64Array} currentCoeffs - Current frame coefficients
|
|
169
|
+
* @param {Float64Array} prevCoeffs - Previous frame coefficients
|
|
170
170
|
* @returns {number} Energy change in dB (positive values only)
|
|
171
171
|
*/
|
|
172
172
|
function calculateEnergyChange(currentCoeffs, prevCoeffs) {
|
|
@@ -64,9 +64,9 @@ class Carta1Worker {
|
|
|
64
64
|
/**
|
|
65
65
|
* Encode PCM audio to ATRAC1 format
|
|
66
66
|
*
|
|
67
|
-
* @param {
|
|
68
|
-
* - Mono:
|
|
69
|
-
* - Stereo: [left, right]
|
|
67
|
+
* @param {Float64Array|Array<Float64Array>} pcmData - PCM audio data
|
|
68
|
+
* - Mono: Float64Array
|
|
69
|
+
* - Stereo: [left, right] Float64Arrays
|
|
70
70
|
* @param {Object} [options={}] - Encoder options
|
|
71
71
|
* @param {number} [options.transientThresholdLow] - Low band transient threshold
|
|
72
72
|
* @param {number} [options.transientThresholdMid] - Mid band transient threshold
|
package/codec/browser/worker.js
CHANGED
|
@@ -56,7 +56,7 @@ self.onmessage = async (e) => {
|
|
|
56
56
|
/**
|
|
57
57
|
* Encode PCM audio data to ATRAC1 format
|
|
58
58
|
*
|
|
59
|
-
* @param {Array<
|
|
59
|
+
* @param {Array<Float64Array>} pcmData - PCM audio data [mono] or [left, right]
|
|
60
60
|
* @param {Object} options - Encoder options
|
|
61
61
|
* @returns {Promise<Object>} Encoded AEA blob
|
|
62
62
|
*/
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Allocate bits to BFUs using optimized greedy Lp-RDO
|
|
38
|
-
* @param {Array<
|
|
38
|
+
* @param {Array<Float64Array>} bfuData
|
|
39
39
|
* @param {Int32Array} bfuSizes
|
|
40
40
|
* @param {number} maxBfuCount
|
|
41
41
|
* @returns {{bfuCount:number, allocation:Int32Array, scaleFactorIndices:Int32Array}}
|
|
@@ -62,7 +62,7 @@ export function allocateBits(bfuData, bfuSizes, maxBfuCount) {
|
|
|
62
62
|
* bits to the BFUs that provide the greatest distortion reduction per bit spent.
|
|
63
63
|
*
|
|
64
64
|
* @param {number} activeBfuCount
|
|
65
|
-
* @param {Array<
|
|
65
|
+
* @param {Array<Float64Array>} bfuData
|
|
66
66
|
* @param {Int32Array} bfuSizes
|
|
67
67
|
* @param {number} remainingBits
|
|
68
68
|
* @returns {{wordLengths: Int32Array, scaleFactorIndices: Int32Array}}
|
|
@@ -150,7 +150,7 @@ function distributeBitsRDO(activeBfuCount, bfuData, bfuSizes, remainingBits) {
|
|
|
150
150
|
/**
|
|
151
151
|
* Find the optimal scale factor index for a set of coefficients.
|
|
152
152
|
* Same semantics as the original.
|
|
153
|
-
* @param {
|
|
153
|
+
* @param {Float64Array} coefficients
|
|
154
154
|
* @returns {number}
|
|
155
155
|
*/
|
|
156
156
|
export function findScaleFactor(coefficients) {
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Quantize coefficients.
|
|
29
|
-
* @param {
|
|
29
|
+
* @param {Float64Array} coefficients
|
|
30
30
|
* @param {number} scaleFactorIndex
|
|
31
31
|
* @param {number} bitsPerSample
|
|
32
32
|
* @returns {Int32Array} out
|
|
@@ -35,7 +35,6 @@ export function quantize(coefficients, scaleFactorIndex, bitsPerSample) {
|
|
|
35
35
|
const length = coefficients.length
|
|
36
36
|
const out = new Int32Array(length)
|
|
37
37
|
if (bitsPerSample === 0 || scaleFactorIndex === 0) {
|
|
38
|
-
// Zero fast path
|
|
39
38
|
out.fill(0, 0, length)
|
|
40
39
|
return out
|
|
41
40
|
}
|
|
@@ -45,7 +44,7 @@ export function quantize(coefficients, scaleFactorIndex, bitsPerSample) {
|
|
|
45
44
|
const normFactor = quantRange / scaleFactor
|
|
46
45
|
|
|
47
46
|
const hi = quantRange
|
|
48
|
-
const lo = -quantRange
|
|
47
|
+
const lo = -quantRange
|
|
49
48
|
|
|
50
49
|
for (let i = 0; i < length; i++) {
|
|
51
50
|
const x = coefficients[i] * normFactor
|
|
@@ -61,20 +60,19 @@ export function quantize(coefficients, scaleFactorIndex, bitsPerSample) {
|
|
|
61
60
|
* @param {Int32Array} quantized
|
|
62
61
|
* @param {number} scaleFactorIndex
|
|
63
62
|
* @param {number} bitsPerSample
|
|
64
|
-
* @returns {
|
|
63
|
+
* @returns {Float64Array}
|
|
65
64
|
*/
|
|
66
65
|
export function dequantize(quantized, scaleFactorIndex, bitsPerSample) {
|
|
67
66
|
if (bitsPerSample === 0 || scaleFactorIndex === 0) {
|
|
68
|
-
return new
|
|
67
|
+
return new Float64Array(quantized.length)
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
const scaleFactor = SCALE_FACTORS[scaleFactorIndex]
|
|
72
71
|
const quantRange = (1 << (bitsPerSample - QUANTIZATION_SIGN_BIT_SHIFT)) - 1
|
|
73
|
-
const denormFactor = scaleFactor / quantRange
|
|
74
72
|
|
|
75
|
-
const deq = new
|
|
73
|
+
const deq = new Float64Array(quantized.length)
|
|
76
74
|
for (let i = 0; i < quantized.length; i++) {
|
|
77
|
-
deq[i] = quantized[i] *
|
|
75
|
+
deq[i] = (quantized[i] * scaleFactor) / quantRange
|
|
78
76
|
}
|
|
79
77
|
return deq
|
|
80
78
|
}
|
|
@@ -101,9 +99,9 @@ export function dequantize(quantized, scaleFactorIndex, bitsPerSample) {
|
|
|
101
99
|
* - Long blocks (mode 0): Higher frequency resolution, fewer temporal segments
|
|
102
100
|
* - Short blocks (mode 1): Lower frequency resolution, more temporal segments
|
|
103
101
|
*
|
|
104
|
-
* @param {
|
|
102
|
+
* @param {Float64Array} coefficients
|
|
105
103
|
* @param {Array<number>} blockModes
|
|
106
|
-
* @returns {{bfuData: Array<
|
|
104
|
+
* @returns {{bfuData: Array<Float64Array>, bfuSizes: Array<number>, bfuCount: number}}
|
|
107
105
|
*/
|
|
108
106
|
export function groupIntoBFUs(coefficients, blockModes) {
|
|
109
107
|
const bfuData = []
|
|
@@ -123,7 +121,7 @@ export function groupIntoBFUs(coefficients, blockModes) {
|
|
|
123
121
|
const size = SPECS_PER_BFU[bfuIndex]
|
|
124
122
|
const startPos = startPositions[bfuIndex] - bandStart
|
|
125
123
|
const endPos = startPos + size
|
|
126
|
-
const bfu = new
|
|
124
|
+
const bfu = new Float64Array(size)
|
|
127
125
|
|
|
128
126
|
if (startPos >= 0 && endPos <= bandSize) {
|
|
129
127
|
bfu.set(coefficients.subarray(bandStart + startPos, bandStart + endPos))
|
package/codec/core/buffers.js
CHANGED
|
@@ -8,74 +8,74 @@ export class BufferPool {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
// Transform buffers
|
|
10
10
|
this.transformBuffers = {
|
|
11
|
-
64: new
|
|
12
|
-
128: new
|
|
13
|
-
256: new
|
|
14
|
-
512: new
|
|
11
|
+
64: new Float64Array(64),
|
|
12
|
+
128: new Float64Array(128),
|
|
13
|
+
256: new Float64Array(256),
|
|
14
|
+
512: new Float64Array(512),
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// QMF work buffers
|
|
18
18
|
this.qmfWorkBuffers = {
|
|
19
19
|
delay: {
|
|
20
|
-
128: new
|
|
21
|
-
256: new
|
|
22
|
-
512: new
|
|
20
|
+
128: new Float64Array(QMF_DELAY + 128),
|
|
21
|
+
256: new Float64Array(QMF_DELAY + 256),
|
|
22
|
+
512: new Float64Array(QMF_DELAY + 512),
|
|
23
23
|
},
|
|
24
24
|
highBandDelay: {
|
|
25
|
-
128: new
|
|
26
|
-
256: new
|
|
25
|
+
128: new Float64Array(QMF_HIGH_BAND_DELAY + 128),
|
|
26
|
+
256: new Float64Array(QMF_HIGH_BAND_DELAY + 256),
|
|
27
27
|
},
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// QMF delay lines
|
|
31
31
|
this.qmfDelays = {
|
|
32
|
-
lowBand: new
|
|
33
|
-
midBand: new
|
|
34
|
-
highBand: new
|
|
32
|
+
lowBand: new Float64Array(QMF_DELAY),
|
|
33
|
+
midBand: new Float64Array(QMF_DELAY),
|
|
34
|
+
highBand: new Float64Array(QMF_HIGH_BAND_DELAY),
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Transient detector
|
|
38
38
|
this.transientDetection = [
|
|
39
|
-
new
|
|
40
|
-
new
|
|
41
|
-
new
|
|
39
|
+
new Float64Array(64),
|
|
40
|
+
new Float64Array(64),
|
|
41
|
+
new Float64Array(128),
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
// MDCT/IMDCT work buffers
|
|
45
45
|
this.mdctBuffers = {
|
|
46
46
|
16: {
|
|
47
|
-
real: new
|
|
48
|
-
imag: new
|
|
47
|
+
real: new Float64Array(16),
|
|
48
|
+
imag: new Float64Array(16),
|
|
49
49
|
},
|
|
50
50
|
64: {
|
|
51
|
-
real: new
|
|
52
|
-
imag: new
|
|
51
|
+
real: new Float64Array(64),
|
|
52
|
+
imag: new Float64Array(64),
|
|
53
53
|
},
|
|
54
54
|
128: {
|
|
55
|
-
real: new
|
|
56
|
-
imag: new
|
|
55
|
+
real: new Float64Array(128),
|
|
56
|
+
imag: new Float64Array(128),
|
|
57
57
|
},
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// MDCT overlap buffers
|
|
60
|
+
// MDCT overlap buffers
|
|
61
61
|
this.mdctOverlap = [
|
|
62
|
-
new
|
|
63
|
-
new
|
|
64
|
-
new
|
|
62
|
+
new Float64Array(32),
|
|
63
|
+
new Float64Array(32),
|
|
64
|
+
new Float64Array(32),
|
|
65
65
|
]
|
|
66
66
|
|
|
67
|
-
// IMDCT overlap buffers
|
|
67
|
+
// IMDCT overlap buffers
|
|
68
68
|
this.imdctOverlap = [
|
|
69
|
-
new
|
|
70
|
-
new
|
|
71
|
-
new
|
|
69
|
+
new Float64Array(256),
|
|
70
|
+
new Float64Array(256),
|
|
71
|
+
new Float64Array(512),
|
|
72
72
|
]
|
|
73
73
|
|
|
74
74
|
// Spectrum reversal buffers
|
|
75
75
|
this.reversalBuffers = {
|
|
76
|
-
32: new
|
|
77
|
-
128: new
|
|
78
|
-
256: new
|
|
76
|
+
32: new Float64Array(32),
|
|
77
|
+
128: new Float64Array(128),
|
|
78
|
+
256: new Float64Array(256),
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
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,40 +147,20 @@ 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
|
-
//
|
|
163
|
+
// Bit allocation
|
|
221
164
|
export const INV_POWER_OF_TWO = (() => {
|
|
222
165
|
const maxBits = WORD_LENGTH_BITS[MAX_WORD_LENGTH_INDEX]
|
|
223
166
|
const table = new Float64Array(maxBits + 1)
|
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) {
|