@audio/decode-caf 1.3.3 → 1.4.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 +20 -26
- package/decode-caf.d.ts +6 -6
- package/decode-caf.js +12 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @audio/decode-caf
|
|
2
2
|
|
|
3
|
-
Decode Core Audio Format
|
|
4
|
-
|
|
5
|
-
Part of [decode](https://github.com/audiojs/decode).
|
|
3
|
+
Decode Core Audio Format audio to PCM float samples.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -13,44 +11,40 @@ npm i @audio/decode-caf
|
|
|
13
11
|
## Usage
|
|
14
12
|
|
|
15
13
|
```js
|
|
16
|
-
import decode from '@audio/decode-caf'
|
|
17
|
-
|
|
18
|
-
let { channelData, sampleRate } = await decode(cafBuffer)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### Streaming
|
|
14
|
+
import decode, { decoder } from '@audio/decode-caf'
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
import { decoder } from '@audio/decode-caf'
|
|
16
|
+
let { channelData, sampleRate } = decode(cafBytes)
|
|
25
17
|
|
|
26
|
-
let dec =
|
|
18
|
+
let dec = decoder()
|
|
27
19
|
let result = dec.decode(chunk)
|
|
28
20
|
dec.free()
|
|
29
21
|
```
|
|
30
22
|
|
|
23
|
+
`decode()` and `decoder()` are synchronous.
|
|
24
|
+
|
|
31
25
|
## API
|
|
32
26
|
|
|
33
|
-
### `decode(src):
|
|
27
|
+
### `decode(src): AudioData`
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
Decode a complete `Uint8Array` or `ArrayBuffer`.
|
|
36
30
|
|
|
37
|
-
### `decoder():
|
|
31
|
+
### `decoder(): CAFDecoder`
|
|
38
32
|
|
|
39
|
-
|
|
33
|
+
Create a decoder instance.
|
|
40
34
|
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
35
|
+
- `dec.decode(data)`: decode a chunk.
|
|
36
|
+
- `dec.flush()`: discard buffered partial data and return an empty result.
|
|
37
|
+
- `dec.free()`: release resources.
|
|
44
38
|
|
|
45
39
|
## Formats
|
|
46
40
|
|
|
47
|
-
- `lpcm
|
|
48
|
-
- `alaw
|
|
49
|
-
- `ulaw
|
|
50
|
-
- `ima4
|
|
41
|
+
- `lpcm`: 8, 16, 24, and 32-bit signed integer; 32 and 64-bit float; little-endian or big-endian.
|
|
42
|
+
- `alaw`: G.711 A-law.
|
|
43
|
+
- `ulaw`: G.711 µ-law.
|
|
44
|
+
- `ima4`: IMA/QuickTime ADPCM.
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
Other formats (AAC and ALAC packets, as in Apple Loops) throw `CAF: unsupported format '<id>'`.
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
## License
|
|
55
49
|
|
|
56
|
-
|
|
50
|
+
[ॐ](https://github.com/krishnized/license/) · [MIT](./LICENSE)
|
package/decode-caf.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
interface AudioData {
|
|
1
|
+
export interface AudioData {
|
|
2
2
|
channelData: Float32Array[];
|
|
3
3
|
sampleRate: number;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
interface CAFDecoder {
|
|
7
|
-
decode(data: Uint8Array): AudioData;
|
|
7
|
+
decode(data: Uint8Array | ArrayBuffer): AudioData;
|
|
8
8
|
flush(): AudioData;
|
|
9
9
|
free(): void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
export default function decode(src: ArrayBuffer | Uint8Array):
|
|
12
|
+
/** Decode a complete CAF file synchronously. */
|
|
13
|
+
export default function decode(src: ArrayBuffer | Uint8Array): AudioData;
|
|
14
14
|
|
|
15
|
-
/** Create decoder
|
|
16
|
-
export function decoder():
|
|
15
|
+
/** Create a synchronous decoder. */
|
|
16
|
+
export function decoder(): CAFDecoder;
|
package/decode-caf.js
CHANGED
|
@@ -2,31 +2,25 @@
|
|
|
2
2
|
* CAF (Core Audio Format) decoder
|
|
3
3
|
* Decodes CAF containers with lpcm, alaw, ulaw audio to Float32 PCM
|
|
4
4
|
*
|
|
5
|
-
* let { channelData, sampleRate } =
|
|
5
|
+
* let { channelData, sampleRate } = decode(cafbuf)
|
|
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
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @returns {Promise<{channelData: Float32Array[], sampleRate: number}>}
|
|
18
|
-
*/
|
|
19
|
-
export default async function decode(src) {
|
|
20
|
-
let dec = await decoder()
|
|
15
|
+
/** Decode a complete CAF file synchronously. */
|
|
16
|
+
export default function decode(src) {
|
|
17
|
+
let dec = decoder()
|
|
21
18
|
try { return dec.decode(src) }
|
|
22
19
|
finally { dec.free() }
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
* @returns {Promise<{decode(chunk: Uint8Array): {channelData, sampleRate}, flush(), free()}>}
|
|
28
|
-
*/
|
|
29
|
-
export async function decoder() {
|
|
22
|
+
/** Create a synchronous streaming decoder. */
|
|
23
|
+
export function decoder() {
|
|
30
24
|
let hdr = null, left = null, freed = false, dataLeft = Infinity
|
|
31
25
|
return {
|
|
32
26
|
decode(data) {
|
|
@@ -40,9 +34,9 @@ export async function decoder() {
|
|
|
40
34
|
dataLeft = hdr.dataSize
|
|
41
35
|
chunk = chunk.subarray(hdr.dataStart)
|
|
42
36
|
}
|
|
43
|
-
if (dataLeft <= 0) return EMPTY //
|
|
37
|
+
if (dataLeft <= 0) return EMPTY // Ignore chunks after the declared audio data.
|
|
44
38
|
let fb = hdr.frameBytes
|
|
45
|
-
let avail = Math.min(chunk.length, dataLeft) //
|
|
39
|
+
let avail = Math.min(chunk.length, dataLeft) // Do not read past the declared data size.
|
|
46
40
|
let complete = Math.floor(avail / fb) * fb
|
|
47
41
|
if (!complete) { if (chunk.length && dataLeft > chunk.length) left = chunk.slice(); return EMPTY }
|
|
48
42
|
dataLeft -= complete
|
|
@@ -83,6 +77,8 @@ function scanCafHdr(buf) {
|
|
|
83
77
|
channelsPerFrame: dv.getUint32(off + 24, false),
|
|
84
78
|
bitsPerChannel: dv.getUint32(off + 28, false),
|
|
85
79
|
}
|
|
80
|
+
// compressed formats (aac, alac, ...) carry packets this decoder can't read: say so, don't wait forever
|
|
81
|
+
if (!FORMATS.has(desc.formatID)) throw Error(`CAF: unsupported format '${desc.formatID}'`)
|
|
86
82
|
} else if (type === 'data') {
|
|
87
83
|
if (!desc) return null
|
|
88
84
|
let dataStart = off + 4 // skip editCount
|
|
@@ -95,7 +91,7 @@ function scanCafHdr(buf) {
|
|
|
95
91
|
if (formatID === 'alaw' || formatID === 'ulaw') frameBytes = ch
|
|
96
92
|
else if (formatID === 'ima4') frameBytes = desc.bytesPerPacket || 34 * ch // packet = 34 bytes/channel
|
|
97
93
|
else frameBytes = ch * bytesPerSample
|
|
98
|
-
if (!frameBytes)
|
|
94
|
+
if (!frameBytes) throw Error(`CAF: invalid ${formatID} description (${ch} channels, ${bits} bits)`)
|
|
99
95
|
return { ...desc, dataStart, frameBytes, dataSize }
|
|
100
96
|
}
|
|
101
97
|
if (size < 0) break
|