{"version":3,"sources":["src/sdk/Transcription/ConversationTranslator.ts"],"names":[],"mappings":"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","file":"ConversationTranslator.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT license.\r\n// Multi-device Conversation is a Preview feature.\r\n\r\nimport { ConversationConnectionConfig } from \"../../common.speech/Exports\";\r\nimport {\r\n    IDisposable,\r\n    IErrorMessages,\r\n    marshalPromiseToCallbacks\r\n} from \"../../common/Exports\";\r\nimport { Contracts } from \"../Contracts\";\r\nimport {\r\n    AudioConfig,\r\n    CancellationErrorCode,\r\n    CancellationReason,\r\n    ProfanityOption,\r\n    PropertyCollection,\r\n    PropertyId,\r\n    SessionEventArgs,\r\n    SpeechTranslationConfig,\r\n    TranslationRecognitionCanceledEventArgs,\r\n    TranslationRecognitionEventArgs,\r\n    TranslationRecognizer\r\n} from \"../Exports\";\r\nimport { ConversationImpl } from \"./Conversation\";\r\nimport {\r\n    ConversationCommon,\r\n    ConversationExpirationEventArgs,\r\n    ConversationHandler,\r\n    ConversationParticipantsChangedEventArgs,\r\n    ConversationTranslationCanceledEventArgs,\r\n    ConversationTranslationEventArgs,\r\n    IConversationTranslator,\r\n    Participant,\r\n} from \"./Exports\";\r\nimport { Callback, IConversation } from \"./IConversation\";\r\n\r\nexport enum SpeechState {\r\n    Inactive, Connecting, Connected\r\n}\r\n\r\n// tslint:disable:max-classes-per-file\r\n\r\n// child class of TranslationRecognizer meant only for use with ConversationTranslator\r\nclass ConversationTranslationRecognizer extends TranslationRecognizer {\r\n    private privTranslator: ConversationTranslator;\r\n    private privSpeechState: SpeechState;\r\n    public constructor(speechConfig: SpeechTranslationConfig, audioConfig?: AudioConfig, translator?: ConversationTranslator) {\r\n        super(speechConfig, audioConfig);\r\n        this.privSpeechState = SpeechState.Inactive;\r\n        if (!!translator) {\r\n            this.privTranslator = translator;\r\n            this.sessionStarted = () => {\r\n                this.privSpeechState = SpeechState.Connected;\r\n            };\r\n\r\n            this.sessionStopped = () => {\r\n                this.privSpeechState = SpeechState.Inactive;\r\n            };\r\n\r\n            this.recognized = async (tr: TranslationRecognizer, e: TranslationRecognitionEventArgs) => {\r\n                // TODO: add support for getting recognitions from here if own speech\r\n\r\n                // if there is an error connecting to the conversation service from the speech service the error will be returned in the ErrorDetails field.\r\n                if (e.result?.errorDetails) {\r\n                    await this.cancelSpeech();\r\n                    // TODO: format the error message contained in 'errorDetails'\r\n                    this.fireCancelEvent(e.result.errorDetails);\r\n                }\r\n            };\r\n\r\n            this.canceled = async (r: TranslationRecognizer, e: TranslationRecognitionCanceledEventArgs) => {\r\n                if (this.privSpeechState !== SpeechState.Inactive) {\r\n                    try {\r\n                        await this.cancelSpeech();\r\n                    } catch (error) {\r\n                        this.privSpeechState = SpeechState.Inactive;\r\n                    }\r\n                }\r\n            };\r\n        }\r\n    }\r\n    public get state(): SpeechState {\r\n        return this.privSpeechState;\r\n    }\r\n\r\n    public set state(newState: SpeechState) {\r\n        this.privSpeechState = newState;\r\n    }\r\n\r\n    public onConnection(): void {\r\n        this.privSpeechState = SpeechState.Connected;\r\n    }\r\n\r\n    public async onDisconnection(): Promise<void> {\r\n        this.privSpeechState = SpeechState.Inactive;\r\n        await this.cancelSpeech();\r\n    }\r\n\r\n    /**\r\n     * Fire a cancel event\r\n     * @param error\r\n     */\r\n    private fireCancelEvent(error: any): void {\r\n        try {\r\n            if (!!this.privTranslator.canceled) {\r\n                const cancelEvent: ConversationTranslationCanceledEventArgs = new ConversationTranslationCanceledEventArgs(\r\n                    error?.reason ?? CancellationReason.Error,\r\n                    error?.errorDetails ?? error,\r\n                    error?.errorCode ?? CancellationErrorCode.RuntimeError,\r\n                    undefined,\r\n                    error?.sessionId);\r\n\r\n                this.privTranslator.canceled(this.privTranslator, cancelEvent);\r\n            }\r\n        } catch (e) {\r\n            //\r\n        }\r\n    }\r\n\r\n    private async cancelSpeech(): Promise<void> {\r\n        try {\r\n            this.stopContinuousRecognitionAsync();\r\n            await this.privReco?.disconnect();\r\n            this.privSpeechState = SpeechState.Inactive;\r\n        } catch (e) {\r\n            // ignore the error\r\n        }\r\n    }\r\n\r\n}\r\n\r\n/***\r\n * Join, leave or connect to a conversation.\r\n */\r\nexport class ConversationTranslator extends ConversationCommon implements IConversationTranslator, IDisposable {\r\n\r\n    private privSpeechRecognitionLanguage: string;\r\n    private privProperties: PropertyCollection;\r\n    private privIsDisposed: boolean = false;\r\n    private privCTRecognizer: ConversationTranslationRecognizer;\r\n    private privIsSpeaking: boolean = false;\r\n    private privConversation: ConversationImpl;\r\n    private privErrors: IErrorMessages = ConversationConnectionConfig.restErrors;\r\n    private privPlaceholderKey: string = \"abcdefghijklmnopqrstuvwxyz012345\";\r\n    private privPlaceholderRegion: string = \"westus\";\r\n\r\n    public constructor(audioConfig?: AudioConfig) {\r\n        super(audioConfig);\r\n        this.privProperties = new PropertyCollection();\r\n    }\r\n\r\n    public get properties(): PropertyCollection {\r\n        return this.privProperties;\r\n    }\r\n\r\n    public get speechRecognitionLanguage(): string {\r\n        return this.privSpeechRecognitionLanguage;\r\n    }\r\n\r\n    public get participants(): Participant[] {\r\n        return this.privConversation?.participants;\r\n    }\r\n\r\n    public canceled: (sender: ConversationHandler, event: ConversationTranslationCanceledEventArgs) => void;\r\n    public conversationExpiration: (sender: IConversationTranslator, event: ConversationExpirationEventArgs) => void;\r\n    public participantsChanged: (sender: IConversationTranslator, event: ConversationParticipantsChangedEventArgs) => void;\r\n    public sessionStarted: (sender: ConversationHandler, event: SessionEventArgs) => void;\r\n    public sessionStopped: (sender: ConversationHandler, event: SessionEventArgs) => void;\r\n    public textMessageReceived: (sender: IConversationTranslator, event: ConversationTranslationEventArgs) => void;\r\n    public transcribed: (sender: IConversationTranslator, event: ConversationTranslationEventArgs) => void;\r\n    public transcribing: (sender: IConversationTranslator, event: ConversationTranslationEventArgs) => void;\r\n\r\n    /**\r\n     * Join a conversation. If this is the host, pass in the previously created Conversation object.\r\n     * @param conversation\r\n     * @param nickname\r\n     * @param lang\r\n     * @param cb\r\n     * @param err\r\n     */\r\n    public joinConversationAsync(conversation: IConversation, nickname: string, cb?: Callback, err?: Callback): void;\r\n    public joinConversationAsync(conversationId: string, nickname: string, lang: string, cb?: Callback, err?: Callback): void;\r\n    public joinConversationAsync(conversation: any, nickname: string, param1?: string | Callback, param2?: Callback, param3?: Callback): void {\r\n\r\n        try {\r\n\r\n            if (typeof conversation === \"string\") {\r\n\r\n                Contracts.throwIfNullOrUndefined(conversation, this.privErrors.invalidArgs.replace(\"{arg}\", \"conversation id\"));\r\n                Contracts.throwIfNullOrWhitespace(nickname, this.privErrors.invalidArgs.replace(\"{arg}\", \"nickname\"));\r\n\r\n                if (!!this.privConversation) {\r\n                    this.handleError(new Error(this.privErrors.permissionDeniedStart), param3);\r\n                }\r\n\r\n                let lang: string = param1 as string;\r\n                if (lang === undefined || lang === null || lang === \"\") { lang = ConversationConnectionConfig.defaultLanguageCode; }\r\n\r\n                // create a placeholder config\r\n                this.privSpeechTranslationConfig = SpeechTranslationConfig.fromSubscription(\r\n                    this.privPlaceholderKey,\r\n                    this.privPlaceholderRegion);\r\n                this.privSpeechTranslationConfig.setProfanity(ProfanityOption.Masked);\r\n                this.privSpeechTranslationConfig.addTargetLanguage(lang);\r\n                this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.SpeechServiceConnection_RecoLanguage], lang);\r\n                this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.ConversationTranslator_Name], nickname);\r\n\r\n                const endpoint: string = this.privProperties.getProperty(PropertyId.ConversationTranslator_Host);\r\n                if (endpoint) {\r\n                    this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.ConversationTranslator_Host], endpoint);\r\n                }\r\n                const speechEndpointHost: string = this.privProperties.getProperty(PropertyId.SpeechServiceConnection_Host);\r\n                if (speechEndpointHost) {\r\n                    this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.SpeechServiceConnection_Host], speechEndpointHost);\r\n                }\r\n\r\n                // join the conversation\r\n                this.privConversation = new ConversationImpl(this.privSpeechTranslationConfig);\r\n                this.privConversation.conversationTranslator = this;\r\n\r\n                this.privConversation.joinConversationAsync(\r\n                    conversation,\r\n                    nickname,\r\n                    lang,\r\n                    ((result: string) => {\r\n\r\n                        if (!result) {\r\n                            this.handleError(new Error(this.privErrors.permissionDeniedConnect), param3);\r\n                        }\r\n\r\n                        this.privSpeechTranslationConfig.authorizationToken = result;\r\n\r\n                        // connect to the ws\r\n                        this.privConversation.startConversationAsync(\r\n                            (() => {\r\n                                this.handleCallback(param2, param3);\r\n                            }),\r\n                            ((error: any) => {\r\n                                this.handleError(error, param3);\r\n                            }));\r\n\r\n                    }),\r\n                    ((error: any) => {\r\n                        this.handleError(error, param3);\r\n                    }));\r\n\r\n            } else if (typeof conversation === \"object\") {\r\n\r\n                Contracts.throwIfNullOrUndefined(conversation, this.privErrors.invalidArgs.replace(\"{arg}\", \"conversation id\"));\r\n                Contracts.throwIfNullOrWhitespace(nickname, this.privErrors.invalidArgs.replace(\"{arg}\", \"nickname\"));\r\n\r\n                // save the nickname\r\n                this.privProperties.setProperty(PropertyId.ConversationTranslator_Name, nickname);\r\n                // ref the conversation object\r\n                this.privConversation = conversation as ConversationImpl;\r\n                // ref the conversation translator object\r\n                this.privConversation.conversationTranslator = this;\r\n\r\n                Contracts.throwIfNullOrUndefined(this.privConversation, this.privErrors.permissionDeniedConnect);\r\n                Contracts.throwIfNullOrUndefined(this.privConversation.room.token, this.privErrors.permissionDeniedConnect);\r\n\r\n                this.privSpeechTranslationConfig = conversation.config;\r\n\r\n                this.handleCallback(param1 as Callback, param2);\r\n            } else {\r\n                this.handleError(\r\n                    new Error(this.privErrors.invalidArgs.replace(\"{arg}\", \"invalid conversation type\")),\r\n                    param2);\r\n            }\r\n\r\n        } catch (error) {\r\n            this.handleError(error, typeof param1 === \"string\" ? param3 : param2);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Leave the conversation\r\n     * @param cb\r\n     * @param err\r\n     */\r\n    public leaveConversationAsync(cb?: Callback, err?: Callback): void {\r\n\r\n        marshalPromiseToCallbacks((async (): Promise<void> => {\r\n\r\n            // stop the speech websocket\r\n            await this.cancelSpeech();\r\n            // stop the websocket\r\n            await this.privConversation.endConversationImplAsync();\r\n            // https delete request\r\n            await this.privConversation.deleteConversationImplAsync();\r\n            this.dispose();\r\n\r\n        })(), cb, err);\r\n    }\r\n\r\n    /**\r\n     * Send a text message\r\n     * @param message\r\n     * @param cb\r\n     * @param err\r\n     */\r\n    public sendTextMessageAsync(message: string, cb?: Callback, err?: Callback): void {\r\n\r\n        try {\r\n            Contracts.throwIfNullOrUndefined(this.privConversation, this.privErrors.permissionDeniedSend);\r\n            Contracts.throwIfNullOrWhitespace(message, this.privErrors.invalidArgs.replace(\"{arg}\", message));\r\n\r\n            this.privConversation?.sendTextMessageAsync(message, cb, err);\r\n        } catch (error) {\r\n\r\n            this.handleError(error, err);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Start speaking\r\n     * @param cb\r\n     * @param err\r\n     */\r\n    public startTranscribingAsync(cb?: Callback, err?: Callback): void {\r\n        marshalPromiseToCallbacks((async (): Promise<void> => {\r\n            try {\r\n                Contracts.throwIfNullOrUndefined(this.privConversation, this.privErrors.permissionDeniedSend);\r\n                Contracts.throwIfNullOrUndefined(this.privConversation.room.token, this.privErrors.permissionDeniedConnect);\r\n\r\n                if (this.privCTRecognizer === undefined) {\r\n                    await this.connectTranslatorRecognizer();\r\n                }\r\n                Contracts.throwIfNullOrUndefined(this.privCTRecognizer, this.privErrors.permissionDeniedSend);\r\n\r\n                if (!this.canSpeak) {\r\n                    this.handleError(new Error(this.privErrors.permissionDeniedSend), err);\r\n                }\r\n\r\n                await this.startContinuousRecognition();\r\n\r\n                this.privIsSpeaking = true;\r\n            } catch (error) {\r\n                this.privIsSpeaking = false;\r\n                await this.cancelSpeech();\r\n                throw error;\r\n            }\r\n        })(), cb, err);\r\n    }\r\n\r\n    /**\r\n     * Stop speaking\r\n     * @param cb\r\n     * @param err\r\n     */\r\n    public stopTranscribingAsync(cb?: Callback, err?: Callback): void {\r\n        marshalPromiseToCallbacks((async (): Promise<void> => {\r\n            try {\r\n                if (!this.privIsSpeaking) {\r\n                    // stop speech\r\n                    await this.cancelSpeech();\r\n                    return;\r\n                }\r\n\r\n                // stop the recognition but leave the websocket open\r\n                this.privIsSpeaking = false;\r\n                await new Promise((resolve: () => void, reject: (error: string) => void): void => {\r\n                    this.privCTRecognizer?.stopContinuousRecognitionAsync(resolve, reject);\r\n                });\r\n\r\n            } catch (error) {\r\n                await this.cancelSpeech();\r\n            }\r\n        })(), cb, err);\r\n    }\r\n\r\n    public isDisposed(): boolean {\r\n        return this.privIsDisposed;\r\n    }\r\n\r\n    public dispose(reason?: string, success?: () => void, err?: (error: string) => void): void {\r\n        marshalPromiseToCallbacks((async (): Promise<void> => {\r\n            if (this.isDisposed && !this.privIsSpeaking) {\r\n                return;\r\n            }\r\n            await this.cancelSpeech();\r\n            this.privIsDisposed = true;\r\n            this.privSpeechTranslationConfig?.close();\r\n            this.privSpeechRecognitionLanguage = undefined;\r\n            this.privProperties = undefined;\r\n            this.privAudioConfig = undefined;\r\n            this.privSpeechTranslationConfig = undefined;\r\n            this.privConversation?.dispose();\r\n            this.privConversation = undefined;\r\n        })(), success, err);\r\n    }\r\n\r\n    /**\r\n     * Cancel the speech websocket\r\n     */\r\n    private async cancelSpeech(): Promise<void> {\r\n        try {\r\n            this.privIsSpeaking = false;\r\n            await this.privCTRecognizer?.onDisconnection();\r\n            this.privCTRecognizer = undefined;\r\n        } catch (e) {\r\n            // ignore the error\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Connect to the speech translation recognizer.\r\n     * Currently there is no language validation performed before sending the SpeechLanguage code to the service.\r\n     * If it's an invalid language the raw error will be: 'Error during WebSocket handshake: Unexpected response code: 400'\r\n     * e.g. pass in 'fr' instead of 'fr-FR', or a text-only language 'cy'\r\n     */\r\n    private async connectTranslatorRecognizer(): Promise<void> {\r\n        try {\r\n\r\n            if (this.privAudioConfig === undefined) {\r\n                this.privAudioConfig = AudioConfig.fromDefaultMicrophoneInput();\r\n            }\r\n\r\n            // clear the temp subscription key if it's a participant joining\r\n            if (this.privSpeechTranslationConfig.getProperty(PropertyId[PropertyId.SpeechServiceConnection_Key])\r\n                === this.privPlaceholderKey) {\r\n                this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.SpeechServiceConnection_Key], \"\");\r\n            }\r\n\r\n            // TODO\r\n            const token: string = encodeURIComponent(this.privConversation.room.token);\r\n\r\n            let endpointHost: string = this.privSpeechTranslationConfig.getProperty(\r\n                PropertyId[PropertyId.SpeechServiceConnection_Host], ConversationConnectionConfig.speechHost);\r\n            endpointHost = endpointHost.replace(\"{region}\", this.privConversation.room.cognitiveSpeechRegion);\r\n\r\n            const url: string = `wss://${endpointHost}${ConversationConnectionConfig.speechPath}?${ConversationConnectionConfig.configParams.token}=${token}`;\r\n\r\n            this.privSpeechTranslationConfig.setProperty(PropertyId[PropertyId.SpeechServiceConnection_Endpoint], url);\r\n\r\n            this.privCTRecognizer = new ConversationTranslationRecognizer(this.privSpeechTranslationConfig, this.privAudioConfig, this);\r\n        } catch (error) {\r\n            await this.cancelSpeech();\r\n            throw error;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Handle the start speaking request\r\n     */\r\n    private startContinuousRecognition(): Promise<void> {\r\n        return new Promise((resolve: () => void, reject: (error: string) => void): void => {\r\n            this.privCTRecognizer.startContinuousRecognitionAsync(resolve, reject);\r\n        });\r\n    }\r\n\r\n    private get canSpeak(): boolean {\r\n\r\n        // is there a Conversation websocket available and has the Recognizer been set up\r\n        if (!this.privConversation.isConnected || !this.privCTRecognizer) {\r\n            return false;\r\n        }\r\n\r\n        // is the user already speaking\r\n        if (this.privIsSpeaking || this.privCTRecognizer.state === SpeechState.Connected || this.privCTRecognizer.state === SpeechState.Connecting) {\r\n            return false;\r\n        }\r\n\r\n        // is the user muted\r\n        if (this.privConversation.isMutedByHost) {\r\n            return false;\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n}\r\n"]}