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.
@@ -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
+ }