UNPKG

@hahnpro/ms-speech-sdk

Version:
1 lines 12.2 kB
{"version":3,"sources":["src/sdk/AvatarSynthesizer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAEH,eAAe,EACf,2BAA2B,EAC3B,mBAAmB,EACnB,oBAAoB,EACvB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACH,YAAY,EACZ,eAAe,EACf,kBAAkB,EAGlB,YAAY,EAEZ,qBAAqB,EACrB,eAAe,EACf,WAAW,EACd,MAAM,cAAc,CAAC;AAItB;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC7C,OAAO,CAAC,gBAAgB,CAAe;IACvC,OAAO,CAAC,cAAc,CAAiB;IACvC;;;;;OAKG;IACI,mBAAmB,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAExF;;;;;OAKG;gBACgB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY;IAUzE,SAAS,CAAC,yBAAyB,IAAI,IAAI;IAS3C;;;;;;;OAOG;IACU,gBAAgB,CAAC,cAAc,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAwD1F;;;;;;;OAOG;IACU,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAUnE;;;;;;;OAOG;IACU,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAUnE;;;;;;OAMG;IACU,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ/C;;;;;;;;OAQG;IACU,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7C;;;;;OAKG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnC;;OAEG;IACH,IAAW,UAAU,IAAI,YAAY,EAAE,CAEtC;IAGD,SAAS,CAAC,sBAAsB,CAC5B,cAAc,EAAE,eAAe,EAC/B,iBAAiB,EAAE,2BAA2B,EAC9C,iBAAiB,EAAE,iBAAiB,GAAG,oBAAoB;IAS/D,SAAS,CAAC,0BAA0B,CAChC,eAAe,EAAE,eAAe,EAChC,kBAAkB,EAAE,iBAAiB,GAAG,oBAAoB;IAIhE,SAAS,CAAC,uBAAuB,CAAC,YAAY,EAAE,mBAAmB,GAAG,iBAAiB;cAMvE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAgBvF","file":"AvatarSynthesizer.d.ts","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license.\n\nimport { SpeechSynthesisConnectionFactory } from \"../common.speech/SpeechSynthesisConnectionFactory.js\";\nimport { SynthesisRestAdapter } from \"../common.speech/SynthesisRestAdapter.js\";\nimport { SynthesizerConfig } from \"../common.speech/SynthesizerConfig.js\";\nimport {\n AvatarSynthesisAdapter,\n IAuthentication,\n ISynthesisConnectionFactory,\n SpeechServiceConfig,\n SynthesisAdapterBase\n} from \"../common.speech/Exports.js\";\nimport { createNoDashGuid, Deferred, Events, EventType, PlatformEvent } from \"../common/Exports.js\";\nimport { AudioOutputFormatImpl } from \"./Audio/AudioOutputFormat.js\";\nimport {\n AvatarConfig,\n AvatarEventArgs,\n PropertyCollection,\n PropertyId,\n ResultReason,\n SpeechConfig,\n SpeechSynthesisOutputFormat,\n SpeechSynthesisResult,\n SynthesisResult,\n Synthesizer\n} from \"./Exports.js\";\nimport { Contracts } from \"./Contracts.js\";\nimport { SynthesisRequest } from \"./Synthesizer.js\";\n\n/**\n * Defines the avatar synthesizer.\n * @class AvatarSynthesizer\n * Added in version 1.33.0\n *\n * @experimental This feature is experimental and might change or have limited support.\n */\nexport class AvatarSynthesizer extends Synthesizer {\n protected privProperties: PropertyCollection;\n private privAvatarConfig: AvatarConfig;\n private privIceServers: RTCIceServer[];\n /**\n * Defines event handler for avatar events.\n * @member AvatarSynthesizer.prototype.avatarEventReceived\n * @function\n * @public\n */\n public avatarEventReceived: (sender: AvatarSynthesizer, event: AvatarEventArgs) => void;\n\n /**\n * Creates and initializes an instance of this class.\n * @constructor\n * @param {SpeechConfig} speechConfig - The speech config.\n * @param {AvatarConfig} avatarConfig - The talking avatar config.\n */\n public constructor(speechConfig: SpeechConfig, avatarConfig: AvatarConfig) {\n super(speechConfig);\n\n Contracts.throwIfNullOrUndefined(avatarConfig, \"avatarConfig\");\n\n this.privConnectionFactory = new SpeechSynthesisConnectionFactory();\n this.privAvatarConfig = avatarConfig;\n this.implCommonSynthesizeSetup();\n }\n\n protected implCommonSynthesizeSetup(): void {\n super.implCommonSynthesizeSetup();\n\n // The service checks the audio format setting while it ignores it in avatar synthesis.\n this.privAdapter.audioOutputFormat = AudioOutputFormatImpl.fromSpeechSynthesisOutputFormat(\n SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm\n );\n }\n\n /**\n * Starts the talking avatar session and establishes the WebRTC connection.\n * @member AvatarSynthesizer.prototype.startAvatarAsync\n * @function\n * @public\n * @param {AvatarWebRTCConnectionInfo} peerConnection - The peer connection.\n * @returns {Promise<SynthesisResult>} The promise of the connection result.\n */\n public async startAvatarAsync(peerConnection: RTCPeerConnection): Promise<SynthesisResult> {\n Contracts.throwIfNullOrUndefined(peerConnection, \"peerConnection\");\n this.privIceServers = peerConnection.getConfiguration().iceServers;\n Contracts.throwIfNullOrUndefined(this.privIceServers, \"Ice servers must be set.\");\n const iceGatheringDone = new Deferred<void>();\n // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icegatheringstatechange_event\n peerConnection.onicegatheringstatechange = (): void => {\n Events.instance.onEvent(new PlatformEvent(\"peer connection: ice gathering state: \" + peerConnection.iceGatheringState, EventType.Debug));\n if (peerConnection.iceGatheringState === \"complete\") {\n Events.instance.onEvent(new PlatformEvent(\"peer connection: ice gathering complete.\", EventType.Info));\n iceGatheringDone.resolve();\n }\n };\n peerConnection.onicecandidate = (event: RTCPeerConnectionIceEvent): void => {\n if (event.candidate) {\n Events.instance.onEvent(new PlatformEvent(\"peer connection: ice candidate: \" + event.candidate.candidate, EventType.Debug));\n } else {\n Events.instance.onEvent(new PlatformEvent(\"peer connection: ice candidate: complete\", EventType.Debug));\n iceGatheringDone.resolve();\n }\n };\n // Set a timeout for ice gathering, currently 2 seconds.\n setTimeout((): void => {\n if (peerConnection.iceGatheringState !== \"complete\") {\n Events.instance.onEvent(new PlatformEvent(\"peer connection: ice gathering timeout.\", EventType.Warning));\n iceGatheringDone.resolve();\n }\n }, 2000);\n const sdp: RTCSessionDescriptionInit = await peerConnection.createOffer();\n await peerConnection.setLocalDescription(sdp);\n await iceGatheringDone.promise;\n Events.instance.onEvent(new PlatformEvent(\"peer connection: got local SDP.\", EventType.Info));\n this.privProperties.setProperty(PropertyId.TalkingAvatarService_WebRTC_SDP, JSON.stringify(peerConnection.localDescription));\n\n const result: SpeechSynthesisResult = await this.speak(\"\", false);\n if (result.reason !== ResultReason.SynthesizingAudioCompleted) {\n return new SynthesisResult(\n result.resultId,\n result.reason,\n result.errorDetails,\n result.properties,\n );\n }\n const sdpAnswerString: string = atob(result.properties.getProperty(PropertyId.TalkingAvatarService_WebRTC_SDP));\n const sdpAnswer: RTCSessionDescription = new RTCSessionDescription(\n JSON.parse(sdpAnswerString) as RTCSessionDescriptionInit,\n );\n await peerConnection.setRemoteDescription(sdpAnswer);\n return new SynthesisResult(\n result.resultId,\n result.reason,\n undefined,\n result.properties,\n );\n }\n\n /**\n * Speaks plain text asynchronously. The rendered audio and video will be sent via the WebRTC connection.\n * @member AvatarSynthesizer.prototype.speakTextAsync\n * @function\n * @public\n * @param {string} text - The plain text to speak.\n * @returns {Promise<SynthesisResult>} The promise of the synthesis result.\n */\n public async speakTextAsync(text: string): Promise<SynthesisResult> {\n const r = await this.speak(text, false);\n return new SynthesisResult(\n r.resultId,\n r.reason,\n r.errorDetails,\n r.properties,\n );\n }\n\n /**\n * Speaks SSML asynchronously. The rendered audio and video will be sent via the WebRTC connection.\n * @member AvatarSynthesizer.prototype.speakSsmlAsync\n * @function\n * @public\n * @param {string} ssml - The SSML text to speak.\n * @returns {Promise<SynthesisResult>} The promise of the synthesis result.\n */\n public async speakSsmlAsync(ssml: string): Promise<SynthesisResult> {\n const r = await this.speak(ssml, true);\n return new SynthesisResult(\n r.resultId,\n r.reason,\n r.errorDetails,\n r.properties,\n );\n }\n\n /**\n * Speaks text asynchronously. The avatar will switch to idle state.\n * @member AvatarSynthesizer.prototype.stopSpeakingAsync\n * @function\n * @public\n * @returns {Promise<void>} The promise of the void result.\n */\n public async stopSpeakingAsync(): Promise<void> {\n while (this.synthesisRequestQueue.length() > 0) {\n const request = await this.synthesisRequestQueue.dequeue();\n request.err(\"Synthesis is canceled by user.\");\n }\n return this.privAdapter.stopSpeaking();\n }\n\n /**\n * Stops the talking avatar session and closes the WebRTC connection.\n * For now, this is the same as close().\n * You need to create a new AvatarSynthesizer instance to start a new session.\n * @member AvatarSynthesizer.prototype.stopAvatarAsync\n * @function\n * @public\n * @returns {Promise<void>} The promise of the void result.\n */\n public async stopAvatarAsync(): Promise<void> {\n Contracts.throwIfDisposed(this.privDisposed);\n return this.dispose(true);\n }\n\n /**\n * Dispose of associated resources.\n * @member AvatarSynthesizer.prototype.close\n * @function\n * @public\n */\n public async close(): Promise<void> {\n if (this.privDisposed) {\n return;\n }\n\n return this.dispose(true);\n }\n\n /**\n * Gets the ICE servers. Internal use only.\n */\n public get iceServers(): RTCIceServer[] {\n return this.privIceServers;\n }\n\n // Creates the synthesis adapter\n protected createSynthesisAdapter(\n authentication: IAuthentication,\n connectionFactory: ISynthesisConnectionFactory,\n synthesizerConfig: SynthesizerConfig): SynthesisAdapterBase {\n return new AvatarSynthesisAdapter(\n authentication,\n connectionFactory,\n synthesizerConfig,\n this,\n this.privAvatarConfig);\n }\n\n protected createRestSynthesisAdapter(\n _authentication: IAuthentication,\n _synthesizerConfig: SynthesizerConfig): SynthesisRestAdapter {\n return undefined;\n }\n\n protected createSynthesizerConfig(speechConfig: SpeechServiceConfig): SynthesizerConfig {\n const config = super.createSynthesizerConfig(speechConfig);\n config.avatarEnabled = true;\n return config;\n }\n\n protected async speak(text: string, isSSML: boolean): Promise<SpeechSynthesisResult> {\n const requestId = createNoDashGuid();\n const deferredResult = new Deferred<SpeechSynthesisResult>();\n this.synthesisRequestQueue.enqueue(new SynthesisRequest(requestId, text, isSSML,\n (e: SpeechSynthesisResult): void => {\n deferredResult.resolve(e);\n this.privSynthesizing = false;\n void this.adapterSpeak();\n },\n (e: string): void => {\n deferredResult.reject(e);\n this.privSynthesizing = false;\n }));\n void this.adapterSpeak();\n return deferredResult.promise;\n }\n}\n"]}