Merge pull request #541 from Informatic/feature/map-sounds
[rC3] front: implement map sounds
This commit is contained in:
commit
dea7d6dba7
58
front/dist/index.html
vendored
58
front/dist/index.html
vendored
@ -39,7 +39,7 @@
|
|||||||
<link rel="stylesheet" href="/resources/style/style.css">
|
<link rel="stylesheet" href="/resources/style/style.css">
|
||||||
<title>WorkAdventure</title>
|
<title>WorkAdventure</title>
|
||||||
</head>
|
</head>
|
||||||
<body id="body" style="margin: 0">
|
<body id="body" style="margin: 0; background-color: #000">
|
||||||
<div class="main-container" id="main-container">
|
<div class="main-container" id="main-container">
|
||||||
<!-- Create the editor container -->
|
<!-- Create the editor container -->
|
||||||
<div id="game" class="game">
|
<div id="game" class="game">
|
||||||
@ -81,6 +81,62 @@
|
|||||||
<img src="/resources/logos/megaphone.svg"/>
|
<img src="/resources/logos/megaphone.svg"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="audioplayerctrl" class="hidden">
|
||||||
|
<div class="audioplayer">
|
||||||
|
<button type="button" id="audioplayer_mute" class="fa fa-volump-up">
|
||||||
|
<svg
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
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 id="audioplayer_volume_icon_playing">
|
||||||
|
<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>
|
||||||
|
</button>
|
||||||
|
<div class="audioplayer">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
id="audioplayer_volume"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.05"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audioplayer">
|
||||||
|
<label
|
||||||
|
id="label-audioplayer_decrease_while_talking"
|
||||||
|
for="audiooplayer_decrease_while_talking"
|
||||||
|
title="decrease background volume by 50% when entering conversations"
|
||||||
|
>
|
||||||
|
autoreduce
|
||||||
|
<input type="checkbox" id="audioplayer_decrease_while_talking" checked />
|
||||||
|
</label>
|
||||||
|
<div id="audioplayer" style="visibility: hidden"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-playing">
|
||||||
|
<img src="/resources/logos/megaphone.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!--
|
<!--
|
||||||
<div id="webRtc" class="webrtc">
|
<div id="webRtc" class="webrtc">
|
||||||
<div id="activeCam" class="activeCam">
|
<div id="activeCam" class="activeCam">
|
||||||
|
144
front/dist/resources/style/style.css
vendored
144
front/dist/resources/style/style.css
vendored
@ -362,6 +362,148 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.audioplayer:first-child {
|
||||||
|
display: grid;
|
||||||
|
grid: 2rem / 4rem 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audioplayer > button, .audioplayer > div, .audioplayer > label {
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#audioplayerctrl {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 50%;
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
color: white;
|
||||||
|
transition: transform 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#audioplayer_mute {
|
||||||
|
max-width: 5rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#audioplayer_mute:focus, #audioplayer_mute:active {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#audioplayer_mute > svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
#audioplayer_volume_icon_playing.muted {
|
||||||
|
visibility: hidden;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#audioplayerctrl > #audioplayer_volume {
|
||||||
|
width: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sollte eigentlich in den aspect-ratio teil ..
|
||||||
|
*/
|
||||||
|
#audioplayerctrl.loading {
|
||||||
|
transform: translateY(-90%);
|
||||||
|
}
|
||||||
|
#audioplayerctrl.hidden {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Style Input Range
|
||||||
|
* https://www.cssportal.com/style-input-range/
|
||||||
|
*/
|
||||||
|
input[type=range] {
|
||||||
|
height: 28px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
input[type=range]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
input[type=range]::-webkit-slider-runnable-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
animate: 0.2s;
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
}
|
||||||
|
input[type=range]::-webkit-slider-thumb {
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
height: 20px;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin-top: -8.5px;
|
||||||
|
}
|
||||||
|
input[type=range]:focus::-webkit-slider-runnable-track {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
input[type=range]::-moz-range-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
animate: 0.2s;
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
}
|
||||||
|
input[type=range]::-moz-range-thumb {
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
height: 20px;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
input[type=range]::-ms-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
animate: 0.2s;
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
input[type=range]::-ms-fill-lower {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
}
|
||||||
|
input[type=range]::-ms-fill-upper {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
}
|
||||||
|
input[type=range]::-ms-thumb {
|
||||||
|
margin-top: 1px;
|
||||||
|
box-shadow: 1px 1px 1px #000000;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
height: 20px;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
input[type=range]:focus::-ms-fill-lower {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
input[type=range]:focus::-ms-fill-upper {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.game-overlay {
|
.game-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -992,4 +1134,4 @@ div.action p.action-body{
|
|||||||
0% {bottom: 40px;}
|
0% {bottom: 40px;}
|
||||||
50% {bottom: 30px;}
|
50% {bottom: 30px;}
|
||||||
100% {bottom: 40px;}
|
100% {bottom: 40px;}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ import {ResizableScene} from "../Login/ResizableScene";
|
|||||||
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 {PresentationModeIcon} from "../Components/PresentationModeIcon";
|
import {PresentationModeIcon} from "../Components/PresentationModeIcon";
|
||||||
import {ChatModeIcon} from "../Components/ChatModeIcon";
|
import {ChatModeIcon} from "../Components/ChatModeIcon";
|
||||||
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
||||||
@ -499,11 +500,13 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => {
|
this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => {
|
||||||
|
audioManager.decreaseVolume();
|
||||||
this.shareGroupPosition(groupPositionMessage);
|
this.shareGroupPosition(groupPositionMessage);
|
||||||
this.openChatIcon.setVisible(true);
|
this.openChatIcon.setVisible(true);
|
||||||
})
|
})
|
||||||
|
|
||||||
this.connection.onGroupDeleted((groupId: number) => {
|
this.connection.onGroupDeleted((groupId: number) => {
|
||||||
|
audioManager.restoreVolume();
|
||||||
try {
|
try {
|
||||||
this.deleteGroup(groupId);
|
this.deleteGroup(groupId);
|
||||||
this.openChatIcon.setVisible(false);
|
this.openChatIcon.setVisible(false);
|
||||||
@ -591,6 +594,20 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private playAudio(url: string|number|boolean|undefined, loop=false): void {
|
||||||
|
if (url === undefined) {
|
||||||
|
audioManager.unloadAudio();
|
||||||
|
} else {
|
||||||
|
const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/'));
|
||||||
|
const realAudioPath = mapDirUrl + '/' + url;
|
||||||
|
audioManager.loadAudio(realAudioPath);
|
||||||
|
|
||||||
|
if (loop) {
|
||||||
|
audioManager.loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private triggerOnMapLayerPropertyChange(){
|
private triggerOnMapLayerPropertyChange(){
|
||||||
this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => {
|
this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => {
|
||||||
if (newValue) this.onMapExit(newValue as string);
|
if (newValue) this.onMapExit(newValue as string);
|
||||||
@ -651,6 +668,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.connection.setSilent(true);
|
this.connection.setSilent(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.gameMap.onPropertyChange('playAudio', (newValue, oldValue) => {
|
||||||
|
this.playAudio(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.gameMap.onPropertyChange('playAudioLoop', (newValue, oldValue) => {
|
||||||
|
this.playAudio(newValue, true);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMapExit(exitKey: string) {
|
private onMapExit(exitKey: string) {
|
||||||
|
160
front/src/WebRtc/AudioManager.ts
Normal file
160
front/src/WebRtc/AudioManager.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
|
import {isUndefined} from "generic-type-guard";
|
||||||
|
|
||||||
|
enum audioStates {
|
||||||
|
closed = 0,
|
||||||
|
loading = 1,
|
||||||
|
playing = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioPlayerDivId = "audioplayer";
|
||||||
|
const audioPlayerCtrlId = "audioplayerctrl";
|
||||||
|
const animationTime = 500;
|
||||||
|
|
||||||
|
class AudioManager {
|
||||||
|
private opened = audioStates.closed;
|
||||||
|
|
||||||
|
private audioPlayerDiv: HTMLDivElement;
|
||||||
|
private audioPlayerCtrl: HTMLDivElement;
|
||||||
|
private audioPlayerElem: HTMLAudioElement | undefined;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
const storedVolume = localStorage.getItem('volume')
|
||||||
|
if (storedVolume === null) {
|
||||||
|
this.setVolume(1);
|
||||||
|
} else {
|
||||||
|
this.volume = parseFloat(storedVolume);
|
||||||
|
HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_volume').value = storedVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_volume').value = '' + this.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
this.audioPlayerElem.volume = this.naturalVolume(talking && this.decreaseWhileTalking);
|
||||||
|
this.audioPlayerElem.muted = this.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private naturalVolume(makeSofter: boolean = false): number {
|
||||||
|
const volume = this.volume
|
||||||
|
const retVol = makeSofter && !this.volumeReduced ? Math.pow(volume * 0.5, 3) : volume
|
||||||
|
this.volumeReduced = makeSofter
|
||||||
|
return retVol;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setVolume(volume: number): void {
|
||||||
|
this.volume = volume;
|
||||||
|
localStorage.setItem('volume', '' + volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public loadAudio(url: string): 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.changeVolume();
|
||||||
|
this.audioPlayerElem.play();
|
||||||
|
|
||||||
|
const muteElem = HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_mute');
|
||||||
|
muteElem.onclick = (ev: Event)=> {
|
||||||
|
this.muted = !this.muted;
|
||||||
|
this.changeVolume();
|
||||||
|
|
||||||
|
if (this.muted) {
|
||||||
|
HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_volume_icon_playing').classList.add('muted');
|
||||||
|
} else {
|
||||||
|
HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_volume_icon_playing').classList.remove('muted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const volumeElem = HtmlUtils.getElementByIdOrFail<HTMLInputElement>('audioplayer_volume');
|
||||||
|
volumeElem.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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