Justin Haaheim
commited on
Commit
·
42d9c16
1
Parent(s):
a95bed7
[Streaming] Pre-launch cleanup of streaming-react-app and seamless_server (#170)
Browse files- seamless_server/app_pubsub.py +5 -10
- seamless_server/src/connection_tracker.py +0 -64
- streaming-react-app/README.md +6 -57
- streaming-react-app/package.json +1 -5
- streaming-react-app/public/vite.svg +0 -1
- streaming-react-app/src/RoomConfig.tsx +0 -1
- streaming-react-app/src/SeamlessLogo.tsx +0 -33
- streaming-react-app/src/StreamingInterface.css +0 -6
- streaming-react-app/src/StreamingInterface.tsx +4 -17
- streaming-react-app/src/createBufferedSpeechPlayer.ts +1 -5
- streaming-react-app/src/generateNewRoomID.ts +0 -4
- streaming-react-app/src/getTranslationSentencesFromReceivedData.ts +0 -1
- streaming-react-app/src/index.css +0 -0
- streaming-react-app/src/main.tsx +0 -1
- streaming-react-app/vite.config.ts +5 -0
seamless_server/app_pubsub.py
CHANGED
|
@@ -622,7 +622,6 @@ async def configure_stream(sid, config):
|
|
| 622 |
await emit_server_state_update()
|
| 623 |
|
| 624 |
return {"status": "ok", "message": "server_ready"}
|
| 625 |
-
# await sio.emit("server_ready", None, to=sid)
|
| 626 |
|
| 627 |
|
| 628 |
# The config here is a partial config, meaning it may not contain all the config values -- only the ones the user
|
|
@@ -636,14 +635,10 @@ async def set_dynamic_config(
|
|
| 636 |
):
|
| 637 |
session_data = await get_session_data(sid)
|
| 638 |
|
| 639 |
-
# client_id = None
|
| 640 |
member = None
|
| 641 |
-
# room = None
|
| 642 |
|
| 643 |
if session_data:
|
| 644 |
-
# client_id = session_data.get("client_id")
|
| 645 |
member = session_data.get("member_object")
|
| 646 |
-
# room = session_data.get("room_object")
|
| 647 |
|
| 648 |
if member:
|
| 649 |
new_dynamic_config = {
|
|
@@ -661,8 +656,6 @@ async def set_dynamic_config(
|
|
| 661 |
@sio.event
|
| 662 |
@catch_and_log_exceptions_for_sio_event_handlers
|
| 663 |
async def incoming_audio(sid, blob):
|
| 664 |
-
# logger.info(f"[event: incoming_audio] {sid}")
|
| 665 |
-
|
| 666 |
session_data = await get_session_data(sid)
|
| 667 |
|
| 668 |
client_id = None
|
|
@@ -721,9 +714,11 @@ async def incoming_audio(sid, blob):
|
|
| 721 |
blob, dynamic_config=member.transcoder_dynamic_config
|
| 722 |
)
|
| 723 |
|
| 724 |
-
#
|
| 725 |
-
#
|
| 726 |
-
#
|
|
|
|
|
|
|
| 727 |
events = get_transcoder_output_events(member.transcoder)
|
| 728 |
logger.debug(f"[incoming_audio] transcoder output events: {len(events)}")
|
| 729 |
|
|
|
|
| 622 |
await emit_server_state_update()
|
| 623 |
|
| 624 |
return {"status": "ok", "message": "server_ready"}
|
|
|
|
| 625 |
|
| 626 |
|
| 627 |
# The config here is a partial config, meaning it may not contain all the config values -- only the ones the user
|
|
|
|
| 635 |
):
|
| 636 |
session_data = await get_session_data(sid)
|
| 637 |
|
|
|
|
| 638 |
member = None
|
|
|
|
| 639 |
|
| 640 |
if session_data:
|
|
|
|
| 641 |
member = session_data.get("member_object")
|
|
|
|
| 642 |
|
| 643 |
if member:
|
| 644 |
new_dynamic_config = {
|
|
|
|
| 656 |
@sio.event
|
| 657 |
@catch_and_log_exceptions_for_sio_event_handlers
|
| 658 |
async def incoming_audio(sid, blob):
|
|
|
|
|
|
|
| 659 |
session_data = await get_session_data(sid)
|
| 660 |
|
| 661 |
client_id = None
|
|
|
|
| 714 |
blob, dynamic_config=member.transcoder_dynamic_config
|
| 715 |
)
|
| 716 |
|
| 717 |
+
# Send back any available model output
|
| 718 |
+
# NOTE: In theory it would make sense remove this from the incoming_audio handler and
|
| 719 |
+
# handle this in a dedicated thread that checks for output and sends it right away,
|
| 720 |
+
# but in practice for our limited demo use cases this approach didn't add noticeable
|
| 721 |
+
# latency, so we're keeping it simple for now.
|
| 722 |
events = get_transcoder_output_events(member.transcoder)
|
| 723 |
logger.debug(f"[incoming_audio] transcoder output events: {len(events)}")
|
| 724 |
|
seamless_server/src/connection_tracker.py
DELETED
|
@@ -1,64 +0,0 @@
|
|
| 1 |
-
from logging import Logger
|
| 2 |
-
import time
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
class StreamingConnectionInfo:
|
| 6 |
-
def __init__(self, address, active_connections, latest_message_received_timestamp):
|
| 7 |
-
self.address = address
|
| 8 |
-
self.active_connections = active_connections
|
| 9 |
-
self.latest_message_received_timestamp = latest_message_received_timestamp
|
| 10 |
-
|
| 11 |
-
def __repr__(self):
|
| 12 |
-
return str(self)
|
| 13 |
-
|
| 14 |
-
def __str__(self):
|
| 15 |
-
return str(
|
| 16 |
-
{
|
| 17 |
-
"address": self.address,
|
| 18 |
-
"active_connections": self.active_connections,
|
| 19 |
-
"latest_message_received_timestamp": self.latest_message_received_timestamp,
|
| 20 |
-
}
|
| 21 |
-
)
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class ConnectionTracker:
|
| 25 |
-
def __init__(self, logger: Logger):
|
| 26 |
-
self.connections = dict()
|
| 27 |
-
self.logger = logger
|
| 28 |
-
|
| 29 |
-
def __str__(self):
|
| 30 |
-
return str(self.connections)
|
| 31 |
-
|
| 32 |
-
def add_connection(self, address):
|
| 33 |
-
if address not in self.connections:
|
| 34 |
-
self.connections[address] = StreamingConnectionInfo(address, 1, time.time())
|
| 35 |
-
else:
|
| 36 |
-
self.connections[address].active_connections += 1
|
| 37 |
-
self.connections[address].latest_message_received_timestamp = time.time()
|
| 38 |
-
|
| 39 |
-
def log_recent_message(self, address):
|
| 40 |
-
if address in self.connections:
|
| 41 |
-
self.connections[address].latest_message_received_timestamp = time.time()
|
| 42 |
-
else:
|
| 43 |
-
self.logger.warning(
|
| 44 |
-
f"Address {address} not found in connection tracker when attempting to log recent message"
|
| 45 |
-
)
|
| 46 |
-
|
| 47 |
-
def remove_connection(self, address):
|
| 48 |
-
if address in self.connections:
|
| 49 |
-
self.connections[address].active_connections -= 1
|
| 50 |
-
if self.connections[address].active_connections < 0:
|
| 51 |
-
self.logger.warning(
|
| 52 |
-
f"Address {address} has negative active connections ({self.connections[address].active_connections})"
|
| 53 |
-
)
|
| 54 |
-
if self.connections[address].active_connections <= 0:
|
| 55 |
-
del self.connections[address]
|
| 56 |
-
else:
|
| 57 |
-
self.logger.warning(
|
| 58 |
-
f"Address {address} not found in connection tracker when attempting to remove it"
|
| 59 |
-
)
|
| 60 |
-
|
| 61 |
-
def get_active_connection_count(self):
|
| 62 |
-
return sum(
|
| 63 |
-
[connection.active_connections for connection in self.connections.values()]
|
| 64 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
streaming-react-app/README.md
CHANGED
|
@@ -2,64 +2,13 @@
|
|
| 2 |
|
| 3 |
## Getting Started
|
| 4 |
|
| 5 |
-
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
You can provide URL parameters in order to change the behavior of the app. Those are documented in [URLParams.ts](src/URLParams.ts).
|
| 10 |
-
|
| 11 |
-
# Vite Information: React + TypeScript + Vite
|
| 12 |
-
|
| 13 |
-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
| 14 |
-
|
| 15 |
-
Currently, two official plugins are available:
|
| 16 |
-
|
| 17 |
-
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
| 18 |
-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
| 19 |
-
|
| 20 |
-
## Expanding the ESLint configuration
|
| 21 |
-
|
| 22 |
-
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
| 23 |
-
|
| 24 |
-
- Configure the top-level `parserOptions` property like this:
|
| 25 |
-
|
| 26 |
-
```js
|
| 27 |
-
parserOptions: {
|
| 28 |
-
ecmaVersion: 'latest',
|
| 29 |
-
sourceType: 'module',
|
| 30 |
-
project: ['./tsconfig.json', './tsconfig.node.json'],
|
| 31 |
-
tsconfigRootDir: __dirname,
|
| 32 |
-
},
|
| 33 |
-
```
|
| 34 |
|
| 35 |
-
|
| 36 |
-
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
| 37 |
-
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
1. Acquire AWS credentials (not needed if already on an EC2 instance with permissions)
|
| 42 |
-
|
| 43 |
-
On your local mac use the following command.
|
| 44 |
-
|
| 45 |
-
```
|
| 46 |
-
eval $(corp_cloud aws get-creds 790537050551)
|
| 47 |
-
```
|
| 48 |
-
|
| 49 |
-
2. Deploy to AWS
|
| 50 |
-
|
| 51 |
-
Build the react and copy the contents of [dist](dist) folder to s3 bucket and then invalidate the cloudfront (CDN) cache. Note step 2 has been automated using `yarn deploy_dev`
|
| 52 |
-
|
| 53 |
-
To deploy to the (old) seamless-vc s3 bucket:
|
| 54 |
-
|
| 55 |
-
```
|
| 56 |
-
yarn build:dev_vc
|
| 57 |
-
yarn deploy_dev_vc
|
| 58 |
-
```
|
| 59 |
-
|
| 60 |
-
To deploy to the (new) seamless-vr terraform-based s3 bucket:
|
| 61 |
|
| 62 |
-
|
| 63 |
-
yarn build:dev_vr
|
| 64 |
-
yarn deploy_dev_vr
|
| 65 |
-
```
|
|
|
|
| 2 |
|
| 3 |
## Getting Started
|
| 4 |
|
| 5 |
+
This project uses the [Yarn Package Manager](https://yarnpkg.com/).
|
| 6 |
|
| 7 |
+
1. `yarn` - Install project dependencies
|
| 8 |
+
2. `yarn run dev` - Run the app with a development server that supports hot module reloading
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
NOTE: You will either need to provide the server URL via environment variable (you can use the `.env` file for this) or via a url param when you load the react app (example: `http://localhost:5173/?serverURL=localhost:8000`)
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
## URL Parameters
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
You can provide URL parameters in order to change the behavior of the app. Those are documented in [URLParams.ts](src/URLParams.ts).
|
|
|
|
|
|
|
|
|
streaming-react-app/package.json
CHANGED
|
@@ -6,16 +6,12 @@
|
|
| 6 |
"scripts": {
|
| 7 |
"dev": "vite --host --strictPort",
|
| 8 |
"build": "vite build",
|
| 9 |
-
"build:dev_vr": "yarn build --mode deploy_dev_vr",
|
| 10 |
-
"build:dev_vc": "yarn build --mode deploy_dev_vc",
|
| 11 |
"preview": "vite preview",
|
| 12 |
"clean:node-modules": "rm -rf node_modules/",
|
| 13 |
"ts-check": "tsc --noEmit",
|
| 14 |
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
| 15 |
"prettier-check": "cd ../ && yarn run prettier-base --check streaming-react-app",
|
| 16 |
-
"signal": "concurrently --names \"TS,LINT,PRETTIER\" -c \"bgBlack.bold,bgRed.bold,bgCyan.bold\" \"yarn run ts-check\" \"yarn run lint\" \"yarn run prettier-check\""
|
| 17 |
-
"deploy_dev_vr": "aws s3 sync dist/ s3://dev-seamless-vr-www && aws cloudfront create-invalidation --distribution-id E38ZTD4R79FGYF --paths \"/*\"",
|
| 18 |
-
"deploy_dev_vc": "aws s3 sync dist/ s3://seamless-vc.dev.metademolab.com && aws cloudfront create-invalidation --distribution-id E29D1W2ORBP77G --paths \"/*\""
|
| 19 |
},
|
| 20 |
"dependencies": {
|
| 21 |
"@emotion/react": "11.11.1",
|
|
|
|
| 6 |
"scripts": {
|
| 7 |
"dev": "vite --host --strictPort",
|
| 8 |
"build": "vite build",
|
|
|
|
|
|
|
| 9 |
"preview": "vite preview",
|
| 10 |
"clean:node-modules": "rm -rf node_modules/",
|
| 11 |
"ts-check": "tsc --noEmit",
|
| 12 |
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
| 13 |
"prettier-check": "cd ../ && yarn run prettier-base --check streaming-react-app",
|
| 14 |
+
"signal": "concurrently --names \"TS,LINT,PRETTIER\" -c \"bgBlack.bold,bgRed.bold,bgCyan.bold\" \"yarn run ts-check\" \"yarn run lint\" \"yarn run prettier-check\""
|
|
|
|
|
|
|
| 15 |
},
|
| 16 |
"dependencies": {
|
| 17 |
"@emotion/react": "11.11.1",
|
streaming-react-app/public/vite.svg
DELETED
streaming-react-app/src/RoomConfig.tsx
CHANGED
|
@@ -74,7 +74,6 @@ export default function RoomConfig({
|
|
| 74 |
const lockServerValidated: string | null =
|
| 75 |
lockServer && roles['speaker'] ? lockServerName : null;
|
| 76 |
|
| 77 |
-
// TODO: Show error state if roomID isn't valid
|
| 78 |
setJoinInProgress(true);
|
| 79 |
|
| 80 |
const configObject: JoinRoomConfig = {
|
|
|
|
| 74 |
const lockServerValidated: string | null =
|
| 75 |
lockServer && roles['speaker'] ? lockServerName : null;
|
| 76 |
|
|
|
|
| 77 |
setJoinInProgress(true);
|
| 78 |
|
| 79 |
const configObject: JoinRoomConfig = {
|
streaming-react-app/src/SeamlessLogo.tsx
DELETED
|
@@ -1,33 +0,0 @@
|
|
| 1 |
-
function SeamlessLogo() {
|
| 2 |
-
return (
|
| 3 |
-
<svg
|
| 4 |
-
width="24"
|
| 5 |
-
height="24"
|
| 6 |
-
viewBox="0 0 24 24"
|
| 7 |
-
fill="none"
|
| 8 |
-
xmlns="http://www.w3.org/2000/svg">
|
| 9 |
-
<circle cx="12" cy="12" r="12" fill="#1C2B33" />
|
| 10 |
-
<rect x="7" y="9" width="2" height="6" rx="1" fill="white" />
|
| 11 |
-
<rect
|
| 12 |
-
x="15"
|
| 13 |
-
y="9"
|
| 14 |
-
width="2"
|
| 15 |
-
height="6"
|
| 16 |
-
rx="1"
|
| 17 |
-
fill="white"
|
| 18 |
-
fill-opacity="0.5"
|
| 19 |
-
/>
|
| 20 |
-
<rect
|
| 21 |
-
x="11"
|
| 22 |
-
y="6"
|
| 23 |
-
width="2"
|
| 24 |
-
height="12"
|
| 25 |
-
rx="1"
|
| 26 |
-
fill="white"
|
| 27 |
-
fill-opacity="0.5"
|
| 28 |
-
/>
|
| 29 |
-
</svg>
|
| 30 |
-
);
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
export default SeamlessLogo;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
streaming-react-app/src/StreamingInterface.css
CHANGED
|
@@ -47,16 +47,10 @@
|
|
| 47 |
|
| 48 |
.translation-text-container-sra {
|
| 49 |
background-color: #f8f8f8;
|
| 50 |
-
/* flex-grow: 1; make it expand to fill the available space */
|
| 51 |
padding-top: 12px;
|
| 52 |
padding-bottom: 4px;
|
| 53 |
}
|
| 54 |
|
| 55 |
-
.translation-text-sra {
|
| 56 |
-
/* overflow-y: scroll; */
|
| 57 |
-
/* max-height: 500px; */
|
| 58 |
-
}
|
| 59 |
-
|
| 60 |
.text-chunk-sra {
|
| 61 |
margin-bottom: 12px;
|
| 62 |
}
|
|
|
|
| 47 |
|
| 48 |
.translation-text-container-sra {
|
| 49 |
background-color: #f8f8f8;
|
|
|
|
| 50 |
padding-top: 12px;
|
| 51 |
padding-bottom: 4px;
|
| 52 |
}
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
.text-chunk-sra {
|
| 55 |
margin-bottom: 12px;
|
| 56 |
}
|
streaming-react-app/src/StreamingInterface.tsx
CHANGED
|
@@ -98,7 +98,6 @@ async function requestDisplayMediaAudioStream(
|
|
| 98 |
) {
|
| 99 |
const stream = await navigator.mediaDevices.getDisplayMedia({
|
| 100 |
audio: {...config, channelCount: 1},
|
| 101 |
-
// selfBrowserSurface: false, // don't allow the user to select the current tab as the source
|
| 102 |
});
|
| 103 |
console.debug(
|
| 104 |
'[requestDisplayMediaAudioStream] stream created with settings:',
|
|
@@ -183,8 +182,6 @@ export default function StreamingInterface() {
|
|
| 183 |
);
|
| 184 |
|
| 185 |
const [receivedData, setReceivedData] = useState<Array<ServerTextData>>([]);
|
| 186 |
-
// const [translationSentencesAnimated, setTranslationSentencesAnimated] =
|
| 187 |
-
// useState<TranslationSentences>([]);
|
| 188 |
const [
|
| 189 |
translationSentencesAnimatedIndex,
|
| 190 |
setTranslationSentencesAnimatedIndex,
|
|
@@ -261,7 +258,6 @@ export default function StreamingInterface() {
|
|
| 261 |
if (prevAgent?.name !== newAgent?.name) {
|
| 262 |
setTargetLang(newAgent?.targetLangs[0] ?? null);
|
| 263 |
setEnableExpressive(null);
|
| 264 |
-
// setOutputMode(newAgent.modalities[0]);
|
| 265 |
}
|
| 266 |
return newAgent;
|
| 267 |
});
|
|
@@ -310,7 +306,6 @@ export default function StreamingInterface() {
|
|
| 310 |
event: 'config',
|
| 311 |
rate: sampleRate,
|
| 312 |
model_name: modelName,
|
| 313 |
-
// source_language: inputLang,
|
| 314 |
debug: serverDebugFlag,
|
| 315 |
// synchronous processing isn't implemented on the v2 pubsub server, so hardcode this to true
|
| 316 |
async_processing: true,
|
|
@@ -390,11 +385,12 @@ export default function StreamingInterface() {
|
|
| 390 |
const mediaStreamSource = audioContext.createMediaStreamSource(stream);
|
| 391 |
setInputStreamSource(mediaStreamSource);
|
| 392 |
/**
|
| 393 |
-
* NOTE: This currently uses a deprecated way of processing the audio (createScriptProcessor)
|
|
|
|
| 394 |
*
|
| 395 |
* Documentation for the deprecated way of doing it is here: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createScriptProcessor
|
| 396 |
*
|
| 397 |
-
*
|
| 398 |
*/
|
| 399 |
const scriptProcessor = audioContext.createScriptProcessor(16384, 1, 1);
|
| 400 |
setScriptNodeProcessor(scriptProcessor);
|
|
@@ -408,7 +404,6 @@ export default function StreamingInterface() {
|
|
| 408 |
console.warn('[onaudioprocess] socket is null in onaudioprocess');
|
| 409 |
return;
|
| 410 |
}
|
| 411 |
-
// console.debug('[onaudioprocess] event', event);
|
| 412 |
|
| 413 |
if (mutedRef.current) {
|
| 414 |
// We still want to send audio to the server when we're muted to ensure we
|
|
@@ -475,10 +470,6 @@ export default function StreamingInterface() {
|
|
| 475 |
inputStreamSource.disconnect(scriptNodeProcessor);
|
| 476 |
scriptNodeProcessor.disconnect(audioContext.destination);
|
| 477 |
|
| 478 |
-
// From: https://stackoverflow.com/questions/65447236/scriptnode-onaudioprocess-is-deprecated-any-alternative
|
| 479 |
-
// do we also need this??
|
| 480 |
-
// recorder?.stop();
|
| 481 |
-
|
| 482 |
// Release the mic input so we stop showing the red recording icon in the browser
|
| 483 |
inputStream?.getTracks().forEach((track) => track.stop());
|
| 484 |
}
|
|
@@ -518,7 +509,6 @@ export default function StreamingInterface() {
|
|
| 518 |
}
|
| 519 |
|
| 520 |
const onRoomStateUpdate = (roomState: RoomState) => {
|
| 521 |
-
// console.log('[event: room_state_update]', roomState);
|
| 522 |
setRoomState(roomState);
|
| 523 |
};
|
| 524 |
|
|
@@ -632,11 +622,9 @@ export default function StreamingInterface() {
|
|
| 632 |
useEffect(() => {
|
| 633 |
const onScroll = () => {
|
| 634 |
if (isScrolledToDocumentBottom(SCROLLED_TO_BOTTOM_THRESHOLD_PX)) {
|
| 635 |
-
// console.debug('scrolled to bottom!');
|
| 636 |
isScrolledToBottomRef.current = true;
|
| 637 |
return;
|
| 638 |
}
|
| 639 |
-
// console.debug('NOT scrolled to bottom!');
|
| 640 |
isScrolledToBottomRef.current = false;
|
| 641 |
return;
|
| 642 |
};
|
|
@@ -712,7 +700,6 @@ export default function StreamingInterface() {
|
|
| 712 |
valueLabelDisplay="auto"
|
| 713 |
value={gain}
|
| 714 |
onChange={(_event: Event, newValue: number | number[]) => {
|
| 715 |
-
// console.log({event, newValue});
|
| 716 |
if (typeof newValue === 'number') {
|
| 717 |
const scaledGain = getGainScaledValue(newValue);
|
| 718 |
// We want the actual gain node to use the scaled value
|
|
@@ -1002,7 +989,7 @@ export default function StreamingInterface() {
|
|
| 1002 |
}
|
| 1003 |
label="Noise Suppression (Browser)"
|
| 1004 |
/>
|
| 1005 |
-
|
| 1006 |
control={
|
| 1007 |
<Checkbox
|
| 1008 |
checked={
|
|
|
|
| 98 |
) {
|
| 99 |
const stream = await navigator.mediaDevices.getDisplayMedia({
|
| 100 |
audio: {...config, channelCount: 1},
|
|
|
|
| 101 |
});
|
| 102 |
console.debug(
|
| 103 |
'[requestDisplayMediaAudioStream] stream created with settings:',
|
|
|
|
| 182 |
);
|
| 183 |
|
| 184 |
const [receivedData, setReceivedData] = useState<Array<ServerTextData>>([]);
|
|
|
|
|
|
|
| 185 |
const [
|
| 186 |
translationSentencesAnimatedIndex,
|
| 187 |
setTranslationSentencesAnimatedIndex,
|
|
|
|
| 258 |
if (prevAgent?.name !== newAgent?.name) {
|
| 259 |
setTargetLang(newAgent?.targetLangs[0] ?? null);
|
| 260 |
setEnableExpressive(null);
|
|
|
|
| 261 |
}
|
| 262 |
return newAgent;
|
| 263 |
});
|
|
|
|
| 306 |
event: 'config',
|
| 307 |
rate: sampleRate,
|
| 308 |
model_name: modelName,
|
|
|
|
| 309 |
debug: serverDebugFlag,
|
| 310 |
// synchronous processing isn't implemented on the v2 pubsub server, so hardcode this to true
|
| 311 |
async_processing: true,
|
|
|
|
| 385 |
const mediaStreamSource = audioContext.createMediaStreamSource(stream);
|
| 386 |
setInputStreamSource(mediaStreamSource);
|
| 387 |
/**
|
| 388 |
+
* NOTE: This currently uses a deprecated way of processing the audio (createScriptProcessor), but
|
| 389 |
+
* which is easy and convenient for our purposes.
|
| 390 |
*
|
| 391 |
* Documentation for the deprecated way of doing it is here: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createScriptProcessor
|
| 392 |
*
|
| 393 |
+
* In an ideal world this would be migrated to something like this SO answer: https://stackoverflow.com/a/65448287
|
| 394 |
*/
|
| 395 |
const scriptProcessor = audioContext.createScriptProcessor(16384, 1, 1);
|
| 396 |
setScriptNodeProcessor(scriptProcessor);
|
|
|
|
| 404 |
console.warn('[onaudioprocess] socket is null in onaudioprocess');
|
| 405 |
return;
|
| 406 |
}
|
|
|
|
| 407 |
|
| 408 |
if (mutedRef.current) {
|
| 409 |
// We still want to send audio to the server when we're muted to ensure we
|
|
|
|
| 470 |
inputStreamSource.disconnect(scriptNodeProcessor);
|
| 471 |
scriptNodeProcessor.disconnect(audioContext.destination);
|
| 472 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
// Release the mic input so we stop showing the red recording icon in the browser
|
| 474 |
inputStream?.getTracks().forEach((track) => track.stop());
|
| 475 |
}
|
|
|
|
| 509 |
}
|
| 510 |
|
| 511 |
const onRoomStateUpdate = (roomState: RoomState) => {
|
|
|
|
| 512 |
setRoomState(roomState);
|
| 513 |
};
|
| 514 |
|
|
|
|
| 622 |
useEffect(() => {
|
| 623 |
const onScroll = () => {
|
| 624 |
if (isScrolledToDocumentBottom(SCROLLED_TO_BOTTOM_THRESHOLD_PX)) {
|
|
|
|
| 625 |
isScrolledToBottomRef.current = true;
|
| 626 |
return;
|
| 627 |
}
|
|
|
|
| 628 |
isScrolledToBottomRef.current = false;
|
| 629 |
return;
|
| 630 |
};
|
|
|
|
| 700 |
valueLabelDisplay="auto"
|
| 701 |
value={gain}
|
| 702 |
onChange={(_event: Event, newValue: number | number[]) => {
|
|
|
|
| 703 |
if (typeof newValue === 'number') {
|
| 704 |
const scaledGain = getGainScaledValue(newValue);
|
| 705 |
// We want the actual gain node to use the scaled value
|
|
|
|
| 989 |
}
|
| 990 |
label="Noise Suppression (Browser)"
|
| 991 |
/>
|
| 992 |
+
<FormControlLabel
|
| 993 |
control={
|
| 994 |
<Checkbox
|
| 995 |
checked={
|
streaming-react-app/src/createBufferedSpeechPlayer.ts
CHANGED
|
@@ -100,8 +100,7 @@ export default function createBufferedSpeechPlayer({
|
|
| 100 |
);
|
| 101 |
|
| 102 |
source.connect(gainNode);
|
| 103 |
-
|
| 104 |
-
// source.connect(audioContext.destination);
|
| 105 |
const startTime = new Date().getTime();
|
| 106 |
source.start();
|
| 107 |
currentPlayingBufferSource = source;
|
|
@@ -118,9 +117,6 @@ export default function createBufferedSpeechPlayer({
|
|
| 118 |
debug()?.playedAudio(startTime, endTime, buffer);
|
| 119 |
currentPlayingBufferSource = null;
|
| 120 |
|
| 121 |
-
// TODO: should we disconnect source from gain node here?
|
| 122 |
-
// source.disconnect(gainNode);
|
| 123 |
-
|
| 124 |
// We don't set isPlaying = false here because we are attempting to continue playing. It will get set to false if there are no more buffers to play
|
| 125 |
playNextBuffer();
|
| 126 |
};
|
|
|
|
| 100 |
);
|
| 101 |
|
| 102 |
source.connect(gainNode);
|
| 103 |
+
|
|
|
|
| 104 |
const startTime = new Date().getTime();
|
| 105 |
source.start();
|
| 106 |
currentPlayingBufferSource = source;
|
|
|
|
| 117 |
debug()?.playedAudio(startTime, endTime, buffer);
|
| 118 |
currentPlayingBufferSource = null;
|
| 119 |
|
|
|
|
|
|
|
|
|
|
| 120 |
// We don't set isPlaying = false here because we are attempting to continue playing. It will get set to false if there are no more buffers to play
|
| 121 |
playNextBuffer();
|
| 122 |
};
|
streaming-react-app/src/generateNewRoomID.ts
CHANGED
|
@@ -54,7 +54,3 @@ export function getSequentialRoomIDForTestingGenerator(): () => string {
|
|
| 54 |
return result;
|
| 55 |
};
|
| 56 |
}
|
| 57 |
-
|
| 58 |
-
// const generator = getSequentialRoomIDForTestingGenerator();
|
| 59 |
-
|
| 60 |
-
// Array.from({length: 200}, () => console.log(generator()));
|
|
|
|
| 54 |
return result;
|
| 55 |
};
|
| 56 |
}
|
|
|
|
|
|
|
|
|
|
|
|
streaming-react-app/src/getTranslationSentencesFromReceivedData.ts
CHANGED
|
@@ -6,7 +6,6 @@ export default function getTranslationSentencesFromReceivedData(
|
|
| 6 |
return receivedData
|
| 7 |
.reduce(
|
| 8 |
(acc, data) => {
|
| 9 |
-
// TODO: Add special handling if the payload starts/ends with an apostrophe?
|
| 10 |
const newAcc = [
|
| 11 |
...acc.slice(0, -1),
|
| 12 |
acc[acc.length - 1].trim() + ' ' + data.payload,
|
|
|
|
| 6 |
return receivedData
|
| 7 |
.reduce(
|
| 8 |
(acc, data) => {
|
|
|
|
| 9 |
const newAcc = [
|
| 10 |
...acc.slice(0, -1),
|
| 11 |
acc[acc.length - 1].trim() + ' ' + data.payload,
|
streaming-react-app/src/index.css
DELETED
|
File without changes
|
streaming-react-app/src/main.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import React from 'react';
|
| 2 |
import ReactDOM from 'react-dom/client';
|
| 3 |
import App from './App.tsx';
|
| 4 |
-
import './index.css';
|
| 5 |
|
| 6 |
ReactDOM.createRoot(document.getElementById('root')!).render(
|
| 7 |
<React.StrictMode>
|
|
|
|
| 1 |
import React from 'react';
|
| 2 |
import ReactDOM from 'react-dom/client';
|
| 3 |
import App from './App.tsx';
|
|
|
|
| 4 |
|
| 5 |
ReactDOM.createRoot(document.getElementById('root')!).render(
|
| 6 |
<React.StrictMode>
|
streaming-react-app/vite.config.ts
CHANGED
|
@@ -9,6 +9,11 @@ import react from '@vitejs/plugin-react';
|
|
| 9 |
// https://vitejs.dev/config/
|
| 10 |
export default defineConfig(({ command }) => {
|
| 11 |
let define = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
return {
|
| 13 |
plugins: [react()],
|
| 14 |
define: define,
|
|
|
|
| 9 |
// https://vitejs.dev/config/
|
| 10 |
export default defineConfig(({ command }) => {
|
| 11 |
let define = {};
|
| 12 |
+
if (command === 'serve') {
|
| 13 |
+
define = {
|
| 14 |
+
global: {},
|
| 15 |
+
};
|
| 16 |
+
}
|
| 17 |
return {
|
| 18 |
plugins: [react()],
|
| 19 |
define: define,
|