Skip to content

Commit

Permalink
preventing service toggle on start/stop spam
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidijusParsiunas committed Aug 10, 2023
1 parent 30f97aa commit a06e041
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 21 deletions.
4 changes: 2 additions & 2 deletions component/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion component/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "speech-to-element",
"version": "0.1.65",
"version": "0.1.66",
"description": "Transcribe your speech into text, embed it into elements and more",
"main": "dist/index.js",
"scripts": {
Expand Down
28 changes: 14 additions & 14 deletions component/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import {AzureOptions, Options, WebSpeechOptions} from './types/options';
import {WebSpeech} from './services/webSpeech/webSpeech';
import {CommandUtils} from './utils/commandUtils';
import {GlobalState} from './utils/globalState';
import {Azure} from './services/azure/azure';
import {Speech} from './speech';

export default class SpeechToElement {
private static _service: Speech | undefined;

public static toggle(service: 'webspeech', options?: Options & WebSpeechOptions): void;
public static toggle(service: 'azure', options: Options & AzureOptions): void;
public static toggle(service: 'webspeech' | 'azure', options?: Options & WebSpeechOptions & AzureOptions) {
const processedServiceName = service.toLocaleLowerCase().trim();
if (this._service?.recognizing) {
this._service.stop();
if (GlobalState.service?.recognizing) {
this.stop();
} else if (processedServiceName === 'webspeech') {
SpeechToElement.startWebSpeech(options);
} else if (processedServiceName === 'azure') {
Expand All @@ -24,28 +22,30 @@ export default class SpeechToElement {
}

public static startWebSpeech(options?: Options & WebSpeechOptions) {
SpeechToElement.stop();
this._service = new WebSpeech();
this._service.start(options);
if (SpeechToElement.stop()) return;
GlobalState.service = new WebSpeech();
GlobalState.service.start(options);
}

public static isWebSpeechSupported() {
return !!WebSpeech.getAPI();
}

public static startAzure(options: Options & AzureOptions) {
SpeechToElement.stop();
this._service = new Azure();
this._service.start(options);
if (SpeechToElement.stop() || GlobalState.service?.cannotBeStopped) return;
GlobalState.service = new Azure();
GlobalState.service.start(options);
}

public static stop() {
if (this._service?.recognizing) {
this._service.stop();
if (GlobalState.doubleClickDetector()) return true;
if (GlobalState.service?.recognizing) {
GlobalState.service.stop();
}
return false;
}

public static endCommandMode() {
if (this._service) CommandUtils.toggleCommandModeOff(this._service);
if (GlobalState.service) CommandUtils.toggleCommandModeOff(GlobalState.service);
}
}
7 changes: 5 additions & 2 deletions component/src/services/azure/azure.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Recognizer, SpeechRecognitionEventArgs} from 'microsoft-cognitiveservices-speech-sdk';
import {AzureOptions, Options, Translations} from '../../types/options';
import * as sdk from 'microsoft-cognitiveservices-speech-sdk';
import {PreventConnectionStop} from './preventConnectionStop';
import {AzureSpeechConfig} from './azureSpeechConfig';
import {StopTimeout} from '../../utils/stopTimeout';
import {AzureTranscript} from './azureTranscript';
Expand All @@ -12,21 +13,22 @@ declare global {
}
}

// WORK - prevent problems if user clicks the microphone multiple times
// REF - https://github.com/Azure-Samples/cognitive-services-speech-sdk/blob/master/samples/js/browser/index.html#L240
export class Azure extends Speech {
// when service is manually stopped events are still fired, this is used to stop more text being added
private _stopping?: boolean;
private _service?: sdk.SpeechRecognizer;
private _translations?: Translations;
private _retrieveTokenInterval?: NodeJS.Timeout;
_manualConnectionStopPrevention?: NodeJS.Timeout;
private _newTextPadding = ''; // Unlike webspeech there is no automatic space between final results

start(options: Options & AzureOptions) {
start(options: Options & AzureOptions, isDuringReset?: boolean) {
this._newTextPadding = '';
if (this.stopTimeout === undefined) StopTimeout.reset(this, options?.stopAfterSilenceMs);
this.prepareBeforeStart(options); // need to prepare before validation to set onError
this.startAsync(options);
if (!isDuringReset) PreventConnectionStop.applyPrevention(this);
}

private async startAsync(options: Options & AzureOptions) {
Expand Down Expand Up @@ -117,6 +119,7 @@ export class Azure extends Speech {
}

private onSessionStarted() {
PreventConnectionStop.clearPrevention(this);
this.setStateOnStart();
}

Expand Down
18 changes: 18 additions & 0 deletions component/src/services/azure/preventConnectionStop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {Azure} from './azure';

// This is a mechanism used to prevent two azure services starting at once as it cannot actually be stopped when connecting
// however if it does not connect in 800 milliseconds, the user has an option to start a new connection
export class PreventConnectionStop {
public static applyPrevention(azure: Azure) {
clearTimeout(azure._manualConnectionStopPrevention);
azure.cannotBeStopped = true;
azure._manualConnectionStopPrevention = setTimeout(() => {
azure.cannotBeStopped = false;
}, 800);
}

public static clearPrevention(azure: Azure) {
clearTimeout(azure._manualConnectionStopPrevention);
azure.cannotBeStopped = false;
}
}
5 changes: 3 additions & 2 deletions component/src/speech.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export abstract class Speech {
commands?: InternalCommands;
isWaitingForCommand = false;
isTargetInShadow = false;
cannotBeStopped = false; // this is mostly used for Azure to prevent user from stopping when it is connecting

constructor() {
this.resetState();
Expand Down Expand Up @@ -112,7 +113,7 @@ export abstract class Speech {
this.isRestarting = true;
this.stop(true);
this.resetState(true);
this.start(options);
this.start(options, true);
}

// prettier-ignore
Expand Down Expand Up @@ -231,7 +232,7 @@ export abstract class Speech {
this.recognizing = false;
}

abstract start(options?: Options): void;
abstract start(options?: Options, isDuringReset?: boolean): void;

abstract stop(isDuringReset?: boolean): void;
}
15 changes: 15 additions & 0 deletions component/src/utils/globalState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Speech} from '../speech';

export class GlobalState {
public static service: Speech | undefined;
private static doubleClickPending = false;

public static doubleClickDetector() {
if (GlobalState.doubleClickPending) return true;
GlobalState.doubleClickPending = true;
setTimeout(() => {
GlobalState.doubleClickPending = false;
}, 300);
return false;
}
}

0 comments on commit a06e041

Please sign in to comment.