@audio/aiff-decode 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 audiojs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # aiff-decode
2
+
3
+ Decode AIFF and AIFF-C audio to PCM float samples.<br>
4
+ Part of [audio-decode](https://github.com/audiojs/audio-decode).
5
+
6
+ ## Install
7
+
8
+ ```
9
+ npm i @audio/aiff-decode
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```js
15
+ import decode from '@audio/aiff-decode'
16
+
17
+ let { channelData, sampleRate } = await decode(aiffBuffer)
18
+ // channelData: Float32Array[] (one per channel)
19
+ // sampleRate: number
20
+ ```
21
+
22
+ ### Streaming
23
+
24
+ ```js
25
+ import { decoder } from '@audio/aiff-decode'
26
+
27
+ let dec = await decoder()
28
+ let result = dec.decode(chunk)
29
+ dec.free()
30
+ ```
31
+
32
+ ## API
33
+
34
+ ### `decode(src): Promise<AudioData>`
35
+
36
+ Whole-file decode. Accepts `Uint8Array` or `ArrayBuffer`.
37
+
38
+ ### `decoder(): Promise<AIFFDecoder>`
39
+
40
+ Creates a decoder instance.
41
+
42
+ - **`dec.decode(data)`** — decode chunk, returns `{ channelData, sampleRate }`
43
+ - **`dec.flush()`** — flush (returns empty — AIFF is stateless)
44
+ - **`dec.free()`** — release resources
45
+
46
+ ## Formats
47
+
48
+ - AIFF — 8, 16, 24, 32-bit signed integer PCM (big-endian)
49
+ - AIFF-C — `NONE`/`twos` (BE PCM), `sowt` (LE PCM), `fl32`/`fl64` (float), `alaw`, `ulaw`
50
+
51
+ ## License
52
+
53
+ MIT — [krishnized](https://github.com/krishnized/license)
@@ -0,0 +1,16 @@
1
+ interface AudioData {
2
+ channelData: Float32Array[];
3
+ sampleRate: number;
4
+ }
5
+
6
+ interface AIFFDecoder {
7
+ decode(data: Uint8Array): AudioData;
8
+ flush(): AudioData;
9
+ free(): void;
10
+ }
11
+
12
+ /** Decode AIFF/AIFF-C audio buffer to PCM samples */
13
+ export default function decode(src: ArrayBuffer | Uint8Array): Promise<AudioData>;
14
+
15
+ /** Create decoder instance */
16
+ export function decoder(): Promise<AIFFDecoder>;
package/aiff-decode.js ADDED
@@ -0,0 +1,489 @@
1
+ /**
2
+ * AIFF/AIFF-C decoder — pure JS
3
+ * Decodes AIFF and AIFF-C audio to Float32Array PCM samples
4
+ *
5
+ * let { channelData, sampleRate } = await decode(aiffbuf)
6
+ */
7
+
8
+ const EMPTY = Object.freeze({ channelData: [], sampleRate: 0 })
9
+
10
+ // A-law expansion table (ITU-T G.711)
11
+ const ALAW_TBL = new Int16Array(256)
12
+ // mu-law expansion table (ITU-T G.711)
13
+ const ULAW_TBL = new Int16Array(256)
14
+
15
+ ;(function buildTables() {
16
+ for (let i = 0; i < 256; i++) {
17
+ // A-law
18
+ let ax = i ^ 0x55, seg = (ax >> 4) & 7, val = ((ax & 0x0F) << 4) + 8
19
+ if (seg) val = (val + 256) << (seg - 1)
20
+ ALAW_TBL[i] = (ax & 0x80) ? val : -val
21
+
22
+ // mu-law
23
+ let ux = ~i & 0xFF
24
+ seg = (ux >> 4) & 7
25
+ val = ((ux & 0x0F) << 3) + 132
26
+ val <<= seg
27
+ ULAW_TBL[i] = (ux & 0x80) ? (val - 132) : -(val - 132)
28
+ }
29
+ })()
30
+
31
+ /**
32
+ * Read 80-bit IEEE 754 extended precision float
33
+ */
34
+ function readF80(b, o) {
35
+ let sign = (b[o] >> 7) & 1
36
+ let exp = ((b[o] & 0x7F) << 8) | b[o + 1]
37
+ let hi = ((b[o + 2] << 24) | (b[o + 3] << 16) | (b[o + 4] << 8) | b[o + 5]) >>> 0
38
+ let lo = ((b[o + 6] << 24) | (b[o + 7] << 16) | (b[o + 8] << 8) | b[o + 9]) >>> 0
39
+ if (exp === 0 && hi === 0 && lo === 0) return 0
40
+ if (exp === 0x7FFF) return sign ? -Infinity : Infinity
41
+ let f = (hi * 4294967296 + lo) / 9223372036854775808 // / 2^63
42
+ return (sign ? -1 : 1) * f * Math.pow(2, exp - 16383)
43
+ }
44
+
45
+ function str4(b, o) {
46
+ return String.fromCharCode(b[o], b[o + 1], b[o + 2], b[o + 3])
47
+ }
48
+
49
+ function r32(b, o) {
50
+ return ((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]) >>> 0
51
+ }
52
+
53
+ function r16(b, o) {
54
+ return (b[o] << 8) | b[o + 1]
55
+ }
56
+
57
+ /**
58
+ * Parse AIFF/AIFF-C and decode to Float32Array channels
59
+ */
60
+ function parseAiff(buf) {
61
+ if (!buf || buf.length < 12) return EMPTY
62
+
63
+ let b = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
64
+ if (b.length < 12) return EMPTY
65
+
66
+ // Validate FORM header
67
+ if (str4(b, 0) !== 'FORM') throw Error('Not an AIFF file')
68
+ let form = str4(b, 8)
69
+ if (form !== 'AIFF' && form !== 'AIFC') throw Error('Not an AIFF file')
70
+ let isAIFC = form === 'AIFC'
71
+
72
+ let nCh = 0, nFrames = 0, bps = 0, sr = 0, comp = 'NONE'
73
+ let ssndOff = -1, ssndSize = 0
74
+
75
+ // Parse chunks
76
+ let pos = 12, end = Math.min(8 + r32(b, 4), b.length)
77
+ while (pos + 8 <= end) {
78
+ let ckId = str4(b, pos)
79
+ let ckSize = r32(b, pos + 4)
80
+ let ckData = pos + 8
81
+
82
+ if (ckId === 'COMM') {
83
+ if (ckData + 18 > b.length) throw Error('Truncated COMM chunk')
84
+ nCh = r16(b, ckData)
85
+ nFrames = r32(b, ckData + 2)
86
+ bps = r16(b, ckData + 6)
87
+ sr = readF80(b, ckData + 8)
88
+ if (isAIFC && ckSize >= 22 && ckData + 22 <= b.length) {
89
+ comp = str4(b, ckData + 18)
90
+ }
91
+ } else if (ckId === 'SSND') {
92
+ if (ckData + 8 > b.length) throw Error('Truncated SSND chunk')
93
+ let dataOff = r32(b, ckData)
94
+ ssndOff = ckData + 8 + dataOff
95
+ ssndSize = ckSize - 8 - dataOff
96
+ }
97
+
98
+ pos = ckData + ckSize + (ckSize & 1) // pad to even
99
+ }
100
+
101
+ if (!nCh || !nFrames || !sr || ssndOff < 0) return EMPTY
102
+
103
+ // Determine byte depth and decode strategy
104
+ let byteDepth = Math.ceil(bps / 8)
105
+ let frameBytes = byteDepth * nCh
106
+ let actualFrames = Math.min(nFrames, Math.floor(Math.min(ssndSize, b.length - ssndOff) / frameBytes))
107
+ if (actualFrames <= 0) return EMPTY
108
+
109
+ let channelData = Array.from({ length: nCh }, () => new Float32Array(actualFrames))
110
+ let p = ssndOff
111
+
112
+ // Compression-aware decode
113
+ let cUpper = comp.toUpperCase()
114
+ if (cUpper === 'NONE' || cUpper === 'TWOS' || comp === 'twos') {
115
+ // Big-endian signed integer PCM
116
+ decodePCM_BE(b, p, channelData, actualFrames, nCh, bps, byteDepth)
117
+ } else if (comp === 'sowt') {
118
+ // Little-endian signed integer PCM
119
+ decodePCM_LE(b, p, channelData, actualFrames, nCh, bps, byteDepth)
120
+ } else if (comp === 'fl32' || comp === 'FL32') {
121
+ decodeFloat32_BE(b, p, channelData, actualFrames, nCh)
122
+ } else if (comp === 'fl64' || comp === 'FL64') {
123
+ decodeFloat64_BE(b, p, channelData, actualFrames, nCh)
124
+ } else if (comp === 'alaw') {
125
+ decodeLaw(b, p, channelData, actualFrames, nCh, ALAW_TBL)
126
+ } else if (comp === 'ulaw' || comp === 'ULAW') {
127
+ decodeLaw(b, p, channelData, actualFrames, nCh, ULAW_TBL)
128
+ } else if (comp === 'ima4') {
129
+ return decodeIMA4(b, ssndOff, ssndSize, nCh, nFrames, sr)
130
+ } else if (comp === 'GSM ' || comp === 'gsm ') {
131
+ return decodeGSM(b, ssndOff, ssndSize, nCh, nFrames, sr)
132
+ } else {
133
+ throw Error('Unsupported AIFF-C compression: ' + comp)
134
+ }
135
+
136
+ return { channelData, sampleRate: sr }
137
+ }
138
+
139
+ function decodePCM_BE(b, p, channels, frames, nCh, bps, byteDepth) {
140
+ if (bps === 8) {
141
+ // 8-bit: unsigned, center at 128
142
+ for (let i = 0; i < frames; i++)
143
+ for (let c = 0; c < nCh; c++)
144
+ channels[c][i] = (b[p++] - 128) / 128
145
+ } else if (bps === 16) {
146
+ for (let i = 0; i < frames; i++)
147
+ for (let c = 0; c < nCh; c++) {
148
+ let v = (b[p] << 8) | b[p + 1]; p += 2
149
+ channels[c][i] = (v > 32767 ? v - 65536 : v) / 32768
150
+ }
151
+ } else if (bps === 24) {
152
+ for (let i = 0; i < frames; i++)
153
+ for (let c = 0; c < nCh; c++) {
154
+ let v = (b[p] << 16) | (b[p + 1] << 8) | b[p + 2]; p += 3
155
+ channels[c][i] = (v > 8388607 ? v - 16777216 : v) / 8388608
156
+ }
157
+ } else if (bps === 32) {
158
+ for (let i = 0; i < frames; i++)
159
+ for (let c = 0; c < nCh; c++) {
160
+ let v = ((b[p] << 24) | (b[p + 1] << 16) | (b[p + 2] << 8) | b[p + 3]); p += 4
161
+ channels[c][i] = v / 2147483648
162
+ }
163
+ } else {
164
+ throw Error('Unsupported bit depth: ' + bps)
165
+ }
166
+ }
167
+
168
+ function decodePCM_LE(b, p, channels, frames, nCh, bps, byteDepth) {
169
+ if (bps === 8) {
170
+ for (let i = 0; i < frames; i++)
171
+ for (let c = 0; c < nCh; c++)
172
+ channels[c][i] = (b[p++] - 128) / 128
173
+ } else if (bps === 16) {
174
+ for (let i = 0; i < frames; i++)
175
+ for (let c = 0; c < nCh; c++) {
176
+ let v = b[p] | (b[p + 1] << 8); p += 2
177
+ channels[c][i] = (v > 32767 ? v - 65536 : v) / 32768
178
+ }
179
+ } else if (bps === 24) {
180
+ for (let i = 0; i < frames; i++)
181
+ for (let c = 0; c < nCh; c++) {
182
+ let v = b[p] | (b[p + 1] << 8) | (b[p + 2] << 16); p += 3
183
+ channels[c][i] = (v > 8388607 ? v - 16777216 : v) / 8388608
184
+ }
185
+ } else if (bps === 32) {
186
+ for (let i = 0; i < frames; i++)
187
+ for (let c = 0; c < nCh; c++) {
188
+ let v = b[p] | (b[p + 1] << 8) | (b[p + 2] << 16) | (b[p + 3] << 24); p += 4
189
+ channels[c][i] = v / 2147483648
190
+ }
191
+ } else {
192
+ throw Error('Unsupported bit depth: ' + bps)
193
+ }
194
+ }
195
+
196
+ function decodeFloat32_BE(b, p, channels, frames, nCh) {
197
+ let dv = new DataView(b.buffer, b.byteOffset, b.byteLength)
198
+ for (let i = 0; i < frames; i++)
199
+ for (let c = 0; c < nCh; c++) {
200
+ channels[c][i] = dv.getFloat32(p, false); p += 4
201
+ }
202
+ }
203
+
204
+ function decodeFloat64_BE(b, p, channels, frames, nCh) {
205
+ let dv = new DataView(b.buffer, b.byteOffset, b.byteLength)
206
+ for (let i = 0; i < frames; i++)
207
+ for (let c = 0; c < nCh; c++) {
208
+ channels[c][i] = dv.getFloat64(p, false); p += 8
209
+ }
210
+ }
211
+
212
+ function decodeLaw(b, p, channels, frames, nCh, tbl) {
213
+ for (let i = 0; i < frames; i++)
214
+ for (let c = 0; c < nCh; c++)
215
+ channels[c][i] = tbl[b[p++]] / 32768
216
+ }
217
+
218
+ // ===== IMA4 ADPCM (Apple IMA ADPCM) =====
219
+ // 34 bytes/block/channel → 64 samples. COMM nFrames = number of blocks.
220
+
221
+ const IMA_STEP = new Int16Array([7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,50,55,60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279,307,337,371,408,449,494,544,598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066,2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,10442,11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767])
222
+ const IMA_IDX = new Int8Array([-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8])
223
+
224
+ function decodeIMA4(b, ssndOff, ssndSize, nCh, nBlocks, sr) {
225
+ let blockBytes = 34 * nCh
226
+ let totalSamples = nBlocks * 64
227
+ let channelData = Array.from({ length: nCh }, () => new Float32Array(totalSamples))
228
+ let p = ssndOff, sample = 0
229
+
230
+ for (let blk = 0; blk < nBlocks && p + blockBytes <= ssndOff + ssndSize; blk++) {
231
+ for (let c = 0; c < nCh; c++) {
232
+ let bp = p + c * 34
233
+ // Preamble: 16-bit BE — high 9 bits = predictor, low 7 bits = step index
234
+ let preamble = (b[bp] << 8) | b[bp + 1]
235
+ let predictor = preamble & 0xFF80 // top 9 bits, sign-extended via int16
236
+ if (predictor > 32767) predictor -= 65536
237
+ let stepIdx = preamble & 0x7F
238
+ if (stepIdx > 88) stepIdx = 88
239
+
240
+ let out = channelData[c]
241
+ for (let i = 0; i < 32; i++) {
242
+ let byte = b[bp + 2 + i]
243
+ // High nibble first, then low nibble
244
+ for (let nibbleIdx = 0; nibbleIdx < 2; nibbleIdx++) {
245
+ let nibble = nibbleIdx === 0 ? (byte >> 4) & 0xF : byte & 0xF
246
+ let step = IMA_STEP[stepIdx]
247
+ let diff = step >> 3
248
+ if (nibble & 1) diff += step >> 2
249
+ if (nibble & 2) diff += step >> 1
250
+ if (nibble & 4) diff += step
251
+ if (nibble & 8) diff = -diff
252
+ predictor += diff
253
+ if (predictor > 32767) predictor = 32767
254
+ if (predictor < -32768) predictor = -32768
255
+ stepIdx += IMA_IDX[nibble]
256
+ if (stepIdx < 0) stepIdx = 0
257
+ if (stepIdx > 88) stepIdx = 88
258
+ out[sample + i * 2 + nibbleIdx] = predictor / 32768
259
+ }
260
+ }
261
+ }
262
+ p += blockBytes
263
+ sample += 64
264
+ }
265
+
266
+ return { channelData, sampleRate: sr }
267
+ }
268
+
269
+ // ===== GSM 6.10 (Full Rate) =====
270
+ // 33 bytes/frame → 160 samples. RPE-LTP speech codec.
271
+ // Ported from libgsm by Jutta Degener and Carsten Bormann, TU Berlin.
272
+
273
+ // Table 4.3b: Quantization levels of the LTP gain quantizer
274
+ const GSM_QLB = [3277, 11469, 21299, 32767]
275
+ // Table 4.6: Normalized direct mantissa used to compute xM/xmax
276
+ const GSM_FAC = [18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767]
277
+
278
+ // Saturating 16-bit add/sub
279
+ function sadd(a, b) { let s = a + b; return s > 32767 ? 32767 : s < -32768 ? -32768 : s }
280
+ function ssub(a, b) { let s = a - b; return s > 32767 ? 32767 : s < -32768 ? -32768 : s }
281
+ // GSM_MULT_R(a,b): (a*b + 16384) >> 15, with saturation for MIN_WORD*MIN_WORD
282
+ function multr(a, b) {
283
+ if (a === -32768 && b === -32768) return 32767
284
+ return ((a * b + 16384) >> 15) | 0
285
+ }
286
+ // Arithmetic shift right (sign-preserving)
287
+ function asr(x, n) {
288
+ if (n >= 16) return x < 0 ? -1 : 0
289
+ if (n <= -16) return 0
290
+ if (n < 0) return x << -n
291
+ return x >> n // JS >> is always arithmetic
292
+ }
293
+
294
+ function decodeGSM(b, ssndOff, ssndSize, nCh, nFrames, sr) {
295
+ let totalFrames = Math.min(nFrames, Math.floor(ssndSize / 33))
296
+ let channelData = [new Float32Array(totalFrames * 160)]
297
+ let out = channelData[0], p = ssndOff
298
+
299
+ // Decoder state (mirrors struct gsm_state)
300
+ let dp0 = new Int32Array(280) // int32 to avoid overflow in intermediate calcs
301
+ let v = new Int32Array(9)
302
+ let LARpp = [new Int32Array(8), new Int32Array(8)]
303
+ let j = 0, nrp = 40, msr = 0
304
+
305
+ for (let f = 0; f < totalFrames; f++) {
306
+ let { LARc, sub } = gsmBits(b, p)
307
+ let wt = new Int32Array(160)
308
+
309
+ // Process 4 subframes: RPE decode + long-term synthesis
310
+ for (let s = 0; s < 4; s++) {
311
+ let sf = sub[s]
312
+ let erp = new Int32Array(40)
313
+ gsmRpeDecode(sf.xmaxc, sf.Mc, sf.xmc, erp)
314
+
315
+ // Long-term synthesis filtering (4.3.2)
316
+ let Nr = (sf.Nc < 40 || sf.Nc > 120) ? nrp : sf.Nc
317
+ nrp = Nr
318
+ let brp = GSM_QLB[sf.bc]
319
+ // drp is dp0 offset by 120; drp[k] = dp0[120+k], drp[k-Nr] = dp0[120+k-Nr]
320
+ for (let k = 0; k < 40; k++) {
321
+ let drpp = multr(brp, dp0[120 + k - Nr])
322
+ dp0[120 + k] = sadd(erp[k], drpp)
323
+ }
324
+ for (let k = 0; k < 40; k++) wt[s * 40 + k] = dp0[120 + k]
325
+
326
+ // Shift dp0: drp[-120+k] = drp[-80+k] for k=0..119
327
+ for (let k = 0; k < 120; k++) dp0[k] = dp0[k + 40]
328
+ }
329
+
330
+ // Short-term synthesis filter with LARp interpolation (4.2.8-4.2.9)
331
+ let LARpp_j = LARpp[j]
332
+ let LARpp_j_1 = LARpp[j ^ 1]
333
+ j ^= 1 // toggle for next frame
334
+
335
+ gsmDecodeLAR(LARc, LARpp_j)
336
+
337
+ let sr_out = new Int32Array(160)
338
+ let LARp = new Int32Array(8)
339
+
340
+ // Coefficients_0_12: LARp = 3/4 * prev + 1/4 * curr
341
+ for (let i = 0; i < 8; i++)
342
+ LARp[i] = sadd(sadd(LARpp_j_1[i] >> 2, LARpp_j[i] >> 2), LARpp_j_1[i] >> 1)
343
+ gsmLARpToRp(LARp)
344
+ gsmSynthFilter(v, LARp, 13, wt, 0, sr_out, 0)
345
+
346
+ // Coefficients_13_26: LARp = 1/2 * prev + 1/2 * curr
347
+ for (let i = 0; i < 8; i++)
348
+ LARp[i] = sadd(LARpp_j_1[i] >> 1, LARpp_j[i] >> 1)
349
+ gsmLARpToRp(LARp)
350
+ gsmSynthFilter(v, LARp, 14, wt, 13, sr_out, 13)
351
+
352
+ // Coefficients_27_39: LARp = 1/4 * prev + 3/4 * curr
353
+ for (let i = 0; i < 8; i++)
354
+ LARp[i] = sadd(sadd(LARpp_j_1[i] >> 2, LARpp_j[i] >> 2), LARpp_j[i] >> 1)
355
+ gsmLARpToRp(LARp)
356
+ gsmSynthFilter(v, LARp, 13, wt, 27, sr_out, 27)
357
+
358
+ // Coefficients_40_159: LARp = curr
359
+ for (let i = 0; i < 8; i++) LARp[i] = LARpp_j[i]
360
+ gsmLARpToRp(LARp)
361
+ gsmSynthFilter(v, LARp, 120, wt, 40, sr_out, 40)
362
+
363
+ // Postprocessing: de-emphasis + truncation/upscaling
364
+ for (let k = 0; k < 160; k++) {
365
+ msr = sadd(sr_out[k], multr(msr, 28180))
366
+ let s = sadd(msr, msr) & 0xFFF8
367
+ out[f * 160 + k] = (s > 32767 ? s - 65536 : s) / 32768
368
+ }
369
+ p += 33
370
+ }
371
+
372
+ return { channelData, sampleRate: sr }
373
+ }
374
+
375
+ // GSM 6.10 bit unpacking: 33 bytes → LARc[8] + 4 subframes
376
+ function gsmBits(buf, off) {
377
+ let bc = 0
378
+ function r(n) {
379
+ let val = 0
380
+ for (let i = 0; i < n; i++) {
381
+ val = (val << 1) | ((buf[off + (bc >> 3)] >> (7 - (bc & 7))) & 1)
382
+ bc++
383
+ }
384
+ return val
385
+ }
386
+ let LARc = [r(6),r(6),r(5),r(5),r(4),r(4),r(3),r(3)]
387
+ let sub = []
388
+ for (let s = 0; s < 4; s++)
389
+ sub.push({ Nc: r(7), bc: r(2), Mc: r(2), xmaxc: r(6), xmc: [r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3),r(3)] })
390
+ return { LARc, sub }
391
+ }
392
+
393
+ // 4.2.8: Decoding of the coded Log-Area Ratios
394
+ function gsmDecodeLAR(LARc, LARpp) {
395
+ // B*2, MIC, INVA from tables 4.1 and 4.2
396
+ const B2 = [0, 0, 4096, -5120, 188, -3584, -682, -2288]
397
+ const MIC = [-32, -32, -16, -16, -8, -8, -4, -4]
398
+ const INVA = [13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708]
399
+ for (let i = 0; i < 8; i++) {
400
+ let t = sadd(LARc[i], MIC[i]) << 10
401
+ t = ssub(t, B2[i])
402
+ t = multr(INVA[i], t)
403
+ LARpp[i] = sadd(t, t)
404
+ }
405
+ }
406
+
407
+ // 4.2.9.2: LARp to reflection coefficients (in-place)
408
+ function gsmLARpToRp(LARp) {
409
+ for (let i = 0; i < 8; i++) {
410
+ if (LARp[i] < 0) {
411
+ let t = LARp[i] === -32768 ? 32767 : -LARp[i]
412
+ LARp[i] = -(t < 11059 ? t << 1 : t < 20070 ? t + 11059 : sadd(t >> 2, 26112))
413
+ } else {
414
+ let t = LARp[i]
415
+ LARp[i] = t < 11059 ? t << 1 : t < 20070 ? t + 11059 : sadd(t >> 2, 26112)
416
+ }
417
+ }
418
+ }
419
+
420
+ // Short-term synthesis filter (4.2.10, synthesis direction)
421
+ function gsmSynthFilter(v, rrp, k, wt, wtOff, sr, srOff) {
422
+ for (let n = 0; n < k; n++) {
423
+ let sri = wt[wtOff + n]
424
+ for (let i = 7; i >= 0; i--) {
425
+ sri = ssub(sri, multr(rrp[i], v[i]))
426
+ v[i + 1] = sadd(v[i], multr(rrp[i], sri))
427
+ }
428
+ sr[srOff + n] = v[0] = sri
429
+ }
430
+ }
431
+
432
+ // 4.2.16-4.2.17: RPE decoding (inverse quantization + grid positioning)
433
+ function gsmRpeDecode(xmaxc, Mc, xMc, erp) {
434
+ // 4.2.15: Compute exp and mant from xmaxc
435
+ let exp = 0, mant
436
+ if (xmaxc > 15) exp = (xmaxc >> 3) - 1
437
+ mant = xmaxc - (exp << 3)
438
+ if (mant === 0) { exp = -4; mant = 7 }
439
+ else { while (mant <= 7) { mant = (mant << 1) | 1; exp-- }; mant -= 8 }
440
+
441
+ // 4.2.16: APCM inverse quantization
442
+ let fac = GSM_FAC[mant]
443
+ let shift = 6 - exp
444
+ let round = shift > 1 ? 1 << (shift - 1) : 0 // gsm_asl(1, shift-1)
445
+
446
+ let xMp = new Int32Array(13)
447
+ for (let i = 0; i < 13; i++) {
448
+ let t = ((xMc[i] << 1) - 7) << 12
449
+ t = multr(fac, t)
450
+ t = sadd(t, round)
451
+ xMp[i] = asr(t, shift)
452
+ }
453
+
454
+ // 4.2.17: RPE grid positioning
455
+ for (let k = 0; k < 40; k++) erp[k] = 0
456
+ for (let i = 0; i < 13; i++) erp[Mc + 3 * i] = xMp[i]
457
+ }
458
+
459
+ /**
460
+ * Whole-file decode
461
+ * @param {Uint8Array|ArrayBuffer} src
462
+ * @returns {Promise<{channelData: Float32Array[], sampleRate: number}>}
463
+ */
464
+ export default async function decode(src) {
465
+ let buf = src instanceof Uint8Array ? src : src instanceof ArrayBuffer ? new Uint8Array(src) : src
466
+ let dec = await decoder()
467
+ try {
468
+ return dec.decode(buf)
469
+ } finally {
470
+ dec.free()
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Create decoder instance
476
+ * @returns {Promise<{decode(chunk: Uint8Array): {channelData, sampleRate}, flush(), free()}>}
477
+ */
478
+ export async function decoder() {
479
+ let freed = false
480
+ return {
481
+ decode(data) {
482
+ if (freed) throw Error('Decoder already freed')
483
+ if (!data?.length) return EMPTY
484
+ return parseAiff(data)
485
+ },
486
+ flush() { return EMPTY },
487
+ free() { freed = true }
488
+ }
489
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@audio/aiff-decode",
3
+ "version": "1.0.0",
4
+ "description": "Decode AIFF/AIFF-C audio to PCM samples",
5
+ "type": "module",
6
+ "main": "aiff-decode.js",
7
+ "types": "aiff-decode.d.ts",
8
+ "exports": {
9
+ ".": "./aiff-decode.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "aiff-decode.js",
14
+ "aiff-decode.d.ts",
15
+ "LICENSE"
16
+ ],
17
+ "keywords": [
18
+ "aiff",
19
+ "aifc",
20
+ "audio",
21
+ "decode",
22
+ "decoder",
23
+ "pcm"
24
+ ],
25
+ "publishConfig": { "access": "public" },
26
+ "license": "MIT",
27
+ "author": "audiojs",
28
+ "homepage": "https://github.com/audiojs/aiff-decode#readme",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/audiojs/aiff-decode.git"
32
+ },
33
+ "engines": { "node": ">=16" },
34
+ "bugs": "https://github.com/audiojs/aiff-decode/issues",
35
+ "scripts": {
36
+ "test": "node test.js"
37
+ }
38
+ }