From 520184fdebe06e4635f289e9628df685c8fde4c6 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 1 Feb 2022 12:25:14 +0100 Subject: [PATCH 01/15] add talk-icon --- front/dist/resources/icons/icon_talking.svg | 1 + front/src/Phaser/Entity/Character.ts | 10 ++++++++++ front/src/Phaser/Game/GameScene.ts | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 front/dist/resources/icons/icon_talking.svg diff --git a/front/dist/resources/icons/icon_talking.svg b/front/dist/resources/icons/icon_talking.svg new file mode 100644 index 00000000..2f1d03e3 --- /dev/null +++ b/front/dist/resources/icons/icon_talking.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index e1fd3a5a..c6fd0c4a 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -31,6 +31,7 @@ const interactiveRadius = 35; export abstract class Character extends Container { private bubble: SpeechBubble | null = null; private readonly playerName: Text; + private readonly iconTalk: Phaser.GameObjects.Image; public PlayerValue: string; public sprites: Map; protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; @@ -97,6 +98,11 @@ export abstract class Character extends Container { this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX); this.add(this.playerName); + this.iconTalk = new Phaser.GameObjects.Image(scene, 0, -45, 'iconTalk') + .setScale(0.15) + .setVisible(false); + this.add(this.iconTalk); + if (isClickable) { this.setInteractive({ hitArea: new Phaser.Geom.Circle(0, 0, interactiveRadius), @@ -156,6 +162,10 @@ export abstract class Character extends Container { }); } + public showIconTalk(show: boolean = true): void { + this.iconTalk.setVisible(show); + } + public addCompanion(name: string, texturePromise?: Promise): void { if (typeof texturePromise !== "undefined") { this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 23a211a6..215797bb 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -245,6 +245,7 @@ export class GameScene extends DirtyScene { loadCustomTexture(this.load, texture).catch((e) => console.error(e)); } } + this.load.svg('iconTalk', '/resources/icons/icon_talking.svg'); if (touchScreenManager.supportTouchScreen) { this.load.image(joystickBaseKey, joystickBaseImg); @@ -630,6 +631,7 @@ export class GameScene extends DirtyScene { let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { + console.log(peers); const newPeerNumber = peers.size; if (newPeerNumber > oldPeerNumber) { this.playSound("audio-webrtc-in"); From 2c880c65b6cfe5c07372d9936b9515e7c2842ead Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 2 Feb 2022 15:47:38 +0100 Subject: [PATCH 02/15] talk indicators wip --- front/src/Phaser/Game/GameScene.ts | 15 +++++++++++- front/src/WebRtc/VideoPeer.ts | 37 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 39f25f9f..28558e18 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -168,6 +168,7 @@ export class GameScene extends DirtyScene { private peerStoreUnsubscribe!: Unsubscriber; private emoteUnsubscribe!: Unsubscriber; private emoteMenuUnsubscribe!: Unsubscriber; + private volumeStoreUnsubscribes: Map = new Map(); private followUsersColorStoreUnsubscribe!: Unsubscriber; private biggestAvailableAreaStoreUnsubscribe!: () => void; @@ -637,7 +638,19 @@ export class GameScene extends DirtyScene { let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { - console.log(peers); + this.volumeStoreUnsubscribes.forEach(unsubscribe => unsubscribe()); + this.volumeStoreUnsubscribes.clear(); + + for (const [key, videoStream] of peers) { + this.volumeStoreUnsubscribes.set(key, videoStream.volumeStore.subscribe((volume) => { + if (volume) { + console.log(volume); + this.MapPlayersByKey.get(key)?.showIconTalk(volume > 5); + this.markDirty(); + } + })); + } + const newPeerNumber = peers.size; if (newPeerNumber > oldPeerNumber) { this.playSound("audio-webrtc-in"); diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index c3b6d45e..e29cfa23 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -10,6 +10,9 @@ import { playersStore } from "../Stores/PlayersStore"; import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore"; import { getIceServersConfig } from "../Components/Video/utils"; import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils"; +import { SoundMeter } from '../Phaser/Components/SoundMeter'; +import { AudioContext } from 'standardized-audio-context'; +import { Console } from 'console'; const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer"); @@ -33,6 +36,7 @@ export class VideoPeer extends Peer { private onBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription; public readonly streamStore: Readable; + public readonly volumeStore: Readable; public readonly statusStore: Readable; public readonly constraintsStore: Readable; private newMessageSubscribtion: Subscription | undefined; @@ -58,6 +62,7 @@ export class VideoPeer extends Peer { this.uniqueId = "video_" + this.userId; this.streamStore = readable(null, (set) => { + console.log('STREAM STORE INITIALIZE'); const onStream = (stream: MediaStream | null) => { set(stream); }; @@ -69,6 +74,38 @@ export class VideoPeer extends Peer { }; }); + console.log('CREATE VOLUME STORE'); + + this.volumeStore = readable(null, (set) => { + let timeout: ReturnType; + console.log('VOLUME STORE INITIALIZE'); + const unsubscribe = this.streamStore.subscribe((mediaStream) => { + if (mediaStream === null) { + set(null); + return; + } + const soundMeter = new SoundMeter(); + soundMeter.connectToSource(mediaStream, new AudioContext()); + let error = false; + + timeout = setInterval(() => { + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } + } + }, 100); + }); + + return () => { + unsubscribe(); + clearInterval(timeout); + } + }); + this.constraintsStore = readable(null, (set) => { const onData = (chunk: Buffer) => { const message = JSON.parse(chunk.toString("utf8")); From 87dde50251e03ebd3bbca2eee2d2948e1d2dd955 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 2 Feb 2022 16:32:51 +0100 Subject: [PATCH 03/15] some logs --- front/src/Phaser/Game/GameScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 28558e18..8d6a691f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -644,9 +644,9 @@ export class GameScene extends DirtyScene { for (const [key, videoStream] of peers) { this.volumeStoreUnsubscribes.set(key, videoStream.volumeStore.subscribe((volume) => { if (volume) { - console.log(volume); + console.log(`${key}: ${volume}`); this.MapPlayersByKey.get(key)?.showIconTalk(volume > 5); - this.markDirty(); + this.markDirty(); // should be dirty from animation } })); } From e0f5529fa741c52e370f0b9b69f1ce9516cb281d Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 3 Feb 2022 12:21:30 +0100 Subject: [PATCH 04/15] catch no audio track error --- front/src/Phaser/Game/GameScene.ts | 8 ++++++ front/src/Stores/MediaStore.ts | 43 ++++++++++++++++++++++++++++++ front/src/WebRtc/VideoPeer.ts | 12 +++++---- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 8d6a691f..99db0d6c 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -92,6 +92,7 @@ import { MapStore } from "../../Stores/Utils/MapStore"; import { followUsersColorStore } from "../../Stores/FollowStore"; import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler"; import { locale } from "../../i18n/i18n-svelte"; +import { localVolumeStore } from '../../Stores/MediaStore'; export interface GameSceneInitInterface { initPosition: PointInterface | null; reconnecting: boolean; @@ -636,6 +637,13 @@ export class GameScene extends DirtyScene { this.connect(); } + // localVolumeStore.subscribe((volume) => { + // if (volume) { + // this.CurrentPlayer.showIconTalk(volume > 5); + // this.markDirty(); // should be dirty from animation + // } + // }); + let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { this.volumeStoreUnsubscribes.forEach(unsubscribe => unsubscribe()); diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index f1b41430..adc1ac2e 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -10,6 +10,8 @@ import { myCameraVisibilityStore } from "./MyCameraStoreVisibility"; import { peerStore } from "./PeerStore"; import { privacyShutdownStore } from "./PrivacyShutdownStore"; import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError"; +import { SoundMeter } from '../Phaser/Components/SoundMeter'; +import { AudioContext } from 'standardized-audio-context'; /** * A store that contains the camera state requested by the user (on or off). @@ -395,6 +397,47 @@ async function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrac } } +export const localVolumeStore = readable(null, (set) => { + let timeout: ReturnType; + const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => { + console.log('LOCAL VOLUME STORE SUBSCRIBE TO LOCAL STREAM STORE'); + if (localStreamStoreValue.type === "error") { + console.log('D1'); + set(null); + return; + } + const soundMeter = new SoundMeter(); + const mediaStream = localStreamStoreValue.stream; + if (mediaStream === null) { + console.log('D2'); + set(null); + return; + } + console.log('D3'); + console.log(localStreamStoreValue); + soundMeter.connectToSource(mediaStream, new AudioContext()); + let error = false; + + timeout = setInterval(() => { + console.log('local time interval'); + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } + } + }, 100); + }); + + return () => { + console.log('UNSUBSCRIBE FROM LOCAL STREAM STORE'); + unsubscribe(); + clearInterval(timeout); + } +}); + /** * A store containing the MediaStream object (or null if nothing requested, or Error if an error occurred) */ diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index e29cfa23..fab45a71 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -62,7 +62,6 @@ export class VideoPeer extends Peer { this.uniqueId = "video_" + this.userId; this.streamStore = readable(null, (set) => { - console.log('STREAM STORE INITIALIZE'); const onStream = (stream: MediaStream | null) => { set(stream); }; @@ -74,19 +73,22 @@ export class VideoPeer extends Peer { }; }); - console.log('CREATE VOLUME STORE'); - this.volumeStore = readable(null, (set) => { let timeout: ReturnType; - console.log('VOLUME STORE INITIALIZE'); const unsubscribe = this.streamStore.subscribe((mediaStream) => { if (mediaStream === null) { set(null); return; } const soundMeter = new SoundMeter(); - soundMeter.connectToSource(mediaStream, new AudioContext()); let error = false; + try { + soundMeter.connectToSource(mediaStream, new AudioContext()); + } catch (errMsg) { + console.warn(errMsg); + set(null); + return; + } timeout = setInterval(() => { try { From 4424c7cce1bcfc2a24b220ba523e3fdf08fef1f3 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 3 Feb 2022 12:58:31 +0100 Subject: [PATCH 05/15] prevent SoundMeter from being initialized if stream has no audio tracks available --- front/src/Phaser/Game/GameScene.ts | 12 ++--- front/src/Stores/MediaStore.ts | 79 ++++++++++++++---------------- front/src/WebRtc/VideoPeer.ts | 41 +++++++--------- 3 files changed, 63 insertions(+), 69 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 99db0d6c..bdcf976a 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -637,12 +637,12 @@ export class GameScene extends DirtyScene { this.connect(); } - // localVolumeStore.subscribe((volume) => { - // if (volume) { - // this.CurrentPlayer.showIconTalk(volume > 5); - // this.markDirty(); // should be dirty from animation - // } - // }); + localVolumeStore.subscribe((volume) => { + if (volume) { + this.CurrentPlayer.showIconTalk(volume > 5); + this.markDirty(); // should be dirty from animation + } + }); let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index adc1ac2e..fe83d41a 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -397,47 +397,6 @@ async function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrac } } -export const localVolumeStore = readable(null, (set) => { - let timeout: ReturnType; - const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => { - console.log('LOCAL VOLUME STORE SUBSCRIBE TO LOCAL STREAM STORE'); - if (localStreamStoreValue.type === "error") { - console.log('D1'); - set(null); - return; - } - const soundMeter = new SoundMeter(); - const mediaStream = localStreamStoreValue.stream; - if (mediaStream === null) { - console.log('D2'); - set(null); - return; - } - console.log('D3'); - console.log(localStreamStoreValue); - soundMeter.connectToSource(mediaStream, new AudioContext()); - let error = false; - - timeout = setInterval(() => { - console.log('local time interval'); - try { - set(soundMeter.getVolume()); - } catch (err) { - if (!error) { - console.error(err); - error = true; - } - } - }, 100); - }); - - return () => { - console.log('UNSUBSCRIBE FROM LOCAL STREAM STORE'); - unsubscribe(); - clearInterval(timeout); - } -}); - /** * A store containing the MediaStream object (or null if nothing requested, or Error if an error occurred) */ @@ -584,6 +543,44 @@ export const obtainedMediaConstraintStore = derived(null, (set) => { + let timeout: ReturnType; + const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => { + console.log('LOCAL VOLUME STORE SUBSCRIBE TO LOCAL STREAM STORE'); + if (localStreamStoreValue.type === "error") { + console.log('D1'); + set(null); + return; + } + const soundMeter = new SoundMeter(); + const mediaStream = localStreamStoreValue.stream; + if (mediaStream !== null && mediaStream.getAudioTracks().length > 0) { + console.log('D3'); + console.log(localStreamStoreValue); + soundMeter.connectToSource(mediaStream, new AudioContext()); + let error = false; + + timeout = setInterval(() => { + console.log('local time interval'); + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } + } + }, 100); + } + }); + + return () => { + console.log('UNSUBSCRIBE FROM LOCAL STREAM STORE'); + unsubscribe(); + clearInterval(timeout); + } +}); + /** * Device list */ diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index fab45a71..ca808365 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -62,6 +62,7 @@ export class VideoPeer extends Peer { this.uniqueId = "video_" + this.userId; this.streamStore = readable(null, (set) => { + console.log('STREAM STORE INITIALIZE'); const onStream = (stream: MediaStream | null) => { set(stream); }; @@ -73,36 +74,32 @@ export class VideoPeer extends Peer { }; }); + console.log('CREATE VOLUME STORE'); + this.volumeStore = readable(null, (set) => { let timeout: ReturnType; + console.log('VOLUME STORE INITIALIZE'); const unsubscribe = this.streamStore.subscribe((mediaStream) => { - if (mediaStream === null) { - set(null); - return; - } - const soundMeter = new SoundMeter(); - let error = false; - try { + if (mediaStream !== null && mediaStream.getAudioTracks().length > 0) { + const soundMeter = new SoundMeter(); soundMeter.connectToSource(mediaStream, new AudioContext()); - } catch (errMsg) { - console.warn(errMsg); - set(null); - return; - } - - timeout = setInterval(() => { - try { - set(soundMeter.getVolume()); - } catch (err) { - if (!error) { - console.error(err); - error = true; + let error = false; + + timeout = setInterval(() => { + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } } - } - }, 100); + }, 100); + } }); return () => { + console.log('UNSUBSCRIBE'); unsubscribe(); clearInterval(timeout); } From 40aae25e11f0785d99523bd593aaf44cd32439bb Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 3 Feb 2022 13:43:20 +0100 Subject: [PATCH 06/15] listen to local volume change only if in bubble conversation --- front/src/Phaser/Game/GameScene.ts | 29 +++++++++++++--------- front/src/Stores/MediaStore.ts | 39 +++++++++++++++--------------- front/src/WebRtc/VideoPeer.ts | 37 +++++++++++++--------------- 3 files changed, 54 insertions(+), 51 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bdcf976a..bf762fec 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -169,7 +169,9 @@ export class GameScene extends DirtyScene { private peerStoreUnsubscribe!: Unsubscriber; private emoteUnsubscribe!: Unsubscriber; private emoteMenuUnsubscribe!: Unsubscriber; - private volumeStoreUnsubscribes: Map = new Map(); + + private volumeStoreUnsubscribers: Map = new Map(); + private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private followUsersColorStoreUnsubscribe!: Unsubscriber; private biggestAvailableAreaStoreUnsubscribe!: () => void; @@ -637,20 +639,13 @@ export class GameScene extends DirtyScene { this.connect(); } - localVolumeStore.subscribe((volume) => { - if (volume) { - this.CurrentPlayer.showIconTalk(volume > 5); - this.markDirty(); // should be dirty from animation - } - }); - let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { - this.volumeStoreUnsubscribes.forEach(unsubscribe => unsubscribe()); - this.volumeStoreUnsubscribes.clear(); + this.volumeStoreUnsubscribers.forEach(unsubscribe => unsubscribe()); + this.volumeStoreUnsubscribers.clear(); for (const [key, videoStream] of peers) { - this.volumeStoreUnsubscribes.set(key, videoStream.volumeStore.subscribe((volume) => { + this.volumeStoreUnsubscribers.set(key, videoStream.volumeStore.subscribe((volume) => { if (volume) { console.log(`${key}: ${volume}`); this.MapPlayersByKey.get(key)?.showIconTalk(volume > 5); @@ -665,6 +660,18 @@ export class GameScene extends DirtyScene { } else if (newPeerNumber < oldPeerNumber) { this.playSound("audio-webrtc-out"); } + if (newPeerNumber > 0) { + this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { + if (volume) { + this.CurrentPlayer.showIconTalk(volume > 5); + this.markDirty(); // should be dirty from animation + } + }); + } else { + if (this.localVolumeStoreUnsubscriber) { + this.localVolumeStoreUnsubscriber(); + } + } oldPeerNumber = newPeerNumber; }); diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index fe83d41a..df275e39 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -546,36 +546,35 @@ export const obtainedMediaConstraintStore = derived(null, (set) => { let timeout: ReturnType; const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => { - console.log('LOCAL VOLUME STORE SUBSCRIBE TO LOCAL STREAM STORE'); + clearInterval(timeout); if (localStreamStoreValue.type === "error") { - console.log('D1'); set(null); return; } const soundMeter = new SoundMeter(); const mediaStream = localStreamStoreValue.stream; - if (mediaStream !== null && mediaStream.getAudioTracks().length > 0) { - console.log('D3'); - console.log(localStreamStoreValue); - soundMeter.connectToSource(mediaStream, new AudioContext()); - let error = false; - - timeout = setInterval(() => { - console.log('local time interval'); - try { - set(soundMeter.getVolume()); - } catch (err) { - if (!error) { - console.error(err); - error = true; - } - } - }, 100); + + if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) { + set(null); + return; } + + soundMeter.connectToSource(mediaStream, new AudioContext()); + let error = false; + + timeout = setInterval(() => { + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } + } + }, 100); }); return () => { - console.log('UNSUBSCRIBE FROM LOCAL STREAM STORE'); unsubscribe(); clearInterval(timeout); } diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index ca808365..a955f32c 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -62,7 +62,6 @@ export class VideoPeer extends Peer { this.uniqueId = "video_" + this.userId; this.streamStore = readable(null, (set) => { - console.log('STREAM STORE INITIALIZE'); const onStream = (stream: MediaStream | null) => { set(stream); }; @@ -74,32 +73,30 @@ export class VideoPeer extends Peer { }; }); - console.log('CREATE VOLUME STORE'); - this.volumeStore = readable(null, (set) => { let timeout: ReturnType; - console.log('VOLUME STORE INITIALIZE'); const unsubscribe = this.streamStore.subscribe((mediaStream) => { - if (mediaStream !== null && mediaStream.getAudioTracks().length > 0) { - const soundMeter = new SoundMeter(); - soundMeter.connectToSource(mediaStream, new AudioContext()); - let error = false; - - timeout = setInterval(() => { - try { - set(soundMeter.getVolume()); - } catch (err) { - if (!error) { - console.error(err); - error = true; - } - } - }, 100); + if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) { + set(null); + return; } + const soundMeter = new SoundMeter(); + soundMeter.connectToSource(mediaStream, new AudioContext()); + let error = false; + + timeout = setInterval(() => { + try { + set(soundMeter.getVolume()); + } catch (err) { + if (!error) { + console.error(err); + error = true; + } + } + }, 100); }); return () => { - console.log('UNSUBSCRIBE'); unsubscribe(); clearInterval(timeout); } From a4cd626c414f23ff48e0c83a9286ff5cded1ec13 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 3 Feb 2022 14:51:47 +0100 Subject: [PATCH 07/15] SoundMeterWidget no longer instantiate new SoundMeter --- .../EnableCamera/EnableCameraScene.svelte | 15 +++++-- .../HorizontalSoundMeterWidget.svelte | 45 +------------------ front/src/Components/MyCamera.svelte | 16 +++++-- front/src/Components/SoundMeterWidget.svelte | 45 +------------------ .../src/Components/Video/VideoMediaBox.svelte | 14 +++++- 5 files changed, 40 insertions(+), 95 deletions(-) diff --git a/front/src/Components/EnableCamera/EnableCameraScene.svelte b/front/src/Components/EnableCamera/EnableCameraScene.svelte index 4e8fa9bd..2ddf648d 100644 --- a/front/src/Components/EnableCamera/EnableCameraScene.svelte +++ b/front/src/Components/EnableCamera/EnableCameraScene.svelte @@ -5,6 +5,7 @@ audioConstraintStore, cameraListStore, localStreamStore, + localVolumeStore, microphoneListStore, videoConstraintStore, } from "../../Stores/MediaStore"; @@ -18,6 +19,7 @@ export let game: Game; let selectedCamera: string | undefined = undefined; let selectedMicrophone: string | undefined = undefined; + let volume = 0; const enableCameraScene = game.scene.getScene(EnableCameraSceneName) as EnableCameraScene; @@ -38,7 +40,7 @@ let stream: MediaStream | null; - const unsubscribe = localStreamStore.subscribe((value) => { + const unsubscribeLocalStreamStore = localStreamStore.subscribe((value) => { if (value.type === "success") { stream = value.stream; @@ -59,7 +61,14 @@ } }); - onDestroy(unsubscribe); + const unsubscribeLocalVolumeStore = localVolumeStore.subscribe((value) => { + volume = value ?? 0; + }); + + onDestroy(() => { + unsubscribeLocalVolumeStore(); + unsubscribeLocalStreamStore(); + }); function normalizeDeviceName(label: string): string { // remove IDs (that can appear in Chrome, like: "HD Pro Webcam (4df7:4eda)" @@ -86,7 +95,7 @@ {/if} - +
{#if $cameraListStore.length > 1} diff --git a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte index f5e9439f..1e8000ba 100644 --- a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte +++ b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte @@ -1,50 +1,9 @@ -
+
{#each [...Array(NB_BARS).keys()] as i (i)}
{/each} diff --git a/front/src/Components/MyCamera.svelte b/front/src/Components/MyCamera.svelte index e84d763d..f0952084 100644 --- a/front/src/Components/MyCamera.svelte +++ b/front/src/Components/MyCamera.svelte @@ -1,5 +1,5 @@
diff --git a/front/src/Components/Video/VideoMediaBox.svelte b/front/src/Components/Video/VideoMediaBox.svelte index e5199a37..4cf629aa 100644 --- a/front/src/Components/Video/VideoMediaBox.svelte +++ b/front/src/Components/Video/VideoMediaBox.svelte @@ -11,13 +11,14 @@ import type { Streamable } from "../../Stores/StreamableCollectionStore"; import Woka from "../Woka/Woka.svelte"; - import { onMount } from "svelte"; + import { onDestroy, onMount } from "svelte"; import { isMediaBreakpointOnly } from "../../Utils/BreakpointsUtils"; export let clickable = false; export let peer: VideoPeer; let streamStore = peer.streamStore; + let volumeStore = peer.volumeStore; let name = peer.userName; let statusStore = peer.statusStore; let constraintStore = peer.constraintsStore; @@ -29,6 +30,11 @@ let embedScreen: EmbedScreen; let videoContainer: HTMLDivElement; let minimized = isMediaBreakpointOnly("md"); + let volume = 0; + + const unsubscribe = volumeStore.subscribe((value) => { + volume = value ?? 0; + }); if (peer) { embedScreen = { @@ -48,6 +54,10 @@ onMount(() => { resizeObserver.observe(videoContainer); }); + + onDestroy(() => { + unsubscribe(); + });
{#if $constraintStore && $constraintStore.audio !== false} - + {/if}
From d087bc02e951cdaac0396a07b441b6691952dc3b Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 3 Feb 2022 16:00:29 +0100 Subject: [PATCH 08/15] TalkIcon animation --- front/src/Phaser/Components/TalkIcon.ts | 57 +++++++++++++++++++++++++ front/src/Phaser/Entity/Character.ts | 13 +++--- front/src/Phaser/Game/GameScene.ts | 7 +-- 3 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 front/src/Phaser/Components/TalkIcon.ts diff --git a/front/src/Phaser/Components/TalkIcon.ts b/front/src/Phaser/Components/TalkIcon.ts new file mode 100644 index 00000000..02fa71fc --- /dev/null +++ b/front/src/Phaser/Components/TalkIcon.ts @@ -0,0 +1,57 @@ +import { Easing } from '../../types'; + +export class TalkIcon extends Phaser.GameObjects.Image { + + private shown: boolean; + private showAnimationTween?: Phaser.Tweens.Tween; + + private defaultPosition: { x: number, y: number }; + private defaultScale: number; + + constructor(scene: Phaser.Scene, x: number, y: number) { + super(scene, x, y, 'iconTalk'); + + this.defaultPosition = { x, y }; + this.defaultScale = 0.15; + + this.shown = false; + this.setAlpha(0); + this.setScale(this.defaultScale); + + this.scene.add.existing(this); + } + + public show(show: boolean = true): void { + if (this.shown === show) { + return; + } + this.showAnimation(show); + } + + private showAnimation(show: boolean = true) { + if (this.showAnimationTween?.isPlaying()) { + return; + } + this.shown = show; + if (show) { + this.y += 50; + this.scale = 0.05; + this.alpha = 0; + } + this.showAnimationTween = this.scene.tweens.add({ + targets: [ this ], + duration: 350, + alpha: show ? 1 : 0, + y: this.defaultPosition.y, + scale: this.defaultScale, + ease: Easing.BackEaseOut, + onComplete: () => { + this.showAnimationTween = undefined; + } + }); + } + + public isShown(): boolean { + return this.shown; + } +} diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index fd264f03..85ba8779 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -17,6 +17,7 @@ import { Unsubscriber, Writable, writable } from "svelte/store"; import { createColorStore } from "../../Stores/OutlineColorStore"; import type { OutlineableInterface } from "../Game/OutlineableInterface"; import type CancelablePromise from "cancelable-promise"; +import { TalkIcon } from '../Components/TalkIcon'; const playerNameY = -25; @@ -33,7 +34,7 @@ const interactiveRadius = 35; export abstract class Character extends Container implements OutlineableInterface { private bubble: SpeechBubble | null = null; private readonly playerNameText: Text; - private readonly iconTalk: Phaser.GameObjects.Image; + private readonly talkIcon: TalkIcon; public playerName: string; public sprites: Map; protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; @@ -104,10 +105,8 @@ export abstract class Character extends Container implements OutlineableInterfac }, }); - this.iconTalk = new Phaser.GameObjects.Image(scene, 0, -45, 'iconTalk') - .setScale(0.15) - .setVisible(false); - this.add(this.iconTalk); + this.talkIcon = new TalkIcon(scene, 0, -45); + this.add(this.talkIcon); if (isClickable) { this.setInteractive({ @@ -193,8 +192,8 @@ export abstract class Character extends Container implements OutlineableInterfac }); } - public showIconTalk(show: boolean = true): void { - this.iconTalk.setVisible(show); + public showTalkIcon(show: boolean = true): void { + this.talkIcon.show(show); } public addCompanion(name: string, texturePromise?: Promise): void { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bf762fec..9ddb5b96 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -647,9 +647,7 @@ export class GameScene extends DirtyScene { for (const [key, videoStream] of peers) { this.volumeStoreUnsubscribers.set(key, videoStream.volumeStore.subscribe((volume) => { if (volume) { - console.log(`${key}: ${volume}`); - this.MapPlayersByKey.get(key)?.showIconTalk(volume > 5); - this.markDirty(); // should be dirty from animation + this.MapPlayersByKey.get(key)?.showTalkIcon(volume > 5); } })); } @@ -663,8 +661,7 @@ export class GameScene extends DirtyScene { if (newPeerNumber > 0) { this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { if (volume) { - this.CurrentPlayer.showIconTalk(volume > 5); - this.markDirty(); // should be dirty from animation + this.CurrentPlayer.showTalkIcon(volume > 5); } }); } else { From 330b6ea1c58f2869cffec5166ea4424e7957435e Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 15:10:20 +0100 Subject: [PATCH 09/15] prettier --- .../EnableCamera/HorizontalSoundMeterWidget.svelte | 1 - front/src/Phaser/Components/TalkIcon.ts | 11 +++++------ front/src/Stores/MediaStore.ts | 6 +++--- front/src/WebRtc/VideoPeer.ts | 8 ++++---- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte index 1e8000ba..b468bffd 100644 --- a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte +++ b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte @@ -1,5 +1,4 @@
{#if $constraintStore && $constraintStore.audio !== false} - + {/if}
diff --git a/front/src/Phaser/Components/SoundMeter.ts b/front/src/Phaser/Components/SoundMeter.ts index 1d97be50..6e12912f 100644 --- a/front/src/Phaser/Components/SoundMeter.ts +++ b/front/src/Phaser/Components/SoundMeter.ts @@ -1,4 +1,4 @@ -import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context"; +import { AudioContext, IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context"; /** * Class to measure the sound volume of a media stream @@ -6,41 +6,15 @@ import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from " export class SoundMeter { private instant: number; private clip: number; - //private script: ScriptProcessorNode; private analyser: IAnalyserNode | undefined; private dataArray: Uint8Array | undefined; private context: IAudioContext | undefined; private source: IMediaStreamAudioSourceNode | undefined; - constructor() { + constructor(mediaStream: MediaStream) { this.instant = 0.0; this.clip = 0.0; - //this.script = context.createScriptProcessor(2048, 1, 1); - } - - private init(context: IAudioContext) { - this.context = context; - this.analyser = this.context.createAnalyser(); - - this.analyser.fftSize = 2048; - const bufferLength = this.analyser.fftSize; - this.dataArray = new Uint8Array(bufferLength); - } - - public connectToSource(stream: MediaStream, context: IAudioContext): void { - if (this.source !== undefined) { - this.stop(); - } - - this.init(context); - - this.source = this.context?.createMediaStreamSource(stream); - if (this.analyser !== undefined) { - this.source?.connect(this.analyser); - } - //analyser.connect(distortion); - //distortion.connect(this.context.destination); - //this.analyser.connect(this.context.destination); + this.connectToSource(mediaStream, new AudioContext()); } public getVolume(): number { @@ -78,4 +52,29 @@ export class SoundMeter { this.dataArray = undefined; this.source = undefined; } + + private init(context: IAudioContext) { + this.context = context; + this.analyser = this.context.createAnalyser(); + + this.analyser.fftSize = 2048; + const bufferLength = this.analyser.fftSize; + this.dataArray = new Uint8Array(bufferLength); + } + + private connectToSource(stream: MediaStream, context: IAudioContext): void { + if (this.source !== undefined) { + this.stop(); + } + + this.init(context); + + this.source = this.context?.createMediaStreamSource(stream); + if (this.analyser !== undefined) { + this.source?.connect(this.analyser); + } + //analyser.connect(distortion); + //distortion.connect(this.context.destination); + //this.analyser.connect(this.context.destination); + } } diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index be36ca3f..9494eb7e 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -543,23 +543,21 @@ export const obtainedMediaConstraintStore = derived(null, (set) => { +export const localVolumeStore = readable(undefined, (set) => { let timeout: ReturnType; const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => { clearInterval(timeout); if (localStreamStoreValue.type === "error") { - set(null); + set(undefined); return; } - const soundMeter = new SoundMeter(); const mediaStream = localStreamStoreValue.stream; if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) { - set(null); + set(undefined); return; } - - soundMeter.connectToSource(mediaStream, new AudioContext()); + const soundMeter = new SoundMeter(mediaStream); let error = false; timeout = setInterval(() => { diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 12bd2e57..f0ebffdd 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -36,7 +36,7 @@ export class VideoPeer extends Peer { private onBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription; public readonly streamStore: Readable; - public readonly volumeStore: Readable; + public readonly volumeStore: Readable; public readonly statusStore: Readable; public readonly constraintsStore: Readable; private newMessageSubscribtion: Subscription | undefined; @@ -73,15 +73,14 @@ export class VideoPeer extends Peer { }; }); - this.volumeStore = readable(null, (set) => { + this.volumeStore = readable(undefined, (set) => { let timeout: ReturnType; const unsubscribe = this.streamStore.subscribe((mediaStream) => { if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) { - set(null); + set(undefined); return; } - const soundMeter = new SoundMeter(); - soundMeter.connectToSource(mediaStream, new AudioContext()); + const soundMeter = new SoundMeter(mediaStream); let error = false; timeout = setInterval(() => { From 639c456540af41a3d63849e75651f93adb44e06c Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 14 Feb 2022 10:37:13 +0100 Subject: [PATCH 12/15] use png instead of svg for talk icon --- front/dist/resources/icons/icon_talking.svg | 1 - front/src/Phaser/Components/TalkIcon.ts | 2 +- front/src/Phaser/Game/GameScene.ts | 7 ++++--- 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 front/dist/resources/icons/icon_talking.svg diff --git a/front/dist/resources/icons/icon_talking.svg b/front/dist/resources/icons/icon_talking.svg deleted file mode 100644 index 2f1d03e3..00000000 --- a/front/dist/resources/icons/icon_talking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/Phaser/Components/TalkIcon.ts b/front/src/Phaser/Components/TalkIcon.ts index 62257173..78e9379f 100644 --- a/front/src/Phaser/Components/TalkIcon.ts +++ b/front/src/Phaser/Components/TalkIcon.ts @@ -11,7 +11,7 @@ export class TalkIcon extends Phaser.GameObjects.Image { super(scene, x, y, "iconTalk"); this.defaultPosition = { x, y }; - this.defaultScale = 0.15; + this.defaultScale = 0.3; this.shown = false; this.setAlpha(0); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 6a92f6d1..1de19a9f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -251,7 +251,7 @@ export class GameScene extends DirtyScene { loadCustomTexture(this.load, texture).catch((e) => console.error(e)); } } - this.load.svg("iconTalk", "/resources/icons/icon_talking.svg"); + this.load.image("iconTalk", "/resources/icons/icon_talking.png"); if (touchScreenManager.supportTouchScreen) { this.load.image(joystickBaseKey, joystickBaseImg); @@ -646,6 +646,7 @@ export class GameScene extends DirtyScene { this.connect(); } + const talkIconVolumeTreshold = 10; let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { this.volumeStoreUnsubscribers.forEach((unsubscribe) => unsubscribe()); @@ -656,7 +657,7 @@ export class GameScene extends DirtyScene { key, videoStream.volumeStore.subscribe((volume) => { if (volume) { - this.MapPlayersByKey.get(key)?.showTalkIcon(volume > 5); + this.MapPlayersByKey.get(key)?.showTalkIcon(volume > talkIconVolumeTreshold); } }) ); @@ -671,7 +672,7 @@ export class GameScene extends DirtyScene { if (newPeerNumber > 0) { this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { if (volume) { - this.CurrentPlayer.showTalkIcon(volume > 5); + this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold); } }); } else { From 6f416222639e8fb17e32269934525f5650c9489a Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 14 Feb 2022 16:26:50 +0100 Subject: [PATCH 13/15] unignoring resources directory --- front/dist/.gitignore | 1 + front/dist/resources/icons/icon_talking.png | Bin 0 -> 11283 bytes 2 files changed, 1 insertion(+) create mode 100644 front/dist/resources/icons/icon_talking.png diff --git a/front/dist/.gitignore b/front/dist/.gitignore index bc766e57..beb1ee19 100644 --- a/front/dist/.gitignore +++ b/front/dist/.gitignore @@ -4,3 +4,4 @@ index.html style.*.css !env-config.template.js *.png +!/resources/** diff --git a/front/dist/resources/icons/icon_talking.png b/front/dist/resources/icons/icon_talking.png new file mode 100644 index 0000000000000000000000000000000000000000..9d566f4e418689f8732982f9e4c48e4432562d0a GIT binary patch literal 11283 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 v&uazO@UP*P;1v4XEjtJ Date: Wed, 16 Feb 2022 14:56:59 +0100 Subject: [PATCH 14/15] fixed issue with chat indicator persisting after being out of conversation --- front/src/Phaser/Game/GameScene.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 3be00836..9dcc4063 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -670,16 +670,19 @@ export class GameScene extends DirtyScene { this.playSound("audio-webrtc-out"); } if (newPeerNumber > 0) { - this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { - if (volume) { - this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold); - } - }); + if (!this.localVolumeStoreUnsubscriber) { + this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { + if (volume) { + this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold); + } + }); + } } else { this.CurrentPlayer.showTalkIcon(false); this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false)); if (this.localVolumeStoreUnsubscriber) { this.localVolumeStoreUnsubscriber(); + this.localVolumeStoreUnsubscriber = undefined; } } oldPeerNumber = newPeerNumber; From e590cee7536be9e2b3884b34888f3881f487b856 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 16 Feb 2022 15:55:07 +0100 Subject: [PATCH 15/15] forcing close talkIcon indicator when leaving bubble conversation --- front/src/Phaser/Components/TalkIcon.ts | 12 +++++++----- front/src/Phaser/Entity/Character.ts | 4 ++-- front/src/Phaser/Game/GameScene.ts | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/front/src/Phaser/Components/TalkIcon.ts b/front/src/Phaser/Components/TalkIcon.ts index 78e9379f..12b4ebe3 100644 --- a/front/src/Phaser/Components/TalkIcon.ts +++ b/front/src/Phaser/Components/TalkIcon.ts @@ -20,15 +20,17 @@ export class TalkIcon extends Phaser.GameObjects.Image { this.scene.add.existing(this); } - public show(show: boolean = true): void { - if (this.shown === show) { + public show(show: boolean = true, forceClose: boolean = false): void { + if (this.shown === show && !forceClose) { return; } - this.showAnimation(show); + this.showAnimation(show, forceClose); } - private showAnimation(show: boolean = true) { - if (this.showAnimationTween?.isPlaying()) { + private showAnimation(show: boolean = true, forceClose: boolean = false) { + if (forceClose && !show) { + this.showAnimationTween?.stop(); + } else if (this.showAnimationTween?.isPlaying()) { return; } this.shown = show; diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 3987c889..00067897 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -213,8 +213,8 @@ export abstract class Character extends Container implements OutlineableInterfac }); } - public showTalkIcon(show: boolean = true): void { - this.talkIcon.show(show); + public showTalkIcon(show: boolean = true, forceClose: boolean = false): void { + this.talkIcon.show(show, forceClose); } public addCompanion(name: string, texturePromise?: CancelablePromise): void { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9dcc4063..c377b20b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -678,8 +678,8 @@ export class GameScene extends DirtyScene { }); } } else { - this.CurrentPlayer.showTalkIcon(false); - this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false)); + this.CurrentPlayer.showTalkIcon(false, true); + this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true)); if (this.localVolumeStoreUnsubscriber) { this.localVolumeStoreUnsubscriber(); this.localVolumeStoreUnsubscriber = undefined;