ishaq101 commited on
Commit
b38bd93
·
1 Parent(s): a5b2d49

update calling tts after chatbot response

Browse files
Files changed (1) hide show
  1. src/app/components/Main.tsx +19 -48
src/app/components/Main.tsx CHANGED
@@ -11,8 +11,8 @@ import {
11
  streamChat,
12
  type ChatSource,
13
  } from "../../services/api";
14
- import { textToSpeechStreaming } from "../../services/voiceApi";
15
- import { AudioPlayer } from "../../audio/AudioPlayer";
16
  import ChatLayout from "./chat/ChatLayout";
17
  import Sidebar from "./chat/Sidebar";
18
  import ChatWindow from "./chat/ChatWindow";
@@ -401,59 +401,30 @@ export default function Main() {
401
  }
402
  }, [user, ensureRoom]);
403
 
 
 
404
  const playTtsAudio = useCallback(async (
405
  ttsText: string,
406
  onStarted?: () => void,
407
  ): Promise<{ chunks: ArrayBuffer[]; sampleRate: number }> => {
408
- const chunks: ArrayBuffer[] = [];
 
 
409
  try {
410
- const { sampleRate, stream } = await textToSpeechStreaming(ttsText);
411
- const player = new AudioPlayer();
412
- player.init(sampleRate);
413
- const reader = stream.getReader();
414
- let startedNotified = false;
415
- // Track the exact moment the AudioContext actually starts playing so the
416
- // stop timer is anchored to playback start, not stream-end.
417
- let audioStartedAt: number | null = null;
418
- const notifyStarted = () => {
419
- if (!startedNotified) {
420
- startedNotified = true;
421
- audioStartedAt = Date.now();
422
- onStarted?.();
423
- }
424
- };
425
- while (true) {
426
- const { done, value } = await reader.read();
427
- if (done) break;
428
- if (value && value.byteLength > 0) {
429
- const pcm = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength) as ArrayBuffer;
430
- chunks.push(pcm);
431
- player.enqueue(pcm, notifyStarted);
432
- }
433
- }
434
- // Play any remaining buffered audio that didn't reach the threshold (short responses)
435
- player.flush(notifyStarted);
436
- const totalBytes = chunks.reduce((acc, c) => acc + c.byteLength, 0);
437
- const durationMs = (totalBytes / 2 / sampleRate) * 1000;
438
- // Wait until audio actually starts, then wait for the full playback duration.
439
- // Polling in 10ms steps avoids a fixed large buffer while staying responsive.
440
- await new Promise<void>((resolve) => {
441
- const check = () => {
442
- if (audioStartedAt !== null) {
443
- const elapsed = Date.now() - audioStartedAt;
444
- const remaining = Math.max(0, durationMs - elapsed) + 150;
445
- setTimeout(resolve, remaining);
446
- } else {
447
- setTimeout(check, 10);
448
- }
449
- };
450
- check();
451
- });
452
- player.stopImmediately();
453
- return { chunks, sampleRate };
454
  } catch {
455
  // TTS failure is non-fatal
456
- return { chunks, sampleRate: 24000 };
457
  }
458
  }, []);
459
 
 
11
  streamChat,
12
  type ChatSource,
13
  } from "../../services/api";
14
+ import { textToSpeech } from "../../services/voiceApi";
15
+ import { replayAudio } from "../../audio/AudioPlayer";
16
  import ChatLayout from "./chat/ChatLayout";
17
  import Sidebar from "./chat/Sidebar";
18
  import ChatWindow from "./chat/ChatWindow";
 
401
  }
402
  }, [user, ensureRoom]);
403
 
404
+ const cancelTtsRef = useRef<(() => void) | null>(null);
405
+
406
  const playTtsAudio = useCallback(async (
407
  ttsText: string,
408
  onStarted?: () => void,
409
  ): Promise<{ chunks: ArrayBuffer[]; sampleRate: number }> => {
410
+ // Stop any in-flight TTS before starting a new one (e.g. rapid re-query).
411
+ cancelTtsRef.current?.();
412
+ cancelTtsRef.current = null;
413
  try {
414
+ const { pcm, sampleRate } = await textToSpeech(ttsText);
415
+ const durationMs = (pcm.byteLength / 2 / sampleRate) * 1000;
416
+ // Use the same proven playback path as the speaker button: schedule the
417
+ // full response at once via replayAudio, avoiding all streaming complexity.
418
+ const cancel = replayAudio([pcm], sampleRate);
419
+ cancelTtsRef.current = cancel;
420
+ onStarted?.();
421
+ await new Promise<void>((resolve) => setTimeout(resolve, durationMs + 150));
422
+ cancel();
423
+ cancelTtsRef.current = null;
424
+ return { chunks: [pcm], sampleRate };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  } catch {
426
  // TTS failure is non-fatal
427
+ return { chunks: [], sampleRate: 24000 };
428
  }
429
  }, []);
430