Compare commits

...

2 Commits

Author SHA1 Message Date
xquanluu
ceb0339083 wip 2026-01-28 08:57:51 +07:00
xquanluu
f070f262db support noise isolation 2026-01-27 13:28:40 +07:00
6 changed files with 132 additions and 17 deletions

View File

@@ -19,7 +19,8 @@ class TaskConfig extends Task {
'vad',
'ttsStream',
'autoStreamTts',
'disableTtsCache'
'disableTtsCache',
'noiseIsolation'
].forEach((k) => this[k] = this.data[k] || {});
if ('notifyEvents' in this.data) {
@@ -90,6 +91,7 @@ class TaskConfig extends Task {
get hasNotifySttLatency() { return Object.keys(this.data).includes('notifySttLatency'); }
get hasTtsStream() { return Object.keys(this.ttsStream).length; }
get hasDisableTtsCache() { return Object.keys(this.data).includes('disableTtsCache'); }
get hasNoiseIsolation() { return Object.keys(this.data).includes('noiseIsolation'); }
get summary() {
const phrase = [];
@@ -128,6 +130,7 @@ class TaskConfig extends Task {
}
if ('autoStreamTts' in this.data) phrase.push(`enable Say.stream value ${this.data.autoStreamTts ? 'on' : 'off'}`);
if (this.hasDisableTtsCache) phrase.push(`disableTtsCache ${this.data.disableTtsCache ? 'on' : 'off'}`);
if (this.hasNoiseIsolation) phrase.push(`noiseIsolation ${this.noiseIsolation.enable ? 'on' : 'off'}`);
return `${this.name}{${phrase.join(',')}}`;
}
@@ -365,6 +368,17 @@ class TaskConfig extends Task {
this.logger.info(`set disableTtsCache = ${this.disableTtsCache}`);
cs.disableTtsCache = this.data.disableTtsCache;
}
if (this.hasNoiseIsolation) {
const {enable, ...opts} = this.noiseIsolation;
if (enable) {
this.logger.debug({opts}, 'Config: enabling noiseIsolation');
cs.startBackgroundTask('noiseIsolation', {verb: 'noiseIsolation', ...opts});
} else {
this.logger.info('Config: disabling noiseIsolation');
cs.stopBackgroundTask('noiseIsolation');
}
}
}
async kill(cs) {

View File

@@ -99,6 +99,9 @@ function makeTask(logger, obj, parent) {
case TaskName.Alert:
const TaskAlert = require('./alert');
return new TaskAlert(logger, data, parent);
case TaskName.NoiseIsolation:
const TaskNoiseIsolation = require('./noise-isolation');
return new TaskNoiseIsolation(logger, data, parent);
}
// should never reach

View File

@@ -0,0 +1,90 @@
const Task = require('./task');
const {TaskName, TaskPreconditions} = require('../utils/constants');
class TaskNoiseIsolation extends Task {
constructor(logger, opts, parentTask) {
super(logger, opts, parentTask);
this.preconditions = TaskPreconditions.Endpoint;
this.vendor = this.data.vendor || 'krisp';
this.direction = this.data.direction || 'read';
this.level = typeof this.data.level === 'number' ? this.data.level : 100;
this.model = this.data.model;
}
get name() { return TaskName.NoiseIsolation; }
get apiCommand() {
return `uuid_${this.vendor}_noise_isolation`;
}
get summary() {
return `${this.name}{vendor=${this.vendor},direction=${this.direction},level=${this.level}}`;
}
async exec(cs, {ep}) {
await super.exec(cs);
this.ep = ep;
if (!ep?.connected) {
this.logger.info('TaskNoiseIsolation:exec - no endpoint connected');
this.notifyTaskDone();
return;
}
try {
await this._startNoiseIsolation(ep);
await this.awaitTaskDone();
} catch (err) {
this.logger.error({err}, 'TaskNoiseIsolation:exec - error');
}
}
async _startNoiseIsolation(ep) {
// API format: uuid_${vendor}_noise_isolation <uuid> start <direction> [level] [model]
// model is only added if level is set
const args = [ep.uuid, 'start', this.direction];
if (this.level !== 100) {
args.push(this.level);
if (this.model) {
args.push(this.model);
}
}
this.logger.info({args, apiCommand: this.apiCommand}, 'TaskNoiseIsolation:_startNoiseIsolation');
try {
const res = await ep.api(this.apiCommand, args.join(' '));
if (!res.body?.startsWith('+OK')) {
this.logger.error({res}, 'TaskNoiseIsolation:_startNoiseIsolation - error starting noise isolation');
} else {
this.logger.info('TaskNoiseIsolation:_startNoiseIsolation - noise isolation started');
}
} catch (err) {
this.logger.error({err}, 'TaskNoiseIsolation:_startNoiseIsolation - error');
throw err;
}
}
async _stopNoiseIsolation(ep) {
if (!ep?.connected) return;
const args = [ep.uuid, 'stop'];
this.logger.info({args, apiCommand: this.apiCommand}, 'TaskNoiseIsolation:_stopNoiseIsolation');
try {
await ep.api(this.apiCommand, args.join(' '));
this.logger.info('TaskNoiseIsolation:_stopNoiseIsolation - noise isolation stopped');
} catch (err) {
this.logger.info({err}, 'TaskNoiseIsolation:_stopNoiseIsolation - error stopping noise isolation');
}
}
async kill(cs) {
super.kill(cs);
await this._stopNoiseIsolation(this.ep);
this.notifyTaskDone();
}
}
module.exports = TaskNoiseIsolation;

View File

@@ -49,6 +49,9 @@ class BackgroundTaskManager extends Emitter {
case 'ttsStream':
task = await this._initTtsStream(opts);
break;
case 'noiseIsolation':
task = await this._initNoiseIsolation(opts);
break;
default:
break;
}
@@ -194,6 +197,25 @@ class BackgroundTaskManager extends Emitter {
return task;
}
// Initiate Noise Isolation
async _initNoiseIsolation(opts) {
let task;
try {
const t = normalizeJambones(this.logger, [opts]);
task = makeTask(this.logger, t[0]);
const resources = await this.cs._evaluatePreconditions(task);
const {span, ctx} = this.rootSpan.startChildSpan(`background-noiseIsolation:${task.summary}`);
task.span = span;
task.ctx = ctx;
task.exec(this.cs, resources)
.then(this._taskCompleted.bind(this, 'noiseIsolation', task))
.catch(this._taskError.bind(this, 'noiseIsolation', task));
} catch (err) {
this.logger.info(err, 'BackgroundTaskManager:_initNoiseIsolation - Error creating noiseIsolation task');
}
return task;
}
_taskCompleted(type, task) {
this.logger.debug({type, task}, `BackgroundTaskManager:_taskCompleted: task completed, sticky: ${task.sticky}`);
task.removeAllListeners();

View File

@@ -31,7 +31,8 @@
"SayLegacy": "say:legacy",
"Stream": "stream",
"Tag": "tag",
"Transcribe": "transcribe"
"Transcribe": "transcribe",
"NoiseIsolation": "noiseIsolation"
},
"AllowedSipRecVerbs": ["answer", "config", "gather", "transcribe", "listen", "tag", "hangup", "sip:decline"],
"AllowedConfirmSessionVerbs": ["config", "gather", "plays", "say", "tag"],

15
package-lock.json generated
View File

@@ -8048,21 +8048,6 @@
"node": ">= 6.0.0"
}
},
"node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/utf-8-validate": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",