decibri 5.2.4 → 5.3.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/CHANGELOG.md +16 -0
- package/examples/README.md +9 -4
- package/examples/decibri.browser.js +1 -1
- package/index.d.ts +99 -0
- package/index.js +52 -52
- package/package.json +5 -5
- package/src/browser/decibri-browser.js +1 -1
- package/src/decibri.d.ts +143 -0
- package/src/decibri.js +127 -0
- package/src/errors.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,22 @@ For other decibri packages, see:
|
|
|
9
9
|
- Rust core: [crates/decibri/CHANGELOG.md](../../crates/decibri/CHANGELOG.md)
|
|
10
10
|
- Python package: [bindings/python/CHANGELOG.md](../../bindings/python/CHANGELOG.md)
|
|
11
11
|
|
|
12
|
+
## [5.3.0] - 2026-07-31
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `aec` option on `Microphone`: acoustic echo cancellation on the capture path. The short form names the model (`aec: 'tau'`); the object form takes `{ model, tailMs, suppression, referenceSampleRate }`. It runs before the detector tap, so `vadScore` and the `speech` / `silence` events read the echo-removed signal, and it requires `sampleRate` in 8000 to 48000. Native capture only: the browser entry keeps the platform's own `echoCancellation` constraint.
|
|
17
|
+
- `Microphone.pushAecReference(data)`, which queues the far-end audio the canceller cancels against: the same input shapes `Speaker.write` accepts, mono, in played order, at the declared `referenceSampleRate`. It never blocks and never throws on a full queue. When it is pushed does not have to match when it plays: the queue is read at the rate the capture consumes it, so a greeting pushed before the first `data` event, or a whole utterance handed over in one call, is read out over the capture it echoes into and every sample of it is cancelled against. The queue holds two seconds, which bounds how far ahead of its own capture a caller may run.
|
|
18
|
+
- `Microphone.aecMetrics()`, the canceller's transport and cancellation metrics merged with the reference queue's counters, or `null` while echo cancellation is off or capture is not running.
|
|
19
|
+
|
|
20
|
+
Echo cancellation joins automatic gain control as a stage that can drive captured samples above full scale when the limiter is off, because it subtracts its estimate of the echo from the capture and exceeds the capture wherever that estimate is wrong in phase. The limiter runs after it and bounds the output to its ceiling; the `int16` sample format clamps, so an over-scale sample arrives as full scale rather than wrapping, and a `float32` consumer without the limiter should clamp its own output.
|
|
21
|
+
|
|
22
|
+
## [5.2.5] - 2026-07-28
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The packaged addon is rebuilt from the current core, and the bundle regenerate command in `examples/README.md` reproduces the committed file.
|
|
27
|
+
|
|
12
28
|
## [5.2.4] - 2026-07-28
|
|
13
29
|
|
|
14
30
|
### Added
|
package/examples/README.md
CHANGED
|
@@ -71,10 +71,15 @@ npx localtunnel --port 8080
|
|
|
71
71
|
### Regenerating the browser bundle
|
|
72
72
|
|
|
73
73
|
`decibri.browser.js` is generated from the package's browser entry
|
|
74
|
-
(
|
|
75
|
-
checked-in build. Regenerate it after changing the browser source so the
|
|
76
|
-
bundle stays in sync
|
|
74
|
+
(`npm/decibri/src/browser/index.js`) with rolldown, the bundler used to produce
|
|
75
|
+
the checked-in build. Regenerate it after changing the browser source so the
|
|
76
|
+
bundle stays in sync.
|
|
77
|
+
|
|
78
|
+
Run the command from the repository root, with both paths repo-root relative.
|
|
79
|
+
Run from anywhere else and the bundle still builds, but it records the module
|
|
80
|
+
paths relative to that directory instead. The `--banner` flag carries the
|
|
81
|
+
generated file's first line:
|
|
77
82
|
|
|
78
83
|
```bash
|
|
79
|
-
npx rolldown
|
|
84
|
+
npx rolldown npm/decibri/src/browser/index.js --format iife --name decibri --file npm/decibri/examples/decibri.browser.js --banner "// decibri browser bundle. GENERATED from src/browser/index.js. Do not edit by hand; see examples/README.md to regenerate."
|
|
80
85
|
```
|
|
@@ -70,7 +70,7 @@ var decibri = (function() {
|
|
|
70
70
|
var require_decibri_browser = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
71
71
|
const { Emitter } = require_emitter();
|
|
72
72
|
const { WORKLET_SOURCE } = require_worklet_inline();
|
|
73
|
-
const VERSION = "5.
|
|
73
|
+
const VERSION = "5.3.0";
|
|
74
74
|
/**
|
|
75
75
|
* Browser microphone capture.
|
|
76
76
|
*
|
package/index.d.ts
CHANGED
|
@@ -29,6 +29,21 @@ export declare class DecibriBridge {
|
|
|
29
29
|
* number for any realistic count) to match the `vadProbability` getter.
|
|
30
30
|
*/
|
|
31
31
|
get overrunCount(): number
|
|
32
|
+
/**
|
|
33
|
+
* Queue far-end reference audio for the echo canceller. `buffer` is mono
|
|
34
|
+
* PCM bytes in the bridge's configured format, at the declared reference
|
|
35
|
+
* rate, in played order. Never blocks and never fails: a full queue
|
|
36
|
+
* discards and counts rather than erroring, and a push with no active
|
|
37
|
+
* stream (not started, stopped, or echo cancellation off) is a no-op.
|
|
38
|
+
*/
|
|
39
|
+
pushAecReference(buffer: Buffer): void
|
|
40
|
+
/**
|
|
41
|
+
* The echo canceller's transport and cancellation metrics, merged with the
|
|
42
|
+
* reference queue's own counters, or `null` when no stream is active or
|
|
43
|
+
* echo cancellation is off. Counters are returned as f64 (exact JS numbers
|
|
44
|
+
* for any realistic count), matching the `overrunCount` getter.
|
|
45
|
+
*/
|
|
46
|
+
aecMetrics(): AecMetricsJs | null
|
|
32
47
|
/** List all available audio input devices. */
|
|
33
48
|
static devices(): Array<DeviceInfoJs>
|
|
34
49
|
/** Version information. */
|
|
@@ -148,6 +163,62 @@ export declare class FileHandle {
|
|
|
148
163
|
get inputRate(): number
|
|
149
164
|
}
|
|
150
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Echo-cancellation metrics returned to JS by `aecMetrics()`. One object
|
|
168
|
+
* carries the canceller's own report and the reference queue's counters, so a
|
|
169
|
+
* caller reads one surface for the whole diagnosis.
|
|
170
|
+
*/
|
|
171
|
+
export interface AecMetricsJs {
|
|
172
|
+
/**
|
|
173
|
+
* The active delay alignment in samples, or `null` while the estimator is
|
|
174
|
+
* still searching. Staying `null` while `acquisitionParked` climbs is the
|
|
175
|
+
* signature of a canceller with no usable reference: none pushed, not at
|
|
176
|
+
* the declared rate, or not the signal that produced the echo.
|
|
177
|
+
*/
|
|
178
|
+
delaySamples?: number
|
|
179
|
+
/**
|
|
180
|
+
* Smoothed echo-return-loss-enhancement estimate in dB: how much echo the
|
|
181
|
+
* canceller is currently removing. 0 before the filter has converged.
|
|
182
|
+
*/
|
|
183
|
+
erleDb: number
|
|
184
|
+
/**
|
|
185
|
+
* Whether the double-talk detector currently believes the near-end talker
|
|
186
|
+
* is active; adaptation is held while true.
|
|
187
|
+
*/
|
|
188
|
+
doubleTalk: boolean
|
|
189
|
+
/**
|
|
190
|
+
* Near-end samples the canceller could find no far-end sample for while an
|
|
191
|
+
* alignment was active. The core keeps the far-end stream level with the
|
|
192
|
+
* capture, so this stays 0 for a caller who simply stops pushing; a
|
|
193
|
+
* non-zero count means the caller ran further ahead of the capture than
|
|
194
|
+
* the canceller's far-end history reaches.
|
|
195
|
+
*/
|
|
196
|
+
referenceStarved: number
|
|
197
|
+
/**
|
|
198
|
+
* Near-end samples processed while no delay alignment was active: the
|
|
199
|
+
* searching span, not a transport failure.
|
|
200
|
+
*/
|
|
201
|
+
acquisitionParked: number
|
|
202
|
+
/**
|
|
203
|
+
* Times the canceller inferred a capture discontinuity and rebuilt its
|
|
204
|
+
* alignment from the reference frontier.
|
|
205
|
+
*/
|
|
206
|
+
referenceReanchors: number
|
|
207
|
+
/**
|
|
208
|
+
* Far-end samples discarded because a single push exceeded the reference
|
|
209
|
+
* queue's bound, at the declared reference rate. The span they occupied is
|
|
210
|
+
* still represented as silence, so a discard costs the cancellation of
|
|
211
|
+
* that span alone.
|
|
212
|
+
*/
|
|
213
|
+
referenceDropped: number
|
|
214
|
+
/**
|
|
215
|
+
* Far-end samples the core supplied as silence because the caller had
|
|
216
|
+
* pushed none for them, at the capture rate. An accounting figure, not a
|
|
217
|
+
* fault: while nothing is playing, the far end is silence.
|
|
218
|
+
*/
|
|
219
|
+
referenceSilence: number
|
|
220
|
+
}
|
|
221
|
+
|
|
151
222
|
/**
|
|
152
223
|
* Options passed from JS constructor.
|
|
153
224
|
*
|
|
@@ -215,6 +286,34 @@ export interface DecibriOptions {
|
|
|
215
286
|
* DSP: no bundled file and no model path, like `agc`.
|
|
216
287
|
*/
|
|
217
288
|
limiter?: number
|
|
289
|
+
/**
|
|
290
|
+
* Echo canceller model name. The accepted set is owned by the canceller
|
|
291
|
+
* (`AecModel::from_str`); today it is `'tau'`. Absent leaves echo
|
|
292
|
+
* cancellation off. The JS wrapper resolves both public forms (the string
|
|
293
|
+
* shorthand and the `AecOptions` object) into this field and the three
|
|
294
|
+
* below. Pure DSP: no bundled file and no model path, like `highpass`.
|
|
295
|
+
*/
|
|
296
|
+
aec?: string
|
|
297
|
+
/**
|
|
298
|
+
* Echo canceller filter tail in milliseconds: an integer in `16..=500`.
|
|
299
|
+
* Absent takes the canceller's own default. Consulted only when `aec`
|
|
300
|
+
* names a model.
|
|
301
|
+
*/
|
|
302
|
+
aecTailMs?: number
|
|
303
|
+
/**
|
|
304
|
+
* Residual-suppression policy for the echo canceller: `'conservative'` or
|
|
305
|
+
* `'off'`. Absent takes the canceller's own default. Consulted only when
|
|
306
|
+
* `aec` names a model.
|
|
307
|
+
*/
|
|
308
|
+
aecSuppression?: string
|
|
309
|
+
/**
|
|
310
|
+
* Sample rate in Hz of the far-end reference pushed through
|
|
311
|
+
* `pushAecReference`, in `1000..=384000`. Absent means the reference is
|
|
312
|
+
* already at the capture rate; when it names a different rate, the core
|
|
313
|
+
* converts the reference before the canceller sees it. Consulted only
|
|
314
|
+
* when `aec` names a model.
|
|
315
|
+
*/
|
|
316
|
+
aecReferenceSampleRate?: number
|
|
218
317
|
}
|
|
219
318
|
|
|
220
319
|
/** Options passed from JS constructor for output. */
|
package/index.js
CHANGED
|
@@ -77,8 +77,8 @@ function requireNative() {
|
|
|
77
77
|
try {
|
|
78
78
|
const binding = require('@decibri/decibri-android-arm64')
|
|
79
79
|
const bindingPackageVersion = require('@decibri/decibri-android-arm64/package.json').version
|
|
80
|
-
if (bindingPackageVersion !== '5.
|
|
81
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
80
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
81
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
82
82
|
}
|
|
83
83
|
return binding
|
|
84
84
|
} catch (e) {
|
|
@@ -93,8 +93,8 @@ function requireNative() {
|
|
|
93
93
|
try {
|
|
94
94
|
const binding = require('@decibri/decibri-android-arm-eabi')
|
|
95
95
|
const bindingPackageVersion = require('@decibri/decibri-android-arm-eabi/package.json').version
|
|
96
|
-
if (bindingPackageVersion !== '5.
|
|
97
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
96
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
97
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
98
98
|
}
|
|
99
99
|
return binding
|
|
100
100
|
} catch (e) {
|
|
@@ -114,8 +114,8 @@ function requireNative() {
|
|
|
114
114
|
try {
|
|
115
115
|
const binding = require('@decibri/decibri-win32-x64-gnu')
|
|
116
116
|
const bindingPackageVersion = require('@decibri/decibri-win32-x64-gnu/package.json').version
|
|
117
|
-
if (bindingPackageVersion !== '5.
|
|
118
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
117
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
118
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
119
119
|
}
|
|
120
120
|
return binding
|
|
121
121
|
} catch (e) {
|
|
@@ -130,8 +130,8 @@ function requireNative() {
|
|
|
130
130
|
try {
|
|
131
131
|
const binding = require('@decibri/decibri-win32-x64-msvc')
|
|
132
132
|
const bindingPackageVersion = require('@decibri/decibri-win32-x64-msvc/package.json').version
|
|
133
|
-
if (bindingPackageVersion !== '5.
|
|
134
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
133
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
134
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
135
135
|
}
|
|
136
136
|
return binding
|
|
137
137
|
} catch (e) {
|
|
@@ -147,8 +147,8 @@ function requireNative() {
|
|
|
147
147
|
try {
|
|
148
148
|
const binding = require('@decibri/decibri-win32-ia32-msvc')
|
|
149
149
|
const bindingPackageVersion = require('@decibri/decibri-win32-ia32-msvc/package.json').version
|
|
150
|
-
if (bindingPackageVersion !== '5.
|
|
151
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
150
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
151
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
152
152
|
}
|
|
153
153
|
return binding
|
|
154
154
|
} catch (e) {
|
|
@@ -163,8 +163,8 @@ function requireNative() {
|
|
|
163
163
|
try {
|
|
164
164
|
const binding = require('@decibri/decibri-win32-arm64-msvc')
|
|
165
165
|
const bindingPackageVersion = require('@decibri/decibri-win32-arm64-msvc/package.json').version
|
|
166
|
-
if (bindingPackageVersion !== '5.
|
|
167
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
166
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
167
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
168
168
|
}
|
|
169
169
|
return binding
|
|
170
170
|
} catch (e) {
|
|
@@ -182,8 +182,8 @@ function requireNative() {
|
|
|
182
182
|
try {
|
|
183
183
|
const binding = require('@decibri/decibri-darwin-universal')
|
|
184
184
|
const bindingPackageVersion = require('@decibri/decibri-darwin-universal/package.json').version
|
|
185
|
-
if (bindingPackageVersion !== '5.
|
|
186
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
185
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
186
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
187
187
|
}
|
|
188
188
|
return binding
|
|
189
189
|
} catch (e) {
|
|
@@ -198,8 +198,8 @@ function requireNative() {
|
|
|
198
198
|
try {
|
|
199
199
|
const binding = require('@decibri/decibri-darwin-x64')
|
|
200
200
|
const bindingPackageVersion = require('@decibri/decibri-darwin-x64/package.json').version
|
|
201
|
-
if (bindingPackageVersion !== '5.
|
|
202
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
201
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
202
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
203
203
|
}
|
|
204
204
|
return binding
|
|
205
205
|
} catch (e) {
|
|
@@ -214,8 +214,8 @@ function requireNative() {
|
|
|
214
214
|
try {
|
|
215
215
|
const binding = require('@decibri/decibri-darwin-arm64')
|
|
216
216
|
const bindingPackageVersion = require('@decibri/decibri-darwin-arm64/package.json').version
|
|
217
|
-
if (bindingPackageVersion !== '5.
|
|
218
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
217
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
218
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
219
219
|
}
|
|
220
220
|
return binding
|
|
221
221
|
} catch (e) {
|
|
@@ -234,8 +234,8 @@ function requireNative() {
|
|
|
234
234
|
try {
|
|
235
235
|
const binding = require('@decibri/decibri-freebsd-x64')
|
|
236
236
|
const bindingPackageVersion = require('@decibri/decibri-freebsd-x64/package.json').version
|
|
237
|
-
if (bindingPackageVersion !== '5.
|
|
238
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
237
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
238
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
239
239
|
}
|
|
240
240
|
return binding
|
|
241
241
|
} catch (e) {
|
|
@@ -250,8 +250,8 @@ function requireNative() {
|
|
|
250
250
|
try {
|
|
251
251
|
const binding = require('@decibri/decibri-freebsd-arm64')
|
|
252
252
|
const bindingPackageVersion = require('@decibri/decibri-freebsd-arm64/package.json').version
|
|
253
|
-
if (bindingPackageVersion !== '5.
|
|
254
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
253
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
254
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
255
255
|
}
|
|
256
256
|
return binding
|
|
257
257
|
} catch (e) {
|
|
@@ -271,8 +271,8 @@ function requireNative() {
|
|
|
271
271
|
try {
|
|
272
272
|
const binding = require('@decibri/decibri-linux-x64-musl')
|
|
273
273
|
const bindingPackageVersion = require('@decibri/decibri-linux-x64-musl/package.json').version
|
|
274
|
-
if (bindingPackageVersion !== '5.
|
|
275
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
274
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
275
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
276
276
|
}
|
|
277
277
|
return binding
|
|
278
278
|
} catch (e) {
|
|
@@ -287,8 +287,8 @@ function requireNative() {
|
|
|
287
287
|
try {
|
|
288
288
|
const binding = require('@decibri/decibri-linux-x64-gnu')
|
|
289
289
|
const bindingPackageVersion = require('@decibri/decibri-linux-x64-gnu/package.json').version
|
|
290
|
-
if (bindingPackageVersion !== '5.
|
|
291
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
290
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
291
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
292
292
|
}
|
|
293
293
|
return binding
|
|
294
294
|
} catch (e) {
|
|
@@ -305,8 +305,8 @@ function requireNative() {
|
|
|
305
305
|
try {
|
|
306
306
|
const binding = require('@decibri/decibri-linux-arm64-musl')
|
|
307
307
|
const bindingPackageVersion = require('@decibri/decibri-linux-arm64-musl/package.json').version
|
|
308
|
-
if (bindingPackageVersion !== '5.
|
|
309
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
308
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
309
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
310
310
|
}
|
|
311
311
|
return binding
|
|
312
312
|
} catch (e) {
|
|
@@ -321,8 +321,8 @@ function requireNative() {
|
|
|
321
321
|
try {
|
|
322
322
|
const binding = require('@decibri/decibri-linux-arm64-gnu')
|
|
323
323
|
const bindingPackageVersion = require('@decibri/decibri-linux-arm64-gnu/package.json').version
|
|
324
|
-
if (bindingPackageVersion !== '5.
|
|
325
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
324
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
325
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
326
326
|
}
|
|
327
327
|
return binding
|
|
328
328
|
} catch (e) {
|
|
@@ -339,8 +339,8 @@ function requireNative() {
|
|
|
339
339
|
try {
|
|
340
340
|
const binding = require('@decibri/decibri-linux-arm-musleabihf')
|
|
341
341
|
const bindingPackageVersion = require('@decibri/decibri-linux-arm-musleabihf/package.json').version
|
|
342
|
-
if (bindingPackageVersion !== '5.
|
|
343
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
342
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
343
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
344
344
|
}
|
|
345
345
|
return binding
|
|
346
346
|
} catch (e) {
|
|
@@ -355,8 +355,8 @@ function requireNative() {
|
|
|
355
355
|
try {
|
|
356
356
|
const binding = require('@decibri/decibri-linux-arm-gnueabihf')
|
|
357
357
|
const bindingPackageVersion = require('@decibri/decibri-linux-arm-gnueabihf/package.json').version
|
|
358
|
-
if (bindingPackageVersion !== '5.
|
|
359
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
358
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
359
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
360
360
|
}
|
|
361
361
|
return binding
|
|
362
362
|
} catch (e) {
|
|
@@ -373,8 +373,8 @@ function requireNative() {
|
|
|
373
373
|
try {
|
|
374
374
|
const binding = require('@decibri/decibri-linux-loong64-musl')
|
|
375
375
|
const bindingPackageVersion = require('@decibri/decibri-linux-loong64-musl/package.json').version
|
|
376
|
-
if (bindingPackageVersion !== '5.
|
|
377
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
376
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
377
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
378
378
|
}
|
|
379
379
|
return binding
|
|
380
380
|
} catch (e) {
|
|
@@ -389,8 +389,8 @@ function requireNative() {
|
|
|
389
389
|
try {
|
|
390
390
|
const binding = require('@decibri/decibri-linux-loong64-gnu')
|
|
391
391
|
const bindingPackageVersion = require('@decibri/decibri-linux-loong64-gnu/package.json').version
|
|
392
|
-
if (bindingPackageVersion !== '5.
|
|
393
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
392
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
393
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
394
394
|
}
|
|
395
395
|
return binding
|
|
396
396
|
} catch (e) {
|
|
@@ -407,8 +407,8 @@ function requireNative() {
|
|
|
407
407
|
try {
|
|
408
408
|
const binding = require('@decibri/decibri-linux-riscv64-musl')
|
|
409
409
|
const bindingPackageVersion = require('@decibri/decibri-linux-riscv64-musl/package.json').version
|
|
410
|
-
if (bindingPackageVersion !== '5.
|
|
411
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
410
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
411
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
412
412
|
}
|
|
413
413
|
return binding
|
|
414
414
|
} catch (e) {
|
|
@@ -423,8 +423,8 @@ function requireNative() {
|
|
|
423
423
|
try {
|
|
424
424
|
const binding = require('@decibri/decibri-linux-riscv64-gnu')
|
|
425
425
|
const bindingPackageVersion = require('@decibri/decibri-linux-riscv64-gnu/package.json').version
|
|
426
|
-
if (bindingPackageVersion !== '5.
|
|
427
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
426
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
427
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
428
428
|
}
|
|
429
429
|
return binding
|
|
430
430
|
} catch (e) {
|
|
@@ -440,8 +440,8 @@ function requireNative() {
|
|
|
440
440
|
try {
|
|
441
441
|
const binding = require('@decibri/decibri-linux-ppc64-gnu')
|
|
442
442
|
const bindingPackageVersion = require('@decibri/decibri-linux-ppc64-gnu/package.json').version
|
|
443
|
-
if (bindingPackageVersion !== '5.
|
|
444
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
443
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
444
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
445
445
|
}
|
|
446
446
|
return binding
|
|
447
447
|
} catch (e) {
|
|
@@ -456,8 +456,8 @@ function requireNative() {
|
|
|
456
456
|
try {
|
|
457
457
|
const binding = require('@decibri/decibri-linux-s390x-gnu')
|
|
458
458
|
const bindingPackageVersion = require('@decibri/decibri-linux-s390x-gnu/package.json').version
|
|
459
|
-
if (bindingPackageVersion !== '5.
|
|
460
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
459
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
460
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
461
461
|
}
|
|
462
462
|
return binding
|
|
463
463
|
} catch (e) {
|
|
@@ -476,8 +476,8 @@ function requireNative() {
|
|
|
476
476
|
try {
|
|
477
477
|
const binding = require('@decibri/decibri-openharmony-arm64')
|
|
478
478
|
const bindingPackageVersion = require('@decibri/decibri-openharmony-arm64/package.json').version
|
|
479
|
-
if (bindingPackageVersion !== '5.
|
|
480
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
479
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
480
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
481
481
|
}
|
|
482
482
|
return binding
|
|
483
483
|
} catch (e) {
|
|
@@ -492,8 +492,8 @@ function requireNative() {
|
|
|
492
492
|
try {
|
|
493
493
|
const binding = require('@decibri/decibri-openharmony-x64')
|
|
494
494
|
const bindingPackageVersion = require('@decibri/decibri-openharmony-x64/package.json').version
|
|
495
|
-
if (bindingPackageVersion !== '5.
|
|
496
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
495
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
496
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
497
497
|
}
|
|
498
498
|
return binding
|
|
499
499
|
} catch (e) {
|
|
@@ -508,8 +508,8 @@ function requireNative() {
|
|
|
508
508
|
try {
|
|
509
509
|
const binding = require('@decibri/decibri-openharmony-arm')
|
|
510
510
|
const bindingPackageVersion = require('@decibri/decibri-openharmony-arm/package.json').version
|
|
511
|
-
if (bindingPackageVersion !== '5.
|
|
512
|
-
throw new Error(`Native binding package version mismatch, expected 5.
|
|
511
|
+
if (bindingPackageVersion !== '5.3.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
512
|
+
throw new Error(`Native binding package version mismatch, expected 5.3.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
|
513
513
|
}
|
|
514
514
|
return binding
|
|
515
515
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decibri",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Cross-platform audio capture, playback, and processing for Node.js and browsers",
|
|
5
5
|
"main": "src/decibri.js",
|
|
6
6
|
"types": "src/decibri.d.ts",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"MIGRATION.md"
|
|
73
73
|
],
|
|
74
74
|
"optionalDependencies": {
|
|
75
|
-
"@decibri/decibri-win32-x64-msvc": "5.
|
|
76
|
-
"@decibri/decibri-darwin-arm64": "5.
|
|
77
|
-
"@decibri/decibri-linux-x64-gnu": "5.
|
|
78
|
-
"@decibri/decibri-linux-arm64-gnu": "5.
|
|
75
|
+
"@decibri/decibri-win32-x64-msvc": "5.3.0",
|
|
76
|
+
"@decibri/decibri-darwin-arm64": "5.3.0",
|
|
77
|
+
"@decibri/decibri-linux-x64-gnu": "5.3.0",
|
|
78
|
+
"@decibri/decibri-linux-arm64-gnu": "5.3.0"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@napi-rs/cli": "^3.7.0"
|
|
@@ -6,7 +6,7 @@ const { WORKLET_SOURCE } = require('./worklet-inline.js');
|
|
|
6
6
|
// Browser build version. Keep in sync with package.json on each release; the
|
|
7
7
|
// browser bundle cannot read package.json at runtime the way the Node wrapper
|
|
8
8
|
// does, so this is a maintained constant.
|
|
9
|
-
const VERSION = '5.
|
|
9
|
+
const VERSION = '5.3.0';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Browser microphone capture.
|
package/src/decibri.d.ts
CHANGED
|
@@ -61,6 +61,108 @@ export interface VadOptions {
|
|
|
61
61
|
holdoffMs?: number;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Acoustic echo cancellation config object, passed on the `aec` option to tune
|
|
66
|
+
* the canceller. The bare `aec: 'tau'` shorthand selects the model with its
|
|
67
|
+
* defaults; pass this object to override them.
|
|
68
|
+
*
|
|
69
|
+
* The canceller's behaviour while a lost delay alignment is being reacquired
|
|
70
|
+
* is fixed: decibri applies the canceller's graded output transition and does
|
|
71
|
+
* not expose a setting for it.
|
|
72
|
+
*/
|
|
73
|
+
export interface AecOptions {
|
|
74
|
+
/**
|
|
75
|
+
* Which echo canceller model to run. The accepted set is owned by the
|
|
76
|
+
* canceller and grows the way `denoise` grows; today it is `'tau'`, a
|
|
77
|
+
* classical adaptive canceller with no model file. An unknown name is
|
|
78
|
+
* rejected with the canceller's own message naming the accepted set.
|
|
79
|
+
*/
|
|
80
|
+
model: 'tau';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Adaptive filter tail length in milliseconds: how much echo delay spread
|
|
84
|
+
* the canceller can model.
|
|
85
|
+
* @default the canceller's own default (200)
|
|
86
|
+
* @range 16 to 500
|
|
87
|
+
*/
|
|
88
|
+
tailMs?: number;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Residual echo suppression policy. `'conservative'` attenuates the residual
|
|
92
|
+
* echo the linear canceller leaves behind while keeping the near-end voice
|
|
93
|
+
* intact; `'off'` delivers the linear canceller output as-is.
|
|
94
|
+
* @default 'conservative'
|
|
95
|
+
*/
|
|
96
|
+
suppression?: 'conservative' | 'off';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sample rate in Hz of the far-end reference pushed through
|
|
100
|
+
* `pushAecReference`. When it names a rate other than `sampleRate`, decibri
|
|
101
|
+
* converts the reference before the canceller sees it: a reference at an
|
|
102
|
+
* undeclared different rate cancels nothing and reports no error, so the
|
|
103
|
+
* conversion is decibri's rather than the caller's.
|
|
104
|
+
* @default the capture `sampleRate`
|
|
105
|
+
* @range 1000 to 384000
|
|
106
|
+
*/
|
|
107
|
+
referenceSampleRate?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The echo canceller's transport and cancellation metrics, returned by
|
|
112
|
+
* `Microphone.aecMetrics()`. One object carries the canceller's own report and
|
|
113
|
+
* the reference queue's counters.
|
|
114
|
+
*/
|
|
115
|
+
export interface AecMetrics {
|
|
116
|
+
/**
|
|
117
|
+
* The active delay alignment in samples, or `null` while the estimator is
|
|
118
|
+
* still searching. Staying `null` while `acquisitionParked` climbs is the
|
|
119
|
+
* signature of a canceller with no usable reference: none pushed, not at the
|
|
120
|
+
* declared rate, or not the signal that produced the echo.
|
|
121
|
+
*/
|
|
122
|
+
delaySamples: number | null;
|
|
123
|
+
/**
|
|
124
|
+
* Smoothed echo-return-loss-enhancement estimate in dB: how much echo the
|
|
125
|
+
* canceller is currently removing. 0 before the filter has converged.
|
|
126
|
+
*/
|
|
127
|
+
erleDb: number;
|
|
128
|
+
/**
|
|
129
|
+
* Whether the double-talk detector currently believes the near-end talker is
|
|
130
|
+
* active; adaptation is held while true.
|
|
131
|
+
*/
|
|
132
|
+
doubleTalk: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Near-end samples the canceller could find no far-end sample for while an
|
|
135
|
+
* alignment was active. decibri keeps the far-end stream level with the
|
|
136
|
+
* capture, so this stays 0 for a caller who simply stops pushing; a non-zero
|
|
137
|
+
* count means the caller ran further ahead of the capture than the
|
|
138
|
+
* canceller's far-end history reaches.
|
|
139
|
+
*/
|
|
140
|
+
referenceStarved: number;
|
|
141
|
+
/**
|
|
142
|
+
* Near-end samples processed while no delay alignment was active: the
|
|
143
|
+
* searching span, not a transport failure.
|
|
144
|
+
*/
|
|
145
|
+
acquisitionParked: number;
|
|
146
|
+
/**
|
|
147
|
+
* Times the canceller inferred a capture discontinuity and rebuilt its
|
|
148
|
+
* alignment from the reference frontier.
|
|
149
|
+
*/
|
|
150
|
+
referenceReanchors: number;
|
|
151
|
+
/**
|
|
152
|
+
* Far-end samples discarded because a single push exceeded the reference
|
|
153
|
+
* queue's bound, at the declared reference rate. The span they occupied is
|
|
154
|
+
* still represented as silence, so a discard costs the cancellation of that
|
|
155
|
+
* span alone.
|
|
156
|
+
*/
|
|
157
|
+
referenceDropped: number;
|
|
158
|
+
/**
|
|
159
|
+
* Far-end samples decibri supplied as silence because the caller had pushed
|
|
160
|
+
* none for them, at the capture rate. An accounting figure, not a fault:
|
|
161
|
+
* while nothing is playing, the far end is silence.
|
|
162
|
+
*/
|
|
163
|
+
referenceSilence: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
64
166
|
/** Constructor options for `Microphone`. */
|
|
65
167
|
export interface MicrophoneOptions extends ReadableOptions {
|
|
66
168
|
/**
|
|
@@ -186,6 +288,26 @@ export interface MicrophoneOptions extends ReadableOptions {
|
|
|
186
288
|
* @default undefined
|
|
187
289
|
*/
|
|
188
290
|
limiter?: number;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Acoustic echo cancellation applied to the captured audio, removing the
|
|
294
|
+
* echo of far-end audio the caller pushes through `pushAecReference`. The
|
|
295
|
+
* `'tau'` shorthand names the model; an `AecOptions` object tunes it. Omit
|
|
296
|
+
* to leave echo cancellation off (the default), which keeps the capture
|
|
297
|
+
* path unchanged.
|
|
298
|
+
*
|
|
299
|
+
* Runs before the detector tap: with it on, `vadScore` and the `speech` /
|
|
300
|
+
* `silence` events read the echo-removed signal, so playback stops
|
|
301
|
+
* triggering detection. Requires `sampleRate` in 8000 to 48000, narrower
|
|
302
|
+
* than the range the option otherwise accepts. With no reference pushed,
|
|
303
|
+
* the captured audio passes through unchanged.
|
|
304
|
+
*
|
|
305
|
+
* Native capture only: the browser entry does not take this option, because
|
|
306
|
+
* browser capture already carries the platform's own echo cancellation
|
|
307
|
+
* through its `echoCancellation` constraint, on by default.
|
|
308
|
+
* @default undefined
|
|
309
|
+
*/
|
|
310
|
+
aec?: 'tau' | AecOptions;
|
|
189
311
|
}
|
|
190
312
|
|
|
191
313
|
/**
|
|
@@ -245,6 +367,27 @@ export declare class Microphone extends Readable {
|
|
|
245
367
|
*/
|
|
246
368
|
readonly overrunCount: number;
|
|
247
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Queue far-end reference audio for the echo canceller: the audio being
|
|
372
|
+
* played out, pushed as it is played, in played order. Accepts the same
|
|
373
|
+
* input shapes `Speaker.write` accepts (a `Buffer`, any TypedArray, or a
|
|
374
|
+
* `DataView` of PCM bytes in this microphone's `dtype`), mono, at the
|
|
375
|
+
* declared `referenceSampleRate` (the capture rate when unset).
|
|
376
|
+
*
|
|
377
|
+
* Never blocks and never throws on a full queue: samples that do not fit
|
|
378
|
+
* are discarded and counted by `aecMetrics().referenceDropped`. Silence
|
|
379
|
+
* between played audio need not be pushed. A push while capture is not
|
|
380
|
+
* running, or with the `aec` option unset, is a no-op.
|
|
381
|
+
*/
|
|
382
|
+
pushAecReference(data: Buffer | NodeJS.ArrayBufferView): void;
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The echo canceller's transport and cancellation metrics, merged with the
|
|
386
|
+
* reference queue's counters, or `null` when the `aec` option is unset or
|
|
387
|
+
* capture is not running.
|
|
388
|
+
*/
|
|
389
|
+
aecMetrics(): AecMetrics | null;
|
|
390
|
+
|
|
248
391
|
/** List all available audio input devices. */
|
|
249
392
|
static devices(): MicrophoneInfo[];
|
|
250
393
|
|
package/src/decibri.js
CHANGED
|
@@ -342,6 +342,68 @@ class Microphone extends Readable {
|
|
|
342
342
|
throw new RangeError('limiter ceiling must be between -3.0 and 0.0');
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
+
// ── Validate AEC ─────────────────────────────────────────────────────────
|
|
346
|
+
|
|
347
|
+
// Echo cancellation: the 'tau' shorthand names the model, or an
|
|
348
|
+
// { model, tailMs, suppression, referenceSampleRate } object tunes it;
|
|
349
|
+
// absence leaves it off. The model name is deliberately NOT checked
|
|
350
|
+
// against a list here: the canceller owns the accepted set, so the native
|
|
351
|
+
// layer parses it (AecModel::from_str) and an unknown name is rejected by
|
|
352
|
+
// the native constructor with the canceller's own message (a DecibriError
|
|
353
|
+
// with code 'AEC_CONFIG_INVALID'). The three tuning fields are checked
|
|
354
|
+
// here with the same RangeError / TypeError classes the other
|
|
355
|
+
// conditioning options use; the native layer backstops the same checks.
|
|
356
|
+
// The capture-rate window (8000..=48000 with AEC on) is guarded by the
|
|
357
|
+
// core, surfacing as a RangeError from the native constructor.
|
|
358
|
+
const aec = options.aec;
|
|
359
|
+
let aecModel;
|
|
360
|
+
let aecTailMs;
|
|
361
|
+
let aecSuppression;
|
|
362
|
+
let aecReferenceSampleRate;
|
|
363
|
+
if (aec !== undefined) {
|
|
364
|
+
if (typeof aec === 'string') {
|
|
365
|
+
aecModel = aec;
|
|
366
|
+
} else if (aec !== null && typeof aec === 'object' && !Array.isArray(aec)) {
|
|
367
|
+
const { model, tailMs, suppression, referenceSampleRate } = aec;
|
|
368
|
+
if (typeof model !== 'string') {
|
|
369
|
+
throw new TypeError(
|
|
370
|
+
`Invalid aec model: ${JSON.stringify(model)}. Expected a model name string such as 'tau'.`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
aecModel = model;
|
|
374
|
+
if (tailMs !== undefined) {
|
|
375
|
+
if (typeof tailMs !== 'number' || Number.isNaN(tailMs)) {
|
|
376
|
+
throw new TypeError('aec tailMs must be a number');
|
|
377
|
+
}
|
|
378
|
+
if (tailMs < 16 || tailMs > 500) {
|
|
379
|
+
throw new RangeError('aec tailMs must be between 16 and 500');
|
|
380
|
+
}
|
|
381
|
+
aecTailMs = tailMs;
|
|
382
|
+
}
|
|
383
|
+
if (suppression !== undefined) {
|
|
384
|
+
if (suppression !== 'conservative' && suppression !== 'off') {
|
|
385
|
+
throw new TypeError(
|
|
386
|
+
`aec suppression must be 'conservative' or 'off'; got ${JSON.stringify(suppression)}`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
aecSuppression = suppression;
|
|
390
|
+
}
|
|
391
|
+
if (referenceSampleRate !== undefined) {
|
|
392
|
+
if (typeof referenceSampleRate !== 'number' || Number.isNaN(referenceSampleRate)) {
|
|
393
|
+
throw new TypeError('aec referenceSampleRate must be a number');
|
|
394
|
+
}
|
|
395
|
+
if (referenceSampleRate < 1000 || referenceSampleRate > 384000) {
|
|
396
|
+
throw new RangeError('aec referenceSampleRate must be between 1000 and 384000');
|
|
397
|
+
}
|
|
398
|
+
aecReferenceSampleRate = referenceSampleRate;
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
throw new TypeError(
|
|
402
|
+
`Invalid aec value: ${JSON.stringify(aec)}. Expected a model name such as 'tau', or a config object { model, tailMs, suppression, referenceSampleRate }.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
345
407
|
// Internal plumbing: inject the bundled ORT dylib path into the napi
|
|
346
408
|
// constructor whenever an ONNX stage loads (Silero VAD or denoise). If
|
|
347
409
|
// resolution fails (unknown platform, platform package not installed), this
|
|
@@ -377,6 +439,10 @@ class Microphone extends Readable {
|
|
|
377
439
|
highpass,
|
|
378
440
|
agc,
|
|
379
441
|
limiter,
|
|
442
|
+
aec: aecModel,
|
|
443
|
+
aecTailMs,
|
|
444
|
+
aecSuppression,
|
|
445
|
+
aecReferenceSampleRate,
|
|
380
446
|
},
|
|
381
447
|
};
|
|
382
448
|
}
|
|
@@ -535,6 +601,67 @@ class Microphone extends Readable {
|
|
|
535
601
|
return this._native.overrunCount;
|
|
536
602
|
}
|
|
537
603
|
|
|
604
|
+
/**
|
|
605
|
+
* Queue far-end reference audio for the echo canceller: the audio being
|
|
606
|
+
* played out, pushed as it is played, in played order. Accepts the same
|
|
607
|
+
* input shapes `Speaker.write` accepts (a `Buffer`, any TypedArray, or a
|
|
608
|
+
* `DataView` of PCM bytes in this microphone's `dtype`), mono, at the
|
|
609
|
+
* declared `referenceSampleRate` (the capture rate when unset).
|
|
610
|
+
*
|
|
611
|
+
* Never blocks and never throws on a full queue: samples that do not fit
|
|
612
|
+
* are discarded and counted by `aecMetrics().referenceDropped`, and the
|
|
613
|
+
* span they occupied is represented as silence. Silence between played
|
|
614
|
+
* audio need not be pushed; a caller that stops pushing has said nothing is
|
|
615
|
+
* playing. A push while capture is not running, or with the `aec` option
|
|
616
|
+
* unset, is a no-op.
|
|
617
|
+
*
|
|
618
|
+
* @param {Buffer | NodeJS.ArrayBufferView} data PCM samples in the
|
|
619
|
+
* configured `dtype`.
|
|
620
|
+
*/
|
|
621
|
+
pushAecReference(data) {
|
|
622
|
+
let buf;
|
|
623
|
+
if (Buffer.isBuffer(data)) {
|
|
624
|
+
buf = data;
|
|
625
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
626
|
+
// Any TypedArray or DataView: view the same bytes, no copy, exactly as
|
|
627
|
+
// the stream machinery normalizes a typed-array write to a Speaker.
|
|
628
|
+
buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
629
|
+
} else {
|
|
630
|
+
throw new TypeError(
|
|
631
|
+
'pushAecReference requires a Buffer, TypedArray, or DataView of PCM samples in the configured dtype'
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
this._native.pushAecReference(buf);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* The echo canceller's transport and cancellation metrics, merged with the
|
|
639
|
+
* reference queue's counters, or `null` when the `aec` option is unset or
|
|
640
|
+
* capture is not running.
|
|
641
|
+
*
|
|
642
|
+
* `delaySamples` staying `null` while `acquisitionParked` climbs is the
|
|
643
|
+
* signature of a canceller with no usable reference: none pushed, not at
|
|
644
|
+
* the declared rate, or not the signal that produced the echo. A climbing
|
|
645
|
+
* `referenceDropped` means single pushes are exceeding the reference
|
|
646
|
+
* queue's bound.
|
|
647
|
+
*
|
|
648
|
+
* @returns {import('./decibri').AecMetrics | null}
|
|
649
|
+
*/
|
|
650
|
+
aecMetrics() {
|
|
651
|
+
const m = this._native.aecMetrics();
|
|
652
|
+
if (m === null || m === undefined) return null;
|
|
653
|
+
return {
|
|
654
|
+
delaySamples: m.delaySamples ?? null,
|
|
655
|
+
erleDb: m.erleDb,
|
|
656
|
+
doubleTalk: m.doubleTalk,
|
|
657
|
+
referenceStarved: m.referenceStarved,
|
|
658
|
+
acquisitionParked: m.acquisitionParked,
|
|
659
|
+
referenceReanchors: m.referenceReanchors,
|
|
660
|
+
referenceDropped: m.referenceDropped,
|
|
661
|
+
referenceSilence: m.referenceSilence,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
538
665
|
/**
|
|
539
666
|
* List all available input devices on the system.
|
|
540
667
|
* @returns {Array<{index: number, name: string, id: string, maxInputChannels: number, defaultSampleRate: number, isDefault: boolean}>}
|
package/src/errors.js
CHANGED
|
@@ -80,8 +80,11 @@ const RANGE_PREFIXES = [
|
|
|
80
80
|
'frames per buffer must be between',
|
|
81
81
|
'agc target level must be between',
|
|
82
82
|
'limiter ceiling must be between',
|
|
83
|
+
'aec tailMs must be between',
|
|
84
|
+
'aec referenceSampleRate must be between',
|
|
83
85
|
'Silero VAD only supports',
|
|
84
86
|
'VAD threshold must be between',
|
|
87
|
+
'echo cancellation only supports',
|
|
85
88
|
'device index out of range',
|
|
86
89
|
'analysis requires VAD',
|
|
87
90
|
];
|
|
@@ -89,6 +92,7 @@ const RANGE_PREFIXES = [
|
|
|
89
92
|
const TYPE_PREFIXES = [
|
|
90
93
|
"dtype must be 'int16' or 'float32'",
|
|
91
94
|
"format must be 'int16' or 'float32'",
|
|
95
|
+
'aec suppression must be',
|
|
92
96
|
];
|
|
93
97
|
|
|
94
98
|
const DEVICE_CODES = [
|
|
@@ -126,6 +130,7 @@ const BASE_CODES = [
|
|
|
126
130
|
['the requested sample rate conversion is not supported', 'RESAMPLE_CONFIG_INVALID'],
|
|
127
131
|
['the resample chain was fed after it was flushed', 'RESAMPLE_AFTER_FLUSH'],
|
|
128
132
|
['resampler error:', 'RESAMPLE_FAILED'],
|
|
133
|
+
['echo canceller configuration error:', 'AEC_CONFIG_INVALID'],
|
|
129
134
|
['Failed to read audio file', 'FILE_READ_FAILED'],
|
|
130
135
|
['invalid WAV file:', 'WAV_INVALID'],
|
|
131
136
|
['ONNX Runtime was initialized in pid', 'FORK_AFTER_ORT_INIT'],
|