ferrazzipietro commited on
Commit
095321b
·
1 Parent(s): 2d9189f
streaming-react-app/src/StreamingInterface.tsx CHANGED
@@ -1,3 +1,1226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
2
  import Button from '@mui/material/Button';
3
  import Typography from '@mui/material/Typography';
@@ -6,7 +1229,7 @@ import FormControl from '@mui/material/FormControl';
6
  import Select, {SelectChangeEvent} from '@mui/material/Select';
7
  import MenuItem from '@mui/material/MenuItem';
8
  import Stack from '@mui/material/Stack';
9
- import seamlessLogoUrl from './assets/seamless.svg';
10
  import {
11
  AgentCapabilities,
12
  BaseResponse,
@@ -744,42 +1967,44 @@ export default function StreamingInterface() {
744
  src={seamlessLogoUrl}
745
  className="header-icon-sra"
746
  alt="Seamless Translation Logo"
747
- height={24}
748
- width={24}
749
  />
750
 
751
  <div>
752
- <Typography variant="h1" sx={{color: '#65676B'}}>
753
- Seamless Translation
 
 
 
 
 
754
  </Typography>
755
  </div>
756
  </div>
757
  <div className="header-container-sra">
758
  <div>
759
  <Typography variant="body2" sx={{color: '#65676B'}}>
760
- Welcome! This space is limited to one speaker at a time.
761
- If using the live HF space, sharing room code to listeners on another
762
- IP address may not work because it's running on different replicas.
763
- Use headphones if you are both speaker and listener to prevent feedback.
764
  <br/>
765
- If max speakers reached, please duplicate the space <a target="_blank" rel="noopener noreferrer" href="https://huggingface.co/spaces/facebook/seamless-streaming?duplicate=true">here</a>.
766
- In your duplicated space, join a room as speaker or listener (or both),
767
- and share the room code to invite listeners.
768
  <br/>
769
- Check out the seamless_communication <a target="_blank" rel="noopener noreferrer" href="https://github.com/facebookresearch/seamless_communication/tree/main">README</a> for more information.
770
  <br/>
771
- SeamlessStreaming model is a research model and is not released
772
- for production deployment. It is important to use a microphone with
773
- noise cancellation (for e.g. a smartphone), otherwise you may see model hallucination on noises.
774
- It works best if you pause every couple of sentences, or you may wish adjust the VAD threshold
775
- in the model config. The real-time performance will degrade
776
  if you try streaming multiple speakers at the same time.
 
 
 
777
  </Typography>
778
  </div>
779
  </div>
780
  <Stack spacing="22px" direction="column">
781
  <Box>
782
- <RoomConfig
783
  roomState={roomState}
784
  serverState={serverState}
785
  streamingStatus={streamingStatus}
@@ -788,7 +2013,7 @@ export default function StreamingInterface() {
788
  // player to play eagerly, since currently the listener doesn't have any stop/start controls
789
  bufferedSpeechPlayer.start();
790
  }}
791
- />
792
 
793
  {isListener && !isSpeaker && (
794
  <Box
@@ -810,9 +2035,9 @@ export default function StreamingInterface() {
810
  <Divider />
811
 
812
  <Stack spacing="12px" direction="column">
813
- <FormLabel id="output-modes-radio-group-label">
814
  Model
815
- </FormLabel>
816
  <FormControl
817
  disabled={
818
  streamFixedConfigOptionsDisabled ||
@@ -820,10 +2045,10 @@ export default function StreamingInterface() {
820
  }
821
  fullWidth
822
  sx={{minWidth: '14em'}}>
823
- <InputLabel id="model-selector-input-label">
824
  Model
825
- </InputLabel>
826
- <Select
827
  labelId="model-selector-input-label"
828
  label="Model"
829
  onChange={(e: SelectChangeEvent) => {
@@ -845,14 +2070,14 @@ export default function StreamingInterface() {
845
  {agent.name}
846
  </MenuItem>
847
  ))}
848
- </Select>
849
  </FormControl>
850
 
851
  </Stack>
852
 
853
  <Stack spacing={0.5}>
854
  <FormLabel id="output-modes-radio-group-label">
855
- Output
856
  </FormLabel>
857
 
858
  <Box sx={{paddingTop: 2, paddingBottom: 1}}>
@@ -917,7 +2142,7 @@ export default function StreamingInterface() {
917
  spacing={1}
918
  alignItems="flex-start"
919
  sx={{flexGrow: 1}}>
920
- {currentAgent?.dynamicParams?.includes(
921
  'expressive',
922
  ) && (
923
  <FormControlLabel
@@ -937,7 +2162,7 @@ export default function StreamingInterface() {
937
  }
938
  label="Expressive"
939
  />
940
- )}
941
 
942
  {isListener && (
943
  <Box
@@ -961,10 +2186,10 @@ export default function StreamingInterface() {
961
  justifyContent="space-between">
962
  <Box sx={{flex: 1}}>
963
  <FormControl disabled={streamFixedConfigOptionsDisabled}>
964
- <FormLabel id="input-source-radio-group-label">
965
  Input Source
966
- </FormLabel>
967
- <RadioGroup
968
  aria-labelledby="input-source-radio-group-label"
969
  value={inputSource}
970
  onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
@@ -980,8 +2205,8 @@ export default function StreamingInterface() {
980
  control={<Radio />}
981
  label={label}
982
  />
983
- ))}
984
- </RadioGroup>
985
  </FormControl>
986
  </Box>
987
 
@@ -1217,3 +2442,4 @@ export default function StreamingInterface() {
1217
  </div>
1218
  );
1219
  }
 
 
1
+ // import {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
2
+ // import Button from '@mui/material/Button';
3
+ // import Typography from '@mui/material/Typography';
4
+ // import InputLabel from '@mui/material/InputLabel';
5
+ // import FormControl from '@mui/material/FormControl';
6
+ // import Select, {SelectChangeEvent} from '@mui/material/Select';
7
+ // import MenuItem from '@mui/material/MenuItem';
8
+ // import Stack from '@mui/material/Stack';
9
+ // import seamlessLogoUrl from './assets/seamless.svg';
10
+ // import {
11
+ // AgentCapabilities,
12
+ // BaseResponse,
13
+ // BrowserAudioStreamConfig,
14
+ // DynamicConfig,
15
+ // PartialDynamicConfig,
16
+ // SUPPORTED_INPUT_SOURCES,
17
+ // SUPPORTED_OUTPUT_MODES,
18
+ // ServerExceptionData,
19
+ // ServerSpeechData,
20
+ // ServerState,
21
+ // ServerTextData,
22
+ // StartStreamEventConfig,
23
+ // StreamingStatus,
24
+ // SupportedInputSource,
25
+ // SupportedOutputMode,
26
+ // TranslationSentences,
27
+ // } from './types/StreamingTypes';
28
+ // import FormLabel from '@mui/material/FormLabel';
29
+ // import RadioGroup from '@mui/material/RadioGroup';
30
+ // import FormControlLabel from '@mui/material/FormControlLabel';
31
+ // import Radio from '@mui/material/Radio';
32
+ // import './StreamingInterface.css';
33
+ // import RoomConfig from './RoomConfig';
34
+ // import Divider from '@mui/material/Divider';
35
+ // import {useSocket} from './useSocket';
36
+ // import {RoomState} from './types/RoomState';
37
+ // import useStable from './useStable';
38
+ // import float32To16BitPCM from './float32To16BitPCM';
39
+ // import createBufferedSpeechPlayer from './createBufferedSpeechPlayer';
40
+ // import Checkbox from '@mui/material/Checkbox';
41
+ // import Alert from '@mui/material/Alert';
42
+ // import isScrolledToDocumentBottom from './isScrolledToDocumentBottom';
43
+ // import Box from '@mui/material/Box';
44
+ // import Slider from '@mui/material/Slider';
45
+ // import VolumeDown from '@mui/icons-material/VolumeDown';
46
+ // import VolumeUp from '@mui/icons-material/VolumeUp';
47
+ // import Mic from '@mui/icons-material/Mic';
48
+ // import MicOff from '@mui/icons-material/MicOff';
49
+ // import XRDialog from './react-xr/XRDialog';
50
+ // import getTranslationSentencesFromReceivedData from './getTranslationSentencesFromReceivedData';
51
+ // import {
52
+ // sliceTranslationSentencesUpToIndex,
53
+ // getTotalSentencesLength,
54
+ // } from './sliceTranslationSentencesUtils';
55
+ // import Blink from './Blink';
56
+ // import {CURSOR_BLINK_INTERVAL_MS} from './cursorBlinkInterval';
57
+ // import {getURLParams} from './URLParams';
58
+ // import debug from './debug';
59
+ // import DebugSection from './DebugSection';
60
+ // import Switch from '@mui/material/Switch';
61
+ // import Grid from '@mui/material/Grid';
62
+ // import {getLanguageFromThreeLetterCode} from './languageLookup';
63
+ // import HeadphonesIcon from '@mui/icons-material/Headphones';
64
+
65
+ // const AUDIO_STREAM_DEFAULTS = {
66
+ // userMedia: {
67
+ // echoCancellation: false,
68
+ // noiseSuppression: true,
69
+ // },
70
+ // displayMedia: {
71
+ // echoCancellation: false,
72
+ // noiseSuppression: false,
73
+ // },
74
+ // } as const;
75
+
76
+ // async function requestUserMediaAudioStream(
77
+ // config: BrowserAudioStreamConfig = AUDIO_STREAM_DEFAULTS['userMedia'],
78
+ // ) {
79
+ // const stream = await navigator.mediaDevices.getUserMedia({
80
+ // audio: {...config, channelCount: 1},
81
+ // });
82
+ // console.debug(
83
+ // '[requestUserMediaAudioStream] stream created with settings:',
84
+ // stream.getAudioTracks()?.[0]?.getSettings(),
85
+ // );
86
+ // return stream;
87
+ // }
88
+
89
+ // async function requestDisplayMediaAudioStream(
90
+ // config: BrowserAudioStreamConfig = AUDIO_STREAM_DEFAULTS['displayMedia'],
91
+ // ) {
92
+ // const stream = await navigator.mediaDevices.getDisplayMedia({
93
+ // audio: {...config, channelCount: 1},
94
+ // });
95
+ // console.debug(
96
+ // '[requestDisplayMediaAudioStream] stream created with settings:',
97
+ // stream.getAudioTracks()?.[0]?.getSettings(),
98
+ // );
99
+ // return stream;
100
+ // }
101
+
102
+ // const buttonLabelMap: {[key in StreamingStatus]: string} = {
103
+ // stopped: 'Start Streaming',
104
+ // running: 'Stop Streaming',
105
+ // starting: 'Starting...',
106
+ // };
107
+
108
+ // const BUFFER_LIMIT = 1;
109
+
110
+ // const SCROLLED_TO_BOTTOM_THRESHOLD_PX = 36;
111
+
112
+ // const GAIN_MULTIPLIER_OVER_1 = 3;
113
+
114
+ // const getGainScaledValue = (value) =>
115
+ // value > 1 ? (value - 1) * GAIN_MULTIPLIER_OVER_1 + 1 : value;
116
+
117
+ // const TOTAL_ACTIVE_TRANSCODER_WARNING_THRESHOLD = 2;
118
+
119
+ // const MAX_SERVER_EXCEPTIONS_TRACKED = 500;
120
+
121
+ // export const TYPING_ANIMATION_DELAY_MS = 6;
122
+
123
+ // export default function StreamingInterface() {
124
+ // const urlParams = getURLParams();
125
+ // const debugParam = urlParams.debug;
126
+ // const [animateTextDisplay, setAnimateTextDisplay] = useState<boolean>(
127
+ // urlParams.animateTextDisplay,
128
+ // );
129
+
130
+ // const socketObject = useSocket();
131
+ // const {socket, clientID} = socketObject;
132
+
133
+ // const [serverState, setServerState] = useState<ServerState | null>(null);
134
+ // const [agent, setAgent] = useState<AgentCapabilities | null>(null);
135
+ // const model = agent?.name ?? null;
136
+ // const agentsCapabilities: Array<AgentCapabilities> =
137
+ // serverState?.agentsCapabilities ?? [];
138
+ // const currentAgent: AgentCapabilities | null =
139
+ // agentsCapabilities.find((agent) => agent.name === model) ?? null;
140
+
141
+ // const [serverExceptions, setServerExceptions] = useState<
142
+ // Array<ServerExceptionData>
143
+ // >([]);
144
+ // const [roomState, setRoomState] = useState<RoomState | null>(null);
145
+ // const roomID = roomState?.room_id ?? null;
146
+ // const isSpeaker =
147
+ // (clientID != null && roomState?.speakers.includes(clientID)) ?? false;
148
+ // const isListener =
149
+ // (clientID != null && roomState?.listeners.includes(clientID)) ?? false;
150
+
151
+ // const [streamingStatus, setStreamingStatus] =
152
+ // useState<StreamingStatus>('stopped');
153
+
154
+ // const isStreamConfiguredRef = useRef<boolean>(false);
155
+ // const [hasMaxSpeakers, setHasMaxSpeakers] = useState<boolean>(false);
156
+
157
+ // const [outputMode, setOutputMode] = useState<SupportedOutputMode>('s2s&t');
158
+ // const [inputSource, setInputSource] =
159
+ // useState<SupportedInputSource>('userMedia');
160
+ // const [enableNoiseSuppression, setEnableNoiseSuppression] = useState<
161
+ // boolean | null
162
+ // >(null);
163
+ // const [enableEchoCancellation, setEnableEchoCancellation] = useState<
164
+ // boolean | null
165
+ // >(null);
166
+
167
+ // // Dynamic Params:
168
+ // const [targetLang, setTargetLang] = useState<string | null>(null);
169
+ // const [enableExpressive, setEnableExpressive] = useState<boolean | null>(
170
+ // null,
171
+ // );
172
+
173
+ // const [serverDebugFlag, setServerDebugFlag] = useState<boolean>(
174
+ // debugParam ?? false,
175
+ // );
176
+
177
+ // const [receivedData, setReceivedData] = useState<Array<ServerTextData>>([]);
178
+ // const [
179
+ // translationSentencesAnimatedIndex,
180
+ // setTranslationSentencesAnimatedIndex,
181
+ // ] = useState<number>(0);
182
+
183
+ // const lastTranslationResultRef = useRef<HTMLDivElement | null>(null);
184
+
185
+ // const [inputStream, setInputStream] = useState<MediaStream | null>(null);
186
+ // const [inputStreamSource, setInputStreamSource] =
187
+ // useState<MediaStreamAudioSourceNode | null>(null);
188
+ // const audioContext = useStable<AudioContext>(() => new AudioContext());
189
+ // const [scriptNodeProcessor, setScriptNodeProcessor] =
190
+ // useState<ScriptProcessorNode | null>(null);
191
+
192
+ // const [muted, setMuted] = useState<boolean>(false);
193
+ // // The onaudioprocess script needs an up-to-date reference to the muted state, so
194
+ // // we use a ref here and keep it in sync via useEffect
195
+ // const mutedRef = useRef<boolean>(muted);
196
+ // useEffect(() => {
197
+ // mutedRef.current = muted;
198
+ // }, [muted]);
199
+
200
+ // const [gain, setGain] = useState<number>(1);
201
+
202
+ // const isScrolledToBottomRef = useRef<boolean>(isScrolledToDocumentBottom());
203
+
204
+ // // Some config options must be set when starting streaming and cannot be chaned dynamically.
205
+ // // This controls whether they are disabled or not
206
+ // const streamFixedConfigOptionsDisabled =
207
+ // streamingStatus !== 'stopped' || roomID == null;
208
+
209
+ // const bufferedSpeechPlayer = useStable(() => {
210
+ // const player = createBufferedSpeechPlayer({
211
+ // onStarted: () => {
212
+ // console.debug('📢 PLAYBACK STARTED 📢');
213
+ // },
214
+ // onEnded: () => {
215
+ // console.debug('🛑 PLAYBACK ENDED 🛑');
216
+ // },
217
+ // });
218
+
219
+ // // Start the player now so it eagerly plays audio when it arrives
220
+ // player.start();
221
+ // return player;
222
+ // });
223
+
224
+ // const translationSentencesBase: TranslationSentences =
225
+ // getTranslationSentencesFromReceivedData(receivedData);
226
+
227
+ // const translationSentencesBaseTotalLength = getTotalSentencesLength(
228
+ // translationSentencesBase,
229
+ // );
230
+
231
+ // const translationSentences: TranslationSentences = animateTextDisplay
232
+ // ? sliceTranslationSentencesUpToIndex(
233
+ // translationSentencesBase,
234
+ // translationSentencesAnimatedIndex,
235
+ // )
236
+ // : translationSentencesBase;
237
+
238
+ // // We want the blinking cursor to show before any text has arrived, so let's add an empty string so that the cursor shows up
239
+ // const translationSentencesWithEmptyStartingString =
240
+ // streamingStatus === 'running' && translationSentences.length === 0
241
+ // ? ['']
242
+ // : translationSentences;
243
+
244
+ // /******************************************
245
+ // * Event Handlers
246
+ // ******************************************/
247
+
248
+ // const setAgentAndUpdateParams = useCallback(
249
+ // (newAgent: AgentCapabilities | null) => {
250
+ // setAgent((prevAgent) => {
251
+ // if (prevAgent?.name !== newAgent?.name) {
252
+ // setTargetLang(newAgent?.targetLangs[0] ?? null);
253
+ // setEnableExpressive(null);
254
+ // }
255
+ // return newAgent;
256
+ // });
257
+ // },
258
+ // [],
259
+ // );
260
+
261
+ // const onSetDynamicConfig = useCallback(
262
+ // async (partialConfig: PartialDynamicConfig) => {
263
+ // return new Promise<void>((resolve, reject) => {
264
+ // if (socket == null) {
265
+ // reject(new Error('[onSetDynamicConfig] socket is null '));
266
+ // return;
267
+ // }
268
+
269
+ // socket.emit(
270
+ // 'set_dynamic_config',
271
+ // partialConfig,
272
+ // (result: BaseResponse) => {
273
+ // console.log('[emit result: set_dynamic_config]', result);
274
+ // if (result.status === 'ok') {
275
+ // resolve();
276
+ // } else {
277
+ // reject();
278
+ // }
279
+ // },
280
+ // );
281
+ // });
282
+ // },
283
+ // [socket],
284
+ // );
285
+
286
+ // const configureStreamAsync = ({sampleRate}: {sampleRate: number}) => {
287
+ // return new Promise<void>((resolve, reject) => {
288
+ // if (socket == null) {
289
+ // reject(new Error('[configureStreamAsync] socket is null '));
290
+ // return;
291
+ // }
292
+ // const modelName = agent?.name ?? null;
293
+ // if (modelName == null) {
294
+ // reject(new Error('[configureStreamAsync] modelName is null '));
295
+ // return;
296
+ // }
297
+
298
+ // const config: StartStreamEventConfig = {
299
+ // event: 'config',
300
+ // rate: sampleRate,
301
+ // model_name: modelName,
302
+ // debug: serverDebugFlag,
303
+ // // synchronous processing isn't implemented on the v2 pubsub server, so hardcode this to true
304
+ // async_processing: true,
305
+ // buffer_limit: BUFFER_LIMIT,
306
+ // model_type: outputMode,
307
+ // };
308
+
309
+ // console.log('[configureStreamAsync] sending config', config);
310
+
311
+ // socket.emit('configure_stream', config, (statusObject) => {
312
+ // setHasMaxSpeakers(statusObject.message === 'max_speakers')
313
+ // if (statusObject.status === 'ok') {
314
+ // isStreamConfiguredRef.current = true;
315
+ // console.debug(
316
+ // '[configureStreamAsync] stream configured!',
317
+ // statusObject,
318
+ // );
319
+ // resolve();
320
+ // } else {
321
+ // isStreamConfiguredRef.current = false;
322
+ // reject(
323
+ // new Error(
324
+ // `[configureStreamAsync] configure_stream returned status: ${statusObject.status}`,
325
+ // ),
326
+ // );
327
+ // return;
328
+ // }
329
+ // });
330
+ // });
331
+ // };
332
+
333
+ // const startStreaming = async () => {
334
+ // if (streamingStatus !== 'stopped') {
335
+ // console.warn(
336
+ // `Attempting to start stream when status is ${streamingStatus}`,
337
+ // );
338
+ // return;
339
+ // }
340
+
341
+ // setStreamingStatus('starting');
342
+
343
+ // if (audioContext.state === 'suspended') {
344
+ // console.warn('audioContext was suspended! resuming...');
345
+ // await audioContext.resume();
346
+ // }
347
+
348
+ // let stream: MediaStream | null = null;
349
+
350
+ // try {
351
+ // if (inputSource === 'userMedia') {
352
+ // stream = await requestUserMediaAudioStream({
353
+ // noiseSuppression:
354
+ // enableNoiseSuppression ??
355
+ // AUDIO_STREAM_DEFAULTS['userMedia'].noiseSuppression,
356
+ // echoCancellation:
357
+ // enableEchoCancellation ??
358
+ // AUDIO_STREAM_DEFAULTS['userMedia'].echoCancellation,
359
+ // });
360
+ // } else if (inputSource === 'displayMedia') {
361
+ // stream = await requestDisplayMediaAudioStream({
362
+ // noiseSuppression:
363
+ // enableNoiseSuppression ??
364
+ // AUDIO_STREAM_DEFAULTS['displayMedia'].noiseSuppression,
365
+ // echoCancellation:
366
+ // enableEchoCancellation ??
367
+ // AUDIO_STREAM_DEFAULTS['displayMedia'].echoCancellation,
368
+ // });
369
+ // } else {
370
+ // throw new Error(`Unsupported input source requested: ${inputSource}`);
371
+ // }
372
+ // setInputStream(stream);
373
+ // } catch (e) {
374
+ // console.error('[startStreaming] media stream request failed:', e);
375
+ // setStreamingStatus('stopped');
376
+ // return;
377
+ // }
378
+
379
+ // const mediaStreamSource = audioContext.createMediaStreamSource(stream);
380
+ // setInputStreamSource(mediaStreamSource);
381
+ // /**
382
+ // * NOTE: This currently uses a deprecated way of processing the audio (createScriptProcessor), but
383
+ // * which is easy and convenient for our purposes.
384
+ // *
385
+ // * Documentation for the deprecated way of doing it is here: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createScriptProcessor
386
+ // *
387
+ // * In an ideal world this would be migrated to something like this SO answer: https://stackoverflow.com/a/65448287
388
+ // */
389
+ // const scriptProcessor = audioContext.createScriptProcessor(16384, 1, 1);
390
+ // setScriptNodeProcessor(scriptProcessor);
391
+
392
+ // scriptProcessor.onaudioprocess = (event) => {
393
+ // if (isStreamConfiguredRef.current === false) {
394
+ // console.debug('[onaudioprocess] stream is not configured yet!');
395
+ // return;
396
+ // }
397
+ // if (socket == null) {
398
+ // console.warn('[onaudioprocess] socket is null in onaudioprocess');
399
+ // return;
400
+ // }
401
+
402
+ // if (mutedRef.current) {
403
+ // // We still want to send audio to the server when we're muted to ensure we
404
+ // // get any remaining audio back from the server, so let's pass an array length 1 with a value of 0
405
+ // const mostlyEmptyInt16Array = new Int16Array(1);
406
+ // socket.emit('incoming_audio', mostlyEmptyInt16Array);
407
+ // } else {
408
+ // const float32Audio = event.inputBuffer.getChannelData(0);
409
+ // const pcm16Audio = float32To16BitPCM(float32Audio);
410
+ // socket.emit('incoming_audio', pcm16Audio);
411
+ // }
412
+
413
+ // debug()?.sentAudio(event);
414
+ // };
415
+
416
+ // mediaStreamSource.connect(scriptProcessor);
417
+ // scriptProcessor.connect(audioContext.destination);
418
+
419
+ // bufferedSpeechPlayer.start();
420
+
421
+ // try {
422
+ // if (targetLang == null) {
423
+ // throw new Error('[startStreaming] targetLang cannot be nullish');
424
+ // }
425
+
426
+ // // When we are starting the stream we want to pass all the dynamic config values
427
+ // // available before actually configuring and starting the stream
428
+ // const fullDynamicConfig: DynamicConfig = {
429
+ // targetLanguage: targetLang,
430
+ // expressive: enableExpressive,
431
+ // };
432
+
433
+ // await onSetDynamicConfig(fullDynamicConfig);
434
+
435
+ // // NOTE: this needs to be the *audioContext* sample rate, not the sample rate of the input stream. Not entirely sure why.
436
+ // await configureStreamAsync({
437
+ // sampleRate: audioContext.sampleRate,
438
+ // });
439
+ // } catch (e) {
440
+ // console.error('configureStreamAsync failed', e);
441
+ // setStreamingStatus('stopped');
442
+ // return;
443
+ // }
444
+
445
+ // setStreamingStatus('running');
446
+ // };
447
+
448
+ // const stopStreaming = useCallback(async () => {
449
+ // if (streamingStatus === 'stopped') {
450
+ // console.warn(
451
+ // `Attempting to stop stream when status is ${streamingStatus}`,
452
+ // );
453
+ // return;
454
+ // }
455
+
456
+ // // Stop the speech playback right away
457
+ // bufferedSpeechPlayer.stop();
458
+
459
+ // if (inputStreamSource == null || scriptNodeProcessor == null) {
460
+ // console.error(
461
+ // 'inputStreamSource || scriptNodeProcessor is null in stopStreaming',
462
+ // );
463
+ // } else {
464
+ // inputStreamSource.disconnect(scriptNodeProcessor);
465
+ // scriptNodeProcessor.disconnect(audioContext.destination);
466
+
467
+ // // Release the mic input so we stop showing the red recording icon in the browser
468
+ // inputStream?.getTracks().forEach((track) => track.stop());
469
+ // }
470
+
471
+ // if (socket == null) {
472
+ // console.warn('Unable to emit stop_stream because socket is null');
473
+ // } else {
474
+ // socket.emit('stop_stream', (result) => {
475
+ // console.debug('[emit result: stop_stream]', result);
476
+ // });
477
+ // }
478
+
479
+ // setStreamingStatus('stopped');
480
+ // }, [
481
+ // audioContext.destination,
482
+ // bufferedSpeechPlayer,
483
+ // inputStream,
484
+ // inputStreamSource,
485
+ // scriptNodeProcessor,
486
+ // socket,
487
+ // streamingStatus,
488
+ // ]);
489
+
490
+ // const onClearTranscriptForAll = useCallback(() => {
491
+ // if (socket != null) {
492
+ // socket.emit('clear_transcript_for_all');
493
+ // }
494
+ // }, [socket]);
495
+
496
+ // /******************************************
497
+ // * Effects
498
+ // ******************************************/
499
+
500
+ // useEffect(() => {
501
+ // if (socket == null) {
502
+ // return;
503
+ // }
504
+
505
+ // const onRoomStateUpdate = (roomState: RoomState) => {
506
+ // setRoomState(roomState);
507
+ // };
508
+
509
+ // socket.on('room_state_update', onRoomStateUpdate);
510
+
511
+ // return () => {
512
+ // socket.off('room_state_update', onRoomStateUpdate);
513
+ // };
514
+ // }, [socket]);
515
+
516
+ // useEffect(() => {
517
+ // if (socket != null) {
518
+ // const onTranslationText = (data: ServerTextData) => {
519
+ // setReceivedData((prev) => [...prev, data]);
520
+ // debug()?.receivedText(data.payload);
521
+ // };
522
+
523
+ // const onTranslationSpeech = (data: ServerSpeechData) => {
524
+ // bufferedSpeechPlayer.addAudioToBuffer(data.payload, data.sample_rate);
525
+ // };
526
+
527
+ // socket.on('translation_text', onTranslationText);
528
+ // socket.on('translation_speech', onTranslationSpeech);
529
+
530
+ // return () => {
531
+ // socket.off('translation_text', onTranslationText);
532
+ // socket.off('translation_speech', onTranslationSpeech);
533
+ // };
534
+ // }
535
+ // }, [bufferedSpeechPlayer, socket]);
536
+
537
+ // useEffect(() => {
538
+ // if (socket != null) {
539
+ // const onServerStateUpdate = (newServerState: ServerState) => {
540
+ // setServerState(newServerState);
541
+
542
+ // // If a client creates a server lock, we want to stop streaming if we're not them
543
+ // if (
544
+ // newServerState.serverLock?.isActive === true &&
545
+ // newServerState.serverLock?.clientID !== clientID &&
546
+ // streamingStatus === 'running'
547
+ // ) {
548
+ // stopStreaming();
549
+ // }
550
+
551
+ // const firstAgentNullable = newServerState.agentsCapabilities[0];
552
+ // if (agent == null && firstAgentNullable != null) {
553
+ // setAgentAndUpdateParams(firstAgentNullable);
554
+ // }
555
+ // };
556
+
557
+ // socket.on('server_state_update', onServerStateUpdate);
558
+
559
+ // return () => {
560
+ // socket.off('server_state_update', onServerStateUpdate);
561
+ // };
562
+ // }
563
+ // }, [
564
+ // agent,
565
+ // clientID,
566
+ // setAgentAndUpdateParams,
567
+ // socket,
568
+ // stopStreaming,
569
+ // streamingStatus,
570
+ // ]);
571
+
572
+ // useEffect(() => {
573
+ // if (socket != null) {
574
+ // const onServerException = (
575
+ // exceptionDataWithoutClientTime: ServerExceptionData,
576
+ // ) => {
577
+ // const exceptionData = {
578
+ // ...exceptionDataWithoutClientTime,
579
+ // timeStringClient: new Date(
580
+ // exceptionDataWithoutClientTime['timeEpochMs'],
581
+ // ).toLocaleString(),
582
+ // };
583
+
584
+ // setServerExceptions((prev) =>
585
+ // [exceptionData, ...prev].slice(0, MAX_SERVER_EXCEPTIONS_TRACKED),
586
+ // );
587
+ // console.error(
588
+ // `[server_exception] The server encountered an exception: ${exceptionData['message']}`,
589
+ // exceptionData,
590
+ // );
591
+ // };
592
+
593
+ // socket.on('server_exception', onServerException);
594
+
595
+ // return () => {
596
+ // socket.off('server_exception', onServerException);
597
+ // };
598
+ // }
599
+ // }, [socket]);
600
+
601
+ // useEffect(() => {
602
+ // if (socket != null) {
603
+ // const onClearTranscript = () => {
604
+ // setReceivedData([]);
605
+ // setTranslationSentencesAnimatedIndex(0);
606
+ // };
607
+
608
+ // socket.on('clear_transcript', onClearTranscript);
609
+
610
+ // return () => {
611
+ // socket.off('clear_transcript', onClearTranscript);
612
+ // };
613
+ // }
614
+ // }, [socket]);
615
+
616
+ // useEffect(() => {
617
+ // const onScroll = () => {
618
+ // if (isScrolledToDocumentBottom(SCROLLED_TO_BOTTOM_THRESHOLD_PX)) {
619
+ // isScrolledToBottomRef.current = true;
620
+ // return;
621
+ // }
622
+ // isScrolledToBottomRef.current = false;
623
+ // return;
624
+ // };
625
+
626
+ // document.addEventListener('scroll', onScroll);
627
+
628
+ // return () => {
629
+ // document.removeEventListener('scroll', onScroll);
630
+ // };
631
+ // }, []);
632
+
633
+ // useLayoutEffect(() => {
634
+ // if (
635
+ // lastTranslationResultRef.current != null &&
636
+ // isScrolledToBottomRef.current
637
+ // ) {
638
+ // // Scroll the div to the most recent entry
639
+ // lastTranslationResultRef.current.scrollIntoView();
640
+ // }
641
+ // // Run the effect every time data is received, so that
642
+ // // we scroll to the bottom even if we're just adding text to
643
+ // // a pre-existing chunk
644
+ // }, [receivedData]);
645
+
646
+ // useEffect(() => {
647
+ // if (!animateTextDisplay) {
648
+ // return;
649
+ // }
650
+
651
+ // if (
652
+ // translationSentencesAnimatedIndex < translationSentencesBaseTotalLength
653
+ // ) {
654
+ // const timeout = setTimeout(() => {
655
+ // setTranslationSentencesAnimatedIndex((prev) => prev + 1);
656
+ // debug()?.startRenderText();
657
+ // }, TYPING_ANIMATION_DELAY_MS);
658
+
659
+ // return () => clearTimeout(timeout);
660
+ // } else {
661
+ // debug()?.endRenderText();
662
+ // }
663
+ // }, [
664
+ // animateTextDisplay,
665
+ // translationSentencesAnimatedIndex,
666
+ // translationSentencesBaseTotalLength,
667
+ // ]);
668
+
669
+ // /******************************************
670
+ // * Sub-components
671
+ // ******************************************/
672
+
673
+ // const volumeSliderNode = (
674
+ // <Stack
675
+ // spacing={2}
676
+ // direction="row"
677
+ // sx={{mb: 1, width: '100%'}}
678
+ // alignItems="center">
679
+ // <VolumeDown color="primary" />
680
+ // <Slider
681
+ // aria-label="Volume"
682
+ // defaultValue={1}
683
+ // scale={getGainScaledValue}
684
+ // min={0}
685
+ // max={3}
686
+ // step={0.1}
687
+ // marks={[
688
+ // {value: 0, label: '0%'},
689
+ // {value: 1, label: '100%'},
690
+ // {value: 2, label: '400%'},
691
+ // {value: 3, label: '700%'},
692
+ // ]}
693
+ // valueLabelFormat={(value) => `${(value * 100).toFixed(0)}%`}
694
+ // valueLabelDisplay="auto"
695
+ // value={gain}
696
+ // onChange={(_event: Event, newValue: number | number[]) => {
697
+ // if (typeof newValue === 'number') {
698
+ // const scaledGain = getGainScaledValue(newValue);
699
+ // // We want the actual gain node to use the scaled value
700
+ // bufferedSpeechPlayer.setGain(scaledGain);
701
+ // // But we want react state to keep track of the non-scaled value
702
+ // setGain(newValue);
703
+ // } else {
704
+ // console.error(
705
+ // `[volume slider] Unexpected non-number value: ${newValue}`,
706
+ // );
707
+ // }
708
+ // }}
709
+ // />
710
+ // <VolumeUp color="primary" />
711
+ // </Stack>
712
+ // );
713
+
714
+ // const xrDialogComponent = (
715
+ // <XRDialog
716
+ // animateTextDisplay={
717
+ // animateTextDisplay &&
718
+ // translationSentencesAnimatedIndex == translationSentencesBaseTotalLength
719
+ // }
720
+ // bufferedSpeechPlayer={bufferedSpeechPlayer}
721
+ // translationSentences={translationSentences}
722
+ // roomState={roomState}
723
+ // roomID={roomID}
724
+ // startStreaming={startStreaming}
725
+ // stopStreaming={stopStreaming}
726
+ // debugParam={debugParam}
727
+ // onARHidden={() => {
728
+ // setAnimateTextDisplay(urlParams.animateTextDisplay);
729
+ // }}
730
+ // onARVisible={() => setAnimateTextDisplay(false)}
731
+ // />
732
+ // );
733
+
734
+ // return (
735
+ // <div className="app-wrapper-sra">
736
+ // <Box
737
+ // // eslint-disable-next-line @typescript-eslint/ban-ts-comment
738
+ // // @ts-ignore Not sure why it's complaining about complexity here
739
+ // sx={{width: '100%', maxWidth: '660px', minWidth: '320px'}}>
740
+ // <div className="main-container-sra">
741
+ // <div className="top-section-sra horizontal-padding-sra">
742
+ // <div className="header-container-sra">
743
+ // <img
744
+ // src={seamlessLogoUrl}
745
+ // className="header-icon-sra"
746
+ // alt="Seamless Translation Logo"
747
+ // height={24}
748
+ // width={24}
749
+ // />
750
+
751
+ // <div>
752
+ // <Typography variant="h1" sx={{color: '#65676B'}}>
753
+ // Seamless Translation
754
+ // </Typography>
755
+ // </div>
756
+ // </div>
757
+ // <div className="header-container-sra">
758
+ // <div>
759
+ // <Typography variant="body2" sx={{color: '#65676B'}}>
760
+ // Welcome! This space is limited to one speaker at a time.
761
+ // If using the live HF space, sharing room code to listeners on another
762
+ // IP address may not work because it's running on different replicas.
763
+ // Use headphones if you are both speaker and listener to prevent feedback.
764
+ // <br/>
765
+ // If max speakers reached, please duplicate the space <a target="_blank" rel="noopener noreferrer" href="https://huggingface.co/spaces/facebook/seamless-streaming?duplicate=true">here</a>.
766
+ // In your duplicated space, join a room as speaker or listener (or both),
767
+ // and share the room code to invite listeners.
768
+ // <br/>
769
+ // Check out the seamless_communication <a target="_blank" rel="noopener noreferrer" href="https://github.com/facebookresearch/seamless_communication/tree/main">README</a> for more information.
770
+ // <br/>
771
+ // SeamlessStreaming model is a research model and is not released
772
+ // for production deployment. It is important to use a microphone with
773
+ // noise cancellation (for e.g. a smartphone), otherwise you may see model hallucination on noises.
774
+ // It works best if you pause every couple of sentences, or you may wish adjust the VAD threshold
775
+ // in the model config. The real-time performance will degrade
776
+ // if you try streaming multiple speakers at the same time.
777
+ // </Typography>
778
+ // </div>
779
+ // </div>
780
+ // <Stack spacing="22px" direction="column">
781
+ // <Box>
782
+ // <RoomConfig
783
+ // roomState={roomState}
784
+ // serverState={serverState}
785
+ // streamingStatus={streamingStatus}
786
+ // onJoinRoomOrUpdateRoles={() => {
787
+ // // If the user has switched from speaker to listener we need to tell the
788
+ // // player to play eagerly, since currently the listener doesn't have any stop/start controls
789
+ // bufferedSpeechPlayer.start();
790
+ // }}
791
+ // />
792
+
793
+ // {isListener && !isSpeaker && (
794
+ // <Box
795
+ // sx={{
796
+ // paddingX: 6,
797
+ // paddingBottom: 2,
798
+ // marginY: 2,
799
+ // display: 'flex',
800
+ // flexDirection: 'column',
801
+ // alignItems: 'center',
802
+ // }}>
803
+ // {volumeSliderNode}
804
+ // </Box>
805
+ // )}
806
+ // </Box>
807
+
808
+ // {isSpeaker && (
809
+ // <>
810
+ // <Divider />
811
+
812
+ // <Stack spacing="12px" direction="column">
813
+ // <FormLabel id="output-modes-radio-group-label">
814
+ // Model
815
+ // </FormLabel>
816
+ // <FormControl
817
+ // disabled={
818
+ // streamFixedConfigOptionsDisabled ||
819
+ // agentsCapabilities.length === 0
820
+ // }
821
+ // fullWidth
822
+ // sx={{minWidth: '14em'}}>
823
+ // <InputLabel id="model-selector-input-label">
824
+ // Model
825
+ // </InputLabel>
826
+ // <Select
827
+ // labelId="model-selector-input-label"
828
+ // label="Model"
829
+ // onChange={(e: SelectChangeEvent) => {
830
+ // const newAgent =
831
+ // agentsCapabilities.find(
832
+ // (agent) => e.target.value === agent.name,
833
+ // ) ?? null;
834
+ // if (newAgent == null) {
835
+ // console.error(
836
+ // 'Unable to find agent with name',
837
+ // e.target.value,
838
+ // );
839
+ // }
840
+ // setAgentAndUpdateParams(newAgent);
841
+ // }}
842
+ // value={model ?? ''}>
843
+ // {agentsCapabilities.map((agent) => (
844
+ // <MenuItem value={agent.name} key={agent.name}>
845
+ // {agent.name}
846
+ // </MenuItem>
847
+ // ))}
848
+ // </Select>
849
+ // </FormControl>
850
+
851
+ // </Stack>
852
+
853
+ // <Stack spacing={0.5}>
854
+ // <FormLabel id="output-modes-radio-group-label">
855
+ // Output
856
+ // </FormLabel>
857
+
858
+ // <Box sx={{paddingTop: 2, paddingBottom: 1}}>
859
+ // <FormControl fullWidth sx={{minWidth: '14em'}}>
860
+ // <InputLabel id="target-selector-input-label">
861
+ // Target Language
862
+ // </InputLabel>
863
+ // <Select
864
+ // labelId="target-selector-input-label"
865
+ // label="Target Language"
866
+ // onChange={(e: SelectChangeEvent) => {
867
+ // setTargetLang(e.target.value);
868
+ // onSetDynamicConfig({
869
+ // targetLanguage: e.target.value,
870
+ // });
871
+ // }}
872
+ // value={targetLang ?? ''}>
873
+ // {currentAgent?.targetLangs.map((langCode) => (
874
+ // <MenuItem value={langCode} key={langCode}>
875
+ // {getLanguageFromThreeLetterCode(langCode) != null
876
+ // ? `${getLanguageFromThreeLetterCode(
877
+ // langCode,
878
+ // )} (${langCode})`
879
+ // : langCode}
880
+ // </MenuItem>
881
+ // ))}
882
+ // </Select>
883
+ // </FormControl>
884
+ // </Box>
885
+
886
+ // <Grid container>
887
+ // <Grid item xs={12} sm={4}>
888
+ // <FormControl
889
+ // disabled={streamFixedConfigOptionsDisabled}>
890
+ // <RadioGroup
891
+ // aria-labelledby="output-modes-radio-group-label"
892
+ // value={outputMode}
893
+ // onChange={(e) =>
894
+ // setOutputMode(
895
+ // e.target.value as SupportedOutputMode,
896
+ // )
897
+ // }
898
+ // name="output-modes-radio-buttons-group">
899
+ // {
900
+ // // TODO: Use supported modalities from agentCapabilities
901
+ // SUPPORTED_OUTPUT_MODES.map(({value, label}) => (
902
+ // <FormControlLabel
903
+ // key={value}
904
+ // value={value}
905
+ // control={<Radio />}
906
+ // label={label}
907
+ // />
908
+ // ))
909
+ // }
910
+ // </RadioGroup>
911
+ // </FormControl>
912
+ // </Grid>
913
+
914
+ // <Grid item xs={12} sm={8}>
915
+ // <Stack
916
+ // direction="column"
917
+ // spacing={1}
918
+ // alignItems="flex-start"
919
+ // sx={{flexGrow: 1}}>
920
+ // {currentAgent?.dynamicParams?.includes(
921
+ // 'expressive',
922
+ // ) && (
923
+ // <FormControlLabel
924
+ // control={
925
+ // <Switch
926
+ // checked={enableExpressive ?? false}
927
+ // onChange={(
928
+ // event: React.ChangeEvent<HTMLInputElement>,
929
+ // ) => {
930
+ // const newValue = event.target.checked;
931
+ // setEnableExpressive(newValue);
932
+ // onSetDynamicConfig({
933
+ // expressive: newValue,
934
+ // });
935
+ // }}
936
+ // />
937
+ // }
938
+ // label="Expressive"
939
+ // />
940
+ // )}
941
+
942
+ // {isListener && (
943
+ // <Box
944
+ // sx={{
945
+ // flexGrow: 1,
946
+ // paddingX: 1.5,
947
+ // paddingY: 1.5,
948
+ // width: '100%',
949
+ // }}>
950
+ // {volumeSliderNode}
951
+ // </Box>
952
+ // )}
953
+ // </Stack>
954
+ // </Grid>
955
+ // </Grid>
956
+ // </Stack>
957
+
958
+ // <Stack
959
+ // direction="row"
960
+ // spacing={2}
961
+ // justifyContent="space-between">
962
+ // <Box sx={{flex: 1}}>
963
+ // <FormControl disabled={streamFixedConfigOptionsDisabled}>
964
+ // <FormLabel id="input-source-radio-group-label">
965
+ // Input Source
966
+ // </FormLabel>
967
+ // <RadioGroup
968
+ // aria-labelledby="input-source-radio-group-label"
969
+ // value={inputSource}
970
+ // onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
971
+ // setInputSource(
972
+ // e.target.value as SupportedInputSource,
973
+ // )
974
+ // }
975
+ // name="input-source-radio-buttons-group">
976
+ // {SUPPORTED_INPUT_SOURCES.map(({label, value}) => (
977
+ // <FormControlLabel
978
+ // key={value}
979
+ // value={value}
980
+ // control={<Radio />}
981
+ // label={label}
982
+ // />
983
+ // ))}
984
+ // </RadioGroup>
985
+ // </FormControl>
986
+ // </Box>
987
+
988
+ // <Box sx={{flex: 1, flexGrow: 2}}>
989
+ // <FormControl disabled={streamFixedConfigOptionsDisabled}>
990
+ // <FormLabel>Options</FormLabel>
991
+ // <FormControlLabel
992
+ // control={
993
+ // <Checkbox
994
+ // checked={
995
+ // enableNoiseSuppression ??
996
+ // AUDIO_STREAM_DEFAULTS[inputSource]
997
+ // .noiseSuppression
998
+ // }
999
+ // onChange={(
1000
+ // event: React.ChangeEvent<HTMLInputElement>,
1001
+ // ) =>
1002
+ // setEnableNoiseSuppression(event.target.checked)
1003
+ // }
1004
+ // />
1005
+ // }
1006
+ // label="Noise Suppression"
1007
+ // />
1008
+ // <FormControlLabel
1009
+ // control={
1010
+ // <Checkbox
1011
+ // checked={
1012
+ // enableEchoCancellation ??
1013
+ // AUDIO_STREAM_DEFAULTS[inputSource]
1014
+ // .echoCancellation
1015
+ // }
1016
+ // onChange={(
1017
+ // event: React.ChangeEvent<HTMLInputElement>,
1018
+ // ) =>
1019
+ // setEnableEchoCancellation(event.target.checked)
1020
+ // }
1021
+ // />
1022
+ // }
1023
+ // label="Echo Cancellation (not recommended)"
1024
+ // />
1025
+ // <FormControlLabel
1026
+ // control={
1027
+ // <Checkbox
1028
+ // checked={serverDebugFlag}
1029
+ // onChange={(
1030
+ // event: React.ChangeEvent<HTMLInputElement>,
1031
+ // ) => setServerDebugFlag(event.target.checked)}
1032
+ // />
1033
+ // }
1034
+ // label="Enable Server Debugging"
1035
+ // />
1036
+ // </FormControl>
1037
+ // </Box>
1038
+ // </Stack>
1039
+
1040
+ // {isSpeaker &&
1041
+ // isListener &&
1042
+ // inputSource === 'userMedia' &&
1043
+ // !enableEchoCancellation &&
1044
+ // gain !== 0 && (
1045
+ // <div>
1046
+ // <Alert severity="warning" icon={<HeadphonesIcon />}>
1047
+ // Headphones required to prevent feedback.
1048
+ // </Alert>
1049
+ // </div>
1050
+ // )}
1051
+
1052
+ // {isSpeaker && enableEchoCancellation && (
1053
+ // <div>
1054
+ // <Alert severity="warning">
1055
+ // We don't recommend using echo cancellation as it may
1056
+ // distort the input audio. If possible, use headphones and
1057
+ // disable echo cancellation instead.
1058
+ // </Alert>
1059
+ // </div>
1060
+ // )}
1061
+
1062
+ // <Stack direction="row" spacing={2}>
1063
+ // {streamingStatus === 'stopped' ? (
1064
+ // <Button
1065
+ // variant="contained"
1066
+ // onClick={startStreaming}
1067
+ // disabled={
1068
+ // roomID == null ||
1069
+ // // Prevent users from starting streaming if there is a server lock with an active session
1070
+ // (serverState?.serverLock?.isActive === true &&
1071
+ // serverState.serverLock.clientID !== clientID)
1072
+ // }>
1073
+ // {buttonLabelMap[streamingStatus]}
1074
+ // </Button>
1075
+ // ) : (
1076
+ // <Button
1077
+ // variant="contained"
1078
+ // color={
1079
+ // streamingStatus === 'running' ? 'error' : 'primary'
1080
+ // }
1081
+ // disabled={
1082
+ // streamingStatus === 'starting' || roomID == null
1083
+ // }
1084
+ // onClick={stopStreaming}>
1085
+ // {buttonLabelMap[streamingStatus]}
1086
+ // </Button>
1087
+ // )}
1088
+
1089
+ // <Box>
1090
+ // <Button
1091
+ // variant="contained"
1092
+ // aria-label={muted ? 'Unmute' : 'Mute'}
1093
+ // color={muted ? 'info' : 'primary'}
1094
+ // onClick={() => setMuted((prev) => !prev)}
1095
+ // sx={{
1096
+ // borderRadius: 100,
1097
+ // paddingX: 0,
1098
+ // minWidth: '36px',
1099
+ // }}>
1100
+ // {muted ? <MicOff /> : <Mic />}
1101
+ // </Button>
1102
+ // </Box>
1103
+
1104
+ // {roomID == null ? null : (
1105
+ // <Box
1106
+ // sx={{
1107
+ // flexGrow: 1,
1108
+ // display: 'flex',
1109
+ // justifyContent: 'flex-end',
1110
+ // }}>
1111
+ // {xrDialogComponent}
1112
+ // </Box>
1113
+ // )}
1114
+ // </Stack>
1115
+
1116
+ // {serverExceptions.length > 0 && (
1117
+ // <div>
1118
+ // <Alert severity="error">
1119
+ // {`The server encountered an exception. See the browser console for details. You may need to refresh the page to continue using the app.`}
1120
+ // </Alert>
1121
+ // </div>
1122
+ // )}
1123
+ // {serverState != null && hasMaxSpeakers && (
1124
+ // <div>
1125
+ // <Alert severity="error">
1126
+ // {`Maximum number of speakers reached. Please try again at a later time.`}
1127
+ // </Alert>
1128
+ // </div>
1129
+ // )}
1130
+ // {serverState != null &&
1131
+ // serverState.totalActiveTranscoders >=
1132
+ // TOTAL_ACTIVE_TRANSCODER_WARNING_THRESHOLD && (
1133
+ // <div>
1134
+ // <Alert severity="warning">
1135
+ // {`The server currently has ${serverState?.totalActiveTranscoders} active streaming sessions. Performance may be degraded.`}
1136
+ // </Alert>
1137
+ // </div>
1138
+ // )}
1139
+
1140
+ // {serverState?.serverLock != null &&
1141
+ // serverState.serverLock.clientID !== clientID && (
1142
+ // <div>
1143
+ // <Alert severity="warning">
1144
+ // {`The server is currently locked. Priority will be given to that client when they are streaming, and your streaming session may be halted abruptly.`}
1145
+ // </Alert>
1146
+ // </div>
1147
+ // )}
1148
+ // </>
1149
+ // )}
1150
+ // </Stack>
1151
+
1152
+ // {isListener && !isSpeaker && (
1153
+ // <Box sx={{marginBottom: 1, marginTop: 2}}>
1154
+ // {xrDialogComponent}
1155
+ // </Box>
1156
+ // )}
1157
+ // </div>
1158
+
1159
+ // {debugParam && roomID != null && <DebugSection />}
1160
+
1161
+ // <div className="translation-text-container-sra horizontal-padding-sra">
1162
+ // <Stack
1163
+ // direction="row"
1164
+ // spacing={2}
1165
+ // sx={{mb: '16px', alignItems: 'center'}}>
1166
+ // <Typography variant="h1" sx={{fontWeight: 700, flexGrow: 1}}>
1167
+ // Transcript
1168
+ // </Typography>
1169
+ // {isSpeaker && (
1170
+ // <Button
1171
+ // variant="text"
1172
+ // size="small"
1173
+ // onClick={onClearTranscriptForAll}>
1174
+ // Clear Transcript for All
1175
+ // </Button>
1176
+ // )}
1177
+ // </Stack>
1178
+ // <Stack direction="row">
1179
+ // <div className="translation-text-sra">
1180
+ // {translationSentencesWithEmptyStartingString.map(
1181
+ // (sentence, index, arr) => {
1182
+ // const isLast = index === arr.length - 1;
1183
+ // const maybeRef = isLast
1184
+ // ? {ref: lastTranslationResultRef}
1185
+ // : {};
1186
+ // return (
1187
+ // <div className="text-chunk-sra" key={index} {...maybeRef}>
1188
+ // <Typography variant="body1">
1189
+ // {sentence}
1190
+ // {animateTextDisplay && isLast && (
1191
+ // <Blink
1192
+ // intervalMs={CURSOR_BLINK_INTERVAL_MS}
1193
+ // shouldBlink={
1194
+ // (roomState?.activeTranscoders ?? 0) > 0
1195
+ // }>
1196
+ // <Typography
1197
+ // component="span"
1198
+ // variant="body1"
1199
+ // sx={{
1200
+ // display: 'inline-block',
1201
+ // transform: 'scaleY(1.25) translateY(-1px)',
1202
+ // }}>
1203
+ // {'|'}
1204
+ // </Typography>
1205
+ // </Blink>
1206
+ // )}
1207
+ // </Typography>
1208
+ // </div>
1209
+ // );
1210
+ // },
1211
+ // )}
1212
+ // </div>
1213
+ // </Stack>
1214
+ // </div>
1215
+ // </div>
1216
+ // </Box>
1217
+ // </div>
1218
+ // );
1219
+ // }
1220
+
1221
+
1222
+
1223
+
1224
  import {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
1225
  import Button from '@mui/material/Button';
1226
  import Typography from '@mui/material/Typography';
 
1229
  import Select, {SelectChangeEvent} from '@mui/material/Select';
1230
  import MenuItem from '@mui/material/MenuItem';
1231
  import Stack from '@mui/material/Stack';
1232
+ import seamlessLogoUrl from './assets/illy.svg';
1233
  import {
1234
  AgentCapabilities,
1235
  BaseResponse,
 
1967
  src={seamlessLogoUrl}
1968
  className="header-icon-sra"
1969
  alt="Seamless Translation Logo"
1970
+ height={150}
1971
+ width={225}
1972
  />
1973
 
1974
  <div>
1975
+ <Typography variant="h1" sx={{color: '#800020'}}>
1976
+ Illy's translator
1977
+ </Typography>
1978
+ <Typography variant="body2" sx={{color: '#800020'}}>
1979
+ <span style={{ fontStyle: 'italic' }}>
1980
+ Natale 2023
1981
+ </span>
1982
  </Typography>
1983
  </div>
1984
  </div>
1985
  <div className="header-container-sra">
1986
  <div>
1987
  <Typography variant="body2" sx={{color: '#65676B'}}>
1988
+ Hey <strong style={{ fontWeight: 'bold' }}>Illy</strong>, <strong style={{ fontWeight: 'bold' }}>it's lovely to see you!</strong>
 
 
 
1989
  <br/>
1990
+ You can use this platform to translate from/to Italian and many some other languages.
1991
+ <br/>
1992
+ Use headphones if you are both speaker and listener to prevent feedback.
1993
  <br/>
 
1994
  <br/>
1995
+ <a target="_blank" rel="noopener noreferrer" href="https://ai.meta.com/research/seamless-communication/">SeamlessStreaming</a> is
1996
+ a research model and streaming quality works best if you pause
1997
+ every couple of sentences. The real-time performance will degrade
 
 
1998
  if you try streaming multiple speakers at the same time.
1999
+ <br/>
2000
+ <br/>
2001
+ Let's try!
2002
  </Typography>
2003
  </div>
2004
  </div>
2005
  <Stack spacing="22px" direction="column">
2006
  <Box>
2007
+ { <RoomConfig
2008
  roomState={roomState}
2009
  serverState={serverState}
2010
  streamingStatus={streamingStatus}
 
2013
  // player to play eagerly, since currently the listener doesn't have any stop/start controls
2014
  bufferedSpeechPlayer.start();
2015
  }}
2016
+ /> }
2017
 
2018
  {isListener && !isSpeaker && (
2019
  <Box
 
2035
  <Divider />
2036
 
2037
  <Stack spacing="12px" direction="column">
2038
+ {/* <FormLabel id="output-modes-radio-group-label">
2039
  Model
2040
+ </FormLabel> */}
2041
  <FormControl
2042
  disabled={
2043
  streamFixedConfigOptionsDisabled ||
 
2045
  }
2046
  fullWidth
2047
  sx={{minWidth: '14em'}}>
2048
+ {/* <InputLabel id="model-selector-input-label">
2049
  Model
2050
+ </InputLabel> */}
2051
+ {/* <Select
2052
  labelId="model-selector-input-label"
2053
  label="Model"
2054
  onChange={(e: SelectChangeEvent) => {
 
2070
  {agent.name}
2071
  </MenuItem>
2072
  ))}
2073
+ </Select> */}
2074
  </FormControl>
2075
 
2076
  </Stack>
2077
 
2078
  <Stack spacing={0.5}>
2079
  <FormLabel id="output-modes-radio-group-label">
2080
+ Illy, can you please select the target language?
2081
  </FormLabel>
2082
 
2083
  <Box sx={{paddingTop: 2, paddingBottom: 1}}>
 
2142
  spacing={1}
2143
  alignItems="flex-start"
2144
  sx={{flexGrow: 1}}>
2145
+ {/* {currentAgent?.dynamicParams?.includes(
2146
  'expressive',
2147
  ) && (
2148
  <FormControlLabel
 
2162
  }
2163
  label="Expressive"
2164
  />
2165
+ )} */}
2166
 
2167
  {isListener && (
2168
  <Box
 
2186
  justifyContent="space-between">
2187
  <Box sx={{flex: 1}}>
2188
  <FormControl disabled={streamFixedConfigOptionsDisabled}>
2189
+ {/* <FormLabel id="input-source-radio-group-label">
2190
  Input Source
2191
+ </FormLabel> */}
2192
+ {/* <RadioGroup
2193
  aria-labelledby="input-source-radio-group-label"
2194
  value={inputSource}
2195
  onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
 
2205
  control={<Radio />}
2206
  label={label}
2207
  />
2208
+ ))} */}
2209
+ {/* </RadioGroup> */}
2210
  </FormControl>
2211
  </Box>
2212
 
 
2442
  </div>
2443
  );
2444
  }
2445
+