@sage-rsc/talking-head-react 1.0.44 → 1.0.46
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/dist/index.cjs +2 -2
- package/dist/index.js +358 -334
- package/package.json +1 -1
- package/src/components/TalkingHeadAvatar.jsx +82 -17
package/package.json
CHANGED
|
@@ -282,14 +282,70 @@ const TalkingHeadAvatar = forwardRef(({
|
|
|
282
282
|
if (options.onSpeechEnd && talkingHeadRef.current) {
|
|
283
283
|
const talkingHead = talkingHeadRef.current;
|
|
284
284
|
|
|
285
|
-
// Store original onAudioEnd if it exists
|
|
286
|
-
const originalOnAudioEnd = talkingHead.onAudioEnd;
|
|
287
|
-
|
|
288
285
|
// Set up a polling mechanism to detect when speech finishes
|
|
289
|
-
//
|
|
286
|
+
// Wait for audio to actually start playing before checking if it's finished
|
|
290
287
|
let checkInterval = null;
|
|
291
288
|
let checkCount = 0;
|
|
292
|
-
|
|
289
|
+
let audioStarted = false;
|
|
290
|
+
const maxChecks = 1200; // 60 seconds max (50ms intervals)
|
|
291
|
+
const maxWaitForAudioStart = 10000; // 10 seconds max to wait for audio to start
|
|
292
|
+
|
|
293
|
+
// First, wait for audio to actually start playing (API call completes and audio is added to playlist)
|
|
294
|
+
let waitForAudioStartCount = 0;
|
|
295
|
+
const waitForAudioStart = setInterval(() => {
|
|
296
|
+
waitForAudioStartCount++;
|
|
297
|
+
|
|
298
|
+
// Check if audio has started playing (audioPlaylist has items OR isAudioPlaying is true)
|
|
299
|
+
// Also check if isSpeaking is true (indicating API call has started processing)
|
|
300
|
+
if (talkingHead && talkingHead.isSpeaking && (
|
|
301
|
+
(talkingHead.audioPlaylist && talkingHead.audioPlaylist.length > 0) ||
|
|
302
|
+
(talkingHead.isAudioPlaying === true)
|
|
303
|
+
)) {
|
|
304
|
+
audioStarted = true;
|
|
305
|
+
clearInterval(waitForAudioStart);
|
|
306
|
+
|
|
307
|
+
// Now start checking if speech has finished
|
|
308
|
+
checkInterval = setInterval(checkSpeechFinished, 50);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Also check if speech queue is empty and not speaking (meaning all sentences processed)
|
|
312
|
+
// This handles the case where text was split into sentences but all are processed
|
|
313
|
+
const speechQueueEmpty = !talkingHead.speechQueue || talkingHead.speechQueue.length === 0;
|
|
314
|
+
|
|
315
|
+
if (talkingHead && !talkingHead.isSpeaking && speechQueueEmpty &&
|
|
316
|
+
(!talkingHead.audioPlaylist || talkingHead.audioPlaylist.length === 0) &&
|
|
317
|
+
(!talkingHead.isAudioPlaying || talkingHead.isAudioPlaying === false)) {
|
|
318
|
+
// All speech has finished (all sentences processed and audio finished)
|
|
319
|
+
clearInterval(waitForAudioStart);
|
|
320
|
+
try {
|
|
321
|
+
options.onSpeechEnd();
|
|
322
|
+
} catch (e) {
|
|
323
|
+
console.error('Error in onSpeechEnd callback:', e);
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Timeout if audio doesn't start within reasonable time
|
|
329
|
+
if (waitForAudioStartCount * 50 > maxWaitForAudioStart) {
|
|
330
|
+
clearInterval(waitForAudioStart);
|
|
331
|
+
// Check if speech has actually started (isSpeaking should be true)
|
|
332
|
+
// If isSpeaking is false, the speech might have failed or completed very quickly
|
|
333
|
+
if (talkingHead && talkingHead.isSpeaking) {
|
|
334
|
+
// Still waiting for API, but assume it will start soon
|
|
335
|
+
audioStarted = true;
|
|
336
|
+
checkInterval = setInterval(checkSpeechFinished, 50);
|
|
337
|
+
} else if (talkingHead && !talkingHead.isSpeaking && speechQueueEmpty &&
|
|
338
|
+
(!talkingHead.audioPlaylist || talkingHead.audioPlaylist.length === 0) &&
|
|
339
|
+
(!talkingHead.isAudioPlaying || talkingHead.isAudioPlaying === false)) {
|
|
340
|
+
// Speech never started or finished immediately, call callback
|
|
341
|
+
try {
|
|
342
|
+
options.onSpeechEnd();
|
|
343
|
+
} catch (e) {
|
|
344
|
+
console.error('Error in onSpeechEnd callback:', e);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}, 50);
|
|
293
349
|
|
|
294
350
|
const checkSpeechFinished = () => {
|
|
295
351
|
checkCount++;
|
|
@@ -307,31 +363,40 @@ const TalkingHeadAvatar = forwardRef(({
|
|
|
307
363
|
return;
|
|
308
364
|
}
|
|
309
365
|
|
|
310
|
-
//
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
366
|
+
// Only check if audio has started playing
|
|
367
|
+
if (!audioStarted) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check if speech has finished:
|
|
372
|
+
// 1. Not speaking OR speech queue is empty
|
|
373
|
+
// 2. Audio playlist is empty (no more audio to play)
|
|
374
|
+
// 3. Not currently playing audio
|
|
375
|
+
// 4. Speech queue is empty (all sentences have been processed)
|
|
376
|
+
const speechQueueEmpty = !talkingHead.speechQueue || talkingHead.speechQueue.length === 0;
|
|
377
|
+
|
|
378
|
+
const isFinished = talkingHead &&
|
|
379
|
+
(!talkingHead.isSpeaking || talkingHead.isSpeaking === false) &&
|
|
380
|
+
(!talkingHead.audioPlaylist || talkingHead.audioPlaylist.length === 0) &&
|
|
381
|
+
(!talkingHead.isAudioPlaying || talkingHead.isAudioPlaying === false) &&
|
|
382
|
+
speechQueueEmpty;
|
|
383
|
+
|
|
384
|
+
if (isFinished) {
|
|
315
385
|
if (checkInterval) {
|
|
316
386
|
clearInterval(checkInterval);
|
|
317
387
|
checkInterval = null;
|
|
318
388
|
}
|
|
319
389
|
|
|
320
|
-
// Small delay to ensure everything is settled
|
|
390
|
+
// Small delay to ensure everything is settled
|
|
321
391
|
setTimeout(() => {
|
|
322
392
|
try {
|
|
323
393
|
options.onSpeechEnd();
|
|
324
394
|
} catch (e) {
|
|
325
395
|
console.error('Error in onSpeechEnd callback:', e);
|
|
326
396
|
}
|
|
327
|
-
},
|
|
397
|
+
}, 50);
|
|
328
398
|
}
|
|
329
399
|
};
|
|
330
|
-
|
|
331
|
-
// Start checking after a minimal delay (to allow speech to start)
|
|
332
|
-
setTimeout(() => {
|
|
333
|
-
checkInterval = setInterval(checkSpeechFinished, 50);
|
|
334
|
-
}, 100);
|
|
335
400
|
}
|
|
336
401
|
|
|
337
402
|
if (talkingHeadRef.current.lipsync && Object.keys(talkingHeadRef.current.lipsync).length > 0) {
|