@memori.ai/memori-react 7.8.0-rc.0 → 7.8.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.
- package/CHANGELOG.md +17 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +113 -103
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +15 -24
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/layouts/ZoomedFullBody.js +7 -0
- package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
- package/dist/components/layouts/zoomed-full-body.css +1 -1
- package/dist/context/visemeContext.js +2 -2
- package/dist/context/visemeContext.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +115 -105
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +15 -24
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/layouts/ZoomedFullBody.js +7 -0
- package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
- package/esm/components/layouts/zoomed-full-body.css +1 -1
- package/esm/context/visemeContext.js +2 -2
- package/esm/context/visemeContext.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.tsx +86 -92
- package/src/components/MemoriWidget/MemoriWidget.tsx +26 -33
- package/src/components/layouts/ZoomedFullBody.tsx +10 -1
- package/src/components/layouts/zoomed-full-body.css +1 -1
- package/src/context/visemeContext.tsx +2 -2
- package/src/index.stories.tsx +41 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useRef,
|
|
1
|
+
import React, { useEffect, useRef, useMemo, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Vector3,
|
|
4
4
|
Euler,
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from 'three';
|
|
12
12
|
import { useAnimations, useGLTF } from '@react-three/drei';
|
|
13
13
|
import { useGraph, useFrame } from '@react-three/fiber';
|
|
14
|
-
import { correctMaterials, isSkinnedMesh } from '../../../../../helpers/utils';
|
|
15
14
|
|
|
16
15
|
interface FullbodyAvatarProps {
|
|
17
16
|
url: string;
|
|
@@ -41,6 +40,7 @@ interface FullbodyAvatarProps {
|
|
|
41
40
|
emotionMorphTargets: Record<string, number>;
|
|
42
41
|
}
|
|
43
42
|
|
|
43
|
+
|
|
44
44
|
const AVATAR_POSITION = new Vector3(0, -1, 0);
|
|
45
45
|
const AVATAR_ROTATION = new Euler(0.175, 0, 0);
|
|
46
46
|
const AVATAR_POSITION_ZOOMED = new Vector3(0, -1.45, 0);
|
|
@@ -51,24 +51,23 @@ const ANIMATION_URLS = {
|
|
|
51
51
|
'https://assets.memori.ai/api/v2/asset/0e49aa5d-f757-4292-a170-d843c2839a41.glb',
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
// Blink configuration
|
|
55
54
|
const BLINK_CONFIG = {
|
|
56
55
|
minInterval: 1000,
|
|
57
56
|
maxInterval: 5000,
|
|
58
57
|
blinkDuration: 150,
|
|
59
58
|
};
|
|
60
59
|
|
|
61
|
-
const
|
|
60
|
+
const EMOTION_SMOOTHING = 0.3;
|
|
61
|
+
const VISME_SMOOTHING = 0.5;
|
|
62
|
+
|
|
62
63
|
|
|
63
64
|
export default function FullbodyAvatar({
|
|
64
65
|
url,
|
|
65
66
|
sex,
|
|
66
|
-
onLoaded,
|
|
67
67
|
currentBaseAction,
|
|
68
68
|
timeScale,
|
|
69
69
|
isZoomed,
|
|
70
70
|
eyeBlink,
|
|
71
|
-
morphTargetSmoothing = 0.5,
|
|
72
71
|
updateCurrentViseme,
|
|
73
72
|
setMorphTargetDictionary,
|
|
74
73
|
setMorphTargetInfluences,
|
|
@@ -76,55 +75,51 @@ export default function FullbodyAvatar({
|
|
|
76
75
|
}: FullbodyAvatarProps) {
|
|
77
76
|
const { scene } = useGLTF(url);
|
|
78
77
|
const { animations } = useGLTF(ANIMATION_URLS[sex]);
|
|
79
|
-
const { nodes, materials } = useGraph(scene);
|
|
80
78
|
const { actions } = useAnimations(animations, scene);
|
|
81
79
|
|
|
82
|
-
const
|
|
80
|
+
const mixerRef = useRef<AnimationMixer>();
|
|
83
81
|
const headMeshRef = useRef<SkinnedMesh>();
|
|
84
82
|
const currentActionRef = useRef<AnimationAction | null>(null);
|
|
85
|
-
const
|
|
83
|
+
const isTransitioningToIdleRef = useRef(false);
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const blinkStartTime = useRef(0);
|
|
85
|
+
const lastBlinkTimeRef = useRef(0);
|
|
86
|
+
const nextBlinkTimeRef = useRef(0);
|
|
87
|
+
const isBlinkingRef = useRef(false);
|
|
88
|
+
const blinkStartTimeRef = useRef(0);
|
|
92
89
|
|
|
93
|
-
// Morph targets
|
|
94
90
|
const currentEmotionRef = useRef<Record<string, number>>({});
|
|
95
91
|
const previousEmotionKeysRef = useRef<Set<string>>(new Set());
|
|
96
92
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
// Memoize the scene traversal
|
|
94
|
+
const headMesh = useMemo(() => {
|
|
95
|
+
let foundMesh: SkinnedMesh | undefined;
|
|
100
96
|
scene.traverse((object: Object3D) => {
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const initialInfluences = Object.keys(
|
|
108
|
-
object.morphTargetDictionary
|
|
109
|
-
).reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
|
|
110
|
-
setMorphTargetInfluences(initialInfluences);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
97
|
+
if (
|
|
98
|
+
object instanceof SkinnedMesh &&
|
|
99
|
+
(object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar')
|
|
100
|
+
) {
|
|
101
|
+
foundMesh = object;
|
|
113
102
|
}
|
|
114
103
|
});
|
|
104
|
+
return foundMesh;
|
|
105
|
+
}, [scene]);
|
|
115
106
|
|
|
116
|
-
onLoaded?.();
|
|
117
|
-
|
|
118
|
-
return () => {
|
|
119
|
-
Object.values(materials).forEach(material => material.dispose());
|
|
120
|
-
Object.values(nodes)
|
|
121
|
-
.filter(isSkinnedMesh)
|
|
122
|
-
.forEach(mesh => mesh.geometry.dispose());
|
|
123
|
-
};
|
|
124
|
-
}, [materials, nodes, url, onLoaded, scene]);
|
|
125
|
-
|
|
126
|
-
// Handle base animation changes
|
|
127
107
|
useEffect(() => {
|
|
108
|
+
if (headMesh) {
|
|
109
|
+
headMeshRef.current = headMesh;
|
|
110
|
+
if (headMesh.morphTargetDictionary && headMesh.morphTargetInfluences) {
|
|
111
|
+
setMorphTargetDictionary(headMesh.morphTargetDictionary);
|
|
112
|
+
const initialInfluences = Object.keys(
|
|
113
|
+
headMesh.morphTargetDictionary
|
|
114
|
+
).reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
|
|
115
|
+
setMorphTargetInfluences(initialInfluences);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
mixerRef.current = new AnimationMixer(scene);
|
|
119
|
+
}, [headMesh, scene, setMorphTargetDictionary, setMorphTargetInfluences]);
|
|
120
|
+
|
|
121
|
+
// Memoize the animation change handler
|
|
122
|
+
const handleAnimationChange = useCallback(() => {
|
|
128
123
|
if (!actions || !currentBaseAction.action) return;
|
|
129
124
|
|
|
130
125
|
const newAction = actions[currentBaseAction.action];
|
|
@@ -141,15 +136,11 @@ export default function FullbodyAvatar({
|
|
|
141
136
|
if (currentActionRef.current) {
|
|
142
137
|
currentActionRef.current.fadeOut(fadeOutDuration);
|
|
143
138
|
}
|
|
144
|
-
|
|
145
|
-
console.log(newAction);
|
|
139
|
+
|
|
146
140
|
newAction.reset().fadeIn(fadeInDuration).play();
|
|
147
141
|
currentActionRef.current = newAction;
|
|
148
|
-
|
|
149
|
-
// Set the time scale for the new action
|
|
150
142
|
newAction.timeScale = timeScale;
|
|
151
143
|
|
|
152
|
-
// If it's an emotion animation, set it to play once and then transition to idle
|
|
153
144
|
if (
|
|
154
145
|
currentBaseAction.action.startsWith('Gioia') ||
|
|
155
146
|
currentBaseAction.action.startsWith('Rabbia') ||
|
|
@@ -159,55 +150,55 @@ export default function FullbodyAvatar({
|
|
|
159
150
|
) {
|
|
160
151
|
newAction.setLoop(LoopOnce, 1);
|
|
161
152
|
newAction.clampWhenFinished = true;
|
|
162
|
-
|
|
153
|
+
isTransitioningToIdleRef.current = true;
|
|
163
154
|
}
|
|
164
155
|
}, [actions, currentBaseAction, timeScale]);
|
|
165
156
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
handleAnimationChange();
|
|
159
|
+
}, [handleAnimationChange]);
|
|
160
|
+
|
|
161
|
+
// Optimize the frame update function
|
|
162
|
+
const updateFrame = useCallback(
|
|
163
|
+
(currentTime: number) => {
|
|
164
|
+
if (
|
|
165
|
+
!headMeshRef.current ||
|
|
166
|
+
!headMeshRef.current.morphTargetDictionary ||
|
|
167
|
+
!headMeshRef.current.morphTargetInfluences
|
|
168
|
+
)
|
|
169
|
+
return;
|
|
173
170
|
|
|
174
|
-
// Handle blinking
|
|
175
171
|
let blinkValue = 0;
|
|
176
172
|
if (eyeBlink) {
|
|
177
|
-
if (currentTime >=
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
173
|
+
if (currentTime >= nextBlinkTimeRef.current && !isBlinkingRef.current) {
|
|
174
|
+
isBlinkingRef.current = true;
|
|
175
|
+
blinkStartTimeRef.current = currentTime;
|
|
176
|
+
lastBlinkTimeRef.current = currentTime;
|
|
177
|
+
nextBlinkTimeRef.current =
|
|
182
178
|
currentTime +
|
|
183
179
|
Math.random() *
|
|
184
180
|
(BLINK_CONFIG.maxInterval - BLINK_CONFIG.minInterval) +
|
|
185
181
|
BLINK_CONFIG.minInterval;
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
if (
|
|
184
|
+
if (isBlinkingRef.current) {
|
|
189
185
|
const blinkProgress =
|
|
190
|
-
(currentTime -
|
|
186
|
+
(currentTime - blinkStartTimeRef.current) /
|
|
187
|
+
BLINK_CONFIG.blinkDuration;
|
|
191
188
|
if (blinkProgress <= 0.5) {
|
|
192
|
-
// Eyes closing
|
|
193
189
|
blinkValue = blinkProgress * 2;
|
|
194
190
|
} else if (blinkProgress <= 1) {
|
|
195
|
-
// Eyes opening
|
|
196
191
|
blinkValue = 2 - blinkProgress * 2;
|
|
197
192
|
} else {
|
|
198
|
-
|
|
199
|
-
isBlinking.current = false;
|
|
193
|
+
isBlinkingRef.current = false;
|
|
200
194
|
blinkValue = 0;
|
|
201
195
|
}
|
|
202
196
|
}
|
|
203
197
|
}
|
|
204
198
|
|
|
205
199
|
const currentViseme = updateCurrentViseme(currentTime / 1000);
|
|
206
|
-
|
|
207
|
-
// Create a set of current emotion keys
|
|
208
200
|
const currentEmotionKeys = new Set(Object.keys(emotionMorphTargets));
|
|
209
201
|
|
|
210
|
-
// Reset old emotion morph targets
|
|
211
202
|
previousEmotionKeysRef.current.forEach(key => {
|
|
212
203
|
if (!currentEmotionKeys.has(key)) {
|
|
213
204
|
const index = headMeshRef.current!.morphTargetDictionary![key];
|
|
@@ -220,75 +211,78 @@ export default function FullbodyAvatar({
|
|
|
220
211
|
}
|
|
221
212
|
});
|
|
222
213
|
|
|
223
|
-
// Update morph targets
|
|
224
214
|
Object.entries(headMeshRef.current.morphTargetDictionary).forEach(
|
|
225
215
|
([key, index]) => {
|
|
226
216
|
if (typeof index === 'number') {
|
|
227
217
|
let targetValue = 0;
|
|
228
218
|
|
|
229
|
-
|
|
230
|
-
if (Object.prototype.hasOwnProperty.call(emotionMorphTargets, key)) {
|
|
219
|
+
if (currentEmotionKeys.has(key)) {
|
|
231
220
|
const targetEmotionValue = emotionMorphTargets[key];
|
|
232
221
|
const currentEmotionValue = currentEmotionRef.current[key] || 0;
|
|
233
222
|
const newEmotionValue = MathUtils.lerp(
|
|
234
223
|
currentEmotionValue,
|
|
235
|
-
targetEmotionValue * 2,
|
|
236
|
-
|
|
224
|
+
targetEmotionValue * 2.5,
|
|
225
|
+
EMOTION_SMOOTHING
|
|
237
226
|
);
|
|
238
227
|
currentEmotionRef.current[key] = newEmotionValue;
|
|
239
228
|
targetValue += newEmotionValue;
|
|
240
229
|
}
|
|
241
230
|
|
|
242
|
-
// Handle visemes (additive layer)
|
|
243
231
|
if (currentViseme && key === currentViseme.name) {
|
|
244
|
-
targetValue += currentViseme.weight * 1
|
|
232
|
+
targetValue += currentViseme.weight * 1;
|
|
245
233
|
}
|
|
246
234
|
|
|
247
|
-
// Handle blinking (additive layer, only for 'eyesClosed')
|
|
248
235
|
if (key === 'eyesClosed' && eyeBlink) {
|
|
249
236
|
targetValue += blinkValue;
|
|
250
237
|
}
|
|
251
238
|
|
|
252
|
-
// Clamp the final value between 0 and 1
|
|
253
239
|
targetValue = MathUtils.clamp(targetValue, 0, 1);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
240
|
+
if (
|
|
241
|
+
headMeshRef.current &&
|
|
242
|
+
headMeshRef.current.morphTargetInfluences
|
|
243
|
+
) {
|
|
257
244
|
headMeshRef.current.morphTargetInfluences[index] = MathUtils.lerp(
|
|
258
245
|
headMeshRef.current.morphTargetInfluences[index],
|
|
259
246
|
targetValue,
|
|
260
|
-
|
|
247
|
+
VISME_SMOOTHING
|
|
261
248
|
);
|
|
262
249
|
}
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
252
|
);
|
|
266
253
|
|
|
267
|
-
// Update the set of previous emotion keys for the next frame
|
|
268
254
|
previousEmotionKeysRef.current = currentEmotionKeys;
|
|
269
255
|
|
|
270
|
-
|
|
271
|
-
if (isTransitioningToIdle && currentActionRef.current) {
|
|
256
|
+
if (isTransitioningToIdleRef.current && currentActionRef.current) {
|
|
272
257
|
if (
|
|
273
258
|
currentActionRef.current.time >=
|
|
274
259
|
currentActionRef.current.getClip().duration
|
|
275
260
|
) {
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
261
|
+
const idleNumber = Math.floor(Math.random() * 5) + 1;
|
|
262
|
+
const idleAction =
|
|
263
|
+
actions[`Idle${idleNumber === 3 ? 4 : idleNumber}`];
|
|
279
264
|
|
|
280
265
|
if (idleAction) {
|
|
281
266
|
currentActionRef.current.fadeOut(0.5);
|
|
282
267
|
idleAction.reset().fadeIn(0.5).play();
|
|
283
268
|
currentActionRef.current = idleAction;
|
|
284
|
-
|
|
269
|
+
isTransitioningToIdleRef.current = false;
|
|
285
270
|
}
|
|
286
271
|
}
|
|
287
272
|
}
|
|
288
273
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
274
|
+
mixerRef.current?.update(0.01);
|
|
275
|
+
},
|
|
276
|
+
[
|
|
277
|
+
actions,
|
|
278
|
+
emotionMorphTargets,
|
|
279
|
+
eyeBlink,
|
|
280
|
+
updateCurrentViseme,
|
|
281
|
+
]
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
useFrame(state => {
|
|
285
|
+
updateFrame(state.clock.getElapsedTime() * 1000);
|
|
292
286
|
});
|
|
293
287
|
|
|
294
288
|
return (
|
|
@@ -543,13 +543,10 @@ const MemoriWidget = ({
|
|
|
543
543
|
const [hideEmissions, setHideEmissions] = useState(false);
|
|
544
544
|
|
|
545
545
|
const {
|
|
546
|
-
updateCurrentViseme,
|
|
547
546
|
startProcessing,
|
|
548
|
-
resetAndStartProcessing,
|
|
549
|
-
isProcessing,
|
|
550
547
|
addViseme,
|
|
551
548
|
stopProcessing,
|
|
552
|
-
resetVisemeQueue
|
|
549
|
+
resetVisemeQueue
|
|
553
550
|
} = useViseme();
|
|
554
551
|
|
|
555
552
|
useEffect(() => {
|
|
@@ -1963,7 +1960,7 @@ const MemoriWidget = ({
|
|
|
1963
1960
|
onEndSpeakStartListen();
|
|
1964
1961
|
};
|
|
1965
1962
|
|
|
1966
|
-
//
|
|
1963
|
+
// Clear any existing visemes before starting new speech
|
|
1967
1964
|
resetVisemeQueue();
|
|
1968
1965
|
|
|
1969
1966
|
// Set up the viseme event handler
|
|
@@ -1978,7 +1975,9 @@ const MemoriWidget = ({
|
|
|
1978
1975
|
speechSynthesizer.speakSsmlAsync(
|
|
1979
1976
|
`<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(
|
|
1980
1977
|
userLang
|
|
1981
|
-
)}"><voice name="${getTTSVoice(
|
|
1978
|
+
)}"><voice name="${getTTSVoice(
|
|
1979
|
+
userLang
|
|
1980
|
+
)}"><s>${replaceTextWithPhonemes(
|
|
1982
1981
|
textToSpeak,
|
|
1983
1982
|
userLang.toLowerCase()
|
|
1984
1983
|
)}</s></voice></speak>`,
|
|
@@ -1987,24 +1986,18 @@ const MemoriWidget = ({
|
|
|
1987
1986
|
setIsPlayingAudio(true);
|
|
1988
1987
|
memoriSpeaking = true;
|
|
1989
1988
|
|
|
1989
|
+
// Process the viseme data
|
|
1990
|
+
startProcessing();
|
|
1991
|
+
|
|
1990
1992
|
try {
|
|
1991
1993
|
// Decode the audio data
|
|
1992
|
-
audioContext
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
currentSource.onended = () => {
|
|
2001
|
-
console.log('ended');
|
|
2002
|
-
setIsPlayingAudio(false);
|
|
2003
|
-
memoriSpeaking = false;
|
|
2004
|
-
stopProcessing();
|
|
2005
|
-
resetVisemeQueue();
|
|
2006
|
-
emitEndSpeakEvent();
|
|
2007
|
-
};
|
|
1994
|
+
audioContext.decodeAudioData(result.audioData, function (buffer) {
|
|
1995
|
+
source.buffer = buffer;
|
|
1996
|
+
source.connect(audioContext.destination);
|
|
1997
|
+
|
|
1998
|
+
if (history.length < 1 || (isSafari && isIOS)) {
|
|
1999
|
+
source.start(0);
|
|
2000
|
+
}
|
|
2008
2001
|
});
|
|
2009
2002
|
|
|
2010
2003
|
// Handle the audio context state changes
|
|
@@ -2013,28 +2006,29 @@ const MemoriWidget = ({
|
|
|
2013
2006
|
audioContext.state === 'suspended' ||
|
|
2014
2007
|
audioContext.state === 'closed'
|
|
2015
2008
|
) {
|
|
2016
|
-
console.log('suspended');
|
|
2017
2009
|
source.disconnect();
|
|
2018
2010
|
setIsPlayingAudio(false);
|
|
2019
2011
|
stopProcessing();
|
|
2020
2012
|
resetVisemeQueue();
|
|
2021
2013
|
memoriSpeaking = false;
|
|
2022
|
-
emitEndSpeakEvent();
|
|
2023
2014
|
} else if ((audioContext.state as string) === 'interrupted') {
|
|
2024
|
-
console.log('interrupted');
|
|
2025
|
-
stopProcessing();
|
|
2026
|
-
resetVisemeQueue();
|
|
2027
|
-
|
|
2028
2015
|
audioContext.resume();
|
|
2029
2016
|
}
|
|
2030
2017
|
};
|
|
2018
|
+
|
|
2019
|
+
audioContext.resume();
|
|
2020
|
+
|
|
2021
|
+
if (speechSynthesizer) {
|
|
2022
|
+
speechSynthesizer.close();
|
|
2023
|
+
speechSynthesizer = null;
|
|
2024
|
+
}
|
|
2031
2025
|
} catch (e) {
|
|
2032
2026
|
console.warn('speak error: ', e);
|
|
2033
2027
|
window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
|
|
2034
|
-
setIsPlayingAudio(false);
|
|
2035
|
-
memoriSpeaking = false;
|
|
2036
2028
|
stopProcessing();
|
|
2037
2029
|
resetVisemeQueue();
|
|
2030
|
+
setIsPlayingAudio(false);
|
|
2031
|
+
memoriSpeaking = false;
|
|
2038
2032
|
|
|
2039
2033
|
if (speechSynthesizer) {
|
|
2040
2034
|
speechSynthesizer.close();
|
|
@@ -2045,7 +2039,6 @@ const MemoriWidget = ({
|
|
|
2045
2039
|
} else {
|
|
2046
2040
|
audioContext.resume();
|
|
2047
2041
|
stopProcessing();
|
|
2048
|
-
resetVisemeQueue();
|
|
2049
2042
|
setIsPlayingAudio(false);
|
|
2050
2043
|
memoriSpeaking = false;
|
|
2051
2044
|
emitEndSpeakEvent();
|
|
@@ -2055,9 +2048,9 @@ const MemoriWidget = ({
|
|
|
2055
2048
|
console.error('speak:', error);
|
|
2056
2049
|
window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
|
|
2057
2050
|
setIsPlayingAudio(false);
|
|
2058
|
-
memoriSpeaking = false;
|
|
2059
2051
|
stopProcessing();
|
|
2060
2052
|
resetVisemeQueue();
|
|
2053
|
+
memoriSpeaking = false;
|
|
2061
2054
|
emitEndSpeakEvent();
|
|
2062
2055
|
}
|
|
2063
2056
|
);
|
|
@@ -3425,4 +3418,4 @@ const MemoriWidget = ({
|
|
|
3425
3418
|
);
|
|
3426
3419
|
};
|
|
3427
3420
|
|
|
3428
|
-
export default MemoriWidget;
|
|
3421
|
+
export default MemoriWidget;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import Spin from '../ui/Spin';
|
|
3
3
|
import { LayoutProps } from '../MemoriWidget/MemoriWidget';
|
|
4
4
|
|
|
@@ -21,6 +21,15 @@ const ZoomedFullBodyLayout: React.FC<LayoutProps> = ({
|
|
|
21
21
|
loading = false,
|
|
22
22
|
poweredBy,
|
|
23
23
|
}) => {
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
document.body.style.overflow = 'hidden';
|
|
27
|
+
return () => {
|
|
28
|
+
document.body.style.overflow = '';
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
|
|
24
33
|
return (
|
|
25
34
|
<>
|
|
26
35
|
{integrationStyle}
|
|
@@ -53,9 +53,9 @@ const VISEME_MAP: { [key: number]: string } = {
|
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
const DEFAULT_VISEME_DURATION = 0.04; //0; // Reduced from 0.4 for smoother transitions
|
|
56
|
-
const VISEME_OVERLAP = 0.
|
|
56
|
+
const VISEME_OVERLAP = 0.02; // Slightly increased from 0.04 for more overlap
|
|
57
57
|
const SMOOTHING_FACTOR = 0.35; // New constant for weight smoothing
|
|
58
|
-
const TIME_OFFSET =
|
|
58
|
+
const TIME_OFFSET = 0; // Adjust this value as needed (in seconds)
|
|
59
59
|
const PRELOAD_TIME = 1; // Preload visemes 0.5 seconds in advance
|
|
60
60
|
|
|
61
61
|
export const VisemeProvider: React.FC<{ children: React.ReactNode }> = ({
|
package/src/index.stories.tsx
CHANGED
|
@@ -28,6 +28,31 @@ export const Anonymous = Template.bind({});
|
|
|
28
28
|
Anonymous.args = {
|
|
29
29
|
ownerUserName: 'nzambello',
|
|
30
30
|
memoriName: 'Nicola',
|
|
31
|
+
tenantID: 'www.aisuru.com',
|
|
32
|
+
engineURL: 'https://engine.memori.ai',
|
|
33
|
+
apiURL: 'https://backend.memori.ai',
|
|
34
|
+
baseURL: 'https://www.aisuru.com',
|
|
35
|
+
uiLang: 'IT',
|
|
36
|
+
spokenLang: 'IT',
|
|
37
|
+
layout: 'ZOOMED_FULL_BODY',
|
|
38
|
+
showInstruct: 'false',
|
|
39
|
+
showSettings: 'true',
|
|
40
|
+
showClear: 'false',
|
|
41
|
+
showAIicon: 'true',
|
|
42
|
+
showWhyThisAnswer: 'true',
|
|
43
|
+
showTypingText: 'false',
|
|
44
|
+
showOnlyLastMessages: 'false',
|
|
45
|
+
showTranslationOriginal: 'false',
|
|
46
|
+
showCopyButton: 'false',
|
|
47
|
+
showShare: 'true',
|
|
48
|
+
showLogin: 'false',
|
|
49
|
+
enableAudio: 'true',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Nunzio = Template.bind({});
|
|
53
|
+
Nunzio.args = {
|
|
54
|
+
ownerUserName: 'nunzio.fiore',
|
|
55
|
+
memoriName: 'Nunzio',
|
|
31
56
|
tenantID: 'aisuru.com',
|
|
32
57
|
apiURL: 'https://backend.memori.ai',
|
|
33
58
|
baseURL: 'https://www.aisuru.com',
|
|
@@ -95,18 +120,6 @@ Giovanna.args = {
|
|
|
95
120
|
uiLang: 'IT',
|
|
96
121
|
spokenLang: 'IT',
|
|
97
122
|
layout: 'ZOOMED_FULL_BODY',
|
|
98
|
-
showInstruct: 'false',
|
|
99
|
-
showSettings: 'true',
|
|
100
|
-
showClear: 'false',
|
|
101
|
-
showAIicon: 'true',
|
|
102
|
-
showWhyThisAnswer: 'true',
|
|
103
|
-
showTypingText: 'false',
|
|
104
|
-
showOnlyLastMessages: 'false',
|
|
105
|
-
showTranslationOriginal: 'false',
|
|
106
|
-
showCopyButton: 'false',
|
|
107
|
-
showShare: 'true',
|
|
108
|
-
showLogin: 'false',
|
|
109
|
-
enableAudio: 'true',
|
|
110
123
|
};
|
|
111
124
|
|
|
112
125
|
export const GiovannaProva = Template.bind({});
|
|
@@ -122,6 +135,21 @@ GiovannaProva.args = {
|
|
|
122
135
|
uiLang: 'EN',
|
|
123
136
|
spokenLang: 'IT',
|
|
124
137
|
layout: 'ZOOMED_FULL_BODY',
|
|
138
|
+
integrationID: 'e92ac275-39b5-474d-8f9e-826cc5284f1e',
|
|
139
|
+
initialQuestion: 'inizio simulazione',
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const NunzioFiore = Template.bind({});
|
|
143
|
+
NunzioFiore.args = {
|
|
144
|
+
ownerUserName: 'nunzio.fiore',
|
|
145
|
+
memoriName: 'Nunzio',
|
|
146
|
+
tenantID: 'www.aisuru.com',
|
|
147
|
+
engineURL: 'https://engine.memori.ai',
|
|
148
|
+
apiURL: 'https://backend.memori.ai',
|
|
149
|
+
baseURL: 'https://www.aisuru.com',
|
|
150
|
+
uiLang: 'IT',
|
|
151
|
+
spokenLang: 'IT',
|
|
152
|
+
layout: 'ZOOMED_FULL_BODY',
|
|
125
153
|
showInstruct: 'false',
|
|
126
154
|
showSettings: 'true',
|
|
127
155
|
showClear: 'false',
|
|
@@ -134,10 +162,9 @@ GiovannaProva.args = {
|
|
|
134
162
|
showShare: 'true',
|
|
135
163
|
showLogin: 'false',
|
|
136
164
|
enableAudio: 'true',
|
|
137
|
-
integrationID: 'e92ac275-39b5-474d-8f9e-826cc5284f1e',
|
|
138
|
-
initialQuestion: 'inizio simulazione',
|
|
139
165
|
};
|
|
140
166
|
|
|
167
|
+
|
|
141
168
|
const TemplateWithBatchButton: Story<Props> = args => (
|
|
142
169
|
<div>
|
|
143
170
|
<button
|