Spaces:
Running
Running
Update src/components/control-tray/ControlTray.tsx
Browse files
src/components/control-tray/ControlTray.tsx
CHANGED
@@ -5,7 +5,7 @@ import React, { memo, RefObject, useEffect, useState, useCallback, useRef } from
|
|
5 |
import { useLiveAPIContext } from "../../contexts/LiveAPIContext";
|
6 |
import { AudioRecorder } from "../../lib/audio-recorder";
|
7 |
import Logo from '../logo/Logo';
|
8 |
-
import { PauseIconWithSurroundPulse, microphoneIcon, cameraIcon, stopCamIcon } from '../icons';
|
9 |
|
10 |
const SvgSwitchCameraIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-[22px] h-[22px]"><path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/><path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/><path d="m17 12-3-3 3-3"/><path d="m7 12 3 3-3 3"/></svg>;
|
11 |
|
@@ -23,7 +23,7 @@ export type ControlTrayProps = {
|
|
23 |
};
|
24 |
|
25 |
const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange, supportsVideo, isAppMicActive, onAppMicToggle, isAppCamActive, onAppCamToggle, currentFacingMode, onFacingModeChange, onUserSpeakingChange }) => {
|
26 |
-
const { client, connected, connect, volume
|
27 |
const audioRecorderRef = useRef<AudioRecorder | null>(null);
|
28 |
const [activeLocalVideoStream, setActiveLocalVideoStream] = useState<MediaStream | null>(null);
|
29 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
@@ -32,43 +32,37 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
32 |
|
33 |
useEffect(() => {
|
34 |
if (!audioRecorderRef.current) {
|
35 |
-
|
36 |
}
|
37 |
const audioRecorder = audioRecorderRef.current;
|
38 |
-
|
39 |
-
|
|
|
40 |
if (client && connected && isAppMicActive) {
|
41 |
client.sendRealtimeInput([{ mimeType: "audio/pcm;rate=16000", data: base64 }]);
|
|
|
|
|
42 |
}
|
43 |
};
|
44 |
|
45 |
-
const
|
46 |
-
|
47 |
-
|
48 |
-
}
|
49 |
-
|
50 |
-
const handleStop = () => {
|
51 |
-
setUserVolume(0);
|
52 |
-
onUserSpeakingChange(false);
|
53 |
-
};
|
54 |
|
55 |
-
if (
|
56 |
-
audioRecorder.on(
|
57 |
-
|
58 |
-
audioRecorder.
|
59 |
-
audioRecorder.start();
|
60 |
-
} else if (!isAppMicActive && audioRecorder.recording) {
|
61 |
-
audioRecorder.stop();
|
62 |
}
|
63 |
-
|
64 |
return () => {
|
65 |
-
|
66 |
-
|
67 |
-
|
|
|
68 |
};
|
69 |
-
}, [
|
70 |
|
71 |
-
// ... (بقیه useEffect ها و توابع بدون تغییر باقی میمانند)
|
72 |
useEffect(() => {
|
73 |
if (videoRef.current) {
|
74 |
if (videoRef.current.srcObject !== activeLocalVideoStream) {
|
@@ -199,10 +193,13 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
199 |
}
|
200 |
};
|
201 |
|
|
|
|
|
202 |
return (
|
203 |
<footer id="footer-controls" className="footer-controls-html-like">
|
204 |
<canvas style={{ display: "none" }} ref={renderCanvasRef} />
|
205 |
<div id="mic-button" className="control-button mic-button-color" onClick={handleMicToggle}>
|
|
|
206 |
{isAppMicActive ? <PauseIconWithSurroundPulse userVolume={userVolume} /> : microphoneIcon}
|
207 |
</div>
|
208 |
|
@@ -212,8 +209,8 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
212 |
isMini={true}
|
213 |
isActive={true}
|
214 |
isAi={false}
|
215 |
-
speakingVolume={
|
216 |
-
isUserSpeaking={
|
217 |
/>
|
218 |
</div>
|
219 |
)}
|
|
|
5 |
import { useLiveAPIContext } from "../../contexts/LiveAPIContext";
|
6 |
import { AudioRecorder } from "../../lib/audio-recorder";
|
7 |
import Logo from '../logo/Logo';
|
8 |
+
import { PauseIconWithSurroundPulse, microphoneIcon, cameraIcon, stopCamIcon, pauseIcon } from '../icons';
|
9 |
|
10 |
const SvgSwitchCameraIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-[22px] h-[22px]"><path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/><path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/><path d="m17 12-3-3 3-3"/><path d="m7 12 3 3-3 3"/></svg>;
|
11 |
|
|
|
23 |
};
|
24 |
|
25 |
const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange, supportsVideo, isAppMicActive, onAppMicToggle, isAppCamActive, onAppCamToggle, currentFacingMode, onFacingModeChange, onUserSpeakingChange }) => {
|
26 |
+
const { client, connected, connect, volume } = useLiveAPIContext();
|
27 |
const audioRecorderRef = useRef<AudioRecorder | null>(null);
|
28 |
const [activeLocalVideoStream, setActiveLocalVideoStream] = useState<MediaStream | null>(null);
|
29 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
|
|
32 |
|
33 |
useEffect(() => {
|
34 |
if (!audioRecorderRef.current) {
|
35 |
+
audioRecorderRef.current = new AudioRecorder();
|
36 |
}
|
37 |
const audioRecorder = audioRecorderRef.current;
|
38 |
+
|
39 |
+
// --- 👇 تغییر اصلی اینجاست: دریافت ولوم از رویداد data 👇 ---
|
40 |
+
const onData = (base64: string, volume: number) => {
|
41 |
if (client && connected && isAppMicActive) {
|
42 |
client.sendRealtimeInput([{ mimeType: "audio/pcm;rate=16000", data: base64 }]);
|
43 |
+
setUserVolume(volume);
|
44 |
+
onUserSpeakingChange(volume > 0.01);
|
45 |
}
|
46 |
};
|
47 |
|
48 |
+
const onStop = () => {
|
49 |
+
setUserVolume(0);
|
50 |
+
onUserSpeakingChange(false);
|
51 |
+
}
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
+
if (connected && isAppMicActive) {
|
54 |
+
audioRecorder.on("data", onData).on("stop", onStop).start();
|
55 |
+
} else {
|
56 |
+
if (audioRecorder.recording) audioRecorder.stop();
|
|
|
|
|
|
|
57 |
}
|
|
|
58 |
return () => {
|
59 |
+
if (audioRecorder) {
|
60 |
+
audioRecorder.off("data", onData).off("stop", onStop);
|
61 |
+
if (audioRecorder.recording) audioRecorder.stop();
|
62 |
+
}
|
63 |
};
|
64 |
+
}, [connected, client, isAppMicActive, onUserSpeakingChange]);
|
65 |
|
|
|
66 |
useEffect(() => {
|
67 |
if (videoRef.current) {
|
68 |
if (videoRef.current.srcObject !== activeLocalVideoStream) {
|
|
|
193 |
}
|
194 |
};
|
195 |
|
196 |
+
const isSpeaking = onUserSpeakingChange.length > 0 && userVolume > 0.01;
|
197 |
+
|
198 |
return (
|
199 |
<footer id="footer-controls" className="footer-controls-html-like">
|
200 |
<canvas style={{ display: "none" }} ref={renderCanvasRef} />
|
201 |
<div id="mic-button" className="control-button mic-button-color" onClick={handleMicToggle}>
|
202 |
+
{/* استفاده از آیکون جدید با انیمیشن */}
|
203 |
{isAppMicActive ? <PauseIconWithSurroundPulse userVolume={userVolume} /> : microphoneIcon}
|
204 |
</div>
|
205 |
|
|
|
209 |
isMini={true}
|
210 |
isActive={true}
|
211 |
isAi={false}
|
212 |
+
speakingVolume={volume}
|
213 |
+
isUserSpeaking={isSpeaking}
|
214 |
/>
|
215 |
</div>
|
216 |
)}
|