@audio/decode 3.11.0 → 3.11.2
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/audio-decode.js +24 -14
- package/package.json +3 -2
- package/readme.md +2 -0
package/audio-decode.js
CHANGED
|
@@ -77,7 +77,7 @@ export async function* decodeChunked(source, format) {
|
|
|
77
77
|
|
|
78
78
|
// --- format registration ---
|
|
79
79
|
|
|
80
|
-
function reg(name, load) {
|
|
80
|
+
function reg(name, load, dedupe) {
|
|
81
81
|
decode[name] = fmt(name, async () => {
|
|
82
82
|
let mod = await load()
|
|
83
83
|
// @audio/* packages export { decoder, default }
|
|
@@ -86,7 +86,8 @@ function reg(name, load) {
|
|
|
86
86
|
return streamDecoder(
|
|
87
87
|
chunk => codec.decode(chunk),
|
|
88
88
|
codec.flush ? () => codec.flush() : null,
|
|
89
|
-
codec.free ? () => codec.free() : null
|
|
89
|
+
codec.free ? () => codec.free() : null,
|
|
90
|
+
dedupe
|
|
90
91
|
)
|
|
91
92
|
}
|
|
92
93
|
// wasm-audio-decoders export class with .ready
|
|
@@ -96,7 +97,8 @@ function reg(name, load) {
|
|
|
96
97
|
return streamDecoder(
|
|
97
98
|
chunk => codec.decode(chunk),
|
|
98
99
|
codec.flush ? () => codec.flush() : null,
|
|
99
|
-
codec.free ? () => codec.free() : null
|
|
100
|
+
codec.free ? () => codec.free() : null,
|
|
101
|
+
dedupe
|
|
100
102
|
)
|
|
101
103
|
})
|
|
102
104
|
}
|
|
@@ -121,23 +123,25 @@ function fmt(name, init) {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
// --- codecs ---
|
|
126
|
+
// dedupe (3rd arg): decoder upmixes mono sources to duplicate stereo (verified for mp3;
|
|
127
|
+
// aac/wma flagged conservatively — same lossy-wasm family). Exact containers never dedupe.
|
|
124
128
|
|
|
125
|
-
reg('mp3', () => import('@audio/decode-mp3'))
|
|
129
|
+
reg('mp3', () => import('@audio/decode-mp3'), true)
|
|
126
130
|
reg('flac', () => import('@audio/decode-flac'))
|
|
127
131
|
reg('opus', () => import('@audio/decode-opus'))
|
|
128
132
|
reg('oga', () => import('@audio/decode-vorbis'))
|
|
129
133
|
|
|
130
|
-
reg('m4a', () => import('@audio/decode-aac'))
|
|
134
|
+
reg('m4a', () => import('@audio/decode-aac'), true)
|
|
131
135
|
|
|
132
136
|
reg('wav', () => import('@audio/decode-wav'))
|
|
133
137
|
reg('qoa', () => import('@audio/decode-qoa'))
|
|
134
138
|
|
|
135
|
-
reg('aac', () => import('@audio/decode-aac'))
|
|
139
|
+
reg('aac', () => import('@audio/decode-aac'), true)
|
|
136
140
|
reg('aiff', () => import('@audio/decode-aiff'))
|
|
137
141
|
reg('caf', () => import('@audio/decode-caf'))
|
|
138
142
|
reg('webm', () => import('@audio/decode-webm'))
|
|
139
143
|
reg('amr', () => import('@audio/decode-amr'))
|
|
140
|
-
reg('wma', () => import('@audio/decode-wma'))
|
|
144
|
+
reg('wma', () => import('@audio/decode-wma'), true)
|
|
141
145
|
|
|
142
146
|
/**
|
|
143
147
|
* StreamDecoder — a callable function:
|
|
@@ -146,26 +150,30 @@ reg('wma', () => import('@audio/decode-wma'))
|
|
|
146
150
|
* dec.flush() — flush without freeing
|
|
147
151
|
* dec.free() — release resources without flushing
|
|
148
152
|
*/
|
|
149
|
-
function streamDecoder(onDecode, onFlush, onFree) {
|
|
153
|
+
function streamDecoder(onDecode, onFlush, onFree, dedupe) {
|
|
150
154
|
let done = false
|
|
155
|
+
// dedupe: this codec's decoder upmixes mono sources to duplicate stereo — collapse it
|
|
156
|
+
// back. Sticky per stream: the moment channels genuinely differ, never collapse again
|
|
157
|
+
// (a real stereo file with a dual-mono passage must not flicker channel count).
|
|
158
|
+
let dd = dedupe ? { live: true } : null
|
|
151
159
|
let fn = async (chunk) => {
|
|
152
160
|
if (chunk) {
|
|
153
161
|
if (done) throw Error('Decoder already freed')
|
|
154
|
-
try { return norm(await onDecode(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk))) }
|
|
162
|
+
try { return norm(await onDecode(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)), dd) }
|
|
155
163
|
catch (e) { done = true; onFree?.(); throw e }
|
|
156
164
|
}
|
|
157
165
|
// null/undefined = end of stream
|
|
158
166
|
if (done) return EMPTY
|
|
159
167
|
done = true
|
|
160
168
|
try {
|
|
161
|
-
let result = onFlush ? norm(await onFlush()) : EMPTY
|
|
169
|
+
let result = onFlush ? norm(await onFlush(), dd) : EMPTY
|
|
162
170
|
onFree?.()
|
|
163
171
|
return result
|
|
164
172
|
} catch (e) { onFree?.(); throw e }
|
|
165
173
|
}
|
|
166
174
|
fn.flush = async () => {
|
|
167
175
|
if (done) return EMPTY
|
|
168
|
-
return onFlush ? norm(await onFlush()) : EMPTY
|
|
176
|
+
return onFlush ? norm(await onFlush(), dd) : EMPTY
|
|
169
177
|
}
|
|
170
178
|
fn.free = () => {
|
|
171
179
|
if (done) return
|
|
@@ -176,17 +184,19 @@ function streamDecoder(onDecode, onFlush, onFree) {
|
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
// extract { channelData, sampleRate } from codec result
|
|
179
|
-
function norm(r) {
|
|
187
|
+
function norm(r, dd) {
|
|
180
188
|
if (!r?.channelData?.length) return EMPTY
|
|
181
189
|
let { channelData, sampleRate, samplesDecoded } = r
|
|
182
190
|
if (samplesDecoded != null && samplesDecoded < channelData[0].length)
|
|
183
191
|
channelData = channelData.map(ch => ch.subarray(0, samplesDecoded))
|
|
184
192
|
if (!channelData[0]?.length) return EMPTY
|
|
185
|
-
// collapse duplicate stereo to mono
|
|
186
|
-
|
|
193
|
+
// collapse duplicate stereo to mono — only for codecs flagged as mono-upmixing;
|
|
194
|
+
// exact containers (wav/flac/…) keep their declared width (dual-mono is legitimate)
|
|
195
|
+
if (dd?.live && channelData.length === 2) {
|
|
187
196
|
let a = channelData[0], b = channelData[1], same = true
|
|
188
197
|
for (let i = 0; i < a.length; i += 37) { if (a[i] !== b[i]) { same = false; break } }
|
|
189
198
|
if (same) channelData = [a]
|
|
199
|
+
else dd.live = false
|
|
190
200
|
}
|
|
191
201
|
return { channelData, sampleRate }
|
|
192
202
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@audio/decode",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.2",
|
|
4
4
|
"description": "Decode audio data in node or browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -92,5 +92,6 @@
|
|
|
92
92
|
},
|
|
93
93
|
"engines": {
|
|
94
94
|
"node": ">=18"
|
|
95
|
-
}
|
|
95
|
+
},
|
|
96
|
+
"funding": "https://github.com/sponsors/audiojs"
|
|
96
97
|
}
|
package/readme.md
CHANGED
|
@@ -151,4 +151,6 @@ worker.onmessage = ({ data }) => { /* { channelData, sampleRate } */ }
|
|
|
151
151
|
* [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) – full encoding/decoding library.
|
|
152
152
|
-->
|
|
153
153
|
|
|
154
|
+
<p align="center">Licensing: the umbrella and most codec atoms are MIT; three atoms inherit their upstream codec implementation's license — <a href="./packages/decode-aac">@audio/decode-aac</a> GPL-2.0, <a href="./packages/decode-wma">@audio/decode-wma</a> GPL-2.0-or-later, <a href="./packages/decode-amr">@audio/decode-amr</a> Apache-2.0. Install only the codecs whose licenses fit your project — the umbrella loads them on demand.</p>
|
|
155
|
+
|
|
154
156
|
<p align="center"><a href="./LICENSE">MIT</a> • <a href="https://github.com/krishnized/license/">ॐ</a>
|