carta1 1.1.2 → 1.1.3
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 +104 -96
- 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
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 float32
|
|
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 {Float32Array|Array<Float32Array>} Audio frame data
|
|
229
|
+
* - Mono: Float32Array of samples
|
|
230
|
+
* - Stereo: [left, right] Float32Arrays
|
|
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 float32 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 {Float32Array|Array<Float32Array>} Processed frame data
|
|
314
314
|
*/
|
|
315
315
|
_processFrameBuffer(frameBuffer, frameSize) {
|
|
316
316
|
if (this.channels === 1) {
|
|
317
|
-
const frame = new
|
|
317
|
+
const frame = new Float32Array(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 Float32Array(frameSize)
|
|
324
|
+
const right = new Float32Array(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 float32 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 {Float32Array} samples - Time-domain samples
|
|
14
14
|
* @param {number} fftSize - FFT size (must be power of 2)
|
|
15
|
-
* @returns {
|
|
15
|
+
* @returns {Float32Array} 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 Float32Array(fftSize)
|
|
19
|
+
const imag = new Float32Array(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 Float32Array(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 {Float32Array} currentCoeffs - Current frame coefficients
|
|
40
|
+
* @param {Float32Array} 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 {Float32Array} currentCoeffs - Current frame coefficients
|
|
60
|
+
* @param {Float32Array} 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 {Float32Array} 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 {Float32Array} 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 {Float32Array} currentCoeffs - Current frame coefficients
|
|
169
|
+
* @param {Float32Array} 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 {Float32Array|Array<Float32Array>} pcmData - PCM audio data
|
|
68
|
+
* - Mono: Float32Array
|
|
69
|
+
* - Stereo: [left, right] Float32Arrays
|
|
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<Float32Array>} 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<Float32Array>} 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<Float32Array>} 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 {Float32Array} 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 {Float32Array} coefficients
|
|
30
30
|
* @param {number} scaleFactorIndex
|
|
31
31
|
* @param {number} bitsPerSample
|
|
32
32
|
* @returns {Int32Array} out
|
|
@@ -60,17 +60,17 @@ export function quantize(coefficients, scaleFactorIndex, bitsPerSample) {
|
|
|
60
60
|
* @param {Int32Array} quantized
|
|
61
61
|
* @param {number} scaleFactorIndex
|
|
62
62
|
* @param {number} bitsPerSample
|
|
63
|
-
* @returns {
|
|
63
|
+
* @returns {Float32Array}
|
|
64
64
|
*/
|
|
65
65
|
export function dequantize(quantized, scaleFactorIndex, bitsPerSample) {
|
|
66
66
|
if (bitsPerSample === 0 || scaleFactorIndex === 0) {
|
|
67
|
-
return new
|
|
67
|
+
return new Float32Array(quantized.length)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const scaleFactor = SCALE_FACTORS[scaleFactorIndex]
|
|
71
71
|
const quantRange = (1 << (bitsPerSample - QUANTIZATION_SIGN_BIT_SHIFT)) - 1
|
|
72
72
|
|
|
73
|
-
const deq = new
|
|
73
|
+
const deq = new Float32Array(quantized.length)
|
|
74
74
|
for (let i = 0; i < quantized.length; i++) {
|
|
75
75
|
deq[i] = (quantized[i] * scaleFactor) / quantRange
|
|
76
76
|
}
|
|
@@ -99,9 +99,9 @@ export function dequantize(quantized, scaleFactorIndex, bitsPerSample) {
|
|
|
99
99
|
* - Long blocks (mode 0): Higher frequency resolution, fewer temporal segments
|
|
100
100
|
* - Short blocks (mode 1): Lower frequency resolution, more temporal segments
|
|
101
101
|
*
|
|
102
|
-
* @param {
|
|
102
|
+
* @param {Float32Array} coefficients
|
|
103
103
|
* @param {Array<number>} blockModes
|
|
104
|
-
* @returns {{bfuData: Array<
|
|
104
|
+
* @returns {{bfuData: Array<Float32Array>, bfuSizes: Array<number>, bfuCount: number}}
|
|
105
105
|
*/
|
|
106
106
|
export function groupIntoBFUs(coefficients, blockModes) {
|
|
107
107
|
const bfuData = []
|
|
@@ -121,7 +121,7 @@ export function groupIntoBFUs(coefficients, blockModes) {
|
|
|
121
121
|
const size = SPECS_PER_BFU[bfuIndex]
|
|
122
122
|
const startPos = startPositions[bfuIndex] - bandStart
|
|
123
123
|
const endPos = startPos + size
|
|
124
|
-
const bfu = new
|
|
124
|
+
const bfu = new Float32Array(size)
|
|
125
125
|
|
|
126
126
|
if (startPos >= 0 && endPos <= bandSize) {
|
|
127
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 Float32Array(64),
|
|
12
|
+
128: new Float32Array(128),
|
|
13
|
+
256: new Float32Array(256),
|
|
14
|
+
512: new Float32Array(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 Float32Array(QMF_DELAY + 128),
|
|
21
|
+
256: new Float32Array(QMF_DELAY + 256),
|
|
22
|
+
512: new Float32Array(QMF_DELAY + 512),
|
|
23
23
|
},
|
|
24
24
|
highBandDelay: {
|
|
25
|
-
128: new
|
|
26
|
-
256: new
|
|
25
|
+
128: new Float32Array(QMF_HIGH_BAND_DELAY + 128),
|
|
26
|
+
256: new Float32Array(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 Float32Array(QMF_DELAY),
|
|
33
|
+
midBand: new Float32Array(QMF_DELAY),
|
|
34
|
+
highBand: new Float32Array(QMF_HIGH_BAND_DELAY),
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Transient detector
|
|
38
38
|
this.transientDetection = [
|
|
39
|
-
new
|
|
40
|
-
new
|
|
41
|
-
new
|
|
39
|
+
new Float32Array(64),
|
|
40
|
+
new Float32Array(64),
|
|
41
|
+
new Float32Array(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 Float32Array(16),
|
|
48
|
+
imag: new Float32Array(16),
|
|
49
49
|
},
|
|
50
50
|
64: {
|
|
51
|
-
real: new
|
|
52
|
-
imag: new
|
|
51
|
+
real: new Float32Array(64),
|
|
52
|
+
imag: new Float32Array(64),
|
|
53
53
|
},
|
|
54
54
|
128: {
|
|
55
|
-
real: new
|
|
56
|
-
imag: new
|
|
55
|
+
real: new Float32Array(128),
|
|
56
|
+
imag: new Float32Array(128),
|
|
57
57
|
},
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// MDCT overlap buffers
|
|
60
|
+
// MDCT overlap buffers (encoder)
|
|
61
61
|
this.mdctOverlap = [
|
|
62
|
-
new
|
|
63
|
-
new
|
|
64
|
-
new
|
|
62
|
+
new Float32Array(32),
|
|
63
|
+
new Float32Array(32),
|
|
64
|
+
new Float32Array(32),
|
|
65
65
|
]
|
|
66
66
|
|
|
67
|
-
// IMDCT overlap buffers
|
|
67
|
+
// IMDCT overlap buffers (decoder)
|
|
68
68
|
this.imdctOverlap = [
|
|
69
|
-
new
|
|
70
|
-
new
|
|
71
|
-
new
|
|
69
|
+
new Float32Array(256),
|
|
70
|
+
new Float32Array(256),
|
|
71
|
+
new Float32Array(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 Float32Array(32),
|
|
77
|
+
128: new Float32Array(128),
|
|
78
|
+
256: new Float32Array(256),
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
package/codec/core/constants.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
export const SAMPLE_RATE = 44100
|
|
7
7
|
export const SAMPLES_PER_FRAME = 512
|
|
8
8
|
export const FRAME_RATE = SAMPLE_RATE / SAMPLES_PER_FRAME
|
|
9
|
-
export const CODEC_DELAY = 266
|
|
10
9
|
|
|
11
10
|
// AEA file format
|
|
12
11
|
export const AEA_MAGIC = new Uint8Array([0x00, 0x08, 0x00, 0x00])
|
|
@@ -37,16 +36,6 @@ export const BFU_AMOUNTS_COUNT = 8
|
|
|
37
36
|
export const BFU_AMOUNTS = new Int32Array([20, 28, 32, 36, 40, 44, 48, 52])
|
|
38
37
|
export const BFU_BAND_BOUNDARIES = new Int32Array([20, 36, 52])
|
|
39
38
|
|
|
40
|
-
// BFU center frequencies (Hz)
|
|
41
|
-
export const BFU_FREQUENCIES = new Float64Array([
|
|
42
|
-
172.3, 516.8, 861.3, 1205.9, 1464.3, 1636.5, 1808.8, 1981.1, 2239.5, 2584.0,
|
|
43
|
-
2928.5, 3273.0, 3574.5, 3832.9, 4091.3, 4349.7, 4608.1, 4866.5, 5124.9,
|
|
44
|
-
5383.3, 5641.7, 5900.1, 6158.5, 6416.9, 6696.8, 6998.3, 7299.8, 7601.2,
|
|
45
|
-
7945.8, 8333.3, 8720.9, 9108.5, 9517.7, 9948.3, 10379.0, 10809.7, 11283.4,
|
|
46
|
-
11800.2, 12317.0, 12833.8, 13350.6, 13867.4, 14384.2, 14901.0, 15590.0,
|
|
47
|
-
16451.4, 17312.7, 18174.0, 19035.4, 19896.7, 20758.0, 21619.3,
|
|
48
|
-
])
|
|
49
|
-
|
|
50
39
|
// BFU start positions
|
|
51
40
|
export const BFU_START_LONG = new Int32Array([
|
|
52
41
|
0, 8, 16, 24, 32, 36, 40, 44, 48, 56, 64, 72, 80, 86, 92, 98, 104, 110, 116,
|
|
@@ -82,7 +71,7 @@ export const QMF_DELAY = 46
|
|
|
82
71
|
export const QMF_HIGH_BAND_DELAY = 39
|
|
83
72
|
|
|
84
73
|
// QMF prototype filter coefficients
|
|
85
|
-
export const QMF_COEFFS = new
|
|
74
|
+
export const QMF_COEFFS = new Float32Array([
|
|
86
75
|
-0.00001461907, -0.00009205479, -0.000056157569, 0.00030117269, 0.0002422519,
|
|
87
76
|
-0.00085293897, -0.0005205574, 0.0020340169, 0.00078333891, -0.0042153862,
|
|
88
77
|
-0.00075614988, 0.0078402944, -0.000061169922, -0.01344162, 0.0024626821,
|
|
@@ -92,7 +81,7 @@ export const QMF_COEFFS = new Float64Array([
|
|
|
92
81
|
|
|
93
82
|
// QMF window function
|
|
94
83
|
export const QMF_WINDOW = (() => {
|
|
95
|
-
const window = new
|
|
84
|
+
const window = new Float32Array(QMF_TAPS)
|
|
96
85
|
for (let i = 0; i < 24; i++) {
|
|
97
86
|
window[i] = QMF_COEFFS[i] * 2.0
|
|
98
87
|
window[47 - i] = QMF_COEFFS[i] * 2.0
|
|
@@ -102,7 +91,7 @@ export const QMF_WINDOW = (() => {
|
|
|
102
91
|
|
|
103
92
|
// QMF even/odd taps for optimization
|
|
104
93
|
export const QMF_EVEN = (() => {
|
|
105
|
-
const even = new
|
|
94
|
+
const even = new Float32Array(24)
|
|
106
95
|
for (let i = 0; i < 24; i++) {
|
|
107
96
|
even[i] = QMF_WINDOW[i * 2]
|
|
108
97
|
}
|
|
@@ -110,7 +99,7 @@ export const QMF_EVEN = (() => {
|
|
|
110
99
|
})()
|
|
111
100
|
|
|
112
101
|
export const QMF_ODD = (() => {
|
|
113
|
-
const odd = new
|
|
102
|
+
const odd = new Float32Array(24)
|
|
114
103
|
for (let i = 0; i < 24; i++) {
|
|
115
104
|
odd[i] = QMF_WINDOW[i * 2 + 1]
|
|
116
105
|
}
|
package/codec/io/processor.js
CHANGED
|
@@ -30,12 +30,6 @@ import {
|
|
|
30
30
|
WAV_DATA_OFFSET,
|
|
31
31
|
} from '../core/constants.js'
|
|
32
32
|
import { serializeFrame, deserializeFrame, AeaFile } from './serialization.js'
|
|
33
|
-
import {
|
|
34
|
-
withFlushSamples,
|
|
35
|
-
withDelayCompensation,
|
|
36
|
-
withStereoFlushSamples,
|
|
37
|
-
withStereoDelayCompensation,
|
|
38
|
-
} from '../utils.js'
|
|
39
33
|
|
|
40
34
|
/**
|
|
41
35
|
* Audio processing utility class providing high-level encoding and decoding operations
|
|
@@ -43,7 +37,7 @@ import {
|
|
|
43
37
|
export class AudioProcessor {
|
|
44
38
|
/**
|
|
45
39
|
* Encodes a stream of audio frames to ATRAC1 format
|
|
46
|
-
* @param {AsyncIterable<
|
|
40
|
+
* @param {AsyncIterable<Float32Array>|AsyncIterable<[Float32Array, Float32Array]>} audioFrames - Audio frame stream
|
|
47
41
|
* @param {Object} [options={}] - Encoding options
|
|
48
42
|
* @param {number} [options.channelCount=1] - Number of audio channels (1 or 2)
|
|
49
43
|
* @param {Function} [options.onProgress] - Progress callback function
|
|
@@ -73,7 +67,7 @@ export class AudioProcessor {
|
|
|
73
67
|
|
|
74
68
|
/**
|
|
75
69
|
* Internal method for encoding mono audio streams
|
|
76
|
-
* @param {AsyncIterable<
|
|
70
|
+
* @param {AsyncIterable<Float32Array>} audioFrames - Mono audio frame stream
|
|
77
71
|
* @param {Function} onProgress - Progress callback function
|
|
78
72
|
* @param {EncoderOptions} encoderOptions - Encoder configuration
|
|
79
73
|
* @returns {AsyncGenerator<Object>} Stream of encoded frame data
|
|
@@ -83,7 +77,7 @@ export class AudioProcessor {
|
|
|
83
77
|
const options = encoderOptions || new EncoderOptions()
|
|
84
78
|
const encoder = encode(options)
|
|
85
79
|
let frameIndex = 0
|
|
86
|
-
for await (const frame of
|
|
80
|
+
for await (const frame of audioFrames) {
|
|
87
81
|
const result = encoder(frame)
|
|
88
82
|
yield result
|
|
89
83
|
|
|
@@ -95,7 +89,7 @@ export class AudioProcessor {
|
|
|
95
89
|
|
|
96
90
|
/**
|
|
97
91
|
* Internal method for encoding stereo audio streams
|
|
98
|
-
* @param {AsyncIterable<[
|
|
92
|
+
* @param {AsyncIterable<[Float32Array, Float32Array]>} audioFrames - Stereo audio frame stream
|
|
99
93
|
* @param {Function} onProgress - Progress callback function
|
|
100
94
|
* @param {EncoderOptions} encoderOptions - Encoder configuration
|
|
101
95
|
* @returns {AsyncGenerator<Object>} Stream of encoded frame data (left then right)
|
|
@@ -107,9 +101,7 @@ export class AudioProcessor {
|
|
|
107
101
|
const rightEncoder = encode(options)
|
|
108
102
|
let frameIndex = 0
|
|
109
103
|
|
|
110
|
-
for await (const [leftFrame, rightFrame] of
|
|
111
|
-
audioFrames
|
|
112
|
-
)) {
|
|
104
|
+
for await (const [leftFrame, rightFrame] of audioFrames) {
|
|
113
105
|
const leftResult = leftEncoder(leftFrame)
|
|
114
106
|
const rightResult = rightEncoder(rightFrame)
|
|
115
107
|
|
|
@@ -128,7 +120,7 @@ export class AudioProcessor {
|
|
|
128
120
|
* @param {Object} [options={}] - Decoding options
|
|
129
121
|
* @param {number} [options.channelCount=1] - Number of audio channels (1 or 2)
|
|
130
122
|
* @param {Function} [options.onProgress] - Progress callback function
|
|
131
|
-
* @returns {AsyncGenerator<
|
|
123
|
+
* @returns {AsyncGenerator<Float32Array>|AsyncGenerator<[Float32Array, Float32Array]>} Stream of decoded PCM frames
|
|
132
124
|
* @throws {Error} If unsupported channel count is provided
|
|
133
125
|
*/
|
|
134
126
|
static async *decodeStream(encodedFrames, options = {}) {
|
|
@@ -147,7 +139,7 @@ export class AudioProcessor {
|
|
|
147
139
|
* Internal method for decoding mono audio streams
|
|
148
140
|
* @param {AsyncIterable<Object>} encodedFrames - Mono encoded frame stream
|
|
149
141
|
* @param {Function} onProgress - Progress callback function
|
|
150
|
-
* @returns {AsyncGenerator<
|
|
142
|
+
* @returns {AsyncGenerator<Float32Array>} Stream of decoded mono PCM frames
|
|
151
143
|
* @private
|
|
152
144
|
*/
|
|
153
145
|
static async *_decodeMonoStream(encodedFrames, onProgress) {
|
|
@@ -167,14 +159,14 @@ export class AudioProcessor {
|
|
|
167
159
|
}
|
|
168
160
|
|
|
169
161
|
// Always apply delay compensation
|
|
170
|
-
yield*
|
|
162
|
+
yield* rawPcmFrames()
|
|
171
163
|
}
|
|
172
164
|
|
|
173
165
|
/**
|
|
174
166
|
* Internal method for decoding stereo audio streams
|
|
175
167
|
* @param {AsyncIterable<Object>} encodedFrames - Stereo encoded frame stream (interleaved left/right)
|
|
176
168
|
* @param {Function} onProgress - Progress callback function
|
|
177
|
-
* @returns {AsyncGenerator<[
|
|
169
|
+
* @returns {AsyncGenerator<[Float32Array, Float32Array]>} Stream of decoded stereo PCM frame pairs
|
|
178
170
|
* @private
|
|
179
171
|
*/
|
|
180
172
|
static async *_decodeStereoStream(encodedFrames, onProgress) {
|
|
@@ -220,14 +212,14 @@ export class AudioProcessor {
|
|
|
220
212
|
}
|
|
221
213
|
|
|
222
214
|
// Always apply stereo delay compensation
|
|
223
|
-
yield*
|
|
215
|
+
yield* rawPcmFrames()
|
|
224
216
|
}
|
|
225
217
|
|
|
226
218
|
/**
|
|
227
219
|
* Converts channel buffer arrays to frame-based stream format
|
|
228
|
-
* @param {
|
|
220
|
+
* @param {Float32Array[]} buffers - Array of channel buffers (1 for mono, 2 for stereo)
|
|
229
221
|
* @param {number} [frameSize=SAMPLES_PER_FRAME] - Frame size in samples
|
|
230
|
-
* @returns {Generator<
|
|
222
|
+
* @returns {Generator<Float32Array>|Generator<[Float32Array, Float32Array]>} Stream of audio frames
|
|
231
223
|
* @throws {Error} If unsupported channel count is provided
|
|
232
224
|
*/
|
|
233
225
|
static *frameBufferToFrames(buffers, frameSize = SAMPLES_PER_FRAME) {
|
|
@@ -236,7 +228,7 @@ export class AudioProcessor {
|
|
|
236
228
|
if (channelCount === 1) {
|
|
237
229
|
const [left] = buffers
|
|
238
230
|
for (let i = 0; i < left.length; i += frameSize) {
|
|
239
|
-
const frame = new
|
|
231
|
+
const frame = new Float32Array(frameSize)
|
|
240
232
|
const end = Math.min(i + frameSize, left.length)
|
|
241
233
|
for (let j = 0; j < end - i; j++) {
|
|
242
234
|
frame[j] = left[i + j]
|
|
@@ -248,8 +240,8 @@ export class AudioProcessor {
|
|
|
248
240
|
const maxLength = Math.max(left.length, right.length)
|
|
249
241
|
|
|
250
242
|
for (let i = 0; i < maxLength; i += frameSize) {
|
|
251
|
-
const leftFrame = new
|
|
252
|
-
const rightFrame = new
|
|
243
|
+
const leftFrame = new Float32Array(frameSize)
|
|
244
|
+
const rightFrame = new Float32Array(frameSize)
|
|
253
245
|
|
|
254
246
|
const end = Math.min(i + frameSize, maxLength)
|
|
255
247
|
|
|
@@ -327,7 +319,7 @@ export class AudioProcessor {
|
|
|
327
319
|
|
|
328
320
|
/**
|
|
329
321
|
* Creates a WAV file blob from PCM audio frames
|
|
330
|
-
* @param {
|
|
322
|
+
* @param {Float32Array[]|Float32Array|[Float32Array, Float32Array][]} pcmFrames - PCM audio frames
|
|
331
323
|
* @param {number} [channelCount=1] - Number of audio channels
|
|
332
324
|
* @param {number} [sampleRate=SAMPLE_RATE] - Audio sample rate
|
|
333
325
|
* @returns {Blob} WAV file blob
|
|
@@ -347,7 +339,7 @@ export class AudioProcessor {
|
|
|
347
339
|
|
|
348
340
|
/**
|
|
349
341
|
* Creates a mono WAV file blob from PCM frames
|
|
350
|
-
* @param {
|
|
342
|
+
* @param {Float32Array[]} frames - Array of mono PCM frames
|
|
351
343
|
* @param {number} sampleRate - Audio sample rate
|
|
352
344
|
* @returns {Blob} Mono WAV file blob
|
|
353
345
|
* @private
|
|
@@ -383,7 +375,7 @@ export class AudioProcessor {
|
|
|
383
375
|
|
|
384
376
|
/**
|
|
385
377
|
* Creates a stereo WAV file blob from PCM frame pairs
|
|
386
|
-
* @param {[
|
|
378
|
+
* @param {[Float32Array, Float32Array][]} framePairs - Array of stereo PCM frame pairs
|
|
387
379
|
* @param {number} sampleRate - Audio sample rate
|
|
388
380
|
* @returns {Blob} Stereo WAV file blob
|
|
389
381
|
* @private
|
|
@@ -524,9 +516,9 @@ export class AudioProcessor {
|
|
|
524
516
|
|
|
525
517
|
/**
|
|
526
518
|
* Assembles PCM frames into a continuous buffer
|
|
527
|
-
* @param {
|
|
519
|
+
* @param {Float32Array[]|[Float32Array, Float32Array][]} pcmFrames - Array of PCM frames
|
|
528
520
|
* @param {number} channelCount - Number of audio channels
|
|
529
|
-
* @returns {
|
|
521
|
+
* @returns {Float32Array} Continuous PCM buffer (interleaved for stereo)
|
|
530
522
|
* @throws {Error} If unsupported channel count is provided
|
|
531
523
|
*/
|
|
532
524
|
static assemblePcmFrames(pcmFrames, channelCount) {
|
|
@@ -535,7 +527,7 @@ export class AudioProcessor {
|
|
|
535
527
|
(sum, frame) => sum + frame.length,
|
|
536
528
|
0
|
|
537
529
|
)
|
|
538
|
-
const pcm = new
|
|
530
|
+
const pcm = new Float32Array(totalSamples)
|
|
539
531
|
let offset = 0
|
|
540
532
|
for (const frame of pcmFrames) {
|
|
541
533
|
pcm.set(frame, offset)
|
|
@@ -547,7 +539,7 @@ export class AudioProcessor {
|
|
|
547
539
|
(sum, [left, right]) => sum + Math.max(left.length, right.length),
|
|
548
540
|
0
|
|
549
541
|
)
|
|
550
|
-
const pcm = new
|
|
542
|
+
const pcm = new Float32Array(totalSamples * 2)
|
|
551
543
|
|
|
552
544
|
let pcmOffset = 0
|
|
553
545
|
for (const [leftFrame, rightFrame] of pcmFrames) {
|