Xenova HF Staff commited on
Commit
271af08
·
verified ·
1 Parent(s): bc58933

Upload 13 files

Browse files
Files changed (1) hide show
  1. src/App.jsx +51 -32
src/App.jsx CHANGED
@@ -23,6 +23,7 @@ export default function App() {
23
  const [elapsedTime, setElapsedTime] = useState("00:00");
24
  const worker = useRef(null);
25
 
 
26
  const node = useRef(null);
27
 
28
  useEffect(() => {
@@ -108,15 +109,7 @@ export default function App() {
108
  let ignore = false;
109
 
110
  let outputAudioContext;
111
- const audioStreamPromise = navigator.mediaDevices.getUserMedia({
112
- audio: {
113
- channelCount: 1,
114
- echoCancellation: true,
115
- autoGainControl: true,
116
- noiseSuppression: true,
117
- sampleRate: INPUT_SAMPLE_RATE,
118
- },
119
- });
120
 
121
  audioStreamPromise
122
  .then(async (stream) => {
@@ -218,10 +211,7 @@ export default function App() {
218
 
219
  return () => {
220
  ignore = true;
221
-
222
- audioStreamPromise.then((stream) =>
223
- stream.getTracks().forEach((track) => track.stop()),
224
- );
225
  source?.disconnect();
226
  worklet?.disconnect();
227
  inputAudioContext?.close();
@@ -242,6 +232,28 @@ export default function App() {
242
  return () => clearInterval(interval);
243
  }, [callStarted]);
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  return (
246
  <div className="h-screen min-h-[240px] flex items-center justify-center bg-gray-50 p-4 relative">
247
  <div className="h-full max-h-[320px] w-[640px] bg-white rounded-xl shadow-lg p-8 flex items-center justify-between space-x-16">
@@ -289,31 +301,42 @@ export default function App() {
289
  style={{ animation: "ripple 1.5s ease-out forwards" }}
290
  />
291
  ))}
292
- <div className="absolute z-10 text-lg text-gray-700">
293
- {!ready ? "Loading..." : ""}
294
- {isListening && "Listening..."}
295
- {isSpeaking && "Speaking..."}
296
- </div>
297
  {/* Pulsing loader while initializing */}
298
  <div
299
- className={`absolute w-32 h-32 rounded-full bg-green-200 ${
300
- !ready ? "animate-ping opacity-75" : ""
301
- }`}
302
  style={{ animationDuration: "1.5s" }}
303
  />
304
  {/* Main rings */}
305
  <div
306
- className={`absolute w-32 h-32 rounded-full shadow-inner transition-transform duration-300 ease-out bg-green-300 ${
307
- !ready ? "opacity-0" : ""
308
- }`}
309
  style={{ transform: `scale(${speakingScale})` }}
310
  />
311
  <div
312
- className={`absolute w-32 h-32 rounded-full shadow-inner transition-transform duration-300 ease-out bg-green-200 ${
313
- !ready ? "opacity-0" : ""
314
- }`}
315
  style={{ transform: `scale(${listeningScale})` }}
316
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  </div>
318
 
319
  <div className="space-y-4 w-[140px]">
@@ -338,11 +361,7 @@ export default function App() {
338
  ? "bg-blue-100 text-blue-700 hover:bg-blue-200"
339
  : "bg-blue-100 text-blue-700 opacity-50 cursor-not-allowed"
340
  }`}
341
- onClick={() => {
342
- setCallStartTime(Date.now());
343
- setCallStarted(true);
344
- worker.current?.postMessage({ type: "start_call" });
345
- }}
346
  disabled={!ready}
347
  >
348
  <span>Start call</span>
 
23
  const [elapsedTime, setElapsedTime] = useState("00:00");
24
  const worker = useRef(null);
25
 
26
+ const micStreamRef = useRef(null);
27
  const node = useRef(null);
28
 
29
  useEffect(() => {
 
109
  let ignore = false;
110
 
111
  let outputAudioContext;
112
+ const audioStreamPromise = Promise.resolve(micStreamRef.current);
 
 
 
 
 
 
 
 
113
 
114
  audioStreamPromise
115
  .then(async (stream) => {
 
211
 
212
  return () => {
213
  ignore = true;
214
+ audioStreamPromise.then((s) => s.getTracks().forEach((t) => t.stop()));
 
 
 
215
  source?.disconnect();
216
  worklet?.disconnect();
217
  inputAudioContext?.close();
 
232
  return () => clearInterval(interval);
233
  }, [callStarted]);
234
 
235
+ const handleStartCall = async () => {
236
+ try {
237
+ const stream = await navigator.mediaDevices.getUserMedia({
238
+ audio: {
239
+ channelCount: 1,
240
+ echoCancellation: true,
241
+ autoGainControl: true,
242
+ noiseSuppression: true,
243
+ sampleRate: INPUT_SAMPLE_RATE,
244
+ },
245
+ });
246
+ micStreamRef.current = stream;
247
+
248
+ setCallStartTime(Date.now());
249
+ setCallStarted(true);
250
+ worker.current?.postMessage({ type: "start_call" });
251
+ } catch (err) {
252
+ setError(err.message);
253
+ console.error(err);
254
+ }
255
+ };
256
+
257
  return (
258
  <div className="h-screen min-h-[240px] flex items-center justify-center bg-gray-50 p-4 relative">
259
  <div className="h-full max-h-[320px] w-[640px] bg-white rounded-xl shadow-lg p-8 flex items-center justify-between space-x-16">
 
301
  style={{ animation: "ripple 1.5s ease-out forwards" }}
302
  />
303
  ))}
 
 
 
 
 
304
  {/* Pulsing loader while initializing */}
305
  <div
306
+ className={`absolute w-32 h-32 rounded-full ${
307
+ error ? "bg-red-200" : "bg-green-200"
308
+ } ${!ready ? "animate-ping opacity-75" : ""}`}
309
  style={{ animationDuration: "1.5s" }}
310
  />
311
  {/* Main rings */}
312
  <div
313
+ className={`absolute w-32 h-32 rounded-full shadow-inner transition-transform duration-300 ease-out ${
314
+ error ? "bg-red-300" : "bg-green-300"
315
+ } ${!ready ? "opacity-0" : ""}`}
316
  style={{ transform: `scale(${speakingScale})` }}
317
  />
318
  <div
319
+ className={`absolute w-32 h-32 rounded-full shadow-inner transition-transform duration-300 ease-out ${
320
+ error ? "bg-red-200" : "bg-green-200"
321
+ } ${!ready ? "opacity-0" : ""}`}
322
  style={{ transform: `scale(${listeningScale})` }}
323
  />
324
+ {/* Center text: show error if present, else existing statuses */}
325
+ <div
326
+ className={`absolute z-10 text-lg text-center ${
327
+ error ? "text-red-700" : "text-gray-700"
328
+ }`}
329
+ >
330
+ {error ? (
331
+ error
332
+ ) : (
333
+ <>
334
+ {!ready && "Loading..."}
335
+ {isListening && "Listening..."}
336
+ {isSpeaking && "Speaking..."}
337
+ </>
338
+ )}
339
+ </div>
340
  </div>
341
 
342
  <div className="space-y-4 w-[140px]">
 
361
  ? "bg-blue-100 text-blue-700 hover:bg-blue-200"
362
  : "bg-blue-100 text-blue-700 opacity-50 cursor-not-allowed"
363
  }`}
364
+ onClick={handleStartCall}
 
 
 
 
365
  disabled={!ready}
366
  >
367
  <span>Start call</span>