added pitch level detection to indicate speaking peers
This commit is contained in:
+69
-24
@@ -38,30 +38,23 @@
|
||||
:root {
|
||||
/* common */
|
||||
--bg: linear-gradient(to left, #363434, #000000);
|
||||
|
||||
--msger-top: 50%;
|
||||
--msger-left: 50%;
|
||||
--msger-height: 680px;
|
||||
--msger-width: 420px;
|
||||
|
||||
/* video iframe */
|
||||
--iframe-width: 640px;
|
||||
--iframe-height: 480px;
|
||||
|
||||
/* whiteboard resize */
|
||||
--wb-width: 800px;
|
||||
--wb-height: 600px;
|
||||
--wb-bg: linear-gradient(to left, #1f1e1e, #000000);
|
||||
|
||||
/* my settings */
|
||||
--mySettings-select-w: 100%;
|
||||
|
||||
/* btns hover scale 110% zoom */
|
||||
--btns-hover-scale: scale(1.1);
|
||||
|
||||
/* video elem border color */
|
||||
--elem-border-color: grey 1px solid;
|
||||
|
||||
/* left buttons bar vertical default */
|
||||
--btns-top: 50%;
|
||||
--btns-right: 0%;
|
||||
@@ -69,7 +62,6 @@
|
||||
--btns-margin-left: 0px;
|
||||
--btns-width: 40px;
|
||||
--btns-flex-direction: column;
|
||||
|
||||
/* left buttons bar horizontal
|
||||
--btns-top: 95%;
|
||||
--btns-right: 25%;
|
||||
@@ -78,7 +70,6 @@
|
||||
--btns-width: 600px;
|
||||
--btns-flex-direction: row;
|
||||
*/
|
||||
|
||||
/* neon theme default */
|
||||
--body-bg: black;
|
||||
--msger-bg: linear-gradient(to left, #383838, #000000);
|
||||
@@ -93,8 +84,8 @@
|
||||
--wb-bg: linear-gradient(to left, #1f1e1e, #000000);
|
||||
--wb-hbg: #000000;
|
||||
--my-settings-label-color: white;
|
||||
--hover-color: grey; /* rgb(8, 189, 89); */
|
||||
|
||||
--hover-color: grey;
|
||||
/* rgb(8, 189, 89); */
|
||||
/* dark theme
|
||||
--body-bg: #16171b;
|
||||
--msger-bg: linear-gradient(to left, #383838, #000000);
|
||||
@@ -110,7 +101,6 @@
|
||||
--wb-hbg: #000000;
|
||||
--my-settings-label-color: white;
|
||||
*/
|
||||
|
||||
/* forest theme
|
||||
--body-bg: black;
|
||||
--msger-bg: linear-gradient(to left, #383838, #000000);
|
||||
@@ -126,7 +116,6 @@
|
||||
--wb-hbg: #000000;
|
||||
--my-settings-label-color: white;
|
||||
*/
|
||||
|
||||
/* sky theme
|
||||
--body-bg: black;
|
||||
--msger-bg: linear-gradient(to left, #383838, #000000);
|
||||
@@ -142,7 +131,6 @@
|
||||
--wb-hbg: #000000;
|
||||
--my-settings-label-color: white;
|
||||
*/
|
||||
|
||||
/* ghost theme
|
||||
--body-bg: black;
|
||||
--msger-bg: linear-gradient(to left, transparent, rgba(0, 0, 0, 0.7));
|
||||
@@ -158,7 +146,6 @@
|
||||
--wb-hbg: #000000;
|
||||
--my-settings-label-color: white;
|
||||
*/
|
||||
|
||||
/* https://developer.mozilla.org/it/docs/Web/CSS/object-fit */
|
||||
--video-object-fit: cover;
|
||||
}
|
||||
@@ -187,7 +174,8 @@ body {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
opacity: 0; /* make things invisible upon start */
|
||||
opacity: 0;
|
||||
/* make things invisible upon start */
|
||||
-webkit-animation: fadeIn ease-in 1;
|
||||
-moz-animation: fadeIn ease-in 1;
|
||||
animation: fadeIn ease-in 1;
|
||||
@@ -211,9 +199,11 @@ body {
|
||||
left: 60%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#loadingDiv h1 {
|
||||
font-size: 70px;
|
||||
}
|
||||
|
||||
#loadingDiv pre {
|
||||
font-size: 15px;
|
||||
}
|
||||
@@ -241,6 +231,7 @@ body {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulsate {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
@@ -252,6 +243,7 @@ body {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulsate {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
@@ -274,7 +266,8 @@ body {
|
||||
position: absolute;
|
||||
padding: 10px;
|
||||
background: var(--bg);
|
||||
font-size: small; /* 4 mobile */
|
||||
font-size: small;
|
||||
/* 4 mobile */
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
@@ -297,6 +290,7 @@ body {
|
||||
animation-duration: 1s;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.statusMenu i,
|
||||
.statusMenu p,
|
||||
.statusMenu h4 {
|
||||
@@ -305,6 +299,7 @@ body {
|
||||
border: none;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.statusMenu button {
|
||||
float: right;
|
||||
margin-right: 3px;
|
||||
@@ -313,6 +308,7 @@ body {
|
||||
display: inline;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.statusMenu p:hover,
|
||||
.statusMenu h4:hover,
|
||||
.statusMenu button:hover {
|
||||
@@ -343,25 +339,20 @@ body {
|
||||
display: none;
|
||||
position: absolute;
|
||||
padding: 15px;
|
||||
|
||||
top: var(--btns-top);
|
||||
right: var(--btns-right);
|
||||
left: var(--btns-left);
|
||||
margin-left: var(--btns-margin-left);
|
||||
width: var(--btns-width);
|
||||
flex-direction: var(--btns-flex-direction);
|
||||
|
||||
justify-content: space-around;
|
||||
grid-gap: 0.4rem;
|
||||
|
||||
-webkit-transform: translate(0%, -50%);
|
||||
-ms-transform: translate(0%, -50%);
|
||||
transform: translate(0%, -50%);
|
||||
|
||||
border-radius: 20px;
|
||||
background: transparent;
|
||||
box-shadow: var(--box-shadow);
|
||||
|
||||
-webkit-animation: fadeIn ease-in 1;
|
||||
-moz-animation: fadeIn ease-in 1;
|
||||
animation: fadeIn ease-in 1;
|
||||
@@ -435,6 +426,7 @@ body {
|
||||
color: var(--hover-color);
|
||||
transform: var(--btns-hover-scale);
|
||||
}
|
||||
|
||||
#leaveRoomBtn:hover {
|
||||
color: red;
|
||||
transform: var(--btns-hover-scale);
|
||||
@@ -521,6 +513,7 @@ button {
|
||||
border-radius: 5px;
|
||||
transition: background 0.23s;
|
||||
}
|
||||
|
||||
.msger-header-options button:hover {
|
||||
color: var(--hover-color);
|
||||
transform: var(--btns-hover-scale);
|
||||
@@ -537,14 +530,18 @@ button {
|
||||
padding: 10px;
|
||||
background: var(--msger-bg);
|
||||
}
|
||||
|
||||
.msger-chat::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.msger-chat::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.msger-chat::-webkit-scrollbar-thumb {
|
||||
background: black; /*aqua;*/
|
||||
background: black;
|
||||
/*aqua;*/
|
||||
}
|
||||
|
||||
.msg {
|
||||
@@ -576,16 +573,19 @@ button {
|
||||
.right-msg {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.right-msg .msg-bubble {
|
||||
background: var(--right-msg-bg);
|
||||
border-bottom-right-radius: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.right-msg .private-msg-bubble {
|
||||
background: var(--private-msg-bg);
|
||||
border-bottom-right-radius: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.right-msg .msg-img {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
@@ -806,10 +806,13 @@ video {
|
||||
object-fit: var(--video-object-fit);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
video:fullscreen {
|
||||
object-fit: contain; /* cover; */
|
||||
object-fit: contain;
|
||||
/* cover; */
|
||||
border: var(--elem-border-color);
|
||||
}
|
||||
|
||||
.video {
|
||||
float: left;
|
||||
width: 25vw;
|
||||
@@ -817,10 +820,12 @@ video:fullscreen {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.video.one {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.video.two {
|
||||
width: 49.6vw;
|
||||
height: 99vh;
|
||||
@@ -828,6 +833,7 @@ video:fullscreen {
|
||||
border: var(--elem-border-color);
|
||||
/* --video-object-fit: contain; */
|
||||
}
|
||||
|
||||
.video.three {
|
||||
/* width: 33vw;
|
||||
height: 99vh; */
|
||||
@@ -835,18 +841,21 @@ video:fullscreen {
|
||||
--video-object-fit: cover;
|
||||
border: var(--elem-border-color);
|
||||
}
|
||||
|
||||
.video.four {
|
||||
width: 49.6vw;
|
||||
height: 49vh;
|
||||
--video-object-fit: cover;
|
||||
border: var(--elem-border-color);
|
||||
}
|
||||
|
||||
.video.five {
|
||||
width: 33vw;
|
||||
height: 49vh;
|
||||
--video-object-fit: cover;
|
||||
border: var(--elem-border-color);
|
||||
}
|
||||
|
||||
.video.six {
|
||||
width: 33vw;
|
||||
height: 49vh;
|
||||
@@ -1008,6 +1017,7 @@ video:fullscreen {
|
||||
}
|
||||
|
||||
/* Style the tab */
|
||||
|
||||
.tab {
|
||||
overflow: hidden;
|
||||
border: 1px solid rgb(0, 0, 0);
|
||||
@@ -1015,6 +1025,7 @@ video:fullscreen {
|
||||
}
|
||||
|
||||
/* Style the buttons inside the tab */
|
||||
|
||||
.tab button {
|
||||
background-color: inherit;
|
||||
float: left;
|
||||
@@ -1027,6 +1038,7 @@ video:fullscreen {
|
||||
}
|
||||
|
||||
/* Change background color of buttons on hover */
|
||||
|
||||
.tab button:hover {
|
||||
background-color: transparent;
|
||||
color: var(--hover-color);
|
||||
@@ -1034,11 +1046,13 @@ video:fullscreen {
|
||||
}
|
||||
|
||||
/* Create an active/current tablink class */
|
||||
|
||||
.tab button.active {
|
||||
background-color: rgb(30 29 29);
|
||||
}
|
||||
|
||||
/* Style the tab content */
|
||||
|
||||
.tabcontent {
|
||||
display: none;
|
||||
padding: 6px 12px;
|
||||
@@ -1047,6 +1061,7 @@ video:fullscreen {
|
||||
}
|
||||
|
||||
/* on open display devices tab */
|
||||
|
||||
#tabDevices {
|
||||
display: block;
|
||||
}
|
||||
@@ -1063,10 +1078,12 @@ video:fullscreen {
|
||||
color: white !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.swal2-file {
|
||||
color: white !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.swal2-html-container {
|
||||
color: rgb(165, 165, 165) !important;
|
||||
background-color: transparent !important;
|
||||
@@ -1077,6 +1094,7 @@ video:fullscreen {
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
*/
|
||||
|
||||
.swal2-select {
|
||||
background-color: black !important;
|
||||
color: white !important;
|
||||
@@ -1106,6 +1124,7 @@ video:fullscreen {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulsate {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
@@ -1117,6 +1136,7 @@ video:fullscreen {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulsate {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
@@ -1171,6 +1191,7 @@ video:fullscreen {
|
||||
background-color: transparent;
|
||||
transition: background 0.23s;
|
||||
}
|
||||
|
||||
#sendAbortBtn:hover {
|
||||
color: rgb(255, 0, 0);
|
||||
transform: var(--btns-hover-scale);
|
||||
@@ -1213,6 +1234,7 @@ progress {
|
||||
-moz-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
#videoUrlHeader {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
@@ -1223,6 +1245,7 @@ progress {
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
#videoUrlHeader button {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
@@ -1233,11 +1256,13 @@ progress {
|
||||
color: white;
|
||||
background: black;
|
||||
}
|
||||
|
||||
#videoUrlHeader button:hover {
|
||||
color: var(--hover-color);
|
||||
transform: var(--btns-hover-scale);
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
#videoUrlIframe {
|
||||
width: var(--iframe-width);
|
||||
height: var(--iframe-height);
|
||||
@@ -1304,6 +1329,26 @@ progress {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.speechbar {
|
||||
width: 2%;
|
||||
height: 20%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
.bar {
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-family: arial;
|
||||
font-size: 12px;
|
||||
background-color: #19bb5c;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
z-index
|
||||
7 statusMenu -----
|
||||
|
||||
@@ -170,6 +170,7 @@ let msgerCPList;
|
||||
let msgerEmojiPicker;
|
||||
let emojiPicker;
|
||||
// my settings
|
||||
let myPeerId;
|
||||
let mySettings;
|
||||
let mySettingsHeader;
|
||||
let tabDevicesBtn;
|
||||
@@ -997,6 +998,10 @@ function handleRTCDataChannels(peer_id) {
|
||||
case 'speech':
|
||||
handleDataChannelSpeechTranscript(dataMessage);
|
||||
break;
|
||||
case 'micVolume':
|
||||
dataMessage.peer_id = peer_id; // to create animation on specific video element
|
||||
handlePeerVolume(dataMessage);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('mirotalk_chat_channel', err);
|
||||
@@ -1519,6 +1524,8 @@ function loadRemoteMediaStream(stream, peers, peer_id) {
|
||||
const remotePrivateMsgBtn = document.createElement('button');
|
||||
const remoteYoutubeBtnBtn = document.createElement('button');
|
||||
const remotePeerKickOut = document.createElement('button');
|
||||
const pitchMeter = document.createElement('div');
|
||||
const pitchBar = document.createElement('div');
|
||||
const remoteVideoFullScreenBtn = document.createElement('button');
|
||||
const remoteVideoAvatarImage = document.createElement('img');
|
||||
|
||||
@@ -1583,6 +1590,13 @@ function loadRemoteMediaStream(stream, peers, peer_id) {
|
||||
remoteVideoAvatarImage.setAttribute('id', peer_id + '_avatar');
|
||||
remoteVideoAvatarImage.className = 'videoAvatarImage pulsate';
|
||||
|
||||
//for pitch meter
|
||||
pitchMeter.setAttribute('id', peer_id + '_pitch');
|
||||
pitchBar.setAttribute('id', peer_id + '_pitch_bar');
|
||||
pitchMeter.className = 'speechbar';
|
||||
pitchBar.className = 'bar';
|
||||
pitchBar.style.height = '1%';
|
||||
|
||||
// add elements to remoteStatusMenu div
|
||||
remoteStatusMenu.appendChild(remoteVideoParagraphImg);
|
||||
remoteStatusMenu.appendChild(remoteVideoParagraph);
|
||||
@@ -1607,6 +1621,8 @@ function loadRemoteMediaStream(stream, peers, peer_id) {
|
||||
// add elements to videoWrap div
|
||||
remoteVideoWrap.appendChild(remoteStatusMenu);
|
||||
remoteVideoWrap.appendChild(remoteVideoAvatarImage);
|
||||
pitchMeter.appendChild(pitchBar);
|
||||
remoteVideoWrap.appendChild(pitchMeter);
|
||||
remoteVideoWrap.appendChild(remoteMedia);
|
||||
|
||||
document.body.appendChild(remoteVideoWrap);
|
||||
@@ -1886,6 +1902,7 @@ function setShareRoomBtn() {
|
||||
function setAudioBtn() {
|
||||
audioBtn.addEventListener('click', (e) => {
|
||||
handleAudio(e, false);
|
||||
startPitchDetection();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5536,3 +5553,19 @@ function getSl(selector) {
|
||||
function getEcN(className) {
|
||||
return document.getElementsByClassName(className);
|
||||
}
|
||||
|
||||
// for detecting mic volume
|
||||
function handlePeerVolume(data) {
|
||||
let peer_id = data.peer_id;
|
||||
let element = document.getElementById(peer_id + '_pitch_bar');
|
||||
let volume = data.volume + 25; //for design purpose
|
||||
if (volume > 50) {
|
||||
element.style.backgroundColor = 'orange';
|
||||
}
|
||||
|
||||
element.style.height = volume + '%';
|
||||
setTimeout(function () {
|
||||
element.style.backgroundColor = '#19bb5c';
|
||||
element.style.height = '1%';
|
||||
}, 700);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
function startPitchDetection() {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
|
||||
mediaStreamSource = audioContext.createMediaStreamSource(stream);
|
||||
meter = createAudioMeter(audioContext);
|
||||
mediaStreamSource.connect(meter);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createAudioMeter(audioContext, clipLevel, averaging, clipLag) {
|
||||
const processor = audioContext.createScriptProcessor(512);
|
||||
processor.onaudioprocess = volumeAudioProcess;
|
||||
processor.clipping = false;
|
||||
processor.lastClip = 0;
|
||||
processor.volume = 0;
|
||||
processor.clipLevel = clipLevel || 0.98;
|
||||
processor.averaging = averaging || 0.95;
|
||||
processor.clipLag = clipLag || 750;
|
||||
|
||||
// this will have no effect, since we don't copy the input to the output,
|
||||
// but works around a current Chrome bug.
|
||||
processor.connect(audioContext.destination);
|
||||
|
||||
processor.checkClipping = function () {
|
||||
if (!this.clipping) {
|
||||
return false;
|
||||
}
|
||||
if (this.lastClip + this.clipLag < window.performance.now()) {
|
||||
this.clipping = false;
|
||||
}
|
||||
return this.clipping;
|
||||
};
|
||||
|
||||
processor.shutdown = function () {
|
||||
this.disconnect();
|
||||
this.onaudioprocess = null;
|
||||
};
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
function volumeAudioProcess(event) {
|
||||
const buf = event.inputBuffer.getChannelData(0);
|
||||
const bufLength = buf.length;
|
||||
let sum = 0;
|
||||
let x;
|
||||
|
||||
// Do a root-mean-square on the samples: sum up the squares...
|
||||
for (var i = 0; i < bufLength; i++) {
|
||||
x = buf[i];
|
||||
if (Math.abs(x) >= this.clipLevel) {
|
||||
this.clipping = true;
|
||||
this.lastClip = window.performance.now();
|
||||
}
|
||||
sum += x * x;
|
||||
}
|
||||
|
||||
// ... then take the square root of the sum.
|
||||
const rms = Math.sqrt(sum / bufLength);
|
||||
|
||||
// Now smooth this out with the averaging factor applied
|
||||
// to the previous sample - take the max here because we
|
||||
// want "fast attack, slow release."
|
||||
this.volume = Math.max(rms, this.volume * this.averaging);
|
||||
let final_volume = Math.round(this.volume * 100);
|
||||
if (myAudioStatus && final_volume > 5) {
|
||||
config = {
|
||||
type: 'micVolume',
|
||||
volume: final_volume,
|
||||
};
|
||||
if (thereIsPeerConnections()) {
|
||||
// Send speech transcript through RTC Data Channels
|
||||
for (let peer_id in chatDataChannels) {
|
||||
if (chatDataChannels[peer_id].readyState === 'open')
|
||||
chatDataChannels[peer_id].send(JSON.stringify(config));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,6 @@ access to use this app.
|
||||
<button id="aboutBtn" class="fas fa-question"></button>
|
||||
<button id="leaveRoomBtn" class="fas fa-phone-slash"></button>
|
||||
</div>
|
||||
|
||||
<!-- End left buttons -->
|
||||
|
||||
<!-- Start chat room
|
||||
@@ -384,6 +383,7 @@ access to use this app.
|
||||
<script defer src="https://unpkg.com/tippy.js@6"></script>
|
||||
<script defer src="/socket.io/socket.io.js"></script>
|
||||
<script defer src="../js/client.js"></script>
|
||||
<script defer src="../js/detectSpeaking.js"></script>
|
||||
<script defer src="../js/speechRecognition.js"></script>
|
||||
|
||||
<!-- end of Js scripts -->
|
||||
|
||||
Reference in New Issue
Block a user