@memori.ai/memori-react 7.6.0 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/components/Avatar/Avatar.js +2 -2
  3. package/dist/components/Avatar/Avatar.js.map +1 -1
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +4 -3
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +10 -6
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +11 -17
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +128 -104
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +1 -4
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +2 -4
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  13. package/dist/components/Avatar/AvatarView/index.d.ts +5 -3
  14. package/dist/components/Avatar/AvatarView/index.js +2 -2
  15. package/dist/components/Avatar/AvatarView/index.js.map +1 -1
  16. package/dist/components/MemoriWidget/MemoriWidget.js +117 -118
  17. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  18. package/dist/components/layouts/HiddenChat.js +3 -4
  19. package/dist/components/layouts/HiddenChat.js.map +1 -1
  20. package/dist/components/layouts/ZoomedFullBody.d.ts +2 -2
  21. package/dist/components/layouts/ZoomedFullBody.js +11 -2
  22. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  23. package/dist/components/layouts/hidden-chat.css +23 -23
  24. package/dist/components/layouts/zoomed-full-body.css +16 -0
  25. package/dist/context/visemeContext.d.ts +8 -15
  26. package/dist/context/visemeContext.js +64 -166
  27. package/dist/context/visemeContext.js.map +1 -1
  28. package/dist/helpers/translations.js +10 -2
  29. package/dist/helpers/translations.js.map +1 -1
  30. package/dist/helpers/utils.js +5 -6
  31. package/dist/helpers/utils.js.map +1 -1
  32. package/dist/styles.css +1 -0
  33. package/esm/components/Avatar/Avatar.js +2 -2
  34. package/esm/components/Avatar/Avatar.js.map +1 -1
  35. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +4 -3
  36. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +10 -6
  37. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  38. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +11 -17
  39. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +131 -107
  40. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
  41. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +1 -4
  42. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +2 -4
  43. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  44. package/esm/components/Avatar/AvatarView/index.d.ts +5 -3
  45. package/esm/components/Avatar/AvatarView/index.js +2 -2
  46. package/esm/components/Avatar/AvatarView/index.js.map +1 -1
  47. package/esm/components/MemoriWidget/MemoriWidget.js +117 -118
  48. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  49. package/esm/components/layouts/HiddenChat.js +3 -4
  50. package/esm/components/layouts/HiddenChat.js.map +1 -1
  51. package/esm/components/layouts/ZoomedFullBody.d.ts +2 -2
  52. package/esm/components/layouts/ZoomedFullBody.js +11 -2
  53. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  54. package/esm/components/layouts/hidden-chat.css +23 -23
  55. package/esm/components/layouts/zoomed-full-body.css +16 -0
  56. package/esm/context/visemeContext.d.ts +8 -15
  57. package/esm/context/visemeContext.js +65 -167
  58. package/esm/context/visemeContext.js.map +1 -1
  59. package/esm/helpers/translations.js +10 -2
  60. package/esm/helpers/translations.js.map +1 -1
  61. package/esm/helpers/utils.js +5 -6
  62. package/esm/helpers/utils.js.map +1 -1
  63. package/esm/styles.css +1 -0
  64. package/package.json +1 -1
  65. package/src/components/Avatar/Avatar.stories.tsx +7 -5
  66. package/src/components/Avatar/Avatar.tsx +3 -5
  67. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +20 -19
  68. package/src/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.tsx +206 -140
  69. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +1 -7
  70. package/src/components/Avatar/AvatarView/AvatarView.stories.tsx +36 -24
  71. package/src/components/Avatar/AvatarView/index.tsx +3 -8
  72. package/src/components/MemoriWidget/MemoriWidget.tsx +140 -160
  73. package/src/components/layouts/HiddenChat.tsx +13 -14
  74. package/src/components/layouts/ZoomedFullBody.tsx +38 -29
  75. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +12 -12
  76. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +25 -21
  77. package/src/components/layouts/hidden-chat.css +23 -23
  78. package/src/components/layouts/zoomed-full-body.css +16 -0
  79. package/src/context/visemeContext.tsx +90 -260
  80. package/src/helpers/translations.ts +11 -8
  81. package/src/helpers/utils.ts +9 -8
  82. package/src/styles.css +1 -0
