carta1 1.0.0
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/LICENSE +15 -0
- package/README.md +129 -0
- package/bin/cli.js +679 -0
- package/codec/analysis/psychoacoustics.js +589 -0
- package/codec/analysis/transient.js +226 -0
- package/codec/browser/interface.js +188 -0
- package/codec/browser/worker.js +140 -0
- package/codec/coding/bitallocation.js +407 -0
- package/codec/coding/quantization.js +150 -0
- package/codec/core/bitstream.js +82 -0
- package/codec/core/buffers.js +99 -0
- package/codec/core/constants.js +321 -0
- package/codec/core/options.js +151 -0
- package/codec/index.js +27 -0
- package/codec/io/processor.js +566 -0
- package/codec/io/readers.js +56 -0
- package/codec/io/serialization.js +254 -0
- package/codec/pipeline/decoder.js +278 -0
- package/codec/pipeline/encoder.js +369 -0
- package/codec/transforms/fft.js +69 -0
- package/codec/transforms/mdct.js +225 -0
- package/codec/transforms/qmf.js +127 -0
- package/codec/utils.js +151 -0
- package/dist/carta1-worker-interface.min.js +2 -0
- package/dist/carta1-worker-interface.min.js.map +1 -0
- package/dist/carta1-worker.min.js +2 -0
- package/dist/carta1-worker.min.js.map +1 -0
- package/dist/carta1.min.js +2 -0
- package/dist/carta1.min.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carta1 Audio Codec - Buffer Management
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
NUM_BFUS,
|
|
7
|
+
MAX_BFU_SIZE,
|
|
8
|
+
QMF_DELAY,
|
|
9
|
+
QMF_HIGH_BAND_DELAY,
|
|
10
|
+
PSYMODEL_FFT_SIZE,
|
|
11
|
+
} from './constants.js'
|
|
12
|
+
|
|
13
|
+
export class BufferPool {
|
|
14
|
+
constructor() {
|
|
15
|
+
// Transform buffers
|
|
16
|
+
this.transformBuffers = {
|
|
17
|
+
64: new Float32Array(64),
|
|
18
|
+
128: new Float32Array(128),
|
|
19
|
+
256: new Float32Array(256),
|
|
20
|
+
512: new Float32Array(512),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// QMF work buffers
|
|
24
|
+
this.qmfWorkBuffers = {
|
|
25
|
+
delay: {
|
|
26
|
+
128: new Float32Array(QMF_DELAY + 128),
|
|
27
|
+
256: new Float32Array(QMF_DELAY + 256),
|
|
28
|
+
512: new Float32Array(QMF_DELAY + 512),
|
|
29
|
+
},
|
|
30
|
+
highBandDelay: {
|
|
31
|
+
128: new Float32Array(QMF_HIGH_BAND_DELAY + 128),
|
|
32
|
+
256: new Float32Array(QMF_HIGH_BAND_DELAY + 256),
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// QMF delay lines
|
|
37
|
+
this.qmfDelays = {
|
|
38
|
+
lowBand: new Float32Array(QMF_DELAY),
|
|
39
|
+
midBand: new Float32Array(QMF_DELAY),
|
|
40
|
+
highBand: new Float32Array(QMF_HIGH_BAND_DELAY),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Transient detector
|
|
44
|
+
this.transientDetection = {
|
|
45
|
+
prevLowCoeffs: new Float32Array(128),
|
|
46
|
+
prevMidCoeffs: new Float32Array(128),
|
|
47
|
+
prevHighCoeffs: new Float32Array(256),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// MDCT/IMDCT work buffers
|
|
51
|
+
this.mdctBuffers = {
|
|
52
|
+
16: {
|
|
53
|
+
real: new Float32Array(16),
|
|
54
|
+
imag: new Float32Array(16),
|
|
55
|
+
},
|
|
56
|
+
64: {
|
|
57
|
+
real: new Float32Array(64),
|
|
58
|
+
imag: new Float32Array(64),
|
|
59
|
+
},
|
|
60
|
+
128: {
|
|
61
|
+
real: new Float32Array(128),
|
|
62
|
+
imag: new Float32Array(128),
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// MDCT overlap buffers (encoder)
|
|
67
|
+
this.mdctOverlap = [
|
|
68
|
+
new Float32Array(32),
|
|
69
|
+
new Float32Array(32),
|
|
70
|
+
new Float32Array(32),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
// IMDCT overlap buffers (decoder)
|
|
74
|
+
this.imdctOverlap = [
|
|
75
|
+
new Float32Array(256),
|
|
76
|
+
new Float32Array(256),
|
|
77
|
+
new Float32Array(512),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
// Spectrum reversal buffers
|
|
81
|
+
this.reversalBuffers = {
|
|
82
|
+
32: new Float32Array(32),
|
|
83
|
+
128: new Float32Array(128),
|
|
84
|
+
256: new Float32Array(256),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Psychoacoustic model buffers
|
|
88
|
+
this.psychoAnalysis = {
|
|
89
|
+
flags: new Uint8Array(PSYMODEL_FFT_SIZE / 2),
|
|
90
|
+
threshold: new Float32Array(PSYMODEL_FFT_SIZE / 2 + 1),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Bit allocation buffers
|
|
94
|
+
this.bfuData = new Array(NUM_BFUS)
|
|
95
|
+
for (let i = 0; i < NUM_BFUS; i++) {
|
|
96
|
+
this.bfuData[i] = new Float32Array(MAX_BFU_SIZE)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carta1 Audio Codec - Core Constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Audio format
|
|
6
|
+
export const SAMPLE_RATE = 44100
|
|
7
|
+
export const SAMPLES_PER_FRAME = 512
|
|
8
|
+
export const FRAME_RATE = SAMPLE_RATE / SAMPLES_PER_FRAME
|
|
9
|
+
export const CODEC_DELAY = 266
|
|
10
|
+
|
|
11
|
+
// AEA file format
|
|
12
|
+
export const AEA_MAGIC = new Uint8Array([0x00, 0x08, 0x00, 0x00])
|
|
13
|
+
export const AEA_HEADER_SIZE = 2048
|
|
14
|
+
export const AEA_TITLE_OFFSET = 4
|
|
15
|
+
export const AEA_TITLE_SIZE = 256
|
|
16
|
+
export const AEA_FRAME_COUNT_OFFSET = 260
|
|
17
|
+
export const AEA_CHANNEL_COUNT_OFFSET = 264
|
|
18
|
+
|
|
19
|
+
// Frame structure
|
|
20
|
+
export const SOUND_UNIT_SIZE = 212
|
|
21
|
+
export const FRAME_BITS = SOUND_UNIT_SIZE * 8
|
|
22
|
+
export const FRAME_OVERHEAD_BITS = 40
|
|
23
|
+
export const BITRATE_PER_CHANNEL = SOUND_UNIT_SIZE * FRAME_RATE * 8
|
|
24
|
+
|
|
25
|
+
// BFU (Block Floating Unit) configuration
|
|
26
|
+
export const NUM_BFUS = 52
|
|
27
|
+
export const MAX_BFU_SIZE = 20
|
|
28
|
+
export const BITS_PER_BFU_METADATA = 10
|
|
29
|
+
|
|
30
|
+
export const SPECS_PER_BFU = new Int32Array([
|
|
31
|
+
8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
|
|
32
|
+
7, 7, 9, 9, 9, 9, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20,
|
|
33
|
+
20, 20, 20, 20, 20,
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
export const BFU_AMOUNTS = new Int32Array([20, 28, 32, 36, 40, 44, 48, 52])
|
|
37
|
+
export const BFU_BAND_BOUNDARIES = new Int32Array([20, 36, 52])
|
|
38
|
+
export const BFU_TO_BAND = (() => {
|
|
39
|
+
const table = new Uint8Array(52)
|
|
40
|
+
for (let i = 0; i < BFU_BAND_BOUNDARIES[0]; i++) table[i] = 0
|
|
41
|
+
for (let i = BFU_BAND_BOUNDARIES[0]; i < BFU_BAND_BOUNDARIES[1]; i++)
|
|
42
|
+
table[i] = 1
|
|
43
|
+
for (let i = BFU_BAND_BOUNDARIES[1]; i < BFU_BAND_BOUNDARIES[2]; i++)
|
|
44
|
+
table[i] = 2
|
|
45
|
+
return table
|
|
46
|
+
})()
|
|
47
|
+
|
|
48
|
+
// Perceptually important bfus
|
|
49
|
+
export const CRITICAL_BFUS = new Uint8Array([
|
|
50
|
+
20, 21, 22, 19, 23, 18, 24, 17, 25, 16, 26, 15, 27, 14, 28, 32, 33, 34, 35,
|
|
51
|
+
36, 37, 38, 31, 30, 29,
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
// BFU center frequencies (Hz)
|
|
55
|
+
export const BFU_FREQUENCIES = new Float32Array([
|
|
56
|
+
172.3, 516.8, 861.3, 1205.9, 1464.3, 1636.5, 1808.8, 1981.1, 2239.5, 2584.0,
|
|
57
|
+
2928.5, 3273.0, 3574.5, 3832.9, 4091.3, 4349.7, 4608.1, 4866.5, 5124.9,
|
|
58
|
+
5383.3, 5641.7, 5900.1, 6158.5, 6416.9, 6696.8, 6998.3, 7299.8, 7601.2,
|
|
59
|
+
7945.8, 8333.3, 8720.9, 9108.5, 9517.7, 9948.3, 10379.0, 10809.7, 11283.4,
|
|
60
|
+
11800.2, 12317.0, 12833.8, 13350.6, 13867.4, 14384.2, 14901.0, 15590.0,
|
|
61
|
+
16451.4, 17312.7, 18174.0, 19035.4, 19896.7, 20758.0, 21619.3,
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
// BFU start positions
|
|
65
|
+
export const BFU_START_LONG = new Int32Array([
|
|
66
|
+
0, 8, 16, 24, 32, 36, 40, 44, 48, 56, 64, 72, 80, 86, 92, 98, 104, 110, 116,
|
|
67
|
+
122, 128, 134, 140, 146, 152, 159, 166, 173, 180, 189, 198, 207, 216, 226,
|
|
68
|
+
236, 246, 256, 268, 280, 292, 304, 316, 328, 340, 352, 372, 392, 412, 432,
|
|
69
|
+
452, 472, 492,
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
export const BFU_START_SHORT = new Int32Array([
|
|
73
|
+
0, 32, 64, 96, 8, 40, 72, 104, 12, 44, 76, 108, 20, 52, 84, 116, 26, 58, 90,
|
|
74
|
+
122, 128, 160, 192, 224, 134, 166, 198, 230, 141, 173, 205, 237, 150, 182,
|
|
75
|
+
214, 246, 256, 288, 320, 352, 384, 416, 448, 480, 268, 300, 332, 364, 396,
|
|
76
|
+
428, 460, 492,
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
// Transform sizes
|
|
80
|
+
export const MDCT_SIZE_SHORT = 64
|
|
81
|
+
export const MDCT_SIZE_MID = 256
|
|
82
|
+
export const MDCT_SIZE_LONG = 512
|
|
83
|
+
|
|
84
|
+
// Band sizes
|
|
85
|
+
export const BAND_SIZE_LOW = 128
|
|
86
|
+
export const BAND_SIZE_MID = 128
|
|
87
|
+
export const BAND_SIZE_HIGH = 256
|
|
88
|
+
|
|
89
|
+
// Window positions
|
|
90
|
+
export const WINDOW_START_LONG_HIGH = 112
|
|
91
|
+
export const WINDOW_START_LONG_MID = 48
|
|
92
|
+
export const WINDOW_START_SHORT = 0
|
|
93
|
+
|
|
94
|
+
// IMDCT overlap sizes
|
|
95
|
+
export const IMDCT_OVERLAP_SIZE = 16
|
|
96
|
+
export const IMDCT_QUARTER_SIZE = 4
|
|
97
|
+
export const IMDCT_SHORT_BLOCK_SIZE = 32
|
|
98
|
+
|
|
99
|
+
// Window functions
|
|
100
|
+
export const WINDOW_SHORT = (() => {
|
|
101
|
+
const table = new Float32Array(32)
|
|
102
|
+
for (let i = 0; i < 32; i++) {
|
|
103
|
+
table[i] = Math.sin(((i + 0.5) * Math.PI) / 64)
|
|
104
|
+
}
|
|
105
|
+
return table
|
|
106
|
+
})()
|
|
107
|
+
|
|
108
|
+
// QMF filter configuration
|
|
109
|
+
export const QMF_TAPS = 48
|
|
110
|
+
export const QMF_DELAY = 46
|
|
111
|
+
export const QMF_HIGH_BAND_DELAY = 39
|
|
112
|
+
|
|
113
|
+
// Quantization
|
|
114
|
+
export const MAX_WORD_LENGTH_INDEX = 15
|
|
115
|
+
export const WORD_LENGTH_BITS = new Int32Array([
|
|
116
|
+
0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
117
|
+
])
|
|
118
|
+
export const QUANT_DISTORTION_TABLE = (() => {
|
|
119
|
+
const table = new Float32Array(MAX_WORD_LENGTH_INDEX + 1)
|
|
120
|
+
table[0] = 1.0
|
|
121
|
+
for (let wl = 1; wl <= MAX_WORD_LENGTH_INDEX; wl++) {
|
|
122
|
+
const bitsPerSample = WORD_LENGTH_BITS[wl]
|
|
123
|
+
const quantRange = (1 << (bitsPerSample - 1)) - 1
|
|
124
|
+
table[wl] = 1.0 / (12.0 * quantRange * quantRange)
|
|
125
|
+
}
|
|
126
|
+
return table
|
|
127
|
+
})()
|
|
128
|
+
export const DISTORTION_DELTA_DB = (() => {
|
|
129
|
+
const table = new Float32Array(MAX_WORD_LENGTH_INDEX)
|
|
130
|
+
for (let i = 0; i < MAX_WORD_LENGTH_INDEX; i++) {
|
|
131
|
+
table[i] =
|
|
132
|
+
10 *
|
|
133
|
+
Math.log10(
|
|
134
|
+
QUANT_DISTORTION_TABLE[i + 1] / (QUANT_DISTORTION_TABLE[i] || 1.0)
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
return table
|
|
138
|
+
})()
|
|
139
|
+
export const SCALE_FACTORS = (() => {
|
|
140
|
+
const table = new Float32Array(64)
|
|
141
|
+
for (let i = 0; i < 64; i++) {
|
|
142
|
+
table[i] = Math.pow(2.0, i / 3.0 - 21)
|
|
143
|
+
}
|
|
144
|
+
return table
|
|
145
|
+
})()
|
|
146
|
+
|
|
147
|
+
// Psychoacoustic model
|
|
148
|
+
export const BARK_SCALE = (() => {
|
|
149
|
+
const table = new Float32Array(NUM_BFUS)
|
|
150
|
+
for (let i = 0; i < NUM_BFUS; i++) {
|
|
151
|
+
const freq = BFU_FREQUENCIES[i]
|
|
152
|
+
table[i] =
|
|
153
|
+
13.0 * Math.atan(0.00076 * freq) + 3.5 * Math.atan((freq / 7500) ** 2)
|
|
154
|
+
}
|
|
155
|
+
return table
|
|
156
|
+
})()
|
|
157
|
+
|
|
158
|
+
export const ATH_LINEAR = (() => {
|
|
159
|
+
const table = new Float32Array(NUM_BFUS)
|
|
160
|
+
for (let i = 0; i < NUM_BFUS; i++) {
|
|
161
|
+
const freqKhz = BFU_FREQUENCIES[i] / 1000
|
|
162
|
+
const athDb =
|
|
163
|
+
3.64 * Math.pow(freqKhz, -0.8) -
|
|
164
|
+
6.5 * Math.exp(-0.6 * Math.pow(freqKhz - 3.3, 2)) +
|
|
165
|
+
1e-3 * Math.pow(freqKhz, 4)
|
|
166
|
+
table[i] = Math.pow(10, Math.max(-10, Math.min(96, athDb)) / 10)
|
|
167
|
+
}
|
|
168
|
+
return table
|
|
169
|
+
})()
|
|
170
|
+
|
|
171
|
+
export const SPREADING_MATRIX = (() => {
|
|
172
|
+
const matrix = new Float32Array(NUM_BFUS * NUM_BFUS)
|
|
173
|
+
for (let masker = 0; masker < NUM_BFUS; masker++) {
|
|
174
|
+
for (let masked = 0; masked < NUM_BFUS; masked++) {
|
|
175
|
+
const distance = BARK_SCALE[masker] - BARK_SCALE[masked]
|
|
176
|
+
let spreading = -100.0
|
|
177
|
+
|
|
178
|
+
if (distance >= -3.0 && distance < -1.0) {
|
|
179
|
+
spreading = 17.0 * (distance + 3.0) - 70.0
|
|
180
|
+
} else if (distance >= -1.0 && distance < 0) {
|
|
181
|
+
spreading = 36.0 * distance
|
|
182
|
+
} else if (distance >= 0 && distance < 1.0) {
|
|
183
|
+
spreading = -27.0 * distance
|
|
184
|
+
} else if (distance >= 1.0 && distance < 8.0) {
|
|
185
|
+
spreading = -(distance - 1.0) * 4.0 - 27.0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
matrix[masker * NUM_BFUS + masked] = spreading
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return matrix
|
|
192
|
+
})()
|
|
193
|
+
|
|
194
|
+
// ISO/IEC 11172-3:1993 Psychoacoustic Model Constants
|
|
195
|
+
export const PSYMODEL_MIN_POWER_DB = -200
|
|
196
|
+
export const PSYMODEL_FFT_SIZE = 2048
|
|
197
|
+
export const PSYMODEL_INPUT_SIZE = 512
|
|
198
|
+
export const PSYMODEL_PN = 90.302
|
|
199
|
+
|
|
200
|
+
// Transient detection
|
|
201
|
+
export const TRANSIENT_DETECTION_THRESHOLD = 0.1
|
|
202
|
+
export const TRANSIENT_FLATNESS_EPSILON = 1e-10
|
|
203
|
+
|
|
204
|
+
// FFT sizes for transient detection
|
|
205
|
+
export const FFT_SIZE_LOW = 256
|
|
206
|
+
export const FFT_SIZE_MID = 256
|
|
207
|
+
export const FFT_SIZE_HIGH = 512
|
|
208
|
+
|
|
209
|
+
// Bit allocation
|
|
210
|
+
export const ALLOCATION_DIMINISHING_RETURNS_THRESHOLD = 0.1
|
|
211
|
+
export const ALLOCATION_MAX_ITERATIONS = 100
|
|
212
|
+
|
|
213
|
+
// Quantization
|
|
214
|
+
export const QUANTIZATION_SIGN_BIT_SHIFT = 1
|
|
215
|
+
export const QUANTIZATION_MAX_RANGE_SHIFT = 1
|
|
216
|
+
|
|
217
|
+
// WAV format
|
|
218
|
+
export const WAV_HEADER_SIZE = 44
|
|
219
|
+
export const WAV_RIFF_CHUNK_ID = 0x46464952
|
|
220
|
+
export const WAV_WAVE_FORMAT = 0x45564157
|
|
221
|
+
export const WAV_FMT_CHUNK_ID = 0x20746d66
|
|
222
|
+
export const WAV_DATA_CHUNK_ID = 0x61746164
|
|
223
|
+
export const WAV_PCM_FORMAT = 1
|
|
224
|
+
export const WAV_BYTES_PER_SAMPLE = 2
|
|
225
|
+
export const WAV_BITS_PER_SAMPLE = 16
|
|
226
|
+
export const WAV_PCM_MAX_POSITIVE = 0x7fff
|
|
227
|
+
export const WAV_PCM_MAX_NEGATIVE = 0x8000
|
|
228
|
+
export const WAV_DATA_OFFSET = 36
|
|
229
|
+
|
|
230
|
+
// Frame serialization
|
|
231
|
+
export const FRAME_HEADER_BITS = 16
|
|
232
|
+
export const FRAME_WORD_LENGTH_BITS = 4
|
|
233
|
+
export const FRAME_SCALE_FACTOR_BITS = 6
|
|
234
|
+
|
|
235
|
+
// Display formatting
|
|
236
|
+
export const DISPLAY_HEADER_WIDTH = 64
|
|
237
|
+
export const DISPLAY_FRAME_WIDTH = 32
|
|
238
|
+
export const DISPLAY_FORMAT_WIDTH = 16
|
|
239
|
+
|
|
240
|
+
// Tonal analysis flags
|
|
241
|
+
export const PSYMODEL_NOT_EXAMINED = 0
|
|
242
|
+
export const PSYMODEL_TONAL = 1
|
|
243
|
+
export const PSYMODEL_NON_TONAL = 2
|
|
244
|
+
export const PSYMODEL_IRRELEVANT = 3
|
|
245
|
+
|
|
246
|
+
// Pre-computed conversion factors
|
|
247
|
+
export const PSYMODEL_LOG10_FACTOR = 10 / Math.LN10
|
|
248
|
+
export const PSYMODEL_POW10_FACTOR = Math.LN10 / 10
|
|
249
|
+
|
|
250
|
+
// Hann window for 512 samples (single-band psychoacoustics)
|
|
251
|
+
export const PSYMODEL_HANN_WINDOW = (() => {
|
|
252
|
+
const table = new Float32Array(512)
|
|
253
|
+
const normalizationFactor = Math.sqrt(8.0 / 3.0)
|
|
254
|
+
|
|
255
|
+
for (let i = 0; i < 512; i++) {
|
|
256
|
+
const hann = 0.5 * (1 - Math.cos((2 * Math.PI * i) / 512))
|
|
257
|
+
const normalized = hann * normalizationFactor
|
|
258
|
+
table[i] = Math.round(normalized * 10000) / 10000
|
|
259
|
+
}
|
|
260
|
+
return table
|
|
261
|
+
})()
|
|
262
|
+
|
|
263
|
+
// Critical band boundaries (0-24)
|
|
264
|
+
export const PSYMODEL_CRITICAL_BANDS = new Uint8Array([
|
|
265
|
+
0, 1, 2, 4, 5, 7, 8, 10, 12, 14, 16, 19, 22, 26, 31, 36, 44, 49, 54, 60, 67,
|
|
266
|
+
74, 80, 92, 105,
|
|
267
|
+
])
|
|
268
|
+
|
|
269
|
+
// Threshold table: [frequency_index, bark_value, threshold_value]
|
|
270
|
+
// prettier-ignore
|
|
271
|
+
export const PSYMODEL_THRESHOLD_TABLE = [
|
|
272
|
+
[1.0, 0.85, 13.87], [2.0, 1.694, 2.85], [3.0, 2.525, -1.28], [4.0, 3.337, -3.5],
|
|
273
|
+
[5.0, 4.124, -4.9], [6.0, 4.882, -5.89], [7.0, 5.608, -6.63], [8.0, 6.301, -7.21],
|
|
274
|
+
[9.0, 6.959, -7.68], [10.0, 7.581, -8.08], [11.0, 8.169, -8.43], [12.0, 8.723, -8.75],
|
|
275
|
+
[13.0, 9.244, -9.05], [14.0, 9.734, -9.33], [15.0, 10.195, -9.61], [16.0, 10.629, -9.89],
|
|
276
|
+
[17.0, 11.037, -10.17], [18.0, 11.421, -10.47], [19.0, 11.783, -10.77], [20.0, 12.125, -11.1],
|
|
277
|
+
[21.0, 12.448, -11.44], [22.0, 12.753, -11.79], [23.0, 13.042, -12.17], [24.0, 13.317, -12.56],
|
|
278
|
+
[25.0, 13.577, -12.96], [26.0, 13.825, -13.38], [27.0, 14.062, -13.79], [28.0, 14.288, -14.21],
|
|
279
|
+
[29.0, 14.504, -14.63], [30.0, 14.711, -15.03], [31.0, 14.909, -15.41], [32.0, 15.1, -15.77],
|
|
280
|
+
[33.0, 15.283, -16.09], [34.0, 15.46, -16.37], [35.0, 15.631, -16.6], [36.0, 15.795, -16.78],
|
|
281
|
+
[37.0, 15.955, -16.91], [38.0, 16.11, -16.97], [39.0, 16.26, -16.98], [40.0, 16.405, -16.92],
|
|
282
|
+
[41.0, 16.547, -16.81], [42.0, 16.685, -16.65], [43.0, 16.82, -16.43], [44.0, 16.951, -16.17],
|
|
283
|
+
[45.0, 17.079, -15.87], [46.0, 17.204, -15.54], [47.0, 17.327, -15.19], [48.0, 17.447, -14.82],
|
|
284
|
+
[50.0, 17.68, -14.06], [52.0, 17.904, -13.32], [54.0, 18.121, -12.64], [56.0, 18.331, -12.04],
|
|
285
|
+
[58.0, 18.534, -11.53], [60.0, 18.73, -11.11], [62.0, 18.922, -10.77], [64.0, 19.108, -10.49],
|
|
286
|
+
[66.0, 19.288, -10.26], [68.0, 19.464, -10.07], [70.0, 19.635, -9.89], [72.0, 19.801, -9.72],
|
|
287
|
+
[74.0, 19.963, -9.54], [76.0, 20.12, -9.37], [78.0, 20.273, -9.18], [80.0, 20.421, -8.97],
|
|
288
|
+
[82.0, 20.565, -8.75], [84.0, 20.705, -8.51], [86.0, 20.84, -8.26], [88.0, 20.971, -7.98],
|
|
289
|
+
[90.0, 21.099, -7.68], [92.0, 21.222, -7.36], [94.0, 21.341, -7.02], [96.0, 21.457, -6.65],
|
|
290
|
+
[100.0, 21.676, -5.85], [104.0, 21.882, -4.93], [108.0, 22.074, -3.9], [112.0, 22.253, -2.75],
|
|
291
|
+
[116.0, 22.42, -1.46], [120.0, 22.575, -0.03], [124.0, 22.721, 1.56], [128.0, 22.857, 3.31],
|
|
292
|
+
[132.0, 22.984, 5.23], [136.0, 23.102, 7.34], [140.0, 23.213, 9.64], [144.0, 23.317, 12.15],
|
|
293
|
+
[148.0, 23.414, 14.88], [152.0, 23.506, 17.84], [156.0, 23.592, 21.05], [160.0, 23.673, 24.52],
|
|
294
|
+
[164.0, 23.749, 28.25], [168.0, 23.821, 32.27], [172.0, 23.888, 36.59], [176.0, 23.952, 41.22],
|
|
295
|
+
[180.0, 24.013, 46.18], [184.0, 24.07, 51.49], [188.0, 24.124, 56.0], [192.0, 24.176, 56.0],
|
|
296
|
+
[196.0, 24.225, 56.0], [200.0, 24.271, 56.0], [204.0, 24.316, 56.0], [208.0, 24.358, 56.0],
|
|
297
|
+
[212.0, 24.398, 56.0], [216.0, 24.436, 56.0], [220.0, 24.473, 56.0], [224.0, 24.508, 56.0],
|
|
298
|
+
[228.0, 24.541, 56.0], [232.0, 24.573, 56.0]
|
|
299
|
+
]
|
|
300
|
+
export const PSYMODEL_CB_FREQ_INDICES = PSYMODEL_CRITICAL_BANDS.map(
|
|
301
|
+
(cbIdx) => PSYMODEL_THRESHOLD_TABLE[cbIdx][0] - 1
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
// Frequency to critical band mapping
|
|
305
|
+
export const PSYMODEL_FREQ_TO_CB_MAP = new Uint8Array([
|
|
306
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
|
307
|
+
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
|
308
|
+
41, 42, 43, 44, 45, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53,
|
|
309
|
+
53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62,
|
|
310
|
+
63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 71,
|
|
311
|
+
71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76,
|
|
312
|
+
76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81,
|
|
313
|
+
81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85,
|
|
314
|
+
86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
|
|
315
|
+
90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95,
|
|
316
|
+
95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, 100,
|
|
317
|
+
100, 100, 100, 101, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 103,
|
|
318
|
+
104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
|
|
319
|
+
105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
|
|
320
|
+
105,
|
|
321
|
+
])
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carta1 Audio Codec - Encoder Options
|
|
3
|
+
*
|
|
4
|
+
* This module provides configuration management for the ATRAC1 encoder,
|
|
5
|
+
* including parameter validation and metadata for UI controls.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for the ATRAC1 encoder
|
|
10
|
+
*/
|
|
11
|
+
class EncoderOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Create a new EncoderOptions instance
|
|
14
|
+
* @param {Object} options - Initial option values
|
|
15
|
+
*/
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.values = {
|
|
18
|
+
transientThresholdLow: 1.0,
|
|
19
|
+
transientThresholdMid: 1.5,
|
|
20
|
+
transientThresholdHigh: 2.0,
|
|
21
|
+
normalizationDb: 68,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.metadata = {
|
|
25
|
+
transientThresholdLow: {
|
|
26
|
+
default: this.values.transientThresholdLow,
|
|
27
|
+
name: 'Low Band Transient Threshold',
|
|
28
|
+
description:
|
|
29
|
+
'Controls the transient detection threshold for the low frequency band (0-5.5kHz). Lower values make detection more sensitive, triggering short blocks more easily. Higher values require stronger transients.',
|
|
30
|
+
range: [0.01, 2],
|
|
31
|
+
step: 0.01,
|
|
32
|
+
},
|
|
33
|
+
transientThresholdMid: {
|
|
34
|
+
default: this.values.transientThresholdMid,
|
|
35
|
+
name: 'Mid Band Transient Threshold',
|
|
36
|
+
description:
|
|
37
|
+
'Controls the transient detection threshold for the mid frequency band (5.5-11kHz). Lower values make detection more sensitive, triggering short blocks more easily. Higher values require stronger transients.',
|
|
38
|
+
range: [0.01, 3],
|
|
39
|
+
step: 0.01,
|
|
40
|
+
},
|
|
41
|
+
transientThresholdHigh: {
|
|
42
|
+
default: this.values.transientThresholdHigh,
|
|
43
|
+
name: 'High Band Transient Threshold',
|
|
44
|
+
description:
|
|
45
|
+
'Controls the transient detection threshold for the high frequency band (11-22kHz). Lower values make detection more sensitive, triggering short blocks more easily. Higher values require stronger transients.',
|
|
46
|
+
range: [0.01, 4],
|
|
47
|
+
step: 0.01,
|
|
48
|
+
},
|
|
49
|
+
normalizationDb: {
|
|
50
|
+
default: this.values.normalizationDb,
|
|
51
|
+
name: 'Normalization Target (dB)',
|
|
52
|
+
description:
|
|
53
|
+
'Sets the target maximum level in decibels for psychoacoustic model normalization. This affects the dynamic range allocation and quantization precision. Higher values may increase bitrate but preserve more detail.',
|
|
54
|
+
range: [60, 80],
|
|
55
|
+
step: 1,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Apply user-provided options
|
|
60
|
+
if (options) {
|
|
61
|
+
this.setOptions(options)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Set multiple options at once
|
|
67
|
+
* @param {Object} options - Object containing option key-value pairs
|
|
68
|
+
*/
|
|
69
|
+
setOptions(options) {
|
|
70
|
+
for (const [key, value] of Object.entries(options)) {
|
|
71
|
+
if (key in this.values) {
|
|
72
|
+
this.setValue(key, value)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set a single option value with validation
|
|
79
|
+
* @param {string} key - Option key
|
|
80
|
+
* @param {*} value - Option value
|
|
81
|
+
* @throws {Error} If key is unknown or value is out of range
|
|
82
|
+
*/
|
|
83
|
+
setValue(key, value) {
|
|
84
|
+
if (!(key in this.metadata)) {
|
|
85
|
+
throw new Error(`Unknown option: ${key}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const meta = this.metadata[key]
|
|
89
|
+
const [min, max] = meta.range
|
|
90
|
+
|
|
91
|
+
if (value < min || value > max) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Value for ${key} must be between ${min} and ${max}, got ${value}`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.values[key] = value
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the value of an option
|
|
102
|
+
* @param {string} key - Option key
|
|
103
|
+
* @returns {*} Option value
|
|
104
|
+
* @throws {Error} If key is unknown
|
|
105
|
+
*/
|
|
106
|
+
getValue(key) {
|
|
107
|
+
if (!(key in this.values)) {
|
|
108
|
+
throw new Error(`Unknown option: ${key}`)
|
|
109
|
+
}
|
|
110
|
+
return this.values[key]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get transientThresholdLow() {
|
|
114
|
+
return this.values.transientThresholdLow
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get transientThresholdMid() {
|
|
118
|
+
return this.values.transientThresholdMid
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get transientThresholdHigh() {
|
|
122
|
+
return this.values.transientThresholdHigh
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get normalizationDb() {
|
|
126
|
+
return this.values.normalizationDb
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getMetadata(key) {
|
|
130
|
+
return this.metadata[key]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getAllMetadata() {
|
|
134
|
+
return { ...this.metadata }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
reset() {
|
|
138
|
+
for (const [key, meta] of Object.entries(this.metadata)) {
|
|
139
|
+
this.values[key] = meta.default
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
toObject() {
|
|
144
|
+
return {
|
|
145
|
+
values: { ...this.values },
|
|
146
|
+
metadata: { ...this.metadata },
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { EncoderOptions }
|
package/codec/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carta1 Audio Codec
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { encode } from './pipeline/encoder.js'
|
|
6
|
+
import { decode } from './pipeline/decoder.js'
|
|
7
|
+
import {
|
|
8
|
+
serializeFrame,
|
|
9
|
+
deserializeFrame,
|
|
10
|
+
AeaFile,
|
|
11
|
+
} from './io/serialization.js'
|
|
12
|
+
import { BufferPool } from './core/buffers.js'
|
|
13
|
+
import { EncoderOptions } from './core/options.js'
|
|
14
|
+
import { AudioProcessor } from './io/processor.js'
|
|
15
|
+
import { FFT } from './transforms/fft.js'
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
encode,
|
|
19
|
+
decode,
|
|
20
|
+
serializeFrame,
|
|
21
|
+
deserializeFrame,
|
|
22
|
+
AeaFile,
|
|
23
|
+
BufferPool,
|
|
24
|
+
EncoderOptions,
|
|
25
|
+
AudioProcessor,
|
|
26
|
+
FFT,
|
|
27
|
+
}
|