{"version":3,"sources":["src/common.speech/SynthesisTurn.ts"],"names":[],"mappings":"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","file":"SynthesisTurn.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT license.\r\n\r\nimport {\r\n    createNoDashGuid,\r\n    Deferred,\r\n    Events, IAudioDestination\r\n} from \"../common/Exports\";\r\nimport { AudioOutputFormatImpl } from \"../sdk/Audio/AudioOutputFormat\";\r\nimport { PullAudioOutputStreamImpl } from \"../sdk/Audio/AudioOutputStream\";\r\nimport { ISynthesisMetadata } from \"./ServiceMessages/SynthesisAudioMetadata\";\r\nimport { SynthesisAdapterBase } from \"./SynthesisAdapterBase\";\r\nimport {\r\n    ConnectingToSynthesisServiceEvent,\r\n    SpeechSynthesisEvent,\r\n    SynthesisStartedEvent,\r\n    SynthesisTriggeredEvent,\r\n} from \"./SynthesisEvents\";\r\n\r\nexport interface ISynthesisResponseContext {\r\n    serviceTag: string;\r\n}\r\n\r\nexport interface ISynthesisResponseAudio {\r\n    type: string;\r\n    streamId: string;\r\n}\r\n\r\nexport interface ISynthesisResponse {\r\n    context: ISynthesisResponseContext;\r\n    audio: ISynthesisResponseAudio;\r\n}\r\n\r\nexport class SynthesisTurn {\r\n\r\n    public get requestId(): string {\r\n        return this.privRequestId;\r\n    }\r\n\r\n    public get streamId(): string {\r\n        return this.privStreamId;\r\n    }\r\n\r\n    public set streamId(value: string) {\r\n        this.privStreamId = value;\r\n    }\r\n\r\n    public get audioOutputFormat(): AudioOutputFormatImpl {\r\n        return this.privAudioOutputFormat;\r\n    }\r\n\r\n    public set audioOutputFormat(format: AudioOutputFormatImpl) {\r\n        this.privAudioOutputFormat = format;\r\n    }\r\n\r\n    public get turnCompletionPromise(): Promise<void> {\r\n        return this.privTurnDeferral.promise;\r\n    }\r\n\r\n    public get isSynthesisEnded(): boolean {\r\n        return this.privIsSynthesisEnded;\r\n    }\r\n\r\n    public get isSynthesizing(): boolean {\r\n        return this.privIsSynthesizing;\r\n    }\r\n\r\n    public get currentTextOffset(): number {\r\n        return this.privTextOffset;\r\n    }\r\n\r\n    // The number of bytes received for current turn\r\n    public get bytesReceived(): number {\r\n        return this.privBytesReceived;\r\n    }\r\n\r\n    private privIsDisposed: boolean = false;\r\n    private privAuthFetchEventId: string;\r\n    private privIsSynthesizing: boolean = false;\r\n    private privIsSynthesisEnded: boolean = false;\r\n    private privBytesReceived: number = 0;\r\n    private privRequestId: string;\r\n    private privStreamId: string;\r\n    private privTurnDeferral: Deferred<void>;\r\n    private privInTurn: boolean = false;\r\n    private privAudioOutputFormat: AudioOutputFormatImpl;\r\n    private privAudioOutputStream: PullAudioOutputStreamImpl;\r\n    private privReceivedAudio: ArrayBuffer;\r\n    private privReceivedAudioWithHeader: ArrayBuffer;\r\n    private privTextOffset: number = 0;\r\n    private privNextSearchTextIndex: number = 0;\r\n    private privPartialVisemeAnimation: string;\r\n    private privRawText: string;\r\n    private privIsSSML: boolean;\r\n    private privTurnAudioDestination: IAudioDestination;\r\n\r\n    constructor() {\r\n        this.privRequestId = createNoDashGuid();\r\n        this.privTurnDeferral = new Deferred<void>();\r\n\r\n        // We're not in a turn, so resolve.\r\n        this.privTurnDeferral.resolve();\r\n    }\r\n\r\n    public async getAllReceivedAudio(): Promise<ArrayBuffer> {\r\n        if (!!this.privReceivedAudio) {\r\n            return Promise.resolve(this.privReceivedAudio);\r\n        }\r\n        if (!this.privIsSynthesisEnded) {\r\n            return null;\r\n        }\r\n        await this.readAllAudioFromStream();\r\n        return Promise.resolve(this.privReceivedAudio);\r\n    }\r\n\r\n    public async getAllReceivedAudioWithHeader(): Promise<ArrayBuffer> {\r\n        if (!!this.privReceivedAudioWithHeader) {\r\n            return this.privReceivedAudioWithHeader;\r\n        }\r\n        if (!this.privIsSynthesisEnded) {\r\n            return null;\r\n        }\r\n        if (this.audioOutputFormat.hasHeader) {\r\n            const audio: ArrayBuffer = await this.getAllReceivedAudio();\r\n            this.privReceivedAudioWithHeader = SynthesisAdapterBase.addHeader(audio, this.audioOutputFormat);\r\n            return this.privReceivedAudioWithHeader;\r\n        } else {\r\n            return this.getAllReceivedAudio();\r\n        }\r\n    }\r\n\r\n    public startNewSynthesis(requestId: string, rawText: string, isSSML: boolean, audioDestination?: IAudioDestination): void {\r\n        this.privIsSynthesisEnded = false;\r\n        this.privIsSynthesizing = true;\r\n        this.privRequestId = requestId;\r\n        this.privRawText = rawText;\r\n        this.privIsSSML = isSSML;\r\n        this.privAudioOutputStream = new PullAudioOutputStreamImpl();\r\n        this.privAudioOutputStream.format = this.privAudioOutputFormat;\r\n        this.privReceivedAudio = null;\r\n        this.privReceivedAudioWithHeader = null;\r\n        this.privBytesReceived = 0;\r\n        this.privTextOffset = 0;\r\n        this.privNextSearchTextIndex = 0;\r\n        this.privPartialVisemeAnimation = \"\";\r\n        if (audioDestination !== undefined) {\r\n            this.privTurnAudioDestination = audioDestination;\r\n            this.privTurnAudioDestination.format = this.privAudioOutputFormat;\r\n        }\r\n        this.onEvent(new SynthesisTriggeredEvent(this.requestId, undefined, audioDestination === undefined ? undefined : audioDestination.id()));\r\n    }\r\n\r\n    public onPreConnectionStart = (authFetchEventId: string, connectionId: string): void => {\r\n        this.privAuthFetchEventId = authFetchEventId;\r\n        this.onEvent(new ConnectingToSynthesisServiceEvent(this.privRequestId, this.privAuthFetchEventId));\r\n    }\r\n\r\n    public onAuthCompleted = (isError: boolean, error?: string): void => {\r\n        if (isError) {\r\n            this.onComplete();\r\n        }\r\n    }\r\n\r\n    public onConnectionEstablishCompleted = (statusCode: number, reason?: string): void => {\r\n        if (statusCode === 200) {\r\n            this.onEvent(new SynthesisStartedEvent(this.requestId, this.privAuthFetchEventId));\r\n            this.privBytesReceived = 0;\r\n            return;\r\n        } else if (statusCode === 403) {\r\n            this.onComplete();\r\n        }\r\n    }\r\n\r\n    public onServiceResponseMessage = (responseJson: string): void => {\r\n        const response: ISynthesisResponse = JSON.parse(responseJson);\r\n        this.streamId = response.audio.streamId;\r\n    }\r\n\r\n    public onServiceTurnEndResponse = (): void => {\r\n        this.privInTurn = false;\r\n        this.privTurnDeferral.resolve();\r\n        this.onComplete();\r\n    }\r\n\r\n    public onServiceTurnStartResponse = (): void => {\r\n        if (!!this.privTurnDeferral && !!this.privInTurn) {\r\n            // What? How are we starting a turn with another not done?\r\n            this.privTurnDeferral.reject(\"Another turn started before current completed.\");\r\n            // Avoid UnhandledPromiseRejection if privTurnDeferral is not being awaited\r\n            /* tslint:disable:no-empty */\r\n            this.privTurnDeferral.promise.then().catch(() => { });\r\n        }\r\n        this.privInTurn = true;\r\n        this.privTurnDeferral = new Deferred<void>();\r\n    }\r\n\r\n    public onAudioChunkReceived(data: ArrayBuffer): void {\r\n        if (this.isSynthesizing) {\r\n            this.privAudioOutputStream.write(data);\r\n            this.privBytesReceived += data.byteLength;\r\n            if (this.privTurnAudioDestination !== undefined) {\r\n                this.privTurnAudioDestination.write(data);\r\n            }\r\n        }\r\n    }\r\n\r\n    public onWordBoundaryEvent(text: string): void {\r\n        this.updateTextOffset(text);\r\n    }\r\n\r\n    public onVisemeMetadataReceived(metadata: ISynthesisMetadata): void {\r\n        if (metadata.Data.AnimationChunk !== undefined) {\r\n            this.privPartialVisemeAnimation += metadata.Data.AnimationChunk;\r\n        }\r\n    }\r\n\r\n    public dispose = (error?: string): void => {\r\n        if (!this.privIsDisposed) {\r\n            // we should have completed by now. If we did not its an unknown error.\r\n            this.privIsDisposed = true;\r\n        }\r\n    }\r\n\r\n    public onStopSynthesizing(): void {\r\n        this.onComplete();\r\n    }\r\n\r\n    /**\r\n     * Gets the viseme animation string (merged from animation chunk), and clears the internal\r\n     * partial animation.\r\n     */\r\n    public getAndClearVisemeAnimation(): string {\r\n        const animation: string = this.privPartialVisemeAnimation;\r\n        this.privPartialVisemeAnimation = \"\";\r\n        return animation;\r\n    }\r\n\r\n    protected onEvent = (event: SpeechSynthesisEvent): void => {\r\n        Events.instance.onEvent(event);\r\n    }\r\n\r\n    private updateTextOffset(text: string): void {\r\n        if (this.privTextOffset >= 0) {\r\n            this.privTextOffset = this.privRawText.indexOf(text, this.privNextSearchTextIndex);\r\n            if (this.privTextOffset >= 0) {\r\n                this.privNextSearchTextIndex = this.privTextOffset + text.length;\r\n            }\r\n            if (this.privIsSSML) {\r\n                if (this.privRawText.indexOf(\"<\", this.privTextOffset + 1) > this.privRawText.indexOf(\">\", this.privTextOffset + 1)) {\r\n                    this.updateTextOffset(text);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private onComplete = (): void => {\r\n        if (this.privIsSynthesizing) {\r\n            this.privIsSynthesizing = false;\r\n            this.privIsSynthesisEnded = true;\r\n            this.privAudioOutputStream.close();\r\n            this.privInTurn = false;\r\n            if (this.privTurnAudioDestination !== undefined) {\r\n                this.privTurnAudioDestination.close();\r\n                this.privTurnAudioDestination = undefined;\r\n            }\r\n        }\r\n    }\r\n\r\n    private async readAllAudioFromStream(): Promise<void> {\r\n        if (this.privIsSynthesisEnded) {\r\n            this.privReceivedAudio = new ArrayBuffer(this.bytesReceived);\r\n            try {\r\n                await this.privAudioOutputStream.read(this.privReceivedAudio);\r\n            } catch (e) {\r\n                this.privReceivedAudio = new ArrayBuffer(0);\r\n            }\r\n        }\r\n    }\r\n}\r\n"]}