@@ -17,7 +17,9 @@ export interface Props {
17
17
  isZoomed?: boolean;
18
18
  chatEmission?: any;
19
19
  setMeshRef?: any;
20
- clearVisemes: () => void;
21
- setEmotion: (emotion: string) => void;
20
+ updateCurrentViseme: (currentTime: number) => {
21
+ name: string;
22
+ weight: number;
23
+ } | null;
22
24
  }
23
- export default function ContainerAvatarView({ url, sex, style, rotateAvatar, eyeBlink, headMovement, speaking, fallback, fallbackImg, halfBody, loading, animation, showControls, isZoomed, chatEmission, setMeshRef, clearVisemes, setEmotion, }: Props): JSX.Element;
25
+ export default function ContainerAvatarView({ url, sex, style, rotateAvatar, eyeBlink, headMovement, speaking, fallback, fallbackImg, halfBody, loading, animation, showControls, isZoomed, chatEmission, updateCurrentViseme, }: Props): JSX.Element;
@@ -34,8 +34,8 @@ const getCameraSettings = (halfBody, isZoomed) => halfBody
34
34
  }
35
35
  : { fov: 40, position: [0, 0.0000175, 3] };
36
36
  const getLightingComponent = () => (0, utils_1.isAndroid)() || (0, utils_1.isiOS)() ? ((0, jsx_runtime_1.jsx)(drei_1.SpotLight, { distance: 100, position: [-0.3, 0.2, 1.25], angle: Math.PI / 2, attenuation: 5, anglePower: 5 })) : ((0, jsx_runtime_1.jsx)(drei_1.Environment, { files: "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/venice_sunset_1k.hdr" }));
37
- function ContainerAvatarView({ url, sex, style, rotateAvatar, eyeBlink, headMovement, speaking, fallback, fallbackImg, halfBody = true, loading, animation, showControls = false, isZoomed, chatEmission, setMeshRef, clearVisemes, setEmotion, }) {
38
- return ((0, jsx_runtime_1.jsx)(fiber_1.Canvas, { style: style || (halfBody ? defaultStyles.halfBody : defaultStyles.fullBody), camera: getCameraSettings(halfBody, isZoomed), children: (0, jsx_runtime_1.jsxs)(react_1.Suspense, { fallback: fallback || (0, jsx_runtime_1.jsx)(loader_1.default, { fallbackImg: fallbackImg }), children: [getLightingComponent(), rotateAvatar && (0, jsx_runtime_1.jsx)(drei_1.OrbitControls, { enablePan: false, enableZoom: false }), (0, jsx_runtime_1.jsx)(avatarComponent_1.AvatarView, { url: url, sex: sex, showControls: showControls, loading: loading || false, animation: animation, isZoomed: isZoomed || false, eyeBlink: eyeBlink || false, headMovement: headMovement || false, speaking: speaking || false, halfBody: halfBody || false, chatEmission: chatEmission, setMeshRef: setMeshRef, clearVisemes: clearVisemes, setEmotion: setEmotion })] }) }));
37
+ function ContainerAvatarView({ url, sex, style, rotateAvatar, eyeBlink, headMovement, speaking, fallback, fallbackImg, halfBody = true, loading, animation, showControls = false, isZoomed, chatEmission, updateCurrentViseme, }) {
38
+ return ((0, jsx_runtime_1.jsx)(fiber_1.Canvas, { style: style || (halfBody ? defaultStyles.halfBody : defaultStyles.fullBody), camera: getCameraSettings(halfBody, isZoomed), children: (0, jsx_runtime_1.jsxs)(react_1.Suspense, { fallback: fallback || (0, jsx_runtime_1.jsx)(loader_1.default, { fallbackImg: fallbackImg }), children: [getLightingComponent(), rotateAvatar && (0, jsx_runtime_1.jsx)(drei_1.OrbitControls, { enablePan: false, enableZoom: false }), (0, jsx_runtime_1.jsx)(avatarComponent_1.AvatarView, { url: url, sex: sex, showControls: showControls, loading: loading || false, animation: animation, isZoomed: isZoomed || false, eyeBlink: eyeBlink || false, headMovement: headMovement || false, speaking: speaking || false, halfBody: halfBody || false, chatEmission: chatEmission, updateCurrentViseme: updateCurrentViseme })] }) }));
39
39
  }
40
40
  exports.default = ContainerAvatarView;
41
41
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Avatar/AvatarView/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Avatar/AvatarView/index.tsx"],"names":[],"mappings":";;;;AACA,iCAAwC;AACxC,8CAA4C;AAC5C,4CAA0E;AAC1E,kDAA0D;AAC1D,uEAA6D;AAC7D,yFAAyD;AAsBzD,MAAM,aAAa,GAAG;IACpB,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,OAAO;QAClB,eAAe,EAAE,OAAO;QACxB,YAAY,EAAE,MAAM;KACrB;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,OAAO;QACf,eAAe,EAAE,OAAO;KACzB;CACF,CAAC;AAGF,MAAM,iBAAiB,GAAG,CAAC,QAAiB,EAAE,QAAkB,EAAE,EAAE,CAClE,QAAQ;IACN,CAAC,CAAC;QACE,GAAG,EAAE,EAAE;QACP,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC;KACtB;IACH,CAAC,CAAC,CAAC,QAAQ,IAAI,QAAQ;QACvB,CAAC,CAAC;YAEE,GAAG,EAAE,EAAE;YACP,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;SACvB;QACH,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;AAE/C,MAAM,oBAAoB,GAAG,GAAG,EAAE,CAChC,IAAA,iBAAS,GAAE,IAAI,IAAA,aAAK,GAAE,CAAC,CAAC,CAAC,CACvB,uBAAC,gBAAS,IACR,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,EAC3B,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,EAClB,WAAW,EAAE,CAAC,EACd,UAAU,EAAE,CAAC,GACb,CACH,CAAC,CAAC,CAAC,CACF,uBAAC,kBAAW,IAAC,KAAK,EAAC,+GAA+G,GAAG,CACtI,CAAC;AAIJ,SAAwB,mBAAmB,CAAC,EAC1C,GAAG,EACH,GAAG,EACH,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,QAAQ,GAAG,IAAI,EACf,OAAO,EACP,SAAS,EACT,YAAY,GAAG,KAAK,EACpB,QAAQ,EACR,YAAY,EACZ,mBAAmB,GACb;IACN,OAAO,CACL,uBAAC,cAAM,IACL,KAAK,EACH,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,EAEvE,MAAM,EAAE,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAQ,YAEpD,wBAAC,gBAAQ,IAAC,QAAQ,EAAE,QAAQ,IAAI,uBAAC,gBAAM,IAAC,WAAW,EAAE,WAAW,GAAI,aACjE,oBAAoB,EAAE,EACtB,YAAY,IAAI,uBAAC,oBAAa,IAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,GAAI,EACvE,uBAAC,4BAAU,IACT,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,OAAO,IAAI,KAAK,EACzB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAC3B,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAC3B,YAAY,EAAE,YAAY,IAAI,KAAK,EACnC,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAC3B,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAC3B,YAAY,EAAE,YAAY,EAC1B,mBAAmB,EAAE,mBAAmB,GACxC,IACO,GACJ,CACV,CAAC;AACJ,CAAC;AA7CD,sCA6CC"}
@@ -219,7 +219,11 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
219
219
  const [isPlayingAudio, setIsPlayingAudio] = (0, react_1.useState)(false);
220
220
  const [controlsPosition, setControlsPosition] = (0, react_1.useState)('center');
221
221
  const [hideEmissions, setHideEmissions] = (0, react_1.useState)(false);
222
- const { addVisemeToQueue, processVisemeQueue, clearVisemes, emotion, getAzureStyleForEmotion, } = (0, visemeContext_1.useViseme)();
222
+ const { startProcessing, stopProcessing, addViseme, resetVisemeQueue, isProcessing, } = (0, visemeContext_1.useViseme)();
223
+ const audioContextRef = (0, react_1.useRef)(null);
224
+ const speechSynthesizerRef = (0, react_1.useRef)(null);
225
+ const audioDestinationRef = (0, react_1.useRef)(null);
226
+ const currentSpeechRef = (0, react_1.useRef)(null);
223
227
  (0, react_1.useEffect)(() => {
224
228
  setIsPlayingAudio(!!speechSynthesizer);
225
229
  memoriSpeaking = !!speechSynthesizer;
@@ -1246,16 +1250,43 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1246
1250
  const e = new CustomEvent('MemoriEndSpeak');
1247
1251
  document.dispatchEvent(e);
1248
1252
  };
1249
- const speak = (text) => {
1253
+ const initializeAudioContext = (0, react_1.useCallback)(() => {
1254
+ if (!audioContextRef.current || audioContextRef.current.state === 'closed') {
1255
+ audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
1256
+ }
1257
+ return audioContextRef.current;
1258
+ }, []);
1259
+ const initializeSpeechSynthesizer = (0, react_1.useCallback)((audioConfig) => {
1260
+ if (!speechSynthesizerRef.current && AZURE_COGNITIVE_SERVICES_TTS_KEY) {
1261
+ const speechConfig = speechSdk.SpeechConfig.fromSubscription(AZURE_COGNITIVE_SERVICES_TTS_KEY, 'eastus');
1262
+ speechSynthesizerRef.current = new speechSdk.SpeechSynthesizer(speechConfig, audioConfig);
1263
+ }
1264
+ return speechSynthesizerRef.current;
1265
+ }, []);
1266
+ const stopCurrentSpeech = (0, react_1.useCallback)(() => {
1267
+ if (currentSpeechRef.current) {
1268
+ currentSpeechRef.current.cancel();
1269
+ currentSpeechRef.current = null;
1270
+ }
1271
+ if (audioContextRef.current) {
1272
+ audioContextRef.current.suspend();
1273
+ }
1274
+ if (audioDestinationRef.current) {
1275
+ audioDestinationRef.current.pause();
1276
+ }
1277
+ setIsPlayingAudio(false);
1278
+ stopProcessing();
1279
+ resetVisemeQueue();
1280
+ }, []);
1281
+ const speak = (0, react_1.useCallback)(async (text) => {
1250
1282
  if (!AZURE_COGNITIVE_SERVICES_TTS_KEY || preview) {
1251
1283
  emitEndSpeakEvent();
1252
1284
  return;
1253
1285
  }
1254
1286
  stopListening();
1255
- if (preview)
1256
- return;
1257
- if (muteSpeaker || speakerMuted) {
1258
- memoriSpeaking = false;
1287
+ stopCurrentSpeech();
1288
+ if (preview || muteSpeaker || speakerMuted) {
1289
+ setIsPlayingAudio(false);
1259
1290
  setMemoriTyping(false);
1260
1291
  emitEndSpeakEvent();
1261
1292
  if (continuousSpeech) {
@@ -1263,136 +1294,98 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1263
1294
  }
1264
1295
  return;
1265
1296
  }
1266
- if (audioDestination)
1267
- audioDestination.pause();
1268
- let isSafari = window.navigator.userAgent.includes('Safari') &&
1269
- !window.navigator.userAgent.includes('Chrome');
1270
- let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1271
- if (audioContext.state === 'interrupted') {
1272
- audioContext.resume().then(() => speak(text));
1273
- return;
1274
- }
1275
- if (audioContext.state === 'closed') {
1276
- audioContext = new standardized_audio_context_1.AudioContext();
1277
- let buffer = audioContext.createBuffer(1, 10000, 22050);
1278
- let source = audioContext.createBufferSource();
1279
- source.buffer = buffer;
1280
- source.connect(audioContext.destination);
1281
- }
1282
- else if (audioContext.state === 'suspended') {
1283
- stopAudio();
1284
- audioContext = new standardized_audio_context_1.AudioContext();
1285
- let buffer = audioContext.createBuffer(1, 10000, 22050);
1286
- let source = audioContext.createBufferSource();
1287
- source.buffer = buffer;
1288
- source.connect(audioContext.destination);
1289
- }
1290
- if (!speechSynthesizer) {
1291
- if (!isIOS) {
1292
- audioDestination = new speechSdk.SpeakerAudioDestination();
1297
+ try {
1298
+ const audioContext = initializeAudioContext();
1299
+ await audioContext.resume();
1300
+ if (!audioDestinationRef.current) {
1301
+ audioDestinationRef.current = new speechSdk.SpeakerAudioDestination();
1293
1302
  }
1294
- let audioConfig = speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1295
- speechSynthesizer = new speechSdk.SpeechSynthesizer(speechConfig, audioConfig);
1296
- }
1297
- const source = audioContext.createBufferSource();
1298
- source.addEventListener('ended', () => {
1299
- setIsPlayingAudio(false);
1300
- memoriSpeaking = false;
1301
- });
1302
- audioDestination.onAudioEnd = () => {
1303
- setIsPlayingAudio(false);
1304
- memoriSpeaking = false;
1305
- source.disconnect();
1306
- emitEndSpeakEvent();
1307
- onEndSpeakStartListen();
1308
- };
1309
- clearVisemes();
1310
- speechSynthesizer.visemeReceived = function (_, e) {
1311
- addVisemeToQueue({
1312
- visemeId: e.visemeId,
1313
- audioOffset: e.audioOffset,
1303
+ const audioConfig = speechSdk.AudioConfig.fromSpeakerOutput(audioDestinationRef.current);
1304
+ const speechSynthesizer = initializeSpeechSynthesizer(audioConfig);
1305
+ if (speechSynthesizer) {
1306
+ speechSynthesizer.visemeReceived = (_, e) => {
1307
+ addViseme(e.visemeId, e.audioOffset);
1308
+ console.log('viseme added');
1309
+ };
1310
+ }
1311
+ startProcessing();
1312
+ const textToSpeak = (0, utils_1.escapeHTML)((0, utils_1.stripMarkdown)((0, utils_1.stripEmojis)((0, utils_1.stripHTML)((0, utils_1.stripOutputTags)(text)))));
1313
+ const ssml = `
1314
+ <speak version="1.0"
1315
+ xmlns="http://www.w3.org/2001/10/synthesis"
1316
+ xmlns:mstts="https://www.w3.org/2001/mstts"
1317
+ xml:lang="${getCultureCodeByLanguage(userLang)}">
1318
+ <voice name="${getTTSVoice(userLang)}">
1319
+ <s>${replaceTextWithPhonemes(textToSpeak, userLang.toLowerCase())}</s>
1320
+ </voice>
1321
+ </speak>
1322
+ `;
1323
+ const speakPromise = new Promise((resolve, reject) => {
1324
+ speechSynthesizer === null || speechSynthesizer === void 0 ? void 0 : speechSynthesizer.speakSsmlAsync(ssml, result => resolve(result), error => reject(error));
1314
1325
  });
1315
- };
1316
- const textToSpeak = (0, utils_1.escapeHTML)((0, utils_1.stripMarkdown)((0, utils_1.stripEmojis)((0, utils_1.stripHTML)((0, utils_1.stripOutputTags)(text)))));
1317
- speechSynthesizer.speakSsmlAsync(`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" xml:lang="${getCultureCodeByLanguage(userLang)}"><voice name="${getTTSVoice(userLang)}"><mstts:express-as style="${getAzureStyleForEmotion(emotion)}"><s>${replaceTextWithPhonemes(textToSpeak, userLang.toLowerCase())}</s></mstts:express-as></voice></speak>`, result => {
1318
- if (result) {
1319
- setIsPlayingAudio(true);
1320
- memoriSpeaking = true;
1321
- processVisemeQueue();
1322
- try {
1323
- audioContext.decodeAudioData(result.audioData, function (buffer) {
1324
- source.buffer = buffer;
1325
- source.connect(audioContext.destination);
1326
- if (history.length < 1 || (isSafari && isIOS)) {
1327
- source.start(0);
1328
- }
1329
- });
1330
- audioContext.onstatechange = () => {
1331
- if (audioContext.state === 'suspended' ||
1332
- audioContext.state === 'closed') {
1333
- source.disconnect();
1334
- setIsPlayingAudio(false);
1335
- memoriSpeaking = false;
1336
- }
1337
- else if (audioContext.state === 'interrupted') {
1338
- audioContext.resume();
1339
- }
1340
- };
1341
- audioContext.resume();
1342
- if (speechSynthesizer) {
1343
- speechSynthesizer.close();
1344
- speechSynthesizer = null;
1345
- }
1326
+ currentSpeechRef.current = {
1327
+ cancel: () => {
1328
+ var _a;
1329
+ speechSynthesizer === null || speechSynthesizer === void 0 ? void 0 : speechSynthesizer.close();
1330
+ (_a = audioDestinationRef.current) === null || _a === void 0 ? void 0 : _a.pause();
1346
1331
  }
1347
- catch (e) {
1348
- console.warn('speak error: ', e);
1349
- window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1350
- clearVisemes();
1332
+ };
1333
+ const result = await speakPromise;
1334
+ setIsPlayingAudio(true);
1335
+ if (audioContext && result) {
1336
+ const audioBuffer = await audioContext.decodeAudioData(result.audioData);
1337
+ const source = audioContext.createBufferSource();
1338
+ source.buffer = audioBuffer;
1339
+ source.connect(audioContext.destination);
1340
+ source.onended = () => {
1351
1341
  setIsPlayingAudio(false);
1352
- memoriSpeaking = false;
1353
- if (speechSynthesizer) {
1354
- speechSynthesizer.close();
1355
- speechSynthesizer = null;
1356
- }
1342
+ stopProcessing();
1343
+ resetVisemeQueue();
1344
+ currentSpeechRef.current = null;
1357
1345
  emitEndSpeakEvent();
1358
- }
1346
+ onEndSpeakStartListen();
1347
+ };
1348
+ await audioContext.resume();
1349
+ source.start(0);
1359
1350
  }
1360
1351
  else {
1361
- audioContext.resume();
1362
- clearVisemes();
1363
- setIsPlayingAudio(false);
1364
- memoriSpeaking = false;
1365
- emitEndSpeakEvent();
1352
+ stopProcessing();
1353
+ resetVisemeQueue();
1354
+ throw new Error('No result from speech synthesis');
1366
1355
  }
1367
- }, error => {
1368
- console.error('speak:', error);
1369
- window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1370
- setIsPlayingAudio(false);
1371
- memoriSpeaking = false;
1372
- emitEndSpeakEvent();
1373
- });
1374
- setMemoriTyping(false);
1375
- };
1376
- const stopAudio = () => {
1356
+ }
1357
+ catch (error) {
1358
+ console.error('Speech synthesis error:', error);
1359
+ stopProcessing();
1360
+ resetVisemeQueue();
1361
+ const utterance = new SpeechSynthesisUtterance(text);
1362
+ window.speechSynthesis.speak(utterance);
1363
+ }
1364
+ finally {
1365
+ setMemoriTyping(false);
1366
+ }
1367
+ }, [initializeAudioContext, initializeSpeechSynthesizer, stopCurrentSpeech]);
1368
+ const stopAudio = (0, react_1.useCallback)(() => {
1377
1369
  setIsPlayingAudio(false);
1378
1370
  memoriSpeaking = false;
1379
1371
  try {
1380
- if (speechSynthesizer) {
1381
- speechSynthesizer.close();
1382
- speechSynthesizer = null;
1372
+ if (speechSynthesizerRef.current) {
1373
+ speechSynthesizerRef.current.close();
1374
+ speechSynthesizerRef.current = null;
1383
1375
  }
1384
- if (audioContext.state !== 'closed') {
1385
- audioContext.close();
1376
+ if (audioContextRef.current && audioContextRef.current.state !== 'closed') {
1377
+ audioContextRef.current.close();
1386
1378
  }
1387
- if (audioDestination) {
1388
- audioDestination.pause();
1389
- audioDestination.close();
1379
+ if (audioDestinationRef.current) {
1380
+ audioDestinationRef.current.pause();
1381
+ audioDestinationRef.current.close();
1390
1382
  }
1383
+ stopCurrentSpeech();
1391
1384
  }
1392
1385
  catch (e) {
1393
1386
  console.debug('stopAudio error: ', e);
1394
1387
  }
1395
- };
1388
+ }, [stopCurrentSpeech]);
1396
1389
  (0, react_1.useEffect)(() => {
1397
1390
  let textarea = document.querySelector('#chat-fieldset textarea');
1398
1391
  if (textarea)
@@ -1525,6 +1518,12 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1525
1518
  (0, react_1.useEffect)(() => {
1526
1519
  return () => {
1527
1520
  resetUIEffects();
1521
+ if (speechSynthesizerRef.current) {
1522
+ speechSynthesizerRef.current.close();
1523
+ }
1524
+ if (audioContextRef.current) {
1525
+ audioContextRef.current.close();
1526
+ }
1528
1527
  };
1529
1528
  }, []);
1530
1529
  (0, react_1.useEffect)(() => {