Skip to content

Commit 4c182e2

Browse files
authored
hotfix: voice chat (#5591)
1 parent 424face commit 4c182e2

File tree

9 files changed

+78
-79
lines changed

9 files changed

+78
-79
lines changed

browser-interface/packages/lib/redux/waitFor.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@ export function waitFor(selector: (state: any) => any, actionType?: string | str
1010
return result
1111
}
1212
}
13+
14+
export function* waitForSelector(selector: (state: any) => any) {
15+
if (yield select(selector)) return; // (1)
16+
17+
while (true) {
18+
yield take('*'); // (1a)
19+
if (yield select(selector)) return; // (1b)
20+
}
21+
}

browser-interface/packages/shared/comms/adapters/voice/liveKitVoiceHandler.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,18 @@ export function createLiveKitVoiceHandler(room: Room, globalAudioStream: GlobalA
140140
logger.log('initialized')
141141

142142
return {
143-
setRecording(recording) {
144-
room.localParticipant
145-
.setMicrophoneEnabled(recording)
146-
.then(() => {
147-
if (recordingListener) {
148-
recordingListener(recording)
149-
}
150-
})
151-
.catch((err) => logger.error('Error: ', err, ', recording=', recording))
143+
async setRecording(recording) {
144+
try {
145+
await room.localParticipant.setMicrophoneEnabled(recording)
146+
if (recordingListener) {
147+
recordingListener(recording)
148+
}
149+
} catch(err) {
150+
logger.error('Error: ', err, ', recording=', recording)
151+
if (recordingListener) {
152+
recordingListener(false)
153+
}
154+
}
152155
},
153156
onUserTalking(cb) {
154157
onUserTalkingCallback = cb

browser-interface/packages/shared/comms/adapters/voice/opusVoiceHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const createOpusVoiceHandler = (): VoiceHandler => {
3838
} else {
3939
voiceCommunicator.pause()
4040
}
41+
return Promise.resolve()
4142
},
4243
onUserTalking(cb) {
4344
voiceCommunicator.addStreamPlayingListener((streamId: string, playing: boolean) => {

browser-interface/packages/shared/voiceChat/VoiceHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as rfc4 from 'shared/protocol/decentraland/kernel/comms/rfc4/comms.gen'
33
export type VoiceHandler = {
44
// UI Methods
55
// setTalking is called from the UI or keyboard to broadcast audio
6-
setRecording(recording: boolean): void
6+
setRecording(recording: boolean): Promise<void>
77

88
// used to know if a user is talking or not, for the UI
99
onUserTalking(cb: (userId: string, talking: boolean) => void): void

browser-interface/packages/shared/voiceChat/actions.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,6 @@ export const REQUEST_VOICE_CHAT_RECORDING = '[VC] requestVoiceChatRecording'
3232
export const requestVoiceChatRecording = (recording: boolean) => action(REQUEST_VOICE_CHAT_RECORDING, { recording })
3333
export type RequestVoiceChatRecordingAction = ReturnType<typeof requestVoiceChatRecording>
3434

35-
/**
36-
* Action to toggle voice chat recording
37-
*/
38-
export const REQUEST_TOGGLE_VOICE_CHAT_RECORDING = '[VC] toggleVoiceChatRecording'
39-
export const requestToggleVoiceChatRecording = () => action(REQUEST_TOGGLE_VOICE_CHAT_RECORDING, {})
40-
export type RequestToggleVoiceChatRecordingAction = ReturnType<typeof requestToggleVoiceChatRecording>
41-
4235
/**
4336
* Action triggered when recording starts or stops
4437
*/
@@ -74,7 +67,6 @@ export type VoiceChatActions =
7467
| SetVoiceChatHandlerAction
7568
| SetVoiceChatErrorAction
7669
| RequestVoiceChatRecordingAction
77-
| RequestToggleVoiceChatRecordingAction
7870
| VoiceRecordingUpdateAction
7971
| SetVoiceChatVolumeAction
8072
| SetVoiceChatMuteAction

browser-interface/packages/shared/voiceChat/reducer.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
JOIN_VOICE_CHAT,
33
LEAVE_VOICE_CHAT,
4-
REQUEST_TOGGLE_VOICE_CHAT_RECORDING,
54
REQUEST_VOICE_CHAT_RECORDING,
65
SET_AUDIO_DEVICE,
76
SET_VOICE_CHAT_ERROR,
@@ -50,9 +49,6 @@ export function voiceChatReducer(state?: VoiceChatState, action?: VoiceChatActio
5049
const { payload } = action
5150
return { ...state, requestRecording: payload.recording }
5251
}
53-
case REQUEST_TOGGLE_VOICE_CHAT_RECORDING: {
54-
return { ...state, requestRecording: !state.requestRecording }
55-
}
5652
case SET_VOICE_CHAT_VOLUME: {
5753
const { payload } = action
5854
return { ...state, volume: payload.volume }

browser-interface/packages/shared/voiceChat/sagas.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { apply, call, fork, put, select, take, takeEvery, takeLatest } from 'redux-saga/effects'
1+
import { apply, call, fork, put, select, take, takeEvery } from 'redux-saga/effects'
22
import { trackEvent } from 'shared/analytics/trackEvent'
33
import { positionReportToCommsPositionRfc4 } from 'shared/comms/interface/utils'
44
import { receiveUserTalking } from 'shared/comms/peers'
@@ -10,7 +10,6 @@ import {
1010
JOIN_VOICE_CHAT,
1111
leaveVoiceChat,
1212
LEAVE_VOICE_CHAT,
13-
REQUEST_TOGGLE_VOICE_CHAT_RECORDING,
1413
REQUEST_VOICE_CHAT_RECORDING,
1514
SetAudioDevice,
1615
setVoiceChatError,
@@ -37,7 +36,8 @@ import {
3736
getVoiceHandler,
3837
hasJoinedVoiceChat,
3938
isRequestedVoiceChatRecording,
40-
isVoiceChatAllowedByCurrentScene
39+
isVoiceChatAllowedByCurrentScene,
40+
isVoiceChatRecording
4141
} from './selectors'
4242
import { RootVoiceChatState, VoiceChatState } from './types'
4343
import { VoiceHandler } from './VoiceHandler'
@@ -48,14 +48,14 @@ import { getCommsRoom } from 'shared/comms/selectors'
4848
import { waitForMetaConfigurationInitialization } from 'shared/meta/sagas'
4949
import { incrementCounter } from 'shared/analytics/occurences'
5050
import { RootWorldState } from 'shared/world/types'
51+
import { waitForSelector } from 'lib/redux'
5152

5253
let audioRequestInitialized = false
5354

5455
export function* voiceChatSaga() {
5556
yield fork(reactToNewVoiceChatHandler)
5657

57-
yield takeLatest(REQUEST_VOICE_CHAT_RECORDING, handleRecordingRequest)
58-
yield takeLatest(REQUEST_TOGGLE_VOICE_CHAT_RECORDING, handleRecordingRequest)
58+
yield takeEvery(REQUEST_VOICE_CHAT_RECORDING, handleRecordingRequest)
5959

6060
yield takeEvery(VOICE_PLAYING_UPDATE, handleUserVoicePlaying)
6161

@@ -107,17 +107,25 @@ function* handleConnectVoiceChatToRoom() {
107107
}
108108
}
109109

110+
const notRecording = (store: RootVoiceChatState) => !store.voiceChat.recording
111+
110112
function* handleRecordingRequest() {
111113
const { requestedRecording, voiceHandler, isAllowedByScene } = (yield select(
112114
getHandleRecordingRequestInfo
113115
)) as ReturnType<typeof getHandleRecordingRequestInfo>
114116

115117
if (voiceHandler) {
116118
if (!isAllowedByScene || !requestedRecording) {
117-
voiceHandler.setRecording(false)
119+
120+
// Ensure that we're recording, to stop recording
121+
yield call(waitForSelector, isVoiceChatRecording)
122+
yield call(voiceHandler.setRecording, false)
118123
} else {
119124
yield call(requestUserMediaIfNeeded)
120-
voiceHandler.setRecording(true)
125+
126+
// Ensure that we're not recording, when we try to start recording
127+
yield call(waitForSelector, notRecording)
128+
yield call(voiceHandler.setRecording, true)
121129
}
122130
}
123131
}

browser-interface/packages/unity-interface/BrowserInterface.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ import {
8989
import {
9090
joinVoiceChat,
9191
leaveVoiceChat,
92-
requestToggleVoiceChatRecording,
9392
requestVoiceChatRecording,
9493
setAudioDevice,
9594
setVoiceChatPolicy,
@@ -847,10 +846,6 @@ export class BrowserInterface {
847846
store.dispatch(leaveVoiceChat())
848847
}
849848

850-
public ToggleVoiceChatRecording() {
851-
store.dispatch(requestToggleVoiceChatRecording())
852-
}
853-
854849
public ApplySettings(settingsMessage: { voiceChatVolume: number; voiceChatAllowCategory: number }) {
855850
store.dispatch(setVoiceChatVolume(settingsMessage.voiceChatVolume))
856851
store.dispatch(setVoiceChatPolicy(settingsMessage.voiceChatAllowCategory))
Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,51 @@
1-
using UnityEngine;
1+
using DCL.Interface;
2+
using Newtonsoft.Json;
23
using SocialFeaturesAnalytics;
3-
using System.Collections.Generic;
44
using System;
5-
using Newtonsoft.Json;
5+
using System.Collections.Generic;
6+
using UnityEngine;
67

78
namespace DCL
89
{
910
public class DCLVoiceChatController : MonoBehaviour
1011
{
1112
[Header("InputActions")]
1213
public InputAction_Hold voiceChatAction;
13-
public InputAction_Trigger voiceChatToggleAction;
1414

1515
private InputAction_Hold.Started voiceChatStartedDelegate;
1616
private InputAction_Hold.Finished voiceChatFinishedDelegate;
17-
private InputAction_Trigger.Triggered voiceChatToggleDelegate;
1817

1918
private bool firstTimeVoiceRecorded = true;
2019
private ISocialAnalytics socialAnalytics;
2120
private UserProfileWebInterfaceBridge userProfileWebInterfaceBridge;
2221
private double voiceMessageStartTime = 0;
23-
private bool isVoiceChatToggledOn = false;
22+
23+
private bool isRecording;
2424

2525
void Awake()
2626
{
2727
userProfileWebInterfaceBridge = new UserProfileWebInterfaceBridge();
2828

2929
voiceChatStartedDelegate = (action) => DataStore.i.voiceChat.isRecording.Set(new KeyValuePair<bool, bool>(true, true));
3030
voiceChatFinishedDelegate = (action) => DataStore.i.voiceChat.isRecording.Set(new KeyValuePair<bool, bool>(false, true));
31-
voiceChatToggleDelegate = (action) => ToggleVoiceChatRecording();
3231
voiceChatAction.OnStarted += voiceChatStartedDelegate;
3332
voiceChatAction.OnFinished += voiceChatFinishedDelegate;
34-
voiceChatToggleAction.OnTriggered += voiceChatToggleDelegate;
3533

3634
KernelConfig.i.EnsureConfigInitialized().Then(config => EnableVoiceChat(config.comms.voiceChatEnabled));
3735
KernelConfig.i.OnChange += OnKernelConfigChanged;
3836
DataStore.i.voiceChat.isRecording.OnChange += IsVoiceChatRecordingChanged;
3937
}
4038

39+
40+
private void OnApplicationFocus(bool hasFocus)
41+
{
42+
if (!hasFocus)
43+
{
44+
StopRecording(true);
45+
DataStore.i.voiceChat.isRecording.Set(new KeyValuePair<bool, bool>(false, true));
46+
}
47+
}
48+
4149
void OnDestroy()
4250
{
4351
voiceChatAction.OnStarted -= voiceChatStartedDelegate;
@@ -61,50 +69,38 @@ private void IsVoiceChatRecordingChanged(KeyValuePair<bool, bool> current, KeyVa
6169
if (!DataStore.i.voiceChat.isJoinedToVoiceChat.Get())
6270
return;
6371

64-
CreateSocialAnalyticsIfNeeded();
65-
6672
if (current.Key)
67-
{
68-
if (!isVoiceChatToggledOn)
69-
{
70-
Interface.WebInterface.SendSetVoiceChatRecording(true);
71-
SendFirstTimeMetricIfNeeded();
72-
voiceMessageStartTime = Time.realtimeSinceStartup;
73-
}
74-
}
73+
StartRecording();
7574
else
76-
{
77-
Interface.WebInterface.SendSetVoiceChatRecording(false);
75+
StopRecording(current.Value);
76+
}
7877

79-
socialAnalytics.SendVoiceMessage(
80-
Time.realtimeSinceStartup - voiceMessageStartTime,
81-
(current.Value || isVoiceChatToggledOn) ? VoiceMessageSource.Shortcut : VoiceMessageSource.Button,
82-
userProfileWebInterfaceBridge.GetOwn().userId);
78+
private void StartRecording()
79+
{
80+
if (isRecording) return;
8381

84-
isVoiceChatToggledOn = false;
85-
}
82+
WebInterface.SendSetVoiceChatRecording(true);
83+
84+
CreateSocialAnalyticsIfNeeded();
85+
SendFirstTimeMetricIfNeeded();
86+
voiceMessageStartTime = Time.realtimeSinceStartup;
87+
88+
isRecording = true;
8689
}
8790

88-
private void ToggleVoiceChatRecording()
91+
private void StopRecording(bool usedShortcut)
8992
{
90-
if (!DataStore.i.voiceChat.isJoinedToVoiceChat.Get())
91-
return;
93+
if (!isRecording) return;
9294

93-
Interface.WebInterface.ToggleVoiceChatRecording();
94-
isVoiceChatToggledOn = !isVoiceChatToggledOn;
95+
WebInterface.SendSetVoiceChatRecording(false);
9596

96-
if (isVoiceChatToggledOn)
97-
{
98-
SendFirstTimeMetricIfNeeded();
99-
voiceMessageStartTime = Time.realtimeSinceStartup;
100-
}
101-
else
102-
{
103-
socialAnalytics.SendVoiceMessage(
104-
Time.realtimeSinceStartup - voiceMessageStartTime,
105-
VoiceMessageSource.Shortcut,
106-
userProfileWebInterfaceBridge.GetOwn().userId);
107-
}
97+
CreateSocialAnalyticsIfNeeded();
98+
socialAnalytics.SendVoiceMessage(
99+
Time.realtimeSinceStartup - voiceMessageStartTime,
100+
usedShortcut ? VoiceMessageSource.Shortcut : VoiceMessageSource.Button,
101+
userProfileWebInterfaceBridge.GetOwn().userId);
102+
103+
isRecording = false;
108104
}
109105

110106
private void CreateSocialAnalyticsIfNeeded()
@@ -121,7 +117,6 @@ private void SendFirstTimeMetricIfNeeded()
121117
{
122118
if (firstTimeVoiceRecorded)
123119
{
124-
CreateSocialAnalyticsIfNeeded();
125120
socialAnalytics.SendVoiceMessageStartedByFirstTime();
126121
firstTimeVoiceRecorded = false;
127122
}
@@ -133,4 +128,4 @@ public class VoiceChatStatusPayload
133128
{
134129
public bool isConnected;
135130
}
136-
}
131+
}

0 commit comments

Comments
 (0)