{"version":3,"sources":["src/common.speech/VoiceServiceRecognizer.ts"],"names":[],"mappings":"AAIA,OAAO,EAGH,YAAY,EAIf,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACH,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAKlB,4BAA4B,EAC5B,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAOH,qBAAqB,EACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAuBhF,qBAAa,sBAAuB,SAAQ,qBAAqB;IAC7D,OAAO,CAAC,sBAAsB,CAAe;IAC7C,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,qBAAqB,CAAS;gBAGlC,cAAc,EAAE,eAAe,EAC/B,iBAAiB,EAAE,kBAAkB,EACrC,WAAW,EAAE,YAAY,EACzB,gBAAgB,EAAE,gBAAgB,EAClC,UAAU,EAAE,kBAAkB;IAMlC,IAAW,kBAAkB,CAAC,WAAW,EAAE,YAAY,EAEtD;IAED,SAAS,CAAC,2BAA2B,CAAC,iBAAiB,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IA8DnG,SAAS,CAAC,iBAAiB,CACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,kBAAkB,EACtC,SAAS,EAAE,qBAAqB,EAChC,KAAK,EAAE,MAAM,GAAG,IAAI;IAgBX,aAAa,CAAC,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAe/E,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAKhE,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAKjE,wBAAwB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAMtF,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,4BAA4B,EAAE,CAAC;IAKtF,oBAAoB,CAAC,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAepG,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,4BAA4B,CAAC;YA8C1E,oBAAoB;YAOpB,kBAAkB;YAkBlB,iBAAiB;YAqBjB,iBAAiB;YAiBjB,eAAe;IAiB7B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,oBAAoB;IAuB5B,OAAO,CAAC,YAAY;CAIvB","file":"VoiceServiceRecognizer.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    DeferralMap,\r\n    Deferred,\r\n    IAudioSource,\r\n    IAudioStreamNode,\r\n    IConnection,\r\n    MessageType,\r\n} from \"../common/Exports.js\";\r\nimport { AudioStreamFormatImpl } from \"../sdk/Audio/AudioStreamFormat.js\";\r\nimport { SpeakerRecognitionModel } from \"../sdk/SpeakerRecognitionModel.js\";\r\nimport {\r\n    CancellationErrorCode,\r\n    CancellationReason,\r\n    VoiceProfileClient,\r\n    PropertyCollection,\r\n    PropertyId,\r\n    ResultReason,\r\n    SessionEventArgs,\r\n    VoiceProfileEnrollmentResult,\r\n    VoiceProfilePhraseResult,\r\n    VoiceProfileResult,\r\n    VoiceProfileType,\r\n    VoiceProfile\r\n} from \"../sdk/Exports.js\";\r\nimport {\r\n    CancellationErrorCodePropertyName,\r\n    EnrollmentResponse,\r\n    IProfile,\r\n    ISpeechConfigAudioDevice,\r\n    ProfilePhraseResponse,\r\n    ProfileResponse,\r\n    ServiceRecognizerBase,\r\n} from \"./Exports.js\";\r\nimport { IAuthentication } from \"./IAuthentication.js\";\r\nimport { IConnectionFactory } from \"./IConnectionFactory.js\";\r\nimport { RecognizerConfig } from \"./RecognizerConfig.js\";\r\nimport { SpeechConnectionMessage } from \"./SpeechConnectionMessage.Internal.js\";\r\n\r\ninterface CreateProfile {\r\n    scenario: string;\r\n    locale: string;\r\n    number: string;\r\n}\r\n\r\ninterface PhraseRequest {\r\n    scenario: string;\r\n    locale: string;\r\n}\r\n\r\ninterface SpeakerContext {\r\n    scenario: string;\r\n    profileIds: string[];\r\n    features: {\r\n        interimResult: string;\r\n        progressiveDetection: string;\r\n    };\r\n}\r\n\r\n// eslint-disable-next-line max-classes-per-file\r\nexport class VoiceServiceRecognizer extends ServiceRecognizerBase {\r\n    private privSpeakerAudioSource: IAudioSource;\r\n    private privDeferralMap: DeferralMap = new DeferralMap();\r\n    private privExpectedProfileId: string;\r\n\r\n    public constructor(\r\n        authentication: IAuthentication,\r\n        connectionFactory: IConnectionFactory,\r\n        audioSource: IAudioSource,\r\n        recognizerConfig: RecognizerConfig,\r\n        recognizer: VoiceProfileClient) {\r\n        super(authentication, connectionFactory, audioSource, recognizerConfig, recognizer);\r\n        this.privSpeakerAudioSource = audioSource;\r\n        this.sendPrePayloadJSONOverride = (): Promise<void> => this.noOp();\r\n    }\r\n\r\n    public set SpeakerAudioSource(audioSource: IAudioSource) {\r\n        this.privSpeakerAudioSource = audioSource;\r\n    }\r\n\r\n    protected processTypeSpecificMessages(connectionMessage: SpeechConnectionMessage): Promise<boolean> {\r\n\r\n        let processed: boolean = false;\r\n\r\n        const resultProps: PropertyCollection = new PropertyCollection();\r\n        if (connectionMessage.messageType === MessageType.Text) {\r\n            resultProps.setProperty(PropertyId.SpeechServiceResponse_JsonResult, connectionMessage.textBody);\r\n        }\r\n\r\n        switch (connectionMessage.path.toLowerCase()) {\r\n            // Profile management response for create, fetch, delete, reset\r\n            case \"speaker.profiles\":\r\n                const response: ProfileResponse = JSON.parse(connectionMessage.textBody) as ProfileResponse;\r\n                switch (response.operation.toLowerCase()) {\r\n                    case \"create\":\r\n                        this.handleCreateResponse(response, connectionMessage.requestId);\r\n                        break;\r\n\r\n                    case \"delete\":\r\n                    case \"reset\":\r\n                        this.handleResultResponse(response, connectionMessage.requestId);\r\n                        break;\r\n\r\n                    case \"fetch\":\r\n                        const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\r\n                        this.handleFetchResponse(enrollmentResponse, connectionMessage.requestId);\r\n                        break;\r\n\r\n                    default:\r\n                        break;\r\n                }\r\n                processed = true;\r\n                break;\r\n            // Activation and authorization phrase response\r\n            case \"speaker.phrases\":\r\n                const phraseResponse: ProfilePhraseResponse = JSON.parse(connectionMessage.textBody) as ProfilePhraseResponse;\r\n                this.handlePhrasesResponse(phraseResponse, connectionMessage.requestId);\r\n                processed = true;\r\n                break;\r\n            // Enrollment response\r\n            case \"speaker.profile.enrollment\":\r\n                const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\r\n                const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\r\n                    this.enrollmentReasonFrom(!!enrollmentResponse.enrollment ? enrollmentResponse.enrollment.enrollmentStatus : enrollmentResponse.status.statusCode),\r\n                    !!enrollmentResponse.enrollment ? JSON.stringify(enrollmentResponse.enrollment) : undefined,\r\n                    enrollmentResponse.status.reason,\r\n                    );\r\n                if (!!this.privDeferralMap.getId(connectionMessage.requestId)) {\r\n                    this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(connectionMessage.requestId, result);\r\n                }\r\n                this.privRequestSession.onSpeechEnded();\r\n                processed = true;\r\n                break;\r\n            default:\r\n                break;\r\n        }\r\n        const defferal = new Deferred<boolean>();\r\n        defferal.resolve(processed);\r\n        return defferal.promise;\r\n    }\r\n\r\n    // Cancels recognition.\r\n    protected cancelRecognition(\r\n        sessionId: string,\r\n        requestId: string,\r\n        cancellationReason: CancellationReason,\r\n        errorCode: CancellationErrorCode,\r\n        error: string): void {\r\n\r\n        const properties: PropertyCollection = new PropertyCollection();\r\n        // const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\r\n        properties.setProperty(CancellationErrorCodePropertyName, CancellationErrorCode[errorCode]);\r\n\r\n            const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\r\n                ResultReason.Canceled,\r\n                error,\r\n                error,\r\n                );\r\n            if (!!this.privDeferralMap.getId(requestId)) {\r\n                this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(requestId, result);\r\n            }\r\n    }\r\n\r\n    public async createProfile(profileType: VoiceProfileType, locale: string): Promise<string[]> {\r\n        // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\r\n        this.voiceProfileType = profileType.toString();\r\n        const conPromise: Promise<IConnection> = this.connectImpl();\r\n        try {\r\n            const createProfileDeferral = new Deferred<string[]>();\r\n            await conPromise;\r\n            await this.sendCreateProfile(createProfileDeferral, profileType, locale);\r\n            void this.receiveMessage();\r\n            return createProfileDeferral.promise;\r\n        } catch (err) {\r\n            throw err;\r\n        }\r\n    }\r\n\r\n    public async resetProfile(profile: VoiceProfile): Promise<VoiceProfileResult> {\r\n        this.voiceProfileType = profile.profileType.toString();\r\n        return this.sendCommonRequest<VoiceProfileResult>(\"reset\", profile.profileType, profile);\r\n    }\r\n\r\n    public async deleteProfile(profile: VoiceProfile): Promise<VoiceProfileResult> {\r\n        this.voiceProfileType = profile.profileType.toString();\r\n        return this.sendCommonRequest<VoiceProfileResult>(\"delete\", profile.profileType, profile);\r\n    }\r\n\r\n    public async retrieveEnrollmentResult(profile: VoiceProfile): Promise<VoiceProfileEnrollmentResult> {\r\n        this.voiceProfileType = profile.profileType.toString();\r\n        this.privExpectedProfileId = profile.profileId;\r\n        return this.sendCommonRequest<VoiceProfileEnrollmentResult>(\"fetch\", profile.profileType, profile);\r\n    }\r\n\r\n    public async getAllProfiles(profileType: VoiceProfileType): Promise<VoiceProfileEnrollmentResult[]> {\r\n        this.voiceProfileType = profileType.toString();\r\n        return this.sendCommonRequest<VoiceProfileEnrollmentResult[]>(\"fetch\", profileType);\r\n    }\r\n\r\n    public async getActivationPhrases(profileType: VoiceProfileType, lang: string): Promise<VoiceProfilePhraseResult> {\r\n        this.voiceProfileType = profileType.toString();\r\n        // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\r\n        const conPromise: Promise<IConnection> = this.connectImpl();\r\n        try {\r\n            const getPhrasesDeferral = new Deferred<VoiceProfilePhraseResult>();\r\n            await conPromise;\r\n            await this.sendPhrasesRequest(getPhrasesDeferral, profileType, lang);\r\n            void this.receiveMessage();\r\n            return getPhrasesDeferral.promise;\r\n        } catch (err) {\r\n            throw err;\r\n        }\r\n    }\r\n\r\n    public async enrollProfile(profile: VoiceProfile): Promise<VoiceProfileEnrollmentResult> {\r\n        this.voiceProfileType = profile.profileType.toString();\r\n        const enrollmentDeferral = new Deferred<VoiceProfileEnrollmentResult>();\r\n        this.privRequestSession.startNewRecognition();\r\n        this.privRequestSession.listenForServiceTelemetry(this.privSpeakerAudioSource.events);\r\n\r\n        this.privRecognizerConfig.parameters.setProperty(PropertyId.Speech_SessionId, this.privRequestSession.sessionId);\r\n\r\n        // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\r\n        const conPromise: Promise<IConnection> = this.connectImpl();\r\n\r\n        const preAudioPromise: Promise<void> = this.sendPreAudioMessages(profile, enrollmentDeferral);\r\n\r\n        const node: IAudioStreamNode = await this.privSpeakerAudioSource.attach(this.privRequestSession.audioNodeId);\r\n        const format: AudioStreamFormatImpl = await this.privSpeakerAudioSource.format;\r\n        const deviceInfo: ISpeechConfigAudioDevice = await this.privSpeakerAudioSource.deviceInfo;\r\n\r\n        const audioNode = new ReplayableAudioNode(node, format.avgBytesPerSec);\r\n        await this.privRequestSession.onAudioSourceAttachCompleted(audioNode, false);\r\n\r\n        this.privRecognizerConfig.SpeechServiceConfig.Context.audio = { source: deviceInfo };\r\n\r\n        try {\r\n            await conPromise;\r\n            await preAudioPromise;\r\n        } catch (err) {\r\n            this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.ConnectionFailure, err as string);\r\n        }\r\n\r\n        const sessionStartEventArgs: SessionEventArgs = new SessionEventArgs(this.privRequestSession.sessionId);\r\n\r\n        if (!!this.privRecognizer.sessionStarted) {\r\n            this.privRecognizer.sessionStarted(this.privRecognizer, sessionStartEventArgs);\r\n        }\r\n\r\n        void this.receiveMessage();\r\n        const audioSendPromise = this.sendAudio(audioNode);\r\n\r\n        // /* eslint-disable no-empty */\r\n        audioSendPromise.then((): void => { /* add? return true;*/ }, (error: string): void => {\r\n            this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.RuntimeError, error);\r\n        });\r\n\r\n        return enrollmentDeferral.promise;\r\n    }\r\n\r\n    private async sendPreAudioMessages(profile: VoiceProfile, enrollmentDeferral: Deferred<VoiceProfileEnrollmentResult>): Promise<void> {\r\n        const connection: IConnection = await this.fetchConnection();\r\n        this.privRequestSession.onSpeechContext();\r\n        this.privDeferralMap.add<VoiceProfileEnrollmentResult>(this.privRequestSession.requestId, enrollmentDeferral);\r\n        await this.sendBaseRequest(connection, \"enroll\", this.scenarioFrom(profile.profileType), profile);\r\n    }\r\n\r\n    private async sendPhrasesRequest(getPhrasesDeferral: Deferred<VoiceProfilePhraseResult>, profileType: VoiceProfileType, locale: string): Promise<void> {\r\n        const connection: IConnection = await this.fetchConnection();\r\n        this.privRequestSession.onSpeechContext();\r\n        this.privDeferralMap.add<VoiceProfilePhraseResult>(this.privRequestSession.requestId, getPhrasesDeferral);\r\n        const scenario = this.scenarioFrom(profileType);\r\n\r\n        const profileCreateRequest: PhraseRequest = {\r\n            locale,\r\n            scenario,\r\n        };\r\n        return connection.send(new SpeechConnectionMessage(\r\n            MessageType.Text,\r\n            \"speaker.profile.phrases\",\r\n            this.privRequestSession.requestId,\r\n            \"application/json; charset=utf-8\",\r\n            JSON.stringify(profileCreateRequest)));\r\n    }\r\n\r\n    private async sendCreateProfile(createProfileDeferral: Deferred<string[]>, profileType: VoiceProfileType, locale: string): Promise<void> {\r\n\r\n        const connection: IConnection = await this.fetchConnection();\r\n        this.privRequestSession.onSpeechContext();\r\n        this.privDeferralMap.add<string[]>(this.privRequestSession.requestId, createProfileDeferral);\r\n        const scenario = profileType === VoiceProfileType.TextIndependentIdentification ? \"TextIndependentIdentification\" :\r\n            profileType === VoiceProfileType.TextIndependentVerification ? \"TextIndependentVerification\" : \"TextDependentVerification\";\r\n\r\n        const profileCreateRequest: CreateProfile = {\r\n            locale,\r\n            number: \"1\",\r\n            scenario,\r\n        };\r\n        return connection.send(new SpeechConnectionMessage(\r\n            MessageType.Text,\r\n            \"speaker.profile.create\",\r\n            this.privRequestSession.requestId,\r\n            \"application/json; charset=utf-8\",\r\n            JSON.stringify(profileCreateRequest)));\r\n    }\r\n\r\n    private async sendCommonRequest<T>(operation: string, profileType: VoiceProfileType, profile: VoiceProfile = undefined): Promise<T> {\r\n        // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\r\n        const conPromise: Promise<IConnection> = this.connectImpl();\r\n        try {\r\n            const deferral = new Deferred<T>();\r\n            this.privRequestSession.onSpeechContext();\r\n            await conPromise;\r\n            const connection: IConnection = await this.fetchConnection();\r\n            this.privDeferralMap.add<T>(this.privRequestSession.requestId, deferral);\r\n            await this.sendBaseRequest(connection, operation, this.scenarioFrom(profileType), profile);\r\n            void this.receiveMessage();\r\n            return deferral.promise;\r\n        } catch (err) {\r\n            throw err;\r\n        }\r\n    }\r\n\r\n    private async sendBaseRequest(connection: IConnection, operation: string, scenario: string, profile: VoiceProfile): Promise<void> {\r\n        const profileRequest: { maxPageSize?: number; profileIds?: string[]; scenario: string } = {\r\n            scenario\r\n        };\r\n        if (!!profile) {\r\n            profileRequest.profileIds = [ profile.profileId ];\r\n        } else {\r\n            profileRequest.maxPageSize = -1;\r\n        }\r\n        return connection.send(new SpeechConnectionMessage(\r\n            MessageType.Text,\r\n            `speaker.profile.${operation}`,\r\n            this.privRequestSession.requestId,\r\n            \"application/json; charset=utf-8\",\r\n            JSON.stringify(profileRequest)));\r\n    }\r\n\r\n    private extractSpeakerContext(model: SpeakerRecognitionModel): SpeakerContext {\r\n        return {\r\n            features: {\r\n                interimResult: \"enabled\",\r\n                progressiveDetection: \"disabled\",\r\n            },\r\n            profileIds: model.profileIds,\r\n            scenario: model.scenario,\r\n        };\r\n    }\r\n\r\n    private handlePhrasesResponse(response: ProfilePhraseResponse, requestId: string): void {\r\n        if (!!this.privDeferralMap.getId(requestId)) {\r\n            if (response.status.statusCode.toLowerCase() !== \"success\") {\r\n                const reason: ResultReason = ResultReason.Canceled;\r\n                const result = new VoiceProfilePhraseResult(reason, response.status.statusCode, response.passPhraseType, []);\r\n                this.privDeferralMap.complete<VoiceProfilePhraseResult>(requestId, result);\r\n            } else if (!!response.phrases && response.phrases.length > 0) {\r\n                const reason: ResultReason = ResultReason.EnrollingVoiceProfile;\r\n                const result = new VoiceProfilePhraseResult(reason, response.status.statusCode, response.passPhraseType, response.phrases);\r\n                this.privDeferralMap.complete<VoiceProfilePhraseResult>(requestId, result);\r\n            } else {\r\n                throw new Error(\"Voice Profile get activation phrases failed, no phrases received\");\r\n            }\r\n        } else {\r\n            throw new Error(`Voice Profile get activation phrases request for requestID ${requestId} not found`);\r\n        }\r\n    }\r\n\r\n    private handleCreateResponse(response: ProfileResponse, requestId: string): void {\r\n        if (!!response.profiles && response.profiles.length > 0) {\r\n            if (!!this.privDeferralMap.getId(requestId)) {\r\n                const profileIds: string[] = response.profiles.map((profile: IProfile): string => profile.profileId);\r\n                this.privDeferralMap.complete<string[]>(requestId, profileIds);\r\n            } else {\r\n                throw new Error(`Voice Profile create request for requestID ${requestId} not found`);\r\n            }\r\n        } else {\r\n            throw new Error(\"Voice Profile create failed, no profile id received\");\r\n        }\r\n    }\r\n\r\n    private handleResultResponse(response: ProfileResponse, requestId: string): void {\r\n        if (!!this.privDeferralMap.getId(requestId)) {\r\n            const successReason: ResultReason = response.operation.toLowerCase() === \"delete\" ? ResultReason.DeletedVoiceProfile : ResultReason.ResetVoiceProfile;\r\n            const reason: ResultReason = response.status.statusCode.toLowerCase() === \"success\" ? successReason : ResultReason.Canceled;\r\n            const result = new VoiceProfileResult(reason, `statusCode: ${response.status.statusCode}, errorDetails: ${response.status.reason}`);\r\n            this.privDeferralMap.complete<VoiceProfileResult>(requestId, result);\r\n        } else {\r\n            throw new Error(`Voice Profile create request for requestID ${requestId} not found`);\r\n        }\r\n    }\r\n\r\n    private handleFetchResponse(enrollmentResponse: EnrollmentResponse, requestId: string): void {\r\n        if (!!this.privDeferralMap.getId(requestId) && !!enrollmentResponse.profiles[0]) {\r\n            if (!!this.privExpectedProfileId && enrollmentResponse.profiles.length === 1 && enrollmentResponse.profiles[0].profileId === this.privExpectedProfileId) {\r\n                this.privExpectedProfileId = undefined;\r\n                const profileInfo: IProfile = enrollmentResponse.profiles[0];\r\n                const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\r\n                    this.enrollmentReasonFrom(profileInfo.enrollmentStatus),\r\n                    JSON.stringify(profileInfo),\r\n                    enrollmentResponse.status.reason,\r\n                    );\r\n                this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(requestId, result);\r\n            } else if (enrollmentResponse.profiles.length > 0) {\r\n                const iProfiles: IProfile[] = enrollmentResponse.profiles;\r\n                const profileResults: VoiceProfileEnrollmentResult[] = [];\r\n                for (const profile of iProfiles) {\r\n                    profileResults.push( new VoiceProfileEnrollmentResult(\r\n                        this.enrollmentReasonFrom(profile.enrollmentStatus),\r\n                        JSON.stringify(profile),\r\n                        enrollmentResponse.status.reason,\r\n                    ));\r\n                }\r\n                this.privDeferralMap.complete<VoiceProfileEnrollmentResult[]>(requestId, profileResults);\r\n            }\r\n        } else {\r\n            throw new Error(`Voice Profile fetch request for requestID ${requestId} not found`);\r\n        }\r\n    }\r\n\r\n    private enrollmentReasonFrom(statusCode: string): ResultReason {\r\n        switch (statusCode.toLowerCase()) {\r\n            case \"enrolled\":\r\n                return ResultReason.EnrolledVoiceProfile;\r\n            case \"invalidlocale\":\r\n            case \"invalidphrase\":\r\n            case \"invalidaudioformat\":\r\n            case \"invalidscenario\":\r\n            case \"invalidprofilecount\":\r\n            case \"invalidoperation\":\r\n            case \"audiotooshort\":\r\n            case \"audiotoolong\":\r\n            case \"toomanyenrollments\":\r\n            case \"storageconflict\":\r\n            case \"profilenotfound\":\r\n            case \"incompatibleprofiles\":\r\n            case \"incompleteenrollment\":\r\n                return ResultReason.Canceled;\r\n            default:\r\n                return ResultReason.EnrollingVoiceProfile;\r\n        }\r\n    }\r\n\r\n    private scenarioFrom(profileType: VoiceProfileType): string {\r\n        return profileType === VoiceProfileType.TextIndependentIdentification ? \"TextIndependentIdentification\" :\r\n            profileType === VoiceProfileType.TextIndependentVerification ? \"TextIndependentVerification\" : \"TextDependentVerification\";\r\n    }\r\n}\r\n"]}