Skip to content

Commit

Permalink
Fix WebRTC visualizer bug (#513)
Browse files Browse the repository at this point in the history
Bugfix:
- state changes to speaking before `outAnalyzer` gets set (React doesn't
know about underlying object change)
- setting `audioContext` on the `mediaStream` of LiveKit remote track
  • Loading branch information
farzadab authored Nov 28, 2023
1 parent 524cf2d commit 4cd5eaf
Showing 1 changed file with 15 additions and 1 deletion.
16 changes: 15 additions & 1 deletion packages/voice/src/app/agent/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ export class WebRtcChatManager implements ChatManager {
private socket?: WebSocket;
private room?: Room;
private localAudioTrack?: LocalAudioTrack;
/** True when we should have entered speaking state but didn't due to analyzer not being ready. */
private delayedSpeakingState = false;
private inAnalyzer?: StreamAnalyzer;
private outAnalyzer?: StreamAnalyzer;
private pinger?: NodeJS.Timer;
Expand Down Expand Up @@ -675,8 +677,14 @@ export class WebRtcChatManager implements ChatManager {
const audioTrack = track as RemoteAudioTrack;
audioTrack.on(TrackEvent.AudioPlaybackStarted, () => console.log(`[chat] audio playback started`));
audioTrack.on(TrackEvent.AudioPlaybackFailed, (err) => console.error(`[chat] audio playback failed`, err));
// TODO Farzad: Figure out why setting audioContext here is necessary.
audioTrack.setAudioContext(this.audioContext);
audioTrack.attach(this.audioElement);
this.outAnalyzer = new StreamAnalyzer(this.audioContext, track.mediaStream!);
if (this.delayedSpeakingState) {
this.delayedSpeakingState = false;
this.changeState(ChatManagerState.SPEAKING);
}
}
private handleDataReceived(payload: Uint8Array, participant: any) {
const data = JSON.parse(this.textDecoder.decode(payload));
Expand All @@ -685,7 +693,13 @@ export class WebRtcChatManager implements ChatManager {
console.debug(`[chat] worker RTT: ${elapsed_ms.toFixed(0)} ms`);
} else if (data.type === 'state') {
const newState = data.state;
this.changeState(newState);
if (newState === ChatManagerState.SPEAKING && this.outAnalyzer === undefined) {
// Skip the first speaking state, before we've attached the audio element.
// handleTrackSubscribed will be called soon and will change the state.
this.delayedSpeakingState = true;
} else {
this.changeState(newState);
}
} else if (data.type === 'transcript') {
const finalText = data.transcript.final ? ' FINAL' : '';
console.log(`[chat] input: ${data.transcript.text}${finalText}`);
Expand Down

0 comments on commit 4cd5eaf

Please sign in to comment.