@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 +2 -1
- package/decode-caf.js +48 -0
- package/package.json +1 -1
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 [
|
|
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
|