Adding an option for rnnoise suppression on the native client
parent
7a97a74cd5
commit
444c4d78f7
|
@ -132,8 +132,8 @@ export function running() { return typeof(currentStage) !== "undefined"; }
|
|||
|
||||
export function register_task(stage: Stage, task: Task) {
|
||||
let callee = new Error().stack.split("\n")[2].replace(/^\s*at (Object\.\.\/)?/, "");
|
||||
if(callee.match(/^.* \(([:\\/_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/)) {
|
||||
callee = callee.replace(/^.* \(([:\\/_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/, "$1:$2:$3");
|
||||
if(callee.match(/^.* \(([:\\/~_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/)) {
|
||||
callee = callee.replace(/^.* \(([:\\/~_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/, "$1:$2:$3");
|
||||
}
|
||||
if(!task.function) {
|
||||
debugger;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {AbstractInput, LevelMeter} from "../voice/RecorderBase";
|
||||
import {Registry} from "../events";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
|
||||
export type DeviceQueryResult = {}
|
||||
|
||||
|
@ -8,6 +11,9 @@ export interface AudioRecorderBacked {
|
|||
createLevelMeter(device: IDevice) : Promise<LevelMeter>;
|
||||
|
||||
getDeviceList() : DeviceList;
|
||||
|
||||
isRnNoiseSupported() : boolean;
|
||||
toggleRnNoise(target: boolean);
|
||||
}
|
||||
|
||||
export interface DeviceListEvents {
|
||||
|
@ -156,3 +162,15 @@ export function setRecorderBackend(instance: AudioRecorderBacked) {
|
|||
|
||||
recorderBackend = instance;
|
||||
}
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "audio filter init",
|
||||
priority: 10,
|
||||
function: async () => {
|
||||
const backend = getRecorderBackend();
|
||||
if(backend.isRnNoiseSupported()) {
|
||||
getRecorderBackend().toggleRnNoise(settings.static_global(Settings.KEY_RNNOISE_FILTER));
|
||||
settings.globalChangeListener(Settings.KEY_RNNOISE_FILTER, value => getRecorderBackend().toggleRnNoise(value));
|
||||
}
|
||||
}
|
||||
})
|
|
@ -491,6 +491,13 @@ export class Settings extends StaticSettings {
|
|||
valueType: "boolean",
|
||||
};
|
||||
|
||||
static readonly KEY_RNNOISE_FILTER: ValuedSettingsKey<boolean> = {
|
||||
key: 'rnnoise_filter',
|
||||
defaultValue: true,
|
||||
description: 'Enable the rnnoise filter for supressing background noise',
|
||||
valueType: "boolean",
|
||||
};
|
||||
|
||||
static readonly FN_LOG_ENABLED: (category: string) => SettingsKey<boolean> = category => {
|
||||
return {
|
||||
key: "log." + category.toLowerCase() + ".enabled",
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
//TODO: Use the max limit!
|
||||
|
||||
import {sliderfy} from "../../ui/elements/Slider";
|
||||
import {createModal, Modal} from "../../ui/elements/Modal";
|
||||
import {ClientEntry} from "../../tree/Client";
|
||||
import * as htmltags from "../../ui/htmltags";
|
||||
|
||||
let modal: Modal;
|
||||
|
||||
export function spawnChangeVolume(client: ClientEntry, local: boolean, current: number, max: number | undefined, callback: (number) => void) {
|
||||
if (modal) modal.close();
|
||||
|
||||
let new_value: number;
|
||||
modal = createModal({
|
||||
header: local ? tr("Change local volume") : tr("Change remote volume"),
|
||||
body: function () {
|
||||
let tag = $("#tmpl_change_volume").renderTag({
|
||||
client: htmltags.generate_client_object({
|
||||
add_braces: false,
|
||||
client_name: client.clientNickName(),
|
||||
client_unique_id: client.properties.client_unique_identifier,
|
||||
client_id: client.clientId()
|
||||
}),
|
||||
local: local
|
||||
});
|
||||
|
||||
const container_value = tag.find(".info .value");
|
||||
const set_value = value => {
|
||||
const number = value > 100 ? value - 100 : 100 - value;
|
||||
container_value.html((value == 100 ? "±" : value > 100 ? "+" : "-") + number + "%");
|
||||
|
||||
new_value = value / 100;
|
||||
if (local) callback(new_value);
|
||||
};
|
||||
set_value(current * 100);
|
||||
|
||||
const slider_tag = tag.find(".container-slider");
|
||||
const slider = sliderfy(slider_tag, {
|
||||
initial_value: current * 100,
|
||||
step: 1,
|
||||
max_value: 200,
|
||||
min_value: 0,
|
||||
|
||||
unit: '%'
|
||||
});
|
||||
slider_tag.on('change', event => set_value(parseInt(slider_tag.attr("value"))));
|
||||
|
||||
tag.find(".button-save").on('click', event => {
|
||||
if (typeof (new_value) !== "undefined") callback(new_value);
|
||||
modal.close();
|
||||
});
|
||||
|
||||
tag.find(".button-cancel").on('click', event => {
|
||||
callback(current);
|
||||
modal.close();
|
||||
});
|
||||
|
||||
tag.find(".button-reset").on('click', event => {
|
||||
slider.value(100);
|
||||
});
|
||||
|
||||
tag.find(".button-apply").on('click', event => {
|
||||
callback(new_value);
|
||||
new_value = undefined;
|
||||
});
|
||||
|
||||
return tag.children();
|
||||
},
|
||||
footer: null,
|
||||
|
||||
width: 600
|
||||
});
|
||||
|
||||
modal.close_listener.push(() => modal = undefined);
|
||||
modal.open();
|
||||
modal.htmlTag.find(".modal-body").addClass("modal-volume");
|
||||
}
|
|
@ -6,6 +6,7 @@ import * as log from "tc-shared/log";
|
|||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
import {defaultRecorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {DeviceListState, getRecorderBackend, IDevice} from "tc-shared/audio/recorder";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
|
||||
export type MicrophoneSetting =
|
||||
"volume"
|
||||
|
@ -13,7 +14,8 @@ export type MicrophoneSetting =
|
|||
| "ppt-key"
|
||||
| "ppt-release-delay"
|
||||
| "ppt-release-delay-active"
|
||||
| "threshold-threshold";
|
||||
| "threshold-threshold"
|
||||
| "rnnoise";
|
||||
|
||||
export type MicrophoneDevice = {
|
||||
id: string,
|
||||
|
@ -242,6 +244,10 @@ export function initialize_audio_microphone_controller(events: Registry<Micropho
|
|||
value = defaultRecorder.getPushToTalkDelay() > 0;
|
||||
break;
|
||||
|
||||
case "rnnoise":
|
||||
value = settings.static_global(Settings.KEY_RNNOISE_FILTER);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -296,6 +302,11 @@ export function initialize_audio_microphone_controller(events: Registry<Micropho
|
|||
defaultRecorder.setPushToTalkDelay(Math.abs(defaultRecorder.getPushToTalkDelay()) * (event.value ? 1 : -1));
|
||||
break;
|
||||
|
||||
case "rnnoise":
|
||||
if (!ensure_type("boolean")) return;
|
||||
settings.changeGlobal(Settings.KEY_RNNOISE_FILTER, event.value);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {Button} from "tc-shared/ui/react-elements/Button";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {MicrophoneDevice, MicrophoneSettingsEvents} from "tc-shared/ui/modal/settings/Microphone";
|
||||
|
@ -416,6 +416,32 @@ const PPTDelaySettings = (props: { events: Registry<MicrophoneSettingsEvents> })
|
|||
);
|
||||
}
|
||||
|
||||
const RNNoiseLabel = () => (
|
||||
<VariadicTranslatable text={"Enable RNNoise cancelation ({})"}>
|
||||
<a href={"https://jmvalin.ca/demo/rnnoise/"} target={"_blank"} style={{ margin: 0 }}><Translatable>more info</Translatable></a>
|
||||
</VariadicTranslatable>
|
||||
)
|
||||
|
||||
const RNNoiseSettings = (props: { events: Registry<MicrophoneSettingsEvents> }) => {
|
||||
if(__build.target === "web") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [ enabled, setEnabled ] = useState<boolean | "loading">(() => {
|
||||
props.events.fire("query_setting", { setting: "rnnoise" });
|
||||
return "loading";
|
||||
});
|
||||
props.events.reactUse("notify_setting", event => event.setting === "rnnoise" && setEnabled(event.value));
|
||||
|
||||
return (
|
||||
<Checkbox label={<RNNoiseLabel />}
|
||||
disabled={enabled === "loading"}
|
||||
value={enabled === true}
|
||||
onChange={value => props.events.fire("action_set_setting", { setting: "rnnoise", value: value })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const VadSelector = (props: { events: Registry<MicrophoneSettingsEvents> }) => {
|
||||
const [selectedType, setSelectedType] = useState<VadType | "loading">(() => {
|
||||
props.events.fire("query_setting", {setting: "vad-type"});
|
||||
|
@ -630,6 +656,7 @@ export const MicrophoneSettings = (props: { events: Registry<MicrophoneSettingsE
|
|||
<div className={cssStyle.body}>
|
||||
<div className={cssStyle.containerAdvanced}>
|
||||
<PPTDelaySettings events={props.events}/>
|
||||
<RNNoiseSettings events={props.events} />
|
||||
</div>
|
||||
</div>
|
||||
</HighlightRegion>
|
||||
|
|
|
@ -75,6 +75,7 @@ export interface AbstractInput {
|
|||
readonly events: Registry<InputEvents>;
|
||||
|
||||
currentState() : InputState;
|
||||
destroy();
|
||||
|
||||
start() : Promise<InputStartResult>;
|
||||
stop() : Promise<void>;
|
||||
|
|
|
@ -90,6 +90,12 @@ export class RecorderProfile {
|
|||
this.pptHookRegistered = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
/* TODO */
|
||||
this.input?.destroy();
|
||||
this.input = undefined;
|
||||
}
|
||||
|
||||
async initialize() : Promise<void> {
|
||||
{
|
||||
let config = {};
|
||||
|
|
|
@ -102,6 +102,12 @@ export class WebAudioRecorder implements AudioRecorderBacked {
|
|||
getDeviceList(): DeviceList {
|
||||
return inputDeviceList;
|
||||
}
|
||||
|
||||
isRnNoiseSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleRnNoise(target: boolean) { throw "not supported"; }
|
||||
}
|
||||
|
||||
class JavascriptInput implements AbstractInput {
|
||||
|
@ -138,6 +144,8 @@ class JavascriptInput implements AbstractInput {
|
|||
this.audioScriptProcessorCallback = this.handleAudio.bind(this);
|
||||
}
|
||||
|
||||
destroy() { }
|
||||
|
||||
private handleAudioInitialized() {
|
||||
this.audioContext = aplayer.context();
|
||||
this.audioNodeMute = this.audioContext.createGain();
|
||||
|
|
Loading…
Reference in New Issue