@audio/decode-caf 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/decode-caf.js +23 -29
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -43,6 +43,8 @@ Create a decoder instance.
43
43
  - `ulaw`: G.711 µ-law.
44
44
  - `ima4`: IMA/QuickTime ADPCM.
45
45
 
46
+ Other formats (AAC and ALAC packets, as in Apple Loops) throw `CAF: unsupported format '<id>'`.
47
+
46
48
  ## License
47
49
 
48
50
  [ॐ](https://github.com/krishnized/license/) · [MIT](./LICENSE)
package/decode-caf.js CHANGED
@@ -6,11 +6,27 @@
6
6
  */
7
7
 
8
8
  const EMPTY = Object.freeze({ channelData: [], sampleRate: 0 })
9
+ const FORMATS = new Set(['lpcm', 'alaw', 'ulaw', 'ima4'])
9
10
 
10
11
  // IMA/QuickTime ADPCM tables
11
12
  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])
12
13
  const IMA_IDX = new Int8Array([-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8])
13
14
 
15
+ // G.711 expansion tables (ITU-T), as decode-aiff and decode-wav: int16 PCM per encoded byte
16
+ const ALAW_TBL = new Int16Array(256)
17
+ const ULAW_TBL = new Int16Array(256)
18
+ for (let i = 0; i < 256; i++) {
19
+ let ax = i ^ 0x55, seg = (ax >> 4) & 7, val = ((ax & 0x0F) << 4) + 8
20
+ if (seg) val = (val + 256) << (seg - 1)
21
+ ALAW_TBL[i] = (ax & 0x80) ? val : -val
22
+
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) ? (132 - val) : (val - 132)
28
+ }
29
+
14
30
  /** Decode a complete CAF file synchronously. */
15
31
  export default function decode(src) {
16
32
  let dec = decoder()
@@ -76,6 +92,8 @@ function scanCafHdr(buf) {
76
92
  channelsPerFrame: dv.getUint32(off + 24, false),
77
93
  bitsPerChannel: dv.getUint32(off + 28, false),
78
94
  }
95
+ // compressed formats (aac, alac, ...) carry packets this decoder can't read: say so, don't wait forever
96
+ if (!FORMATS.has(desc.formatID)) throw Error(`CAF: unsupported format '${desc.formatID}'`)
79
97
  } else if (type === 'data') {
80
98
  if (!desc) return null
81
99
  let dataStart = off + 4 // skip editCount
@@ -88,7 +106,7 @@ function scanCafHdr(buf) {
88
106
  if (formatID === 'alaw' || formatID === 'ulaw') frameBytes = ch
89
107
  else if (formatID === 'ima4') frameBytes = desc.bytesPerPacket || 34 * ch // packet = 34 bytes/channel
90
108
  else frameBytes = ch * bytesPerSample
91
- if (!frameBytes) return null
109
+ if (!frameBytes) throw Error(`CAF: invalid ${formatID} description (${ch} channels, ${bits} bits)`)
92
110
  return { ...desc, dataStart, frameBytes, dataSize }
93
111
  }
94
112
  if (size < 0) break
@@ -104,8 +122,8 @@ function decodeCafRaw(raw, hdr) {
104
122
  if (!frames) return EMPTY
105
123
  let samples
106
124
  if (formatID === 'lpcm') samples = decodeLPCM(raw, formatFlags, bits, ch)
107
- else if (formatID === 'alaw') samples = decodeAlaw(raw, ch)
108
- else if (formatID === 'ulaw') samples = decodeUlaw(raw, ch)
125
+ else if (formatID === 'alaw') samples = decodeLaw(raw, ch, ALAW_TBL)
126
+ else if (formatID === 'ulaw') samples = decodeLaw(raw, ch, ULAW_TBL)
109
127
  else if (formatID === 'ima4') samples = decodeIma4(raw, hdr)
110
128
  else throw Error('CAF: unsupported format ' + formatID)
111
129
  return { channelData: samples, sampleRate }
@@ -203,36 +221,12 @@ function decodeLPCM(data, flags, bits, ch) {
203
221
  return channelData
204
222
  }
205
223
 
206
- function alawDecode(val) {
207
- val ^= 0x55
208
- let sign = val & 0x80, seg = (val >> 4) & 7, quant = val & 0x0F
209
- let sample = seg ? ((quant << 1) | 0x21) << (seg - 1) : (quant << 1) | 1
210
- return (sign ? -sample : sample) / 32768
211
- }
212
-
213
- function ulawDecode(val) {
214
- val = ~val & 0xFF
215
- let sign = val & 0x80, exp = (val >> 4) & 7, mant = val & 0x0F
216
- let sample = ((mant << 1) | 0x21) << exp
217
- return (sign ? -(sample - 33) : (sample - 33)) / 32768
218
- }
219
-
220
- function decodeAlaw(data, ch) {
221
- let framesCount = (data.length / ch) | 0
222
- if (!framesCount) return []
223
- let channelData = Array.from({ length: ch }, () => new Float32Array(framesCount))
224
- for (let i = 0, off = 0; i < framesCount; i++)
225
- for (let c = 0; c < ch; c++, off++)
226
- channelData[c][i] = alawDecode(data[off])
227
- return channelData
228
- }
229
-
230
- function decodeUlaw(data, ch) {
224
+ function decodeLaw(data, ch, tbl) {
231
225
  let framesCount = (data.length / ch) | 0
232
226
  if (!framesCount) return []
233
227
  let channelData = Array.from({ length: ch }, () => new Float32Array(framesCount))
234
228
  for (let i = 0, off = 0; i < framesCount; i++)
235
229
  for (let c = 0; c < ch; c++, off++)
236
- channelData[c][i] = ulawDecode(data[off])
230
+ channelData[c][i] = tbl[data[off]] / 32768
237
231
  return channelData
238
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audio/decode-caf",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Decode CAF (Core Audio Format) audio to PCM samples",
5
5
  "type": "module",
6
6
  "main": "decode-caf.js",