Last active
November 15, 2020 17:04
-
-
Save daniilS/32ddbd3557bf34385ab6539bd4154007 to your computer and use it in GitHub Desktop.
Skips annoying silences in lecture recordings, and suppresses noise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Lecture recording silence and noise remover | |
// @namespace https://gist.github.com/daniilS/ | |
// @downloadUrl https://gist.github.com/daniilS/32ddbd3557bf34385ab6539bd4154007/raw/silenceRemover.user.js | |
// @version 2.0.0 | |
// @description Puts more effort into making online lectures tolerable than the rest of the chemistry department combined | |
// @author daniilS | |
// @include *://*.panopto.*/Panopto/Pages/Viewer.aspx* | |
// @grant GM_registerMenuCommand | |
// @grant unsafeWindow | |
// ==/UserScript== | |
(function(){ | |
'use strict'; | |
//global variables so that you can also change them through the console. These should really be sliders in a browser extension popup if I had done this properly | |
unsafeWindow.threshold = -28; //silence threshold in decibels, video will get sped up below this threshold | |
unsafeWindow.maxGap = 0.300; //max silence length in seconds, video will be sped up after this length of silence | |
unsafeWindow.normalSpeed = 1; //default normal video playback rate, set to whatever you normally watch videos at. Can also be changed the way you normally change speed in Panopto | |
unsafeWindow.fastSpeed = 4; //playback rate during silences, values above 4 sometimes make the audio go weird in Chrome, set it to the highest value your browser can handle | |
unsafeWindow.disableScript = false; //set to true to disable the script | |
unsafeWindow.verboseScript = false; //set to true if you want your console spammed to see if the extension is working | |
unsafeWindow.filterQ = 1.1; //Q-factor of the band-pass filter, applied when enabling noise supression | |
unsafeWindow.filterFrequency = 300; //frequency band of the band-pass filter, applied when enabling noise supression | |
let filter; | |
let waitForVideo = function(){ | |
if(document.getElementsByClassName("video-js").length < 1){ | |
setTimeout(waitForVideo, 100); | |
return; | |
} | |
//check if the browser is Firefox and warn the user if it is | |
//the bug in question: https://bugzilla.mozilla.org/show_bug.cgi?id=1517199 | |
if(unsafeWindow.Panopto.Core.Browser.isFirefox){ | |
alert("Due to a known but unfixed bug in Firefox, the silence remover script doesn't work. Please try a different browser (Chrome recommended)."); | |
return; | |
} | |
if(document.getElementsByClassName("video-js")[0].paused){ | |
setTimeout(waitForVideo, 100); | |
return; | |
} | |
let context = new AudioContext(); | |
let videoElement = document.getElementsByClassName("video-js")[0]; //audio, or video of the lecturer's face, or the screen if there's only one video | |
let videoElement1 = document.getElementsByClassName("video-js")[1] || {}; //video of the screen, if separate | |
let video = context.createMediaElementSource(videoElement); | |
let analyser = context.createAnalyser(); //yes I know this should really be an AudioWorkletNode but this was less effort | |
analyser.fftSize = 512; | |
filter = context.createBiquadFilter(); | |
filter.Q.value = 0; | |
filter.frequency.value = unsafeWindow.filterFrequency; | |
filter.type = 'bandpass'; | |
filter.connect(analyser); | |
analyser.connect(context.destination); | |
video.connect(filter); | |
let getLoudness = function(){ | |
let array = new Float32Array(analyser.fftSize); | |
analyser.getFloatTimeDomainData(array); | |
let max = 0; | |
for (let i = 0; i < array.length; i++){ | |
let a = Math.abs(array[i]); | |
max = Math.max(max, a); | |
} | |
let dB = 10 * Math.log10(max); | |
return dB; | |
} | |
let lastLoudness = unsafeWindow.threshold; | |
let timeOffset = undefined; | |
let changeSpeed = function(newSpeed){ | |
document.getElementById("playButton").click(); //pause the video in an attempt to prevent desync | |
videoElement.playbackRate = newSpeed; | |
videoElement1.playbackRate = newSpeed; | |
document.getElementById("playButton").click(); | |
} | |
let silenceDetector = function(){ | |
if(videoElement.paused || unsafeWindow.disableScript){ | |
setTimeout(silenceDetector, 20); | |
return; | |
} | |
let loudness = getLoudness(); | |
if(loudness < unsafeWindow.threshold){ | |
if(lastLoudness > unsafeWindow.threshold){ | |
lastSilence = videoElement.currentTime; | |
} | |
else{ | |
if(videoElement.currentTime - lastSilence >= unsafeWindow.maxGap){ | |
if(videoElement.playbackRate != unsafeWindow.fastSpeed){ | |
changeSpeed(unsafeWindow.fastSpeed); | |
} | |
if(unsafeWindow.verboseScript){ | |
console.log("forwarding"); | |
} | |
} | |
} | |
} | |
else{ | |
if(videoElement.playbackRate != unsafeWindow.normalSpeed){ | |
changeSpeed(unsafeWindow.normalSpeed); | |
} | |
} | |
lastLoudness = loudness; | |
setTimeout(silenceDetector, 20); | |
} | |
let lastSilence = videoElement.currentTime; | |
silenceDetector(); | |
} | |
let waitForSpeedButtons = function(){ | |
if(document.getElementsByClassName("play-speed").length < 1){ | |
setTimeout(waitForSpeedButtons, 100); | |
return; | |
} | |
for(let speedButton of document.getElementsByClassName("play-speed")){ | |
speedButton.onclick = function(e){ | |
unsafeWindow.normalSpeed = unsafeWindow.PanoptoViewer.PlaySpeed[e.target.id]; | |
} | |
} | |
} | |
waitForVideo(); | |
waitForSpeedButtons(); | |
GM_registerMenuCommand("Enable noise suppression", function(){filter.Q.value = unsafeWindow.filterQ; filter.frequency.value = unsafeWindow.filterFrequency;}); | |
GM_registerMenuCommand("Disable noise suppression", function(){filter.Q.value = 0;}); | |
GM_registerMenuCommand("Enable silence skipper", function(){unsafeWindow.disableScript = false;}); | |
GM_registerMenuCommand("Disable silence skipper", function(){unsafeWindow.disableScript = true;}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment