@hahnpro/ms-speech-sdk
Version:
Microsoft Cognitive Services Speech SDK for JavaScript
1 lines • 23.5 kB
Source Map (JSON)
{"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.\n// Licensed under the MIT license.\n\nimport { ReplayableAudioNode } from \"../common.browser/Exports.js\";\nimport {\n DeferralMap,\n Deferred,\n IAudioSource,\n IAudioStreamNode,\n IConnection,\n MessageType,\n} from \"../common/Exports.js\";\nimport { AudioStreamFormatImpl } from \"../sdk/Audio/AudioStreamFormat.js\";\nimport { SpeakerRecognitionModel } from \"../sdk/SpeakerRecognitionModel.js\";\nimport {\n CancellationErrorCode,\n CancellationReason,\n VoiceProfileClient,\n PropertyCollection,\n PropertyId,\n ResultReason,\n SessionEventArgs,\n VoiceProfileEnrollmentResult,\n VoiceProfilePhraseResult,\n VoiceProfileResult,\n VoiceProfileType,\n VoiceProfile\n} from \"../sdk/Exports.js\";\nimport {\n CancellationErrorCodePropertyName,\n EnrollmentResponse,\n IProfile,\n ISpeechConfigAudioDevice,\n ProfilePhraseResponse,\n ProfileResponse,\n ServiceRecognizerBase,\n} from \"./Exports.js\";\nimport { IAuthentication } from \"./IAuthentication.js\";\nimport { IConnectionFactory } from \"./IConnectionFactory.js\";\nimport { RecognizerConfig } from \"./RecognizerConfig.js\";\nimport { SpeechConnectionMessage } from \"./SpeechConnectionMessage.Internal.js\";\n\ninterface CreateProfile {\n scenario: string;\n locale: string;\n number: string;\n}\n\ninterface PhraseRequest {\n scenario: string;\n locale: string;\n}\n\ninterface SpeakerContext {\n scenario: string;\n profileIds: string[];\n features: {\n interimResult: string;\n progressiveDetection: string;\n };\n}\n\n// eslint-disable-next-line max-classes-per-file\nexport class VoiceServiceRecognizer extends ServiceRecognizerBase {\n private privSpeakerAudioSource: IAudioSource;\n private privDeferralMap: DeferralMap = new DeferralMap();\n private privExpectedProfileId: string;\n\n public constructor(\n authentication: IAuthentication,\n connectionFactory: IConnectionFactory,\n audioSource: IAudioSource,\n recognizerConfig: RecognizerConfig,\n recognizer: VoiceProfileClient) {\n super(authentication, connectionFactory, audioSource, recognizerConfig, recognizer);\n this.privSpeakerAudioSource = audioSource;\n this.sendPrePayloadJSONOverride = (): Promise<void> => this.noOp();\n }\n\n public set SpeakerAudioSource(audioSource: IAudioSource) {\n this.privSpeakerAudioSource = audioSource;\n }\n\n protected processTypeSpecificMessages(connectionMessage: SpeechConnectionMessage): Promise<boolean> {\n\n let processed: boolean = false;\n\n const resultProps: PropertyCollection = new PropertyCollection();\n if (connectionMessage.messageType === MessageType.Text) {\n resultProps.setProperty(PropertyId.SpeechServiceResponse_JsonResult, connectionMessage.textBody);\n }\n\n switch (connectionMessage.path.toLowerCase()) {\n // Profile management response for create, fetch, delete, reset\n case \"speaker.profiles\":\n const response: ProfileResponse = JSON.parse(connectionMessage.textBody) as ProfileResponse;\n switch (response.operation.toLowerCase()) {\n case \"create\":\n this.handleCreateResponse(response, connectionMessage.requestId);\n break;\n\n case \"delete\":\n case \"reset\":\n this.handleResultResponse(response, connectionMessage.requestId);\n break;\n\n case \"fetch\":\n const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\n this.handleFetchResponse(enrollmentResponse, connectionMessage.requestId);\n break;\n\n default:\n break;\n }\n processed = true;\n break;\n // Activation and authorization phrase response\n case \"speaker.phrases\":\n const phraseResponse: ProfilePhraseResponse = JSON.parse(connectionMessage.textBody) as ProfilePhraseResponse;\n this.handlePhrasesResponse(phraseResponse, connectionMessage.requestId);\n processed = true;\n break;\n // Enrollment response\n case \"speaker.profile.enrollment\":\n const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\n const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\n this.enrollmentReasonFrom(!!enrollmentResponse.enrollment ? enrollmentResponse.enrollment.enrollmentStatus : enrollmentResponse.status.statusCode),\n !!enrollmentResponse.enrollment ? JSON.stringify(enrollmentResponse.enrollment) : undefined,\n enrollmentResponse.status.reason,\n );\n if (!!this.privDeferralMap.getId(connectionMessage.requestId)) {\n this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(connectionMessage.requestId, result);\n }\n this.privRequestSession.onSpeechEnded();\n processed = true;\n break;\n default:\n break;\n }\n const defferal = new Deferred<boolean>();\n defferal.resolve(processed);\n return defferal.promise;\n }\n\n // Cancels recognition.\n protected cancelRecognition(\n sessionId: string,\n requestId: string,\n cancellationReason: CancellationReason,\n errorCode: CancellationErrorCode,\n error: string): void {\n\n const properties: PropertyCollection = new PropertyCollection();\n // const enrollmentResponse: EnrollmentResponse = JSON.parse(connectionMessage.textBody) as EnrollmentResponse;\n properties.setProperty(CancellationErrorCodePropertyName, CancellationErrorCode[errorCode]);\n\n const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\n ResultReason.Canceled,\n error,\n error,\n );\n if (!!this.privDeferralMap.getId(requestId)) {\n this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(requestId, result);\n }\n }\n\n public async createProfile(profileType: VoiceProfileType, locale: string): Promise<string[]> {\n // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\n this.voiceProfileType = profileType.toString();\n const conPromise: Promise<IConnection> = this.connectImpl();\n try {\n const createProfileDeferral = new Deferred<string[]>();\n await conPromise;\n await this.sendCreateProfile(createProfileDeferral, profileType, locale);\n void this.receiveMessage();\n return createProfileDeferral.promise;\n } catch (err) {\n throw err;\n }\n }\n\n public async resetProfile(profile: VoiceProfile): Promise<VoiceProfileResult> {\n this.voiceProfileType = profile.profileType.toString();\n return this.sendCommonRequest<VoiceProfileResult>(\"reset\", profile.profileType, profile);\n }\n\n public async deleteProfile(profile: VoiceProfile): Promise<VoiceProfileResult> {\n this.voiceProfileType = profile.profileType.toString();\n return this.sendCommonRequest<VoiceProfileResult>(\"delete\", profile.profileType, profile);\n }\n\n public async retrieveEnrollmentResult(profile: VoiceProfile): Promise<VoiceProfileEnrollmentResult> {\n this.voiceProfileType = profile.profileType.toString();\n this.privExpectedProfileId = profile.profileId;\n return this.sendCommonRequest<VoiceProfileEnrollmentResult>(\"fetch\", profile.profileType, profile);\n }\n\n public async getAllProfiles(profileType: VoiceProfileType): Promise<VoiceProfileEnrollmentResult[]> {\n this.voiceProfileType = profileType.toString();\n return this.sendCommonRequest<VoiceProfileEnrollmentResult[]>(\"fetch\", profileType);\n }\n\n public async getActivationPhrases(profileType: VoiceProfileType, lang: string): Promise<VoiceProfilePhraseResult> {\n this.voiceProfileType = profileType.toString();\n // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\n const conPromise: Promise<IConnection> = this.connectImpl();\n try {\n const getPhrasesDeferral = new Deferred<VoiceProfilePhraseResult>();\n await conPromise;\n await this.sendPhrasesRequest(getPhrasesDeferral, profileType, lang);\n void this.receiveMessage();\n return getPhrasesDeferral.promise;\n } catch (err) {\n throw err;\n }\n }\n\n public async enrollProfile(profile: VoiceProfile): Promise<VoiceProfileEnrollmentResult> {\n this.voiceProfileType = profile.profileType.toString();\n const enrollmentDeferral = new Deferred<VoiceProfileEnrollmentResult>();\n this.privRequestSession.startNewRecognition();\n this.privRequestSession.listenForServiceTelemetry(this.privSpeakerAudioSource.events);\n\n this.privRecognizerConfig.parameters.setProperty(PropertyId.Speech_SessionId, this.privRequestSession.sessionId);\n\n // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\n const conPromise: Promise<IConnection> = this.connectImpl();\n\n const preAudioPromise: Promise<void> = this.sendPreAudioMessages(profile, enrollmentDeferral);\n\n const node: IAudioStreamNode = await this.privSpeakerAudioSource.attach(this.privRequestSession.audioNodeId);\n const format: AudioStreamFormatImpl = await this.privSpeakerAudioSource.format;\n const deviceInfo: ISpeechConfigAudioDevice = await this.privSpeakerAudioSource.deviceInfo;\n\n const audioNode = new ReplayableAudioNode(node, format.avgBytesPerSec);\n await this.privRequestSession.onAudioSourceAttachCompleted(audioNode, false);\n\n this.privRecognizerConfig.SpeechServiceConfig.Context.audio = { source: deviceInfo };\n\n try {\n await conPromise;\n await preAudioPromise;\n } catch (err) {\n this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.ConnectionFailure, err as string);\n }\n\n const sessionStartEventArgs: SessionEventArgs = new SessionEventArgs(this.privRequestSession.sessionId);\n\n if (!!this.privRecognizer.sessionStarted) {\n this.privRecognizer.sessionStarted(this.privRecognizer, sessionStartEventArgs);\n }\n\n void this.receiveMessage();\n const audioSendPromise = this.sendAudio(audioNode);\n\n // /* eslint-disable no-empty */\n audioSendPromise.then((): void => { /* add? return true;*/ }, (error: string): void => {\n this.cancelRecognition(this.privRequestSession.sessionId, this.privRequestSession.requestId, CancellationReason.Error, CancellationErrorCode.RuntimeError, error);\n });\n\n return enrollmentDeferral.promise;\n }\n\n private async sendPreAudioMessages(profile: VoiceProfile, enrollmentDeferral: Deferred<VoiceProfileEnrollmentResult>): Promise<void> {\n const connection: IConnection = await this.fetchConnection();\n this.privRequestSession.onSpeechContext();\n this.privDeferralMap.add<VoiceProfileEnrollmentResult>(this.privRequestSession.requestId, enrollmentDeferral);\n await this.sendBaseRequest(connection, \"enroll\", this.scenarioFrom(profile.profileType), profile);\n }\n\n private async sendPhrasesRequest(getPhrasesDeferral: Deferred<VoiceProfilePhraseResult>, profileType: VoiceProfileType, locale: string): Promise<void> {\n const connection: IConnection = await this.fetchConnection();\n this.privRequestSession.onSpeechContext();\n this.privDeferralMap.add<VoiceProfilePhraseResult>(this.privRequestSession.requestId, getPhrasesDeferral);\n const scenario = this.scenarioFrom(profileType);\n\n const profileCreateRequest: PhraseRequest = {\n locale,\n scenario,\n };\n return connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n \"speaker.profile.phrases\",\n this.privRequestSession.requestId,\n \"application/json; charset=utf-8\",\n JSON.stringify(profileCreateRequest)));\n }\n\n private async sendCreateProfile(createProfileDeferral: Deferred<string[]>, profileType: VoiceProfileType, locale: string): Promise<void> {\n\n const connection: IConnection = await this.fetchConnection();\n this.privRequestSession.onSpeechContext();\n this.privDeferralMap.add<string[]>(this.privRequestSession.requestId, createProfileDeferral);\n const scenario = profileType === VoiceProfileType.TextIndependentIdentification ? \"TextIndependentIdentification\" :\n profileType === VoiceProfileType.TextIndependentVerification ? \"TextIndependentVerification\" : \"TextDependentVerification\";\n\n const profileCreateRequest: CreateProfile = {\n locale,\n number: \"1\",\n scenario,\n };\n return connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n \"speaker.profile.create\",\n this.privRequestSession.requestId,\n \"application/json; charset=utf-8\",\n JSON.stringify(profileCreateRequest)));\n }\n\n private async sendCommonRequest<T>(operation: string, profileType: VoiceProfileType, profile: VoiceProfile = undefined): Promise<T> {\n // Start the connection to the service. The promise this will create is stored and will be used by configureConnection().\n const conPromise: Promise<IConnection> = this.connectImpl();\n try {\n const deferral = new Deferred<T>();\n this.privRequestSession.onSpeechContext();\n await conPromise;\n const connection: IConnection = await this.fetchConnection();\n this.privDeferralMap.add<T>(this.privRequestSession.requestId, deferral);\n await this.sendBaseRequest(connection, operation, this.scenarioFrom(profileType), profile);\n void this.receiveMessage();\n return deferral.promise;\n } catch (err) {\n throw err;\n }\n }\n\n private async sendBaseRequest(connection: IConnection, operation: string, scenario: string, profile: VoiceProfile): Promise<void> {\n const profileRequest: { maxPageSize?: number; profileIds?: string[]; scenario: string } = {\n scenario\n };\n if (!!profile) {\n profileRequest.profileIds = [ profile.profileId ];\n } else {\n profileRequest.maxPageSize = -1;\n }\n return connection.send(new SpeechConnectionMessage(\n MessageType.Text,\n `speaker.profile.${operation}`,\n this.privRequestSession.requestId,\n \"application/json; charset=utf-8\",\n JSON.stringify(profileRequest)));\n }\n\n private extractSpeakerContext(model: SpeakerRecognitionModel): SpeakerContext {\n return {\n features: {\n interimResult: \"enabled\",\n progressiveDetection: \"disabled\",\n },\n profileIds: model.profileIds,\n scenario: model.scenario,\n };\n }\n\n private handlePhrasesResponse(response: ProfilePhraseResponse, requestId: string): void {\n if (!!this.privDeferralMap.getId(requestId)) {\n if (response.status.statusCode.toLowerCase() !== \"success\") {\n const reason: ResultReason = ResultReason.Canceled;\n const result = new VoiceProfilePhraseResult(reason, response.status.statusCode, response.passPhraseType, []);\n this.privDeferralMap.complete<VoiceProfilePhraseResult>(requestId, result);\n } else if (!!response.phrases && response.phrases.length > 0) {\n const reason: ResultReason = ResultReason.EnrollingVoiceProfile;\n const result = new VoiceProfilePhraseResult(reason, response.status.statusCode, response.passPhraseType, response.phrases);\n this.privDeferralMap.complete<VoiceProfilePhraseResult>(requestId, result);\n } else {\n throw new Error(\"Voice Profile get activation phrases failed, no phrases received\");\n }\n } else {\n throw new Error(`Voice Profile get activation phrases request for requestID ${requestId} not found`);\n }\n }\n\n private handleCreateResponse(response: ProfileResponse, requestId: string): void {\n if (!!response.profiles && response.profiles.length > 0) {\n if (!!this.privDeferralMap.getId(requestId)) {\n const profileIds: string[] = response.profiles.map((profile: IProfile): string => profile.profileId);\n this.privDeferralMap.complete<string[]>(requestId, profileIds);\n } else {\n throw new Error(`Voice Profile create request for requestID ${requestId} not found`);\n }\n } else {\n throw new Error(\"Voice Profile create failed, no profile id received\");\n }\n }\n\n private handleResultResponse(response: ProfileResponse, requestId: string): void {\n if (!!this.privDeferralMap.getId(requestId)) {\n const successReason: ResultReason = response.operation.toLowerCase() === \"delete\" ? ResultReason.DeletedVoiceProfile : ResultReason.ResetVoiceProfile;\n const reason: ResultReason = response.status.statusCode.toLowerCase() === \"success\" ? successReason : ResultReason.Canceled;\n const result = new VoiceProfileResult(reason, `statusCode: ${response.status.statusCode}, errorDetails: ${response.status.reason}`);\n this.privDeferralMap.complete<VoiceProfileResult>(requestId, result);\n } else {\n throw new Error(`Voice Profile create request for requestID ${requestId} not found`);\n }\n }\n\n private handleFetchResponse(enrollmentResponse: EnrollmentResponse, requestId: string): void {\n if (!!this.privDeferralMap.getId(requestId) && !!enrollmentResponse.profiles[0]) {\n if (!!this.privExpectedProfileId && enrollmentResponse.profiles.length === 1 && enrollmentResponse.profiles[0].profileId === this.privExpectedProfileId) {\n this.privExpectedProfileId = undefined;\n const profileInfo: IProfile = enrollmentResponse.profiles[0];\n const result: VoiceProfileEnrollmentResult = new VoiceProfileEnrollmentResult(\n this.enrollmentReasonFrom(profileInfo.enrollmentStatus),\n JSON.stringify(profileInfo),\n enrollmentResponse.status.reason,\n );\n this.privDeferralMap.complete<VoiceProfileEnrollmentResult>(requestId, result);\n } else if (enrollmentResponse.profiles.length > 0) {\n const iProfiles: IProfile[] = enrollmentResponse.profiles;\n const profileResults: VoiceProfileEnrollmentResult[] = [];\n for (const profile of iProfiles) {\n profileResults.push( new VoiceProfileEnrollmentResult(\n this.enrollmentReasonFrom(profile.enrollmentStatus),\n JSON.stringify(profile),\n enrollmentResponse.status.reason,\n ));\n }\n this.privDeferralMap.complete<VoiceProfileEnrollmentResult[]>(requestId, profileResults);\n }\n } else {\n throw new Error(`Voice Profile fetch request for requestID ${requestId} not found`);\n }\n }\n\n private enrollmentReasonFrom(statusCode: string): ResultReason {\n switch (statusCode.toLowerCase()) {\n case \"enrolled\":\n return ResultReason.EnrolledVoiceProfile;\n case \"invalidlocale\":\n case \"invalidphrase\":\n case \"invalidaudioformat\":\n case \"invalidscenario\":\n case \"invalidprofilecount\":\n case \"invalidoperation\":\n case \"audiotooshort\":\n case \"audiotoolong\":\n case \"toomanyenrollments\":\n case \"storageconflict\":\n case \"profilenotfound\":\n case \"incompatibleprofiles\":\n case \"incompleteenrollment\":\n return ResultReason.Canceled;\n default:\n return ResultReason.EnrollingVoiceProfile;\n }\n }\n\n private scenarioFrom(profileType: VoiceProfileType): string {\n return profileType === VoiceProfileType.TextIndependentIdentification ? \"TextIndependentIdentification\" :\n profileType === VoiceProfileType.TextIndependentVerification ? \"TextIndependentVerification\" : \"TextDependentVerification\";\n }\n}\n"]}