{"version":3,"sources":["src/common.speech/RequestSession.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAKH,YAAY,EACZ,aAAa,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAKH,sBAAsB,EACzB,MAAM,wBAAwB,CAAC;AAGhC,qBAAa,cAAc;IACvB,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,4BAA4B,CAA2B;IAC/D,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,uBAAuB,CAAkB;IACjD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,wBAAwB,CAAa;IAC7C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,sBAAsB,CAAkB;IAChD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,wBAAwB,CAAa;IAC7C,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAiB;IACzC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,sBAAsB,CAAa;gBAExB,aAAa,EAAE,MAAM;IAUxC,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,IAAW,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD;IAED,IAAW,aAAa,IAAI,OAAO,CAElC;IAED,IAAW,aAAa,IAAI,OAAO,CAElC;IAED,IAAW,sBAAsB,IAAI,MAAM,CAE1C;IAED,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,IAAW,qBAAqB,IAAI,MAAM,CAEzC;IAID,IAAW,SAAS,IAAI,MAAM,CAE7B;IAID,IAAW,oBAAoB,IAAI,MAAM,CAExC;IAEM,yBAAyB,CAAC,WAAW,EAAE,YAAY,CAAC,aAAa,CAAC,GAAG,IAAI;IAMzE,mBAAmB,IAAI,IAAI;IAWrB,4BAA4B,CAAC,SAAS,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnG,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAMpE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD,8BAA8B,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclF,wBAAwB,CAAC,qBAAqB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAa7E,eAAe,IAAI,IAAI;IAIvB,0BAA0B,IAAI,IAAI;IAYlC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IASpC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO1C,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOzC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKpC,iBAAiB,IAAI,IAAI;IAInB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B,YAAY,IAAI,MAAM;IAQhB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxC,aAAa,IAAI,IAAI;IAI5B,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;YAOxC,UAAU;YAOV,eAAe;CAQhC","file":"RequestSession.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT license.\r\n\r\nimport { ReplayableAudioNode } from \"../common.browser/Exports.js\";\r\nimport {\r\n    createNoDashGuid,\r\n    Deferred,\r\n    Events,\r\n    IDetachable,\r\n    IEventSource,\r\n    PlatformEvent\r\n} from \"../common/Exports.js\";\r\nimport {\r\n    ConnectingToServiceEvent,\r\n    ListeningStartedEvent,\r\n    RecognitionStartedEvent,\r\n    RecognitionTriggeredEvent,\r\n    SpeechRecognitionEvent,\r\n} from \"./RecognitionEvents.js\";\r\nimport { ServiceTelemetryListener } from \"./ServiceTelemetryListener.Internal.js\";\r\n\r\nexport class RequestSession {\r\n    private privIsDisposed: boolean = false;\r\n    private privServiceTelemetryListener: ServiceTelemetryListener;\r\n    private privDetachables: IDetachable[] = new Array<IDetachable>();\r\n    private privRequestId: string;\r\n    private privAudioSourceId: string;\r\n    private privAudioNodeId: string;\r\n    private privAudioNode: ReplayableAudioNode;\r\n    private privAuthFetchEventId: string;\r\n    private privIsAudioNodeDetached: boolean = false;\r\n    private privIsRecognizing: boolean = false;\r\n    private privIsSpeechEnded: boolean = false;\r\n    private privTurnStartAudioOffset: number = 0;\r\n    private privLastRecoOffset: number = 0;\r\n    private privHypothesisReceived: boolean = false;\r\n    private privBytesSent: number = 0;\r\n    private privRecognitionBytesSent: number = 0;\r\n    private privRecogNumber: number = 0;\r\n    private privSessionId: string;\r\n    private privTurnDeferral: Deferred<void>;\r\n    private privInTurn: boolean = false;\r\n    private privConnectionAttempts: number = 0;\r\n\r\n    public constructor(audioSourceId: string) {\r\n        this.privAudioSourceId = audioSourceId;\r\n        this.privRequestId = createNoDashGuid();\r\n        this.privAudioNodeId = 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 get sessionId(): string {\r\n        return this.privSessionId;\r\n    }\r\n\r\n    public get requestId(): string {\r\n        return this.privRequestId;\r\n    }\r\n\r\n    public get audioNodeId(): string {\r\n        return this.privAudioNodeId;\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 isSpeechEnded(): boolean {\r\n        return this.privIsSpeechEnded;\r\n    }\r\n\r\n    public get isRecognizing(): boolean {\r\n        return this.privIsRecognizing;\r\n    }\r\n\r\n    public get currentTurnAudioOffset(): number {\r\n        return this.privTurnStartAudioOffset;\r\n    }\r\n\r\n    public get recogNumber(): number {\r\n        return this.privRecogNumber;\r\n    }\r\n\r\n    public get numConnectionAttempts(): number {\r\n        return this.privConnectionAttempts;\r\n    }\r\n\r\n    // The number of bytes sent for the current connection.\r\n    // Counter is reset to 0 each time a connection is established.\r\n    public get bytesSent(): number {\r\n        return this.privBytesSent;\r\n    }\r\n\r\n    // The number of bytes sent for the current recognition.\r\n    // Counter is reset to 0 each time recognition is started.\r\n    public get recognitionBytesSent(): number {\r\n        return this.privRecognitionBytesSent;\r\n    }\r\n\r\n    public listenForServiceTelemetry(eventSource: IEventSource<PlatformEvent>): void {\r\n        if (!!this.privServiceTelemetryListener) {\r\n            this.privDetachables.push(eventSource.attachListener(this.privServiceTelemetryListener));\r\n        }\r\n    }\r\n\r\n    public startNewRecognition(): void {\r\n        this.privRecognitionBytesSent = 0;\r\n        this.privIsSpeechEnded = false;\r\n        this.privIsRecognizing = true;\r\n        this.privTurnStartAudioOffset = 0;\r\n        this.privLastRecoOffset = 0;\r\n        this.privRecogNumber++;\r\n        this.privServiceTelemetryListener = new ServiceTelemetryListener(this.privRequestId, this.privAudioSourceId, this.privAudioNodeId);\r\n        this.onEvent(new RecognitionTriggeredEvent(this.requestId, this.privSessionId, this.privAudioSourceId, this.privAudioNodeId));\r\n    }\r\n\r\n    public async onAudioSourceAttachCompleted(audioNode: ReplayableAudioNode, isError: boolean): Promise<void> {\r\n        this.privAudioNode = audioNode;\r\n        this.privIsAudioNodeDetached = false;\r\n\r\n        if (isError) {\r\n            await this.onComplete();\r\n        } else {\r\n            this.onEvent(new ListeningStartedEvent(this.privRequestId, this.privSessionId, this.privAudioSourceId, this.privAudioNodeId));\r\n        }\r\n    }\r\n\r\n    public onPreConnectionStart(authFetchEventId: string, connectionId: string): void {\r\n        this.privAuthFetchEventId = authFetchEventId;\r\n        this.privSessionId = connectionId;\r\n        this.onEvent(new ConnectingToServiceEvent(this.privRequestId, this.privAuthFetchEventId, this.privSessionId));\r\n    }\r\n\r\n    public async onAuthCompleted(isError: boolean): Promise<void> {\r\n        if (isError) {\r\n            await this.onComplete();\r\n        }\r\n    }\r\n\r\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n    public async onConnectionEstablishCompleted(statusCode: number, reason?: string): Promise<void> {\r\n        if (statusCode === 200) {\r\n            this.onEvent(new RecognitionStartedEvent(this.requestId, this.privAudioSourceId, this.privAudioNodeId, this.privAuthFetchEventId, this.privSessionId));\r\n            if (!!this.privAudioNode) {\r\n                this.privAudioNode.replay();\r\n            }\r\n            this.privTurnStartAudioOffset = this.privLastRecoOffset;\r\n            this.privBytesSent = 0;\r\n            return;\r\n        } else if (statusCode === 403) {\r\n            await this.onComplete();\r\n        }\r\n    }\r\n\r\n    public async onServiceTurnEndResponse(continuousRecognition: boolean): Promise<void> {\r\n        this.privTurnDeferral.resolve();\r\n\r\n        if (!continuousRecognition || this.isSpeechEnded) {\r\n            await this.onComplete();\r\n            this.privInTurn = false;\r\n        } else {\r\n            // Start a new request set.\r\n            this.privTurnStartAudioOffset = this.privLastRecoOffset;\r\n            this.privAudioNode.replay();\r\n        }\r\n    }\r\n\r\n    public onSpeechContext(): void {\r\n        this.privRequestId = createNoDashGuid();\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            // eslint-disable-next-line @typescript-eslint/no-empty-function\r\n            this.privTurnDeferral.promise.then().catch((): void => { });\r\n        }\r\n        this.privInTurn = true;\r\n        this.privTurnDeferral = new Deferred<void>();\r\n    }\r\n\r\n    public onHypothesis(offset: number): number {\r\n        const audioReceivedTime = this.privAudioNode.findTimeAtOffset(offset);\r\n        if (!this.privHypothesisReceived) {\r\n            this.privHypothesisReceived = true;\r\n            this.privServiceTelemetryListener.hypothesisReceived(audioReceivedTime);\r\n        }\r\n        return audioReceivedTime > 0 ? Date.now() - audioReceivedTime : 0;\r\n    }\r\n\r\n    public onPhraseRecognized(offset: number): number {\r\n        const audioReceivedTime = this.privAudioNode.findTimeAtOffset(offset);\r\n        this.privServiceTelemetryListener.phraseReceived(audioReceivedTime);\r\n        this.onServiceRecognized(offset);\r\n        return audioReceivedTime > 0 ? Date.now() - audioReceivedTime : 0;\r\n    }\r\n\r\n    public onServiceRecognized(offset: number): void {\r\n        this.privLastRecoOffset = offset;\r\n        this.privHypothesisReceived = false;\r\n        this.privAudioNode.shrinkBuffers(offset);\r\n        this.privConnectionAttempts = 0;\r\n    }\r\n\r\n    public onAudioSent(bytesSent: number): void {\r\n        this.privBytesSent += bytesSent;\r\n        this.privRecognitionBytesSent += bytesSent;\r\n    }\r\n\r\n    public onRetryConnection(): void {\r\n        this.privConnectionAttempts++;\r\n    }\r\n\r\n    public async dispose(): Promise<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            for (const detachable of this.privDetachables) {\r\n                await detachable.detach();\r\n            }\r\n\r\n            if (!!this.privServiceTelemetryListener) {\r\n                this.privServiceTelemetryListener.dispose();\r\n            }\r\n            this.privIsRecognizing = false;\r\n        }\r\n    }\r\n\r\n    public getTelemetry(): string {\r\n        if (this.privServiceTelemetryListener.hasTelemetry) {\r\n            return this.privServiceTelemetryListener.getTelemetry();\r\n        } else {\r\n            return null;\r\n        }\r\n    }\r\n\r\n    public async onStopRecognizing(): Promise<void> {\r\n        await this.onComplete();\r\n    }\r\n\r\n    // Should be called with the audioNode for this session has indicated that it is out of speech.\r\n    public onSpeechEnded(): void {\r\n        this.privIsSpeechEnded = true;\r\n    }\r\n\r\n    protected onEvent(event: SpeechRecognitionEvent): void {\r\n        if (!!this.privServiceTelemetryListener) {\r\n            this.privServiceTelemetryListener.onEvent(event);\r\n        }\r\n        Events.instance.onEvent(event);\r\n    }\r\n\r\n    private async onComplete(): Promise<void> {\r\n        if (!!this.privIsRecognizing) {\r\n            this.privIsRecognizing = false;\r\n            await this.detachAudioNode();\r\n        }\r\n    }\r\n\r\n    private async detachAudioNode(): Promise<void> {\r\n        if (!this.privIsAudioNodeDetached) {\r\n            this.privIsAudioNodeDetached = true;\r\n            if (this.privAudioNode) {\r\n                await this.privAudioNode.detach();\r\n            }\r\n        }\r\n    }\r\n}\r\n"]}