@audio/decode-caf 1.1.2 → 1.2.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Decode Core Audio Format (CAF) audio to PCM float samples.
4
4
 
5
- Part of [audio-decode](https://github.com/audiojs/audio-decode).
5
+ Part of [decode](https://github.com/audiojs/decode).
6
6
 
7
7
  ## Install
8
8
 
@@ -47,6 +47,7 @@ Creates a decoder instance.
47
47
  - `lpcm` — 8, 16, 24, 32-bit signed integer (LE/BE), 32/64-bit float (LE/BE)
48
48
  - `alaw` — G.711 A-law
49
49
  - `ulaw` — G.711 mu-law
50
+ - `ima4` — IMA/QuickTime ADPCM (iOS Voice Memos, GarageBand)
50
51
 
51
52
  ## License
52
53
 
package/decode-caf.js CHANGED
@@ -7,6 +7,10 @@
7
7
 
8
8
  const EMPTY = Object.freeze({ channelData: [], sampleRate: 0 })
9
9
 
10
+ // IMA/QuickTime ADPCM tables
11
+ 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
+ const IMA_IDX = new Int8Array([-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8])
13
+
10
14
  /**
11
15
  * Whole-file decode
12
16
  * @param {Uint8Array|ArrayBuffer} src
@@ -89,6 +93,7 @@ function scanCafHdr(buf) {
89
93
  let bytesPerSample = bits >> 3
90
94
  let frameBytes
91
95
  if (formatID === 'alaw' || formatID === 'ulaw') frameBytes = ch
96
+ else if (formatID === 'ima4') frameBytes = desc.bytesPerPacket || 34 * ch // packet = 34 bytes/channel
92
97
  else frameBytes = ch * bytesPerSample
93
98
  if (!frameBytes) return null
94
99
  return { ...desc, dataStart, frameBytes, dataSize }
@@ -108,10 +113,53 @@ function decodeCafRaw(raw, hdr) {
108
113
  if (formatID === 'lpcm') samples = decodeLPCM(raw, formatFlags, bits, ch)
109
114
  else if (formatID === 'alaw') samples = decodeAlaw(raw, ch)
110
115
  else if (formatID === 'ulaw') samples = decodeUlaw(raw, ch)
116
+ else if (formatID === 'ima4') samples = decodeIma4(raw, hdr)
111
117
  else throw Error('CAF: unsupported format ' + formatID)
112
118
  return { channelData: samples, sampleRate }
113
119
  }
114
120
 
121
+ // QuickTime IMA4 ADPCM — 34-byte packets per channel → 64 frames each.
122
+ // The predictor runs continuously across the whole stream; each packet's 2-byte
123
+ // preamble only snapshots the high 9 bits, so resetting per packet would lose up
124
+ // to 127 LSB. State (predictor + step index) is carried on hdr across chunks.
125
+ function decodeIma4(raw, hdr) {
126
+ let nCh = hdr.channelsPerFrame, fpp = hdr.framesPerPacket || 64
127
+ let pktBytes = 34 * nCh, nPk = Math.floor(raw.length / pktBytes)
128
+ if (!nPk) return []
129
+ let st = (hdr.ima ||= { pred: new Int32Array(nCh), step: new Int32Array(nCh), started: false })
130
+ let init = !st.started
131
+ let ch = Array.from({ length: nCh }, () => new Float32Array(nPk * fpp))
132
+ for (let pk = 0; pk < nPk; pk++) {
133
+ for (let c = 0; c < nCh; c++) {
134
+ let bp = pk * pktBytes + c * 34
135
+ if (init && pk === 0) {
136
+ let pre = (raw[bp] << 8) | raw[bp + 1]
137
+ let p = pre & 0xFF80; if (p > 32767) p -= 65536
138
+ st.pred[c] = p
139
+ st.step[c] = Math.min(pre & 0x7F, 88)
140
+ }
141
+ let pred = st.pred[c], ix = st.step[c], out = ch[c], base = pk * fpp
142
+ for (let i = 0; i < 32; i++) {
143
+ let byte = raw[bp + 2 + i]
144
+ for (let h = 0; h < 2; h++) {
145
+ let nib = h === 0 ? byte & 0x0F : (byte >> 4) & 0x0F
146
+ let step = IMA_STEP[ix], diff = step >> 3
147
+ if (nib & 1) diff += step >> 2
148
+ if (nib & 2) diff += step >> 1
149
+ if (nib & 4) diff += step
150
+ pred += (nib & 8) ? -diff : diff
151
+ if (pred > 32767) pred = 32767; else if (pred < -32768) pred = -32768
152
+ ix += IMA_IDX[nib]; if (ix < 0) ix = 0; else if (ix > 88) ix = 88
153
+ out[base + i * 2 + h] = pred / 32768
154
+ }
155
+ }
156
+ st.pred[c] = pred; st.step[c] = ix
157
+ }
158
+ }
159
+ st.started = true
160
+ return ch
161
+ }
162
+
115
163
  function decodeLPCM(data, flags, bits, ch) {
116
164
  let isFloat = flags & 1, isLE = flags & 2
117
165
  let bytesPerSample = bits >> 3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audio/decode-caf",
3
- "version": "1.1.2",
3
+ "version": "1.2.1",
4
4
  "description": "Decode CAF (Core Audio Format) audio to PCM samples",
5
5
  "type": "module",
6
6
  "main": "decode-caf.js",