WIP: Migrate AudioManager in Svelte (#1325)
* Migrate AudioManager in Svelte * use import type when needed
This commit is contained in:
parent
ac282db1ac
commit
e0fb31fc58
@ -3,9 +3,9 @@ import {
|
|||||||
isTriggerActionMessageEvent,
|
isTriggerActionMessageEvent,
|
||||||
removeActionMessage,
|
removeActionMessage,
|
||||||
triggerActionMessage,
|
triggerActionMessage,
|
||||||
} from './TriggerActionMessageEvent';
|
} from "./TriggerActionMessageEvent";
|
||||||
|
|
||||||
import * as tg from 'generic-type-guard';
|
import * as tg from "generic-type-guard";
|
||||||
|
|
||||||
const isTriggerMessageEventObject = new tg.IsInterface()
|
const isTriggerMessageEventObject = new tg.IsInterface()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
|
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
|
||||||
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
|
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
|
||||||
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
|
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
|
||||||
|
import {audioManagerVisibilityStore} from "../Stores/AudioManagerStore";
|
||||||
|
import AudioManager from "./AudioManager/AudioManager.svelte"
|
||||||
|
|
||||||
export let game: Game;
|
export let game: Game;
|
||||||
|
|
||||||
@ -81,6 +83,11 @@
|
|||||||
<AudioPlaying url={$soundPlayingStore} />
|
<AudioPlaying url={$soundPlayingStore} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $audioManagerVisibilityStore}
|
||||||
|
<div>
|
||||||
|
<AudioManager></AudioManager>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{#if $layoutManagerVisibilityStore}
|
{#if $layoutManagerVisibilityStore}
|
||||||
<div>
|
<div>
|
||||||
<LayoutManager></LayoutManager>
|
<LayoutManager></LayoutManager>
|
||||||
|
119
front/src/Components/AudioManager/AudioManager.svelte
Normal file
119
front/src/Components/AudioManager/AudioManager.svelte
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import audioImg from "../images/audio.svg";
|
||||||
|
import audioMuteImg from "../images/audio-mute.svg";
|
||||||
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
|
import type { audioManagerVolume } from "../../Stores/AudioManagerStore";
|
||||||
|
import {
|
||||||
|
audioManagerFileStore,
|
||||||
|
audioManagerVolumeStore,
|
||||||
|
} from "../../Stores/AudioManagerStore";
|
||||||
|
import {get} from "svelte/store";
|
||||||
|
import type { Unsubscriber } from "svelte/store";
|
||||||
|
import {onDestroy, onMount} from "svelte";
|
||||||
|
|
||||||
|
let HTMLAudioPlayer: HTMLAudioElement;
|
||||||
|
let unsubscriberFileStore: Unsubscriber | null = null;
|
||||||
|
let unsubscriberVolumeStore: Unsubscriber | null = null;
|
||||||
|
|
||||||
|
let volume: number = 1;
|
||||||
|
let decreaseWhileTalking: boolean = true;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
unsubscriberFileStore = audioManagerFileStore.subscribe(() =>{
|
||||||
|
HTMLAudioPlayer.pause();
|
||||||
|
HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop;
|
||||||
|
HTMLAudioPlayer.volume = get(audioManagerVolumeStore).volume;
|
||||||
|
HTMLAudioPlayer.muted = get(audioManagerVolumeStore).muted;
|
||||||
|
HTMLAudioPlayer.play();
|
||||||
|
});
|
||||||
|
unsubscriberVolumeStore = audioManagerVolumeStore.subscribe((audioManager: audioManagerVolume) => {
|
||||||
|
const reduceVolume = audioManager.talking && audioManager.decreaseWhileTalking;
|
||||||
|
if (reduceVolume && !audioManager.volumeReduced) {
|
||||||
|
audioManager.volume *= 0.5;
|
||||||
|
} else if (!reduceVolume && audioManager.volumeReduced) {
|
||||||
|
audioManager.volume *= 2.0;
|
||||||
|
}
|
||||||
|
audioManager.volumeReduced = reduceVolume;
|
||||||
|
HTMLAudioPlayer.volume = audioManager.volume;
|
||||||
|
HTMLAudioPlayer.muted = audioManager.muted;
|
||||||
|
HTMLAudioPlayer.loop = audioManager.loop;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (unsubscriberFileStore) {
|
||||||
|
unsubscriberFileStore();
|
||||||
|
}
|
||||||
|
if (unsubscriberVolumeStore) {
|
||||||
|
unsubscriberVolumeStore();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function onMute() {
|
||||||
|
audioManagerVolumeStore.setMuted(!get(audioManagerVolumeStore).muted);
|
||||||
|
localUserStore.setAudioPlayerMuted(get(audioManagerVolumeStore).muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVolume() {
|
||||||
|
audioManagerVolumeStore.setVolume(volume)
|
||||||
|
localUserStore.setAudioPlayerVolume(get(audioManagerVolumeStore).volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDecrease() {
|
||||||
|
audioManagerVolumeStore.setDecreaseWhileTalking(decreaseWhileTalking);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="main-audio-manager nes-container is-rounded">
|
||||||
|
<div class="audio-manager-player-volume">
|
||||||
|
<img src={$audioManagerVolumeStore.muted ? audioMuteImg : audioImg} alt="player volume" on:click={onMute}>
|
||||||
|
<input type="range" min="0" max="1" step="0.025" bind:value={volume} on:change={setVolume}>
|
||||||
|
</div>
|
||||||
|
<div class="audio-manager-reduce-conversation">
|
||||||
|
<label>
|
||||||
|
reduce in conversations
|
||||||
|
<input type="checkbox" bind:checked={decreaseWhileTalking} on:change={setDecrease}>
|
||||||
|
</label>
|
||||||
|
<section class="audio-manager-file">
|
||||||
|
<audio class="audio-manager-audioplayer" bind:this={HTMLAudioPlayer}>
|
||||||
|
<source src={$audioManagerFileStore}>
|
||||||
|
</audio>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div.main-audio-manager.nes-container.is-rounded {
|
||||||
|
position: relative;
|
||||||
|
top: 0.5rem;
|
||||||
|
max-height: clamp(150px, 10vh, 15vh); //replace @media for small screen
|
||||||
|
width: clamp(200px, 15vw, 15vw);
|
||||||
|
padding: 3px 3px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
background-color: rgb(0,0,0,0.5);
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 50% 50%;
|
||||||
|
color: whitesmoke;
|
||||||
|
text-align: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
div.audio-manager-player-volume {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50px 1fr;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.audio-manager-file {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
3
front/src/Components/images/audio-mute.svg
Normal file
3
front/src/Components/images/audio-mute.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="2em" height="2em" viewBox="0 0 16 16" class="bi bi-volume-up" fill="white" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zM6 5.04L4.312 6.39A.5.5 0 0 1 4 6.5H2v3h2a.5.5 0 0 1 .312.11L6 10.96V5.04z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 376 B |
8
front/src/Components/images/audio.svg
Normal file
8
front/src/Components/images/audio.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="2em" height="2em" viewBox="0 0 16 16" class="bi bi-volume-up" fill="white" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zM6 5.04L4.312 6.39A.5.5 0 0 1 4 6.5H2v3h2a.5.5 0 0 1 .312.11L6 10.96V5.04z" />
|
||||||
|
<g>
|
||||||
|
<path d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" />
|
||||||
|
<path d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" />
|
||||||
|
<path d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 885 B |
@ -1,90 +0,0 @@
|
|||||||
|
|
||||||
const IGNORED_KEYS = new Set([
|
|
||||||
'Esc',
|
|
||||||
'Escape',
|
|
||||||
'Alt',
|
|
||||||
'Meta',
|
|
||||||
'Control',
|
|
||||||
'Ctrl',
|
|
||||||
'Space',
|
|
||||||
'Backspace'
|
|
||||||
])
|
|
||||||
|
|
||||||
export class TextInput extends Phaser.GameObjects.BitmapText {
|
|
||||||
private minUnderLineLength = 4;
|
|
||||||
private underLine: Phaser.GameObjects.Text;
|
|
||||||
private domInput = document.createElement('input');
|
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string,
|
|
||||||
onChange: (text: string) => void) {
|
|
||||||
super(scene, x, y, 'main_font', text, 32);
|
|
||||||
this.setOrigin(0.5).setCenterAlign();
|
|
||||||
this.scene.add.existing(this);
|
|
||||||
|
|
||||||
const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'};
|
|
||||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style);
|
|
||||||
this.underLine.setOrigin(0.5);
|
|
||||||
|
|
||||||
this.domInput.maxLength = maxLength;
|
|
||||||
this.domInput.style.opacity = "0";
|
|
||||||
if (text) {
|
|
||||||
this.domInput.value = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domInput.addEventListener('keydown', event => {
|
|
||||||
if (IGNORED_KEYS.has(event.key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.domInput.addEventListener('input', (event) => {
|
|
||||||
if (event.defaultPrevented) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.text = this.domInput.value;
|
|
||||||
this.underLine.text = this.getUnderLineBody(this.text.length);
|
|
||||||
onChange(this.text);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.body.append(this.domInput);
|
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private getUnderLineBody(textLength:number): string {
|
|
||||||
if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
|
|
||||||
let text = '_______';
|
|
||||||
for (let i = this.minUnderLineLength; i < textLength; i++) {
|
|
||||||
text += '__';
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
getText(): string {
|
|
||||||
return this.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
setX(x: number): this {
|
|
||||||
super.setX(x);
|
|
||||||
this.underLine.x = x;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setY(y: number): this {
|
|
||||||
super.setY(y);
|
|
||||||
this.underLine.y = y+1;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.domInput.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
super.destroy();
|
|
||||||
this.domInput.remove();
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,7 +32,6 @@ import type { RoomConnection } from "../../Connexion/RoomConnection";
|
|||||||
import { Room } from "../../Connexion/Room";
|
import { Room } from "../../Connexion/Room";
|
||||||
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
|
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
|
||||||
import { urlManager } from "../../Url/UrlManager";
|
import { urlManager } from "../../Url/UrlManager";
|
||||||
import { audioManager } from "../../WebRtc/AudioManager";
|
|
||||||
import { TextureError } from "../../Exception/TextureError";
|
import { TextureError } from "../../Exception/TextureError";
|
||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||||
@ -84,6 +83,11 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
|
|||||||
import { SharedVariablesManager } from "./SharedVariablesManager";
|
import { SharedVariablesManager } from "./SharedVariablesManager";
|
||||||
import { playersStore } from "../../Stores/PlayersStore";
|
import { playersStore } from "../../Stores/PlayersStore";
|
||||||
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
||||||
|
import {
|
||||||
|
audioManagerFileStore,
|
||||||
|
audioManagerVisibilityStore,
|
||||||
|
audioManagerVolumeStore,
|
||||||
|
} from "../../Stores/AudioManagerStore";
|
||||||
import { PropertyUtils } from "../Map/PropertyUtils";
|
import { PropertyUtils } from "../Map/PropertyUtils";
|
||||||
import Tileset = Phaser.Tilemaps.Tileset;
|
import Tileset = Phaser.Tilemaps.Tileset;
|
||||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||||
@ -727,12 +731,12 @@ export class GameScene extends DirtyScene {
|
|||||||
this.simplePeer.registerPeerConnectionListener({
|
this.simplePeer.registerPeerConnectionListener({
|
||||||
onConnect(peer) {
|
onConnect(peer) {
|
||||||
//self.openChatIcon.setVisible(true);
|
//self.openChatIcon.setVisible(true);
|
||||||
audioManager.decreaseVolume();
|
audioManagerVolumeStore.setTalking(true);
|
||||||
},
|
},
|
||||||
onDisconnect(userId: number) {
|
onDisconnect(userId: number) {
|
||||||
if (self.simplePeer.getNbConnections() === 0) {
|
if (self.simplePeer.getNbConnections() === 0) {
|
||||||
//self.openChatIcon.setVisible(false);
|
//self.openChatIcon.setVisible(false);
|
||||||
audioManager.restoreVolume();
|
audioManagerVolumeStore.setTalking(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -898,14 +902,16 @@ export class GameScene extends DirtyScene {
|
|||||||
const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined;
|
const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined;
|
||||||
const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined;
|
const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined;
|
||||||
newValue === undefined
|
newValue === undefined
|
||||||
? audioManager.unloadAudio()
|
? audioManagerFileStore.unloadAudio()
|
||||||
: audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop);
|
: audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), volume, loop);
|
||||||
|
audioManagerVisibilityStore.set(!(newValue === undefined));
|
||||||
});
|
});
|
||||||
// TODO: This legacy property should be removed at some point
|
// TODO: This legacy property should be removed at some point
|
||||||
this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => {
|
this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => {
|
||||||
newValue === undefined
|
newValue === undefined
|
||||||
? audioManager.unloadAudio()
|
? audioManagerFileStore.unloadAudio()
|
||||||
: audioManager.playAudio(newValue, this.getMapDirUrl(), undefined, true);
|
: audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), undefined, true);
|
||||||
|
audioManagerVisibilityStore.set(!(newValue === undefined));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gameMap.onPropertyChange("zone", (newValue, oldValue) => {
|
this.gameMap.onPropertyChange("zone", (newValue, oldValue) => {
|
||||||
@ -1323,7 +1329,7 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.stopJitsi();
|
this.stopJitsi();
|
||||||
audioManager.unloadAudio();
|
audioManagerFileStore.unloadAudio();
|
||||||
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
||||||
this.connection?.closeConnection();
|
this.connection?.closeConnection();
|
||||||
this.simplePeer?.closeAllConnections();
|
this.simplePeer?.closeAllConnections();
|
||||||
|
105
front/src/Stores/AudioManagerStore.ts
Normal file
105
front/src/Stores/AudioManagerStore.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
|
export interface audioManagerVolume {
|
||||||
|
muted: boolean;
|
||||||
|
volume: number;
|
||||||
|
decreaseWhileTalking: boolean;
|
||||||
|
volumeReduced: boolean;
|
||||||
|
loop: boolean;
|
||||||
|
talking: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAudioManagerVolumeStore() {
|
||||||
|
const { subscribe, update } = writable<audioManagerVolume>({
|
||||||
|
muted: false,
|
||||||
|
volume: 1,
|
||||||
|
decreaseWhileTalking: true,
|
||||||
|
volumeReduced: false,
|
||||||
|
loop: false,
|
||||||
|
talking: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
setMuted: (newMute: boolean): void => {
|
||||||
|
update((audioPlayerVolume: audioManagerVolume) => {
|
||||||
|
audioPlayerVolume.muted = newMute;
|
||||||
|
return audioPlayerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setVolume: (newVolume: number): void => {
|
||||||
|
update((audioPlayerVolume: audioManagerVolume) => {
|
||||||
|
audioPlayerVolume.volume = newVolume;
|
||||||
|
return audioPlayerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setDecreaseWhileTalking: (newDecrease: boolean): void => {
|
||||||
|
update((audioManagerVolume: audioManagerVolume) => {
|
||||||
|
audioManagerVolume.decreaseWhileTalking = newDecrease;
|
||||||
|
return audioManagerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setVolumeReduced: (newVolumeReduced: boolean): void => {
|
||||||
|
update((audioManagerVolume: audioManagerVolume) => {
|
||||||
|
audioManagerVolume.volumeReduced = newVolumeReduced;
|
||||||
|
return audioManagerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setLoop: (newLoop: boolean): void => {
|
||||||
|
update((audioManagerVolume: audioManagerVolume) => {
|
||||||
|
audioManagerVolume.loop = newLoop;
|
||||||
|
return audioManagerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setTalking: (newTalk: boolean): void => {
|
||||||
|
update((audioManagerVolume: audioManagerVolume) => {
|
||||||
|
audioManagerVolume.talking = newTalk;
|
||||||
|
return audioManagerVolume;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAudioManagerFileStore() {
|
||||||
|
const { subscribe, update } = writable<string>("");
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
playAudio: (
|
||||||
|
url: string | number | boolean,
|
||||||
|
mapDirUrl: string,
|
||||||
|
volume: number | undefined,
|
||||||
|
loop = false
|
||||||
|
): void => {
|
||||||
|
update((file: string) => {
|
||||||
|
const audioPath = url as string;
|
||||||
|
|
||||||
|
if (audioPath.indexOf("://") > 0) {
|
||||||
|
// remote file or stream
|
||||||
|
file = audioPath;
|
||||||
|
} else {
|
||||||
|
// local file, include it relative to map directory
|
||||||
|
file = mapDirUrl + "/" + url;
|
||||||
|
}
|
||||||
|
audioManagerVolumeStore.setVolume(
|
||||||
|
volume ? Math.min(volume, get(audioManagerVolumeStore).volume) : get(audioManagerVolumeStore).volume
|
||||||
|
);
|
||||||
|
audioManagerVolumeStore.setLoop(loop);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unloadAudio: () => {
|
||||||
|
update((file: string) => {
|
||||||
|
audioManagerVolumeStore.setLoop(false);
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const audioManagerVisibilityStore = writable(false);
|
||||||
|
|
||||||
|
export const audioManagerVolumeStore = createAudioManagerVolumeStore();
|
||||||
|
|
||||||
|
export const audioManagerFileStore = createAudioManagerFileStore();
|
@ -1,188 +0,0 @@
|
|||||||
import {HtmlUtils} from "./HtmlUtils";
|
|
||||||
import {isUndefined} from "generic-type-guard";
|
|
||||||
import {localUserStore} from "../Connexion/LocalUserStore";
|
|
||||||
|
|
||||||
enum audioStates {
|
|
||||||
closed = 0,
|
|
||||||
loading = 1,
|
|
||||||
playing = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
const audioPlayerDivId = "audioplayer";
|
|
||||||
const audioPlayerCtrlId = "audioplayerctrl";
|
|
||||||
const audioPlayerVolId = "audioplayer_volume";
|
|
||||||
const audioPlayerMuteId = "audioplayer_volume_icon_playing";
|
|
||||||
const animationTime = 500;
|
|
||||||
|
|
||||||
class AudioManager {
|
|
||||||
private opened = audioStates.closed;
|
|
||||||
|
|
||||||
private audioPlayerDiv: HTMLDivElement;
|
|
||||||
private audioPlayerCtrl: HTMLDivElement;
|
|
||||||
private audioPlayerElem: HTMLAudioElement | undefined;
|
|
||||||
private audioPlayerVol: HTMLInputElement;
|
|
||||||
private audioPlayerMute: HTMLInputElement;
|
|
||||||
|
|
||||||
private volume = 1;
|
|
||||||
private muted = false;
|
|
||||||
private decreaseWhileTalking = true;
|
|
||||||
private volumeReduced = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.audioPlayerDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(audioPlayerDivId);
|
|
||||||
this.audioPlayerCtrl = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(audioPlayerCtrlId);
|
|
||||||
this.audioPlayerVol = HtmlUtils.getElementByIdOrFail<HTMLInputElement>(audioPlayerVolId);
|
|
||||||
this.audioPlayerMute = HtmlUtils.getElementByIdOrFail<HTMLInputElement>(audioPlayerMuteId);
|
|
||||||
|
|
||||||
this.volume = localUserStore.getAudioPlayerVolume();
|
|
||||||
this.audioPlayerVol.value = '' + this.volume;
|
|
||||||
|
|
||||||
this.muted = localUserStore.getAudioPlayerMuted();
|
|
||||||
if (this.muted) {
|
|
||||||
this.audioPlayerMute.classList.add('muted');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public playAudio(url: string|number|boolean, mapDirUrl: string, volume: number|undefined, loop=false): void {
|
|
||||||
const audioPath = url as string;
|
|
||||||
let realAudioPath = '';
|
|
||||||
|
|
||||||
if (audioPath.indexOf('://') > 0) {
|
|
||||||
// remote file or stream
|
|
||||||
realAudioPath = audioPath;
|
|
||||||
} else {
|
|
||||||
// local file, include it relative to map directory
|
|
||||||
realAudioPath = mapDirUrl + '/' + url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadAudio(realAudioPath, volume);
|
|
||||||
|
|
||||||
if (loop) {
|
|
||||||
this.loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private close(): void {
|
|
||||||
this.audioPlayerCtrl.classList.remove('loading');
|
|
||||||
this.audioPlayerCtrl.classList.add('hidden');
|
|
||||||
this.opened = audioStates.closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private load(): void {
|
|
||||||
this.audioPlayerCtrl.classList.remove('hidden');
|
|
||||||
this.audioPlayerCtrl.classList.add('loading');
|
|
||||||
this.opened = audioStates.loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
private open(): void {
|
|
||||||
this.audioPlayerCtrl.classList.remove('hidden', 'loading');
|
|
||||||
this.opened = audioStates.playing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeVolume(talking = false): void {
|
|
||||||
if (isUndefined(this.audioPlayerElem)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reduceVolume = talking && this.decreaseWhileTalking;
|
|
||||||
if (reduceVolume && !this.volumeReduced) {
|
|
||||||
this.volume *= 0.5;
|
|
||||||
} else if (!reduceVolume && this.volumeReduced) {
|
|
||||||
this.volume *= 2.0;
|
|
||||||
}
|
|
||||||
this.volumeReduced = reduceVolume;
|
|
||||||
|
|
||||||
this.audioPlayerElem.volume = this.volume;
|
|
||||||
this.audioPlayerVol.value = '' + this.volume;
|
|
||||||
this.audioPlayerElem.muted = this.muted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setVolume(volume: number): void {
|
|
||||||
this.volume = volume;
|
|
||||||
localUserStore.setAudioPlayerVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadAudio(url: string, volume: number|undefined): void {
|
|
||||||
this.load();
|
|
||||||
|
|
||||||
/* Solution 1, remove whole audio player */
|
|
||||||
this.audioPlayerDiv.innerHTML = ''; // necessary, if switching from one audio context to another! (else both streams would play simultaneously)
|
|
||||||
|
|
||||||
this.audioPlayerElem = document.createElement('audio');
|
|
||||||
this.audioPlayerElem.id = 'audioplayerelem';
|
|
||||||
this.audioPlayerElem.controls = false;
|
|
||||||
this.audioPlayerElem.preload = 'none';
|
|
||||||
|
|
||||||
const srcElem = document.createElement('source');
|
|
||||||
srcElem.type = "audio/mp3";
|
|
||||||
srcElem.src = url;
|
|
||||||
|
|
||||||
this.audioPlayerElem.append(srcElem);
|
|
||||||
|
|
||||||
this.audioPlayerDiv.append(this.audioPlayerElem);
|
|
||||||
this.volume = volume ? Math.min(volume, this.volume) : this.volume;
|
|
||||||
this.changeVolume();
|
|
||||||
this.audioPlayerElem.play();
|
|
||||||
|
|
||||||
const muteElem = HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_mute');
|
|
||||||
muteElem.onclick = (ev: Event) => {
|
|
||||||
this.muted = !this.muted;
|
|
||||||
this.changeVolume();
|
|
||||||
localUserStore.setAudioPlayerMuted(this.muted);
|
|
||||||
|
|
||||||
if (this.muted) {
|
|
||||||
this.audioPlayerMute.classList.add('muted');
|
|
||||||
} else {
|
|
||||||
this.audioPlayerMute.classList.remove('muted');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.audioPlayerVol.oninput = (ev: Event)=> {
|
|
||||||
this.setVolume(parseFloat((<HTMLInputElement>ev.currentTarget).value));
|
|
||||||
this.changeVolume();
|
|
||||||
|
|
||||||
(<HTMLInputElement>ev.currentTarget).blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
const decreaseElem = HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_decrease_while_talking');
|
|
||||||
decreaseElem.oninput = (ev: Event)=> {
|
|
||||||
this.decreaseWhileTalking = (<HTMLInputElement>ev.currentTarget).checked;
|
|
||||||
this.changeVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private loop(): void {
|
|
||||||
if (this.audioPlayerElem !== undefined) {
|
|
||||||
this.audioPlayerElem.loop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unloadAudio(): void {
|
|
||||||
try {
|
|
||||||
const audioElem = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audioplayerelem');
|
|
||||||
this.volume = audioElem.volume;
|
|
||||||
this.muted = audioElem.muted;
|
|
||||||
audioElem.pause();
|
|
||||||
audioElem.loop = false;
|
|
||||||
audioElem.src = "";
|
|
||||||
audioElem.innerHTML = "";
|
|
||||||
audioElem.load();
|
|
||||||
} catch (e) {
|
|
||||||
console.log('No audio element loaded to unload');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public decreaseVolume(): void {
|
|
||||||
this.changeVolume(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public restoreVolume(): void {
|
|
||||||
this.changeVolume(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const audioManager = new AudioManager();
|
|
Loading…
Reference in New Issue
Block a user