Fixed microphone selection / autostart
This commit is contained in:
parent
90b5f3eacf
commit
a0c2d5fcc2
@ -40,9 +40,9 @@ export class NThresholdFilter extends NativeFilter implements ThresholdFilter {
|
||||
readonly type: FilterType.THRESHOLD;
|
||||
private filter: audio.record.ThresholdConsumeFilter;
|
||||
|
||||
private _margin_frames: number = 25; /* 120ms */
|
||||
private _threshold: number = 50;
|
||||
private _callback_level: any;
|
||||
private marginFrames: number = 25; /* 120ms */
|
||||
private threshold: number = 50;
|
||||
private callbackLevel: () => void;
|
||||
|
||||
private _attack_smooth = 0;
|
||||
private _release_smooth = 0;
|
||||
@ -54,14 +54,15 @@ export class NThresholdFilter extends NativeFilter implements ThresholdFilter {
|
||||
|
||||
Object.defineProperty(this, 'callback_level', {
|
||||
get(): any {
|
||||
return this._callback_level;
|
||||
return this.callbackLevel;
|
||||
}, set(v: any): void {
|
||||
if(v === this._callback_level)
|
||||
if(v === this.callbackLevel)
|
||||
return;
|
||||
|
||||
this._callback_level = v;
|
||||
if(this.filter)
|
||||
this.callbackLevel = v;
|
||||
if(this.filter) {
|
||||
this.filter.set_analyze_filter(v);
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
@ -69,15 +70,15 @@ export class NThresholdFilter extends NativeFilter implements ThresholdFilter {
|
||||
}
|
||||
|
||||
getMarginFrames(): number {
|
||||
return this.filter ? this.filter.get_margin_time() * NThresholdFilter.frames_per_second : this._margin_frames;
|
||||
return this.filter ? this.filter.get_margin_time() * NThresholdFilter.frames_per_second : this.marginFrames;
|
||||
}
|
||||
|
||||
getThreshold(): number {
|
||||
return this.filter ? this.filter.get_threshold() : this._threshold;
|
||||
return this.filter ? this.filter.get_threshold() : this.threshold;
|
||||
}
|
||||
|
||||
setMarginFrames(value: number) {
|
||||
this._margin_frames = value;
|
||||
this.marginFrames = value;
|
||||
if(this.filter)
|
||||
this.filter.set_margin_time(value / 960 / 1000);
|
||||
}
|
||||
@ -105,7 +106,7 @@ export class NThresholdFilter extends NativeFilter implements ThresholdFilter {
|
||||
setThreshold(value: number): Promise<void> {
|
||||
if(typeof(value) === "string")
|
||||
value = parseInt(value); /* yes... this happens */
|
||||
this._threshold = value;
|
||||
this.threshold = value;
|
||||
if(this.filter)
|
||||
this.filter.set_threshold(value);
|
||||
return Promise.resolve();
|
||||
@ -113,35 +114,49 @@ export class NThresholdFilter extends NativeFilter implements ThresholdFilter {
|
||||
|
||||
finalize() {
|
||||
if(this.filter) {
|
||||
if(this.handle.getNativeConsumer())
|
||||
if(this.handle.getNativeConsumer()) {
|
||||
this.handle.getNativeConsumer().unregister_filter(this.filter);
|
||||
}
|
||||
|
||||
this.filter = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const consumer = this.handle.getNativeConsumer();
|
||||
if(!consumer)
|
||||
if(!consumer) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.finalize();
|
||||
this.filter = consumer.create_filter_threshold(this._threshold);
|
||||
if(this._callback_level)
|
||||
this.filter.set_analyze_filter(this._callback_level);
|
||||
this.filter.set_margin_time(this._margin_frames / NThresholdFilter.frames_per_second);
|
||||
this.filter = consumer.create_filter_threshold(this.threshold);
|
||||
this.filter.set_margin_time(this.marginFrames / NThresholdFilter.frames_per_second);
|
||||
this.filter.set_attack_smooth(this._attack_smooth);
|
||||
this.filter.set_release_smooth(this._release_smooth);
|
||||
this.updateAnalyzeFilterCallback();
|
||||
}
|
||||
|
||||
registerLevelCallback(callback: (value: number) => void) {
|
||||
this.levelCallbacks.push(callback);
|
||||
this.updateAnalyzeFilterCallback();
|
||||
}
|
||||
|
||||
removeLevelCallback(callback: (value: number) => void) {
|
||||
const index = this.levelCallbacks.indexOf(callback);
|
||||
if(index === -1) return;
|
||||
if(index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.levelCallbacks.splice(index, 1);
|
||||
this.updateAnalyzeFilterCallback();
|
||||
}
|
||||
|
||||
private updateAnalyzeFilterCallback() {
|
||||
if(this.levelCallbacks.length > 0) {
|
||||
this.filter.set_analyze_filter(value => this.levelCallbacks.forEach(callback => callback(value)));
|
||||
} else {
|
||||
this.filter.set_analyze_filter(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,9 @@ import {
|
||||
InputConsumer,
|
||||
InputConsumerType,
|
||||
InputEvents,
|
||||
InputStartError,
|
||||
InputState,
|
||||
LevelMeter, MediaStreamRequestResult
|
||||
LevelMeter,
|
||||
} from "tc-shared/voice/RecorderBase";
|
||||
import {audio} from "tc-native/connection";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
@ -13,9 +14,9 @@ import {Registry} from "tc-shared/events";
|
||||
import {Filter, FilterType, FilterTypeClass} from "tc-shared/voice/Filter";
|
||||
import {NativeFilter, NStateFilter, NThresholdFilter, NVoiceLevelFilter} from "./AudioFilter";
|
||||
import {IDevice} from "tc-shared/audio/recorder";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
import NativeFilterMode = audio.record.FilterMode;
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import NativeFilterMode = audio.record.FilterMode;
|
||||
|
||||
export class NativeInput implements AbstractInput {
|
||||
static readonly instances = [] as NativeInput[];
|
||||
@ -59,19 +60,30 @@ export class NativeInput implements AbstractInput {
|
||||
}
|
||||
}
|
||||
|
||||
async start(): Promise<MediaStreamRequestResult | true> {
|
||||
if(this.state === InputState.RECORDING) {
|
||||
logWarn(LogCategory.VOICE, tr("Tried to start an input recorder twice."));
|
||||
return MediaStreamRequestResult.EBUSY;
|
||||
private setState(newState: InputState) {
|
||||
if(this.state === newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = InputState.INITIALIZING;
|
||||
const oldState = this.state;
|
||||
this.state = newState;
|
||||
this.events.fire("notify_state_changed", { oldState, newState });
|
||||
}
|
||||
|
||||
async start(): Promise<InputStartError | true> {
|
||||
if(this.state !== InputState.PAUSED) {
|
||||
logWarn(LogCategory.VOICE, tr("Input isn't paused"));
|
||||
return InputStartError.EBUSY;
|
||||
}
|
||||
|
||||
this.setState(InputState.INITIALIZING);
|
||||
logTrace(LogCategory.AUDIO, tr("Starting input for device %o", this.deviceId));
|
||||
try {
|
||||
const state = await new Promise<audio.record.DeviceSetResult>(resolve => this.nativeHandle.set_device(this.deviceId, resolve));
|
||||
|
||||
if(state !== "success") {
|
||||
if(state === "invalid-device") {
|
||||
return MediaStreamRequestResult.EDEVICEUNKNOWN;
|
||||
return InputStartError.EDEVICEUNKNOWN;
|
||||
} else if(state === undefined) {
|
||||
throw tr("invalid set device result state");
|
||||
}
|
||||
@ -88,21 +100,28 @@ export class NativeInput implements AbstractInput {
|
||||
}
|
||||
}));
|
||||
|
||||
this.state = InputState.RECORDING;
|
||||
this.setState(InputState.RECORDING);
|
||||
return true;
|
||||
} finally {
|
||||
/* @ts-ignore Typescript isn't smart about awaits in try catch blocks */
|
||||
if(this.state === InputState.INITIALIZING) {
|
||||
this.state = InputState.PAUSED;
|
||||
this.setState(InputState.PAUSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if(this.state === InputState.PAUSED)
|
||||
if(this.state === InputState.PAUSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nativeHandle.stop();
|
||||
this.state = InputState.PAUSED;
|
||||
this.setState(InputState.PAUSED);
|
||||
|
||||
if(this.filtered) {
|
||||
this.filtered = false;
|
||||
this.events.fire("notify_voice_end");
|
||||
}
|
||||
}
|
||||
|
||||
async setDeviceId(device: string | undefined): Promise<void> {
|
||||
@ -110,8 +129,15 @@ export class NativeInput implements AbstractInput {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.stop();
|
||||
} catch (error) {
|
||||
logWarn(LogCategory.GENERAL, tr("Failed to stop microphone recording after device change: %o"), error);
|
||||
}
|
||||
|
||||
const oldDeviceId = this.deviceId;
|
||||
this.deviceId = device;
|
||||
await this.stop();
|
||||
this.events.fire("notify_device_changed", { oldDeviceId, newDeviceId: device });
|
||||
}
|
||||
|
||||
currentDeviceId(): string | undefined {
|
||||
@ -124,10 +150,12 @@ export class NativeInput implements AbstractInput {
|
||||
|
||||
removeFilter(filter: Filter) {
|
||||
const index = this.registeredFilters.indexOf(filter as any);
|
||||
if(index === -1) return;
|
||||
if(index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [ f ] = this.registeredFilters.splice(index, 1);
|
||||
f.finalize();
|
||||
const [ registeredFilter ] = this.registeredFilters.splice(index, 1);
|
||||
registeredFilter.finalize();
|
||||
}
|
||||
|
||||
createFilter<T extends FilterType>(type: T, priority: number): FilterTypeClass<T> {
|
||||
@ -223,7 +251,13 @@ export class NativeInput implements AbstractInput {
|
||||
break;
|
||||
}
|
||||
|
||||
if(this.nativeConsumer.get_filter_mode() === nativeMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldMode = this.getFilterMode();
|
||||
this.nativeConsumer.set_filter_mode(nativeMode);
|
||||
this.events.fire("notify_filter_mode_changed", { oldMode, newMode: mode });
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,13 +296,15 @@ export class NativeLevelMeter implements LevelMeter {
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
if (typeof (error) === "string")
|
||||
if (typeof (error) === "string") {
|
||||
throw error;
|
||||
console.warn(tr("Failed to initialize levelmeter for device %o: %o"), this.targetDevice, error);
|
||||
}
|
||||
|
||||
logWarn(LogCategory.AUDIO, tr("Failed to initialize level meter for device %o: %o"), this.targetDevice, error);
|
||||
throw "initialize failed (lookup console)";
|
||||
}
|
||||
|
||||
/* references this variable, needs a destory() call, else memory leak */
|
||||
/* references this variable, needs a destroy() call, else memory leak */
|
||||
this.nativeFilter.set_analyze_filter(value => {
|
||||
if(this.callback) this.callback(value);
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import * as loader from "tc-loader";
|
||||
|
||||
import * as native from "tc-native/connection";
|
||||
import {audio} from "tc-native/connection";
|
||||
import {LogCategory, logTrace} from "tc-shared/log";
|
||||
|
||||
interface NativeIDevice extends IDevice {
|
||||
isDefault: boolean
|
||||
@ -39,7 +40,9 @@ class InputDeviceList extends AbstractDeviceList {
|
||||
return this.cachedDevices;
|
||||
}
|
||||
|
||||
this.cachedDevices = audio.available_devices()
|
||||
const nativeDeviceList = audio.available_devices();
|
||||
logTrace(LogCategory.AUDIO, tr("Native device list: %o"), nativeDeviceList);
|
||||
this.cachedDevices = nativeDeviceList
|
||||
.filter(e => e.input_supported || e.input_default)
|
||||
.filter(e => e.driver !== "Windows WDM-KS") /* If we're using WDM-KS and opening the microphone view, for some reason the channels get blocked an never release.... */
|
||||
.map(device => {
|
||||
|
@ -93,6 +93,7 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
||||
}
|
||||
|
||||
await recorder?.unmount();
|
||||
const oldRecorder = recorder;
|
||||
this.currentRecorder = recorder;
|
||||
|
||||
try {
|
||||
@ -117,7 +118,10 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
||||
this.currentRecorder = undefined;
|
||||
throw error;
|
||||
}
|
||||
this.events.fire("notify_recorder_changed", {});
|
||||
this.events.fire("notify_recorder_changed", {
|
||||
oldRecorder,
|
||||
newRecorder: recorder
|
||||
});
|
||||
}
|
||||
|
||||
voiceRecorder(): RecorderProfile {
|
||||
|
1
native/serverconnection/exports/exports.d.ts
vendored
1
native/serverconnection/exports/exports.d.ts
vendored
@ -228,7 +228,6 @@ declare module "tc-native/connection" {
|
||||
readonly channelCount: number;
|
||||
readonly frameSize: number;
|
||||
|
||||
/* TODO add some kind of order to may improve CPU performance (Some filters are more intense then others) */
|
||||
get_filters() : ConsumeFilter[];
|
||||
unregister_filter(filter: ConsumeFilter);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "TeaClient",
|
||||
"version": "1.5.0-6",
|
||||
"version": "1.5.0-7",
|
||||
"description": "",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
Loading…
Reference in New Issue
Block a user