@audio/decode-aac 1.0.2 → 1.1.0

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/decode-aac.js CHANGED
@@ -61,6 +61,9 @@ class AACDecoder {
61
61
  this._ptr = 0
62
62
  this._cap = 0
63
63
  this._left = null
64
+ this._m4a = null // { sizes: number[], idx: number } — M4A streaming state
65
+ this._accum = null // Uint8Array[] — M4A header accumulator
66
+ this._accumLen = 0
64
67
  }
65
68
 
66
69
  decode(data) {
@@ -69,15 +72,37 @@ class AACDecoder {
69
72
 
70
73
  let buf = data instanceof Uint8Array ? data : new Uint8Array(data)
71
74
 
72
- // detect M4A (ftyp box at offset 4)
73
- if (buf.length > 8 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70)
74
- return this._decodeM4A(buf)
75
+ // M4A streming phase 2: extract frames by known sizes
76
+ if (this._m4a) return this._feedM4AData(buf)
77
+
78
+ // M4A accumulating: waiting for moov + mdat header
79
+ if (this._accum) {
80
+ this._accum.push(buf)
81
+ this._accumLen += buf.length
82
+ return this._tryM4AInit()
83
+ }
84
+
85
+ // ADTS mode (already initialized)
86
+ if (this.h) return this._decodeADTS(buf)
87
+
88
+ // First call: detect format
89
+ if (buf.length > 8 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) {
90
+ this._accum = [buf]
91
+ this._accumLen = buf.length
92
+ return this._tryM4AInit()
93
+ }
75
94
 
76
95
  return this._decodeADTS(buf)
77
96
  }
78
97
 
79
98
  flush() {
80
- if (this._left) this._left = null
99
+ if (this._accum?.length) {
100
+ // M4A moov-last: one-shot decode on accumulated data
101
+ let buf = this._catAccum()
102
+ this._accum = null; this._accumLen = 0
103
+ return this._decodeM4A(buf)
104
+ }
105
+ this._left = null
81
106
  return EMPTY
82
107
  }
83
108
 
@@ -94,6 +119,86 @@ class AACDecoder {
94
119
  this._ptr = 0
95
120
  this._cap = 0
96
121
  }
122
+ this._accum = null; this._accumLen = 0
123
+ this._m4a = null; this._left = null
124
+ }
125
+
126
+ _catAccum() {
127
+ if (this._accum.length === 1) return this._accum[0]
128
+ let buf = new Uint8Array(this._accumLen), off = 0
129
+ for (let c of this._accum) { buf.set(c, off); off += c.length }
130
+ return buf
131
+ }
132
+
133
+ _tryM4AInit() {
134
+ let buf = this._catAccum()
135
+ let asc = null, stsz = null, stco = null, stsc = null
136
+ let mdatOff = 0, mdatLen = 0
137
+
138
+ parseBoxes(buf, 0, buf.length, (type, data, off) => {
139
+ if (type === 'esds') asc = parseEsds(data)
140
+ else if (type === 'stsz') stsz = parseStsz(data)
141
+ else if (type === 'stco') stco = parseStco(data)
142
+ else if (type === 'co64') stco = parseCo64(data)
143
+ else if (type === 'stsc') stsc = parseStsc(data)
144
+ else if (type === 'mdat') { mdatOff = off; mdatLen = data.length }
145
+ })
146
+
147
+ if (!asc) return EMPTY // moov not found yet
148
+
149
+ // Init WASM decoder with ASC
150
+ let m = this.m, h = m._aac_create()
151
+ let srP = m._aac_sr_ptr(), chP = m._aac_ch_ptr()
152
+ let ptr = this._alloc(asc.length)
153
+ m.HEAPU8.set(asc, ptr)
154
+ let err = m._aac_init2(h, ptr, asc.length, srP, chP)
155
+ if (err < 0) { m._aac_close(h); throw Error('M4A init failed (code ' + err + ')') }
156
+ this.sr = m.getValue(srP, 'i32')
157
+ this.ch = m.getValue(chP, 'i8')
158
+ if (!this.ch) { m._aac_close(h); throw Error('M4A init: no channels in ASC') }
159
+ this.h = h
160
+
161
+ // Extract available frames using stco (correct absolute offsets)
162
+ let frames = (stsz && stco)
163
+ ? extractFrames(buf, stsz, stco, stsc)
164
+ : mdatLen ? scanMdat(buf, mdatOff, mdatLen) : []
165
+
166
+ let decodedIdx = frames.length
167
+ this._m4a = { sizes: stsz || [], idx: decodedIdx }
168
+ this._accum = null; this._accumLen = 0
169
+
170
+ // For streaming: compute leftover mdat data after extracted frames
171
+ if (stsz && decodedIdx < stsz.length && decodedIdx > 0 && stco?.length) {
172
+ let consumed = 0
173
+ for (let i = 0; i < decodedIdx; i++) consumed += stsz[i]
174
+ let dataStart = stco[0] + consumed
175
+ if (dataStart < buf.length) this._left = buf.subarray(dataStart).slice()
176
+ }
177
+
178
+ if (!frames.length) return EMPTY
179
+ return this._feedFrames(frames)
180
+ }
181
+
182
+ _feedM4AData(buf) {
183
+ let st = this._m4a
184
+ if (this._left) {
185
+ let merged = new Uint8Array(this._left.length + buf.length)
186
+ merged.set(this._left); merged.set(buf, this._left.length)
187
+ buf = merged; this._left = null
188
+ }
189
+
190
+ let frames = [], pos = 0
191
+ while (st.idx < st.sizes.length) {
192
+ let sz = st.sizes[st.idx]
193
+ if (pos + sz > buf.length) break
194
+ frames.push(buf.subarray(pos, pos + sz))
195
+ pos += sz
196
+ st.idx++
197
+ }
198
+
199
+ if (pos < buf.length) this._left = buf.subarray(pos).slice()
200
+ if (!frames.length) return EMPTY
201
+ return this._feedFrames(frames)
97
202
  }
98
203
 
99
204
  _alloc(len) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audio/decode-aac",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Decode AAC/M4A audio via FAAD2 WASM",
5
5
  "type": "module",
6
6
  "main": "decode-aac.js",
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "bash build.sh",
17
+ "prepack": "npm run build",
17
18
  "test": "node test.js"
18
19
  },
19
20
  "files": [
package/src/aac.wasm.cjs CHANGED
Binary file