@audio/decode-aiff 1.2.3 → 1.3.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
@@ -1,7 +1,6 @@
1
1
  # @audio/decode-aiff
2
2
 
3
- Decode AIFF and AIFF-C audio to PCM float samples.<br>
4
- Part of [decode](https://github.com/audiojs/decode).
3
+ Decode AIFF and AIFF-C audio to PCM float samples.
5
4
 
6
5
  ## Install
7
6
 
@@ -12,42 +11,36 @@ npm i @audio/decode-aiff
12
11
  ## Usage
13
12
 
14
13
  ```js
15
- import decode from '@audio/decode-aiff'
14
+ import decode, { decoder } from '@audio/decode-aiff'
16
15
 
17
- let { channelData, sampleRate } = await decode(aiffBuffer)
18
- // channelData: Float32Array[] (one per channel)
19
- // sampleRate: number
20
- ```
21
-
22
- ### Streaming
16
+ let { channelData, sampleRate } = decode(aiffBytes)
23
17
 
24
- ```js
25
- import { decoder } from '@audio/decode-aiff'
26
-
27
- let dec = await decoder()
18
+ let dec = decoder()
28
19
  let result = dec.decode(chunk)
29
20
  dec.free()
30
21
  ```
31
22
 
23
+ `decode()` and `decoder()` are synchronous.
24
+
32
25
  ## API
33
26
 
34
- ### `decode(src): Promise<AudioData>`
27
+ ### `decode(src): AudioData`
35
28
 
36
- Whole-file decode. Accepts `Uint8Array` or `ArrayBuffer`.
29
+ Decode a complete `Uint8Array` or `ArrayBuffer`.
37
30
 
38
- ### `decoder(): Promise<AIFFDecoder>`
31
+ ### `decoder(): AIFFDecoder`
39
32
 
40
- Creates a decoder instance.
33
+ Create a decoder instance.
41
34
 
42
- - **`dec.decode(data)`** — decode chunk, returns `{ channelData, sampleRate }`
43
- - **`dec.flush()`** — flush (returns empty — AIFF is stateless)
44
- - **`dec.free()`** — release resources
35
+ - `dec.decode(data)`: decode a chunk.
36
+ - `dec.flush()`: decode buffered `ima4` or `GSM` data; otherwise return an empty result.
37
+ - `dec.free()`: release resources.
45
38
 
46
39
  ## Formats
47
40
 
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`
41
+ - AIFF: 8, 16, 24, and 32-bit signed big-endian PCM.
42
+ - AIFF-C: `NONE`/`twos`, `sowt`, `fl32`, `fl64`, `alaw`, `ulaw`, `ima4`, and `GSM`.
50
43
 
51
44
  ## License
52
45
 
53
- MIT — [krishnized](https://github.com/krishnized/license)
46
+ [ॐ](https://github.com/krishnized/license/) · [MIT](./LICENSE)
package/decode-aiff.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 AIFFDecoder {
7
- decode(data: Uint8Array): AudioData;
7
+ decode(data: Uint8Array | ArrayBuffer): AudioData;
8
8
  flush(): AudioData;
9
9
  free(): void;
10
10
  }
11
11
 
12
- /** Decode AIFF/AIFF-C audio buffer to PCM samples */
13
- export default function decode(src: ArrayBuffer | Uint8Array): Promise<AudioData>;
12
+ /** Decode a complete AIFF or AIFF-C file synchronously. */
13
+ export default function decode(src: ArrayBuffer | Uint8Array): AudioData;
14
14
 
15
- /** Create decoder instance */
16
- export function decoder(): Promise<AIFFDecoder>;
15
+ /** Create a synchronous decoder. */
16
+ export function decoder(): AIFFDecoder;
package/decode-aiff.js CHANGED
@@ -292,8 +292,12 @@ function asr(x, n) {
292
292
  }
293
293
 
294
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)]
295
+ let blocks = Math.floor(Math.min(ssndSize, b.length - ssndOff) / 33)
296
+ // COMM counts samples (libsndfile, so Audacity), or blocks as Apple counts ima4
297
+ let len = nFrames > blocks ? Math.min(nFrames, blocks * 160) : nFrames * 160
298
+ let totalFrames = Math.ceil(len / 160)
299
+ // the last block's padding past len falls off the array (typed arrays drop out-of-range writes)
300
+ let channelData = [new Float32Array(len)]
297
301
  let out = channelData[0], p = ssndOff
298
302
 
299
303
  // Decoder state (mirrors struct gsm_state)
@@ -372,9 +376,9 @@ function decodeGSM(b, ssndOff, ssndSize, nCh, nFrames, sr) {
372
376
  return { channelData, sampleRate: sr }
373
377
  }
374
378
 
375
- // GSM 6.10 bit unpacking: 33 bytes → LARc[8] + 4 subframes
379
+ // GSM 6.10 bit unpacking: 33 bytes = 0xD signature nibble + 260 bits of LARc[8] and 4 subframes
376
380
  function gsmBits(buf, off) {
377
- let bc = 0
381
+ let bc = 4
378
382
  function r(n) {
379
383
  let val = 0
380
384
  for (let i = 0; i < n; i++) {
@@ -441,7 +445,7 @@ function gsmRpeDecode(xmaxc, Mc, xMc, erp) {
441
445
  // 4.2.16: APCM inverse quantization
442
446
  let fac = GSM_FAC[mant]
443
447
  let shift = 6 - exp
444
- let round = shift > 1 ? 1 << (shift - 1) : 0 // gsm_asl(1, shift-1)
448
+ let round = shift > 0 ? 1 << (shift - 1) : 0 // gsm_asl(1, shift-1)
445
449
 
446
450
  let xMp = new Int32Array(13)
447
451
  for (let i = 0; i < 13; i++) {
@@ -456,14 +460,10 @@ function gsmRpeDecode(xmaxc, Mc, xMc, erp) {
456
460
  for (let i = 0; i < 13; i++) erp[Mc + 3 * i] = xMp[i]
457
461
  }
458
462
 
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) {
463
+ /** Decode a complete AIFF file synchronously. */
464
+ export default function decode(src) {
465
465
  let buf = src instanceof Uint8Array ? src : src instanceof ArrayBuffer ? new Uint8Array(src) : src
466
- let dec = await decoder()
466
+ let dec = decoder()
467
467
  try {
468
468
  let result = dec.decode(buf)
469
469
  if (!result.channelData.length) result = dec.flush()
@@ -473,27 +473,26 @@ export default async function decode(src) {
473
473
  }
474
474
  }
475
475
 
476
- /**
477
- * Create decoder instance
478
- * @returns {Promise<{decode(chunk: Uint8Array): {channelData, sampleRate}, flush(), free()}>}
479
- */
480
- export async function decoder() {
481
- let hdr = null, left = null, freed = false
476
+ /** Create a synchronous decoder. */
477
+ export function decoder() {
478
+ let hdr = null, left = null, deferredChunks = null, deferredLength = 0, freed = false
482
479
  return {
483
480
  decode(data) {
484
481
  if (freed) throw Error('Decoder already freed')
485
- if (!data?.length) return EMPTY
482
+ if (!data || !data.byteLength) return EMPTY
486
483
  let chunk = data instanceof Uint8Array ? data : new Uint8Array(data)
487
484
  if (left) { chunk = cat(left, chunk); left = null }
488
485
  if (!hdr) {
489
486
  hdr = scanAiffHdr(chunk)
490
487
  if (!hdr) { left = chunk.slice(); return EMPTY }
491
- // IMA4/GSM: buffer all SSND data, decode on flush
492
- if (hdr.comp === 'ima4' || hdr.comp === 'GSM ' || hdr.comp === 'gsm ') {
493
- left = chunk.slice()
494
- return EMPTY
495
- }
496
- chunk = chunk.subarray(hdr.dataStart)
488
+ deferredChunks = hdr.comp === 'ima4' || hdr.comp === 'GSM ' || hdr.comp === 'gsm ' ? [] : null
489
+ if (!deferredChunks) chunk = chunk.subarray(hdr.dataStart)
490
+ }
491
+ // IMA4 and GSM need the complete container.
492
+ if (deferredChunks) {
493
+ deferredChunks.push(new Uint8Array(chunk))
494
+ deferredLength += chunk.length
495
+ return EMPTY
497
496
  }
498
497
  let fb = hdr.frameBytes
499
498
  let complete = Math.floor(chunk.length / fb) * fb
@@ -502,15 +501,16 @@ export async function decoder() {
502
501
  return decodeAiffRaw(chunk.subarray(0, complete), hdr)
503
502
  },
504
503
  flush() {
505
- if (hdr && left && (hdr.comp === 'ima4' || hdr.comp === 'GSM ' || hdr.comp === 'gsm ')) {
506
- let result = parseAiff(left)
507
- left = null
508
- return result
504
+ if (deferredChunks && deferredLength) {
505
+ let input = new Uint8Array(deferredLength), offset = 0
506
+ for (let chunk of deferredChunks) { input.set(chunk, offset); offset += chunk.length }
507
+ deferredChunks = []; deferredLength = 0
508
+ return parseAiff(input)
509
509
  }
510
510
  left = null
511
511
  return EMPTY
512
512
  },
513
- free() { freed = true; left = null; hdr = null },
513
+ free() { freed = true; left = null; hdr = null; deferredChunks = null; deferredLength = 0 },
514
514
  }
515
515
  }
516
516
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audio/decode-aiff",
3
- "version": "1.2.3",
3
+ "version": "1.3.1",
4
4
  "description": "Decode AIFF/AIFF-C audio to PCM samples",
5
5
  "type": "module",
6
6
  "main": "decode-aiff.js",