{"version":3,"sources":["src/common.speech/SynthesisAdapterBase.ts"],"names":[],"mappings":"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","file":"SynthesisAdapterBase.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license.\n\nimport {\n    ArgumentNullError,\n    ConnectionClosedEvent,\n    ConnectionEvent,\n    ConnectionMessage,\n    ConnectionOpenResponse,\n    ConnectionState,\n    createNoDashGuid,\n    EventSource,\n    IAudioDestination,\n    IConnection,\n    IDisposable,\n    MessageType,\n    Promise,\n    PromiseHelper,\n    PromiseResult,\n    ServiceEvent,\n} from \"../common/Exports\";\nimport { AudioOutputFormatImpl } from \"../sdk/Audio/AudioOutputFormat\";\nimport {\n    CancellationErrorCode,\n    CancellationReason,\n    PropertyCollection,\n    PropertyId,\n    ResultReason,\n    SpeechSynthesisEventArgs,\n    SpeechSynthesisResult,\n    SpeechSynthesisWordBoundaryEventArgs,\n    SpeechSynthesizer,\n} from \"../sdk/Exports\";\nimport { Callback } from \"../sdk/Transcription/IConversation\";\nimport {\n    AgentConfig,\n    CancellationErrorCodePropertyName,\n    ISynthesisConnectionFactory,\n    SynthesisAudioMetadata,\n    SynthesisContext,\n    SynthesisTurn,\n    SynthesizerConfig\n} from \"./Exports\";\nimport { AuthInfo, IAuthentication } from \"./IAuthentication\";\nimport { SpeechConnectionMessage } from \"./SpeechConnectionMessage.Internal\";\n\nexport class SynthesisAdapterBase implements IDisposable {\n    protected privSynthesisTurn: SynthesisTurn;\n    protected privConnectionId: string;\n    protected privSynthesizerConfig: SynthesizerConfig;\n    protected privSpeechSynthesizer: SpeechSynthesizer;\n    protected privSuccessCallback: (e: SpeechSynthesisResult) => void;\n    protected privErrorCallback: (e: string) => void;\n\n    public get synthesisContext(): SynthesisContext {\n        return this.privSynthesisContext;\n    }\n\n    public get agentConfig(): AgentConfig {\n        return this.privAgentConfig;\n    }\n\n    public get connectionEvents(): EventSource<ConnectionEvent> {\n        return this.privConnectionEvents;\n    }\n\n    public get serviceEvents(): EventSource<ServiceEvent> {\n        return this.privServiceEvents;\n    }\n\n    protected speakOverride: (ssml: string, requestId: string, sc: (e: SpeechSynthesisResult) => void, ec: (e: string) => void) => any = undefined;\n\n    // Called when telemetry data is sent to the service.\n    // Used for testing Telemetry capture.\n    public static telemetryData: (json: string) => void;\n    public static telemetryDataEnabled: boolean = true;\n\n    public set activityTemplate(messagePayload: string) { this.privActivityTemplate = messagePayload; }\n    public get activityTemplate(): string { return this.privActivityTemplate; }\n\n    protected receiveMessageOverride: () => any = undefined;\n\n    protected connectImplOverride: (isUnAuthorized: boolean) => any = undefined;\n\n    protected configConnectionOverride: () => any = undefined;\n\n    protected fetchConnectionOverride: () => any = undefined;\n\n    public set audioOutputFormat(format: AudioOutputFormatImpl) {\n        this.privAudioOutputFormat = format;\n        this.privSynthesisTurn.audioOutputFormat = format;\n        if (this.privSessionAudioDestination !== undefined) {\n            this.privSessionAudioDestination.format = format;\n        }\n        if (this.synthesisContext !== undefined) {\n            this.synthesisContext.audioOutputFormat = format;\n        }\n    }\n    private privAuthentication: IAuthentication;\n    private privConnectionFactory: ISynthesisConnectionFactory;\n\n    // A promise for a configured connection.\n    // Do not consume directly, call fetchConnection instead.\n    private privConnectionConfigurationPromise: Promise<IConnection>;\n\n    // A promise for a connection, but one that has not had the speech context sent yet.\n    // Do not consume directly, call fetchConnection instead.\n    private privConnectionPromise: Promise<IConnection>;\n    private privAuthFetchEventId: string;\n    private privIsDisposed: boolean;\n    private privConnectionEvents: EventSource<ConnectionEvent>;\n    private privServiceEvents: EventSource<ServiceEvent>;\n    private privSynthesisContext: SynthesisContext;\n    private privAgentConfig: AgentConfig;\n    private privServiceHasSentMessage: boolean;\n    private privActivityTemplate: string;\n    private privAudioOutputFormat: AudioOutputFormatImpl;\n    private privSessionAudioDestination: IAudioDestination;\n\n    public constructor(\n        authentication: IAuthentication,\n        connectionFactory: ISynthesisConnectionFactory,\n        synthesizerConfig: SynthesizerConfig,\n        speechSynthesizer: SpeechSynthesizer,\n        audioDestination: IAudioDestination) {\n\n        if (!authentication) {\n            throw new ArgumentNullError(\"authentication\");\n        }\n\n        if (!connectionFactory) {\n            throw new ArgumentNullError(\"connectionFactory\");\n        }\n\n        if (!synthesizerConfig) {\n            throw new ArgumentNullError(\"synthesizerConfig\");\n        }\n\n        this.privAuthentication = authentication;\n        this.privConnectionFactory = connectionFactory;\n        this.privSynthesizerConfig = synthesizerConfig;\n        this.privIsDisposed = false;\n        this.privSpeechSynthesizer = speechSynthesizer;\n        this.privSessionAudioDestination = audioDestination;\n        this.privSynthesisTurn = new SynthesisTurn();\n        this.privConnectionEvents = new EventSource<ConnectionEvent>();\n        this.privServiceEvents = new EventSource<ServiceEvent>();\n        this.privSynthesisContext = new SynthesisContext(this.privSpeechSynthesizer);\n        this.privAgentConfig = new AgentConfig();\n\n        this.connectionEvents.attach((connectionEvent: ConnectionEvent): void => {\n            if (connectionEvent.name === \"ConnectionClosedEvent\") {\n                const connectionClosedEvent = connectionEvent as ConnectionClosedEvent;\n                this.cancelSynthesisLocal(CancellationReason.Error,\n                    connectionClosedEvent.statusCode === 1007 ? CancellationErrorCode.BadRequestParameters : CancellationErrorCode.ConnectionFailure,\n                    connectionClosedEvent.reason + \" websocket error code: \" + connectionClosedEvent.statusCode);\n            }\n        });\n    }\n\n    public static addHeader(audio: ArrayBuffer, format: AudioOutputFormatImpl): ArrayBuffer {\n        if (!format.hasHeader) {\n            return audio;\n        }\n        format.updateHeader(audio.byteLength);\n        const tmp = new Uint8Array(audio.byteLength + format.header.byteLength);\n        tmp.set(new Uint8Array(format.header), 0);\n        tmp.set(new Uint8Array(audio), format.header.byteLength);\n        return tmp.buffer;\n    }\n\n    public isDisposed(): boolean {\n        return this.privIsDisposed;\n    }\n\n    public dispose(reason?: string): void {\n        this.privIsDisposed = true;\n        if (this.privSessionAudioDestination !== undefined) {\n            this.privSessionAudioDestination.close();\n        }\n        if (this.privConnectionConfigurationPromise) {\n            this.privConnectionConfigurationPromise.onSuccessContinueWith((connection: IConnection) => {\n                connection.dispose(reason);\n            });\n        }\n    }\n\n    public connect(): void {\n        this.connectImpl().result();\n    }\n\n    public connectAsync(cb?: Callback, err?: Callback): void {\n        this.connectImpl().continueWith((promiseResult: PromiseResult<IConnection>) => {\n            try {\n                if (promiseResult.isError) {\n                    if (!!err) {\n                        err(promiseResult.error);\n                    }\n                } else if (promiseResult.isCompleted) {\n                    if (!!cb) {\n                        cb();\n                    }\n                }\n            } catch (e) {\n                if (!!err) {\n                    err(e);\n                }\n            }\n        });\n    }\n\n    public sendNetworkMessage(path: string, payload: string | ArrayBuffer, success?: () => void, err?: (error: string) => void): void {\n        const type: MessageType = typeof payload === \"string\" ? MessageType.Text : MessageType.Binary;\n        const contentType: string = typeof payload === \"string\" ? \"application/json\" : \"\";\n\n        this.fetchConnection().on((connection: IConnection) => {\n            connection.send(new SpeechConnectionMessage(type, path, this.privSynthesisTurn.requestId, contentType, payload)).on(() => {\n                if (!!success) {\n                    success();\n                }\n            }, (error: string) => {\n                if (!!err) {\n                    err(error);\n                }\n            });\n        }, (error: string) => {\n            if (!!err) {\n                err(error);\n            }\n        });\n    }\n\n    public Speak(\n        text: string,\n        isSSML: boolean,\n        requestId: string,\n        successCallback: (e: SpeechSynthesisResult) => void,\n        errorCallBack: (e: string) => void,\n        audioDestination: IAudioDestination,\n    ): Promise<boolean> {\n\n        let ssml: string;\n\n        if (isSSML) {\n            ssml = text;\n        } else {\n            ssml = this.privSpeechSynthesizer.buildSsml(text);\n        }\n\n        if (this.speakOverride !== undefined) {\n            return this.speakOverride(ssml, requestId, successCallback, errorCallBack);\n        }\n\n        this.privSuccessCallback = successCallback;\n        this.privErrorCallback = errorCallBack;\n\n        this.privSynthesisTurn.startNewSynthesis(requestId, text, isSSML, audioDestination);\n\n        return this.fetchConnection().continueWithPromise<boolean>((connection: PromiseResult<IConnection>) => {\n            if (connection.isError) {\n                this.cancelSynthesisLocal(CancellationReason.Error, CancellationErrorCode.ConnectionFailure, connection.error);\n                return PromiseHelper.fromError(connection.error);\n            }\n            return this.sendSynthesisContext(connection.result).continueWithPromise<boolean>((result: PromiseResult<boolean>): Promise<boolean> => {\n                if (result.isError) {\n                    this.cancelSynthesisLocal(CancellationReason.Error, CancellationErrorCode.ConnectionFailure, result.error);\n                    return PromiseHelper.fromError(result.error);\n                }\n                return this.sendSsmlMessage(connection.result, ssml, requestId).continueWithPromise<boolean>((result: PromiseResult<boolean>): Promise<boolean> => {\n                    if (result.isError) {\n                        this.cancelSynthesisLocal(CancellationReason.Error, CancellationErrorCode.ConnectionFailure, result.error);\n                        return PromiseHelper.fromError(result.error);\n                    }\n\n                    const synthesisStartEventArgs: SpeechSynthesisEventArgs = new SpeechSynthesisEventArgs(\n                        new SpeechSynthesisResult(\n                            requestId,\n                            ResultReason.SynthesizingAudioStarted,\n                        )\n                    );\n\n                    if (!!this.privSpeechSynthesizer.synthesisStarted) {\n                        this.privSpeechSynthesizer.synthesisStarted(this.privSpeechSynthesizer, synthesisStartEventArgs);\n                    }\n\n                    const messageRetrievalPromise = this.receiveMessage();\n                    return PromiseHelper.fromResult(true);\n                });\n            });\n        });\n    }\n\n    // Cancels synthesis.\n    protected cancelSynthesis(\n        requestId: string,\n        cancellationReason: CancellationReason,\n        errorCode: CancellationErrorCode,\n        error: string): void {\n        const properties: PropertyCollection = new PropertyCollection();\n        properties.setProperty(CancellationErrorCodePropertyName, CancellationErrorCode[errorCode]);\n        const result: SpeechSynthesisResult = new SpeechSynthesisResult(\n            requestId,\n            ResultReason.Canceled,\n            undefined,\n            error,\n            properties\n        );\n\n        if (!!this.privSpeechSynthesizer.SynthesisCanceled) {\n            const cancelEvent: SpeechSynthesisEventArgs = new SpeechSynthesisEventArgs(result);\n            try {\n                this.privSpeechSynthesizer.SynthesisCanceled(this.privSpeechSynthesizer, cancelEvent);\n                /* tslint:disable:no-empty */\n            } catch { }\n        }\n\n        if (!!this.privSuccessCallback) {\n            try {\n                this.privSuccessCallback(result);\n                /* tslint:disable:no-empty */\n            } catch { }\n        }\n    }\n\n    // Cancels synthesis.\n    protected cancelSynthesisLocal(\n        cancellationReason: CancellationReason,\n        errorCode: CancellationErrorCode,\n        error: string): void {\n\n        if (!!this.privSynthesisTurn.isSynthesizing) {\n            this.privSynthesisTurn.onStopSynthesizing();\n\n            this.cancelSynthesis(\n                this.privSynthesisTurn.requestId,\n                cancellationReason,\n                errorCode,\n                error);\n        }\n    }\n\n    protected processTypeSpecificMessages(\n        connectionMessage: SpeechConnectionMessage,\n        successCallback?: (e: SpeechSynthesisResult) => void,\n        errorCallBack?: (e: string) => void): boolean {\n        return true;\n    }\n\n    protected receiveMessage = (): Promise<IConnection> => {\n        return this.fetchConnection().on((connection: IConnection): Promise<IConnection> => {\n            return connection.read()\n                .onSuccessContinueWithPromise((message: ConnectionMessage) => {\n\n                    if (this.receiveMessageOverride !== undefined) {\n                        return this.receiveMessageOverride();\n                    }\n                    if (this.privIsDisposed) {\n                        // We're done.\n                        return PromiseHelper.fromResult(undefined);\n                    }\n\n                    // indicates we are draining the queue and it came with no message;\n                    if (!message) {\n                        if (!this.privSynthesisTurn.isSynthesizing) {\n                            return PromiseHelper.fromResult(true);\n                        } else {\n                            return this.receiveMessage();\n                        }\n                    }\n\n                    this.privServiceHasSentMessage = true;\n\n                    const connectionMessage = SpeechConnectionMessage.fromConnectionMessage(message);\n\n                    if (connectionMessage.requestId.toLowerCase() === this.privSynthesisTurn.requestId.toLowerCase()) {\n                        switch (connectionMessage.path.toLowerCase()) {\n                            case \"turn.start\":\n                                this.privSynthesisTurn.onServiceTurnStartResponse();\n                                break;\n                            case \"response\":\n                                this.privSynthesisTurn.onServiceResponseMessage(connectionMessage.textBody);\n                                break;\n                            case \"audio\":\n                                if (this.privSynthesisTurn.streamId.toLowerCase() === connectionMessage.streamId.toLowerCase()\n                                    && !!connectionMessage.binaryBody) {\n                                    this.privSynthesisTurn.onAudioChunkReceived(connectionMessage.binaryBody);\n                                    if (!!this.privSpeechSynthesizer.synthesizing) {\n                                        try {\n                                            const audioWithHeader = SynthesisAdapterBase.addHeader(connectionMessage.binaryBody, this.privSynthesisTurn.audioOutputFormat);\n                                            const ev: SpeechSynthesisEventArgs = new SpeechSynthesisEventArgs(\n                                                new SpeechSynthesisResult(\n                                                    this.privSynthesisTurn.requestId,\n                                                    ResultReason.SynthesizingAudio,\n                                                    audioWithHeader));\n                                            this.privSpeechSynthesizer.synthesizing(this.privSpeechSynthesizer, ev);\n                                        } catch (error) {\n                                            // Not going to let errors in the event handler\n                                            // trip things up.\n                                        }\n                                    }\n                                    if (this.privSessionAudioDestination !== undefined) {\n                                        this.privSessionAudioDestination.write(connectionMessage.binaryBody);\n                                    }\n                                }\n                                break;\n                            case \"audio.metadata\":\n                                const metadataList = SynthesisAudioMetadata.fromJSON(connectionMessage.textBody).Metadata;\n                                for (const metadata of metadataList) {\n                                    if (metadata.Type.toLowerCase() === \"WordBoundary\".toLowerCase()) {\n\n                                        this.privSynthesisTurn.onWordBoundaryEvent(metadata.Data.text.Text);\n\n                                        const ev: SpeechSynthesisWordBoundaryEventArgs = new SpeechSynthesisWordBoundaryEventArgs(\n                                            metadata.Data.Offset,\n                                            metadata.Data.text.Text,\n                                            metadata.Data.text.Length,\n                                            this.privSynthesisTurn.currentTextOffset);\n\n                                        if (!!this.privSpeechSynthesizer.wordBoundary) {\n                                            try {\n                                                this.privSpeechSynthesizer.wordBoundary(this.privSpeechSynthesizer, ev);\n                                            } catch (error) {\n                                                // Not going to let errors in the event handler\n                                                // trip things up.\n                                            }\n                                        }\n                                    }\n                                }\n                                break;\n                            case \"turn.end\":\n                                this.privSynthesisTurn.onServiceTurnEndResponse();\n                                let result: SpeechSynthesisResult;\n                                try {\n                                    result = new SpeechSynthesisResult(\n                                        this.privSynthesisTurn.requestId,\n                                        ResultReason.SynthesizingAudioCompleted,\n                                        this.privSynthesisTurn.allReceivedAudioWithHeader\n                                    );\n                                    if (!!this.privSuccessCallback) {\n                                        this.privSuccessCallback(result);\n                                    }\n                                } catch (error) {\n                                    if (!!this.privErrorCallback) {\n                                        this.privErrorCallback(error);\n                                    }\n                                }\n                                if (this.privSpeechSynthesizer.synthesisCompleted) {\n                                    try {\n                                        this.privSpeechSynthesizer.synthesisCompleted(\n                                            this.privSpeechSynthesizer,\n                                            new SpeechSynthesisEventArgs(result)\n                                        );\n                                    } catch (e) {\n                                        // Not going to let errors in the event handler\n                                        // trip things up.\n                                    }\n                                }\n                                break;\n\n                            default:\n\n                                if (!this.processTypeSpecificMessages(connectionMessage)) {\n                                    // here are some messages that the derived class has not processed, dispatch them to connect class\n                                    if (!!this.privServiceEvents) {\n                                        this.serviceEvents.onEvent(new ServiceEvent(connectionMessage.path.toLowerCase(), connectionMessage.textBody));\n                                    }\n                                }\n\n                        }\n                    }\n\n                    return this.receiveMessage();\n                });\n        }, (error: string) => {\n        });\n    }\n\n    protected sendSynthesisContext = (connection: IConnection): Promise<boolean> => {\n        const synthesisContextJson = this.synthesisContext.toJSON();\n\n        if (synthesisContextJson) {\n            return connection.send(new SpeechConnectionMessage(\n                MessageType.Text,\n                \"synthesis.context\",\n                this.privSynthesisTurn.requestId,\n                \"application/json\",\n                synthesisContextJson));\n        }\n        return PromiseHelper.fromResult(true);\n    }\n\n    // Establishes a websocket connection to the end point.\n    protected connectImpl(isUnAuthorized: boolean = false): Promise<IConnection> {\n\n        if (this.connectImplOverride !== undefined) {\n            return this.connectImplOverride(isUnAuthorized);\n        }\n\n        if (this.privConnectionPromise) {\n            if (this.privConnectionPromise.result().isCompleted &&\n                (this.privConnectionPromise.result().isError\n                    || this.privConnectionPromise.result().result.state() === ConnectionState.Disconnected) &&\n                this.privServiceHasSentMessage === true) {\n                this.privConnectionId = null;\n                this.privConnectionPromise = null;\n                this.privServiceHasSentMessage = false;\n                return this.connectImpl();\n            } else {\n                return this.privConnectionPromise;\n            }\n        }\n\n        this.privAuthFetchEventId = createNoDashGuid();\n        this.privConnectionId = createNoDashGuid();\n\n        this.privSynthesisTurn.onPreConnectionStart(this.privAuthFetchEventId, this.privConnectionId);\n\n        const authPromise = isUnAuthorized ? this.privAuthentication.fetchOnExpiry(this.privAuthFetchEventId) : this.privAuthentication.fetch(this.privAuthFetchEventId);\n\n        this.privConnectionPromise = authPromise\n            .continueWithPromise((result: PromiseResult<AuthInfo>) => {\n                if (result.isError) {\n                    this.privSynthesisTurn.onAuthCompleted(true, result.error);\n                    throw new Error(result.error);\n                } else {\n                    this.privSynthesisTurn.onAuthCompleted(false);\n                }\n\n                const connection: IConnection = this.privConnectionFactory.create(this.privSynthesizerConfig, result.result, this.privConnectionId);\n\n                // Attach to the underlying event. No need to hold onto the detach pointers as in the event the connection goes away,\n                // it'll stop sending events.\n                connection.events.attach((event: ConnectionEvent) => {\n                    this.connectionEvents.onEvent(event);\n                });\n\n                return connection.open().onSuccessContinueWithPromise((response: ConnectionOpenResponse): Promise<IConnection> => {\n                    if (response.statusCode === 200) {\n                        this.privSynthesisTurn.onPreConnectionStart(this.privAuthFetchEventId, this.privConnectionId);\n                        this.privSynthesisTurn.onConnectionEstablishCompleted(response.statusCode);\n\n                        return PromiseHelper.fromResult<IConnection>(connection);\n                    } else if (response.statusCode === 403 && !isUnAuthorized) {\n                        return this.connectImpl(true);\n                    } else {\n                        this.privSynthesisTurn.onConnectionEstablishCompleted(response.statusCode, response.reason);\n                        return PromiseHelper.fromError<IConnection>(`Unable to contact server. StatusCode: ${response.statusCode}, ${this.privSynthesizerConfig.parameters.getProperty(PropertyId.SpeechServiceConnection_Endpoint)} Reason: ${response.reason}`);\n                    }\n                });\n            });\n\n        return this.privConnectionPromise;\n    }\n\n    protected sendSpeechServiceConfig = (connection: IConnection, SpeechServiceConfigJson: string): Promise<boolean> => {\n\n        if (SpeechServiceConfigJson) {\n            return connection.send(new SpeechConnectionMessage(\n                MessageType.Text,\n                \"speech.config\",\n                this.privSynthesisTurn.requestId,\n                \"application/json\",\n                SpeechServiceConfigJson));\n        }\n\n        return PromiseHelper.fromResult(true);\n    }\n\n    protected sendSsmlMessage = (connection: IConnection, ssml: string, requestId: string): Promise<boolean> => {\n        return connection.send(new SpeechConnectionMessage(\n            MessageType.Text,\n            \"ssml\",\n            requestId,\n            \"application/ssml+xml\",\n            ssml));\n    }\n\n    private fetchConnection = (): Promise<IConnection> => {\n        if (this.fetchConnectionOverride !== undefined) {\n            return this.fetchConnectionOverride();\n        }\n\n        return this.configureConnection();\n    }\n\n    // Takes an established websocket connection to the endpoint and sends speech configuration information.\n    private configureConnection(): Promise<IConnection> {\n        if (this.configConnectionOverride !== undefined) {\n            return this.configConnectionOverride();\n        }\n\n        if (this.privConnectionConfigurationPromise) {\n            if (this.privConnectionConfigurationPromise.result().isCompleted &&\n                (this.privConnectionConfigurationPromise.result().isError\n                    || this.privConnectionConfigurationPromise.result().result.state() === ConnectionState.Disconnected)) {\n\n                this.privConnectionConfigurationPromise = null;\n                return this.configureConnection();\n            } else {\n                return this.privConnectionConfigurationPromise;\n            }\n        }\n\n        this.privConnectionConfigurationPromise = this.connectImpl().onSuccessContinueWithPromise((connection: IConnection): Promise<IConnection> => {\n            return this.sendSpeechServiceConfig(connection, this.privSynthesizerConfig.SpeechServiceConfig.serialize())\n                .onSuccessContinueWith((_: boolean) => {\n                    return connection;\n                });\n        });\n\n        return this.privConnectionConfigurationPromise;\n    }\n}\n"]}