Skip to content

Commit

Permalink
Merge pull request #951 from th-ch/audio-context-source
Browse files Browse the repository at this point in the history
Use same audio context/source everywhere
  • Loading branch information
th-ch authored Jan 8, 2023
2 parents fcb92fd + 52b67af commit 35bd62c
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 66 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@
"electron-unhandled": "^4.0.1",
"electron-updater": "^4.6.3",
"filenamify": "^4.3.0",
"hark": "^1.2.3",
"html-to-text": "^8.2.1",
"md5": "^2.3.0",
"mpris-service": "^2.1.2",
Expand Down
13 changes: 7 additions & 6 deletions plugins/audio-compressor/front.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const applyCompressor = () => {
const audioContext = new AudioContext();
const applyCompressor = (e) => {
const audioContext = e.detail.audioContext;

const compressor = audioContext.createDynamicsCompressor();
compressor.threshold.value = -50;
Expand All @@ -8,10 +8,11 @@ const applyCompressor = () => {
compressor.attack.value = 0;
compressor.release.value = 0.25;

const source = audioContext.createMediaElementSource(document.querySelector("video"));

source.connect(compressor);
e.detail.audioSource.connect(compressor);
compressor.connect(audioContext.destination);
};

module.exports = () => document.addEventListener('apiLoaded', applyCompressor, { once: true, passive: true });
module.exports = () =>
document.addEventListener("audioCanPlay", applyCompressor, {
passive: true,
});
153 changes: 106 additions & 47 deletions plugins/skip-silences/front.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,112 @@
const hark = require("hark/hark.bundle.js");

module.exports = (options) => {
let isSilent = false;
let hasAudioStarted = false;

document.addEventListener("apiLoaded", () => {
const video = document.querySelector("video");
const speechEvents = hark(video, {
threshold: -100, // dB (-100 = absolute silence, 0 = loudest)
interval: 2, // ms
});
const skipSilence = () => {
if (options.onlySkipBeginning && hasAudioStarted) {
return;
}

if (isSilent && !video.paused) {
video.currentTime += 0.2; // in s
}
};

speechEvents.on("speaking", function () {
isSilent = false;
hasAudioStarted = true;
});

speechEvents.on("stopped_speaking", function () {
if (
!(
video.paused ||
video.seeking ||
video.ended ||
video.muted ||
video.volume === 0
)
) {
isSilent = true;
const smoothing = 0.1;
const threshold = -100; // dB (-100 = absolute silence, 0 = loudest)
const interval = 2; // ms
const history = 10;
const speakingHistory = Array(history).fill(0);

document.addEventListener(
"audioCanPlay",
(e) => {
const video = document.querySelector("video");
const audioContext = e.detail.audioContext;
const sourceNode = e.detail.audioSource;

// Use an audio analyser similar to Hark
// https://github.com/otalk/hark/blob/master/hark.bundle.js
const analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.smoothingTimeConstant = smoothing;
const fftBins = new Float32Array(analyser.frequencyBinCount);

sourceNode.connect(analyser);
analyser.connect(audioContext.destination);

const looper = () => {
setTimeout(() => {
const currentVolume = getMaxVolume(analyser, fftBins);

let history = 0;
if (currentVolume > threshold && isSilent) {
// trigger quickly, short history
for (
let i = speakingHistory.length - 3;
i < speakingHistory.length;
i++
) {
history += speakingHistory[i];
}
if (history >= 2) {
// Not silent
isSilent = false;
hasAudioStarted = true;
}
} else if (currentVolume < threshold && !isSilent) {
for (let i = 0; i < speakingHistory.length; i++) {
history += speakingHistory[i];
}
if (history == 0) {
// Silent
if (
!(
video.paused ||
video.seeking ||
video.ended ||
video.muted ||
video.volume === 0
)
) {
isSilent = true;
skipSilence();
}
}
}
speakingHistory.shift();
speakingHistory.push(0 + (currentVolume > threshold));

looper();
}, interval);
};
looper();

const skipSilence = () => {
if (options.onlySkipBeginning && hasAudioStarted) {
return;
}

if (isSilent && !video.paused) {
video.currentTime += 0.2; // in s
}
};

video.addEventListener("play", function () {
hasAudioStarted = false;
skipSilence();
});

video.addEventListener("seeked", function () {
hasAudioStarted = false;
skipSilence();
}
});

video.addEventListener("play", function () {
hasAudioStarted = false;
skipSilence();
});

video.addEventListener("seeked", function () {
hasAudioStarted = false;
skipSilence();
});
});
});
},
{
passive: true,
}
);
};

function getMaxVolume(analyser, fftBins) {
var maxVolume = -Infinity;
analyser.getFloatFrequencyData(fftBins);

for (var i = 4, ii = fftBins.length; i < ii; i++) {
if (fftBins[i] > maxVolume && fftBins[i] < 0) {
maxVolume = fftBins[i];
}
}

return maxVolume;
}
26 changes: 26 additions & 0 deletions preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,32 @@ function listenForApiLoad() {
}

function onApiLoaded() {
const video = document.querySelector("video");
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaElementSource(video);

video.addEventListener(
"loadstart",
() => {
// Emit "audioCanPlay" for each video
video.addEventListener(
"canplaythrough",
() => {
document.dispatchEvent(
new CustomEvent("audioCanPlay", {
detail: {
audioContext: audioContext,
audioSource: audioSource,
},
})
);
},
{ once: true }
);
},
{ passive: true }
);

document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api }));

// Remove upgrade button
Expand Down
12 changes: 0 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3527,13 +3527,6 @@ hard-rejection@^2.1.0:
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==

hark@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/hark/-/hark-1.2.3.tgz#959981400f561be5580ecd4321a9f55b16bacbd0"
integrity sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==
dependencies:
wildemitter "^1.2.0"

has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
Expand Down Expand Up @@ -6639,11 +6632,6 @@ widest-line@^3.1.0:
dependencies:
string-width "^4.0.0"

wildemitter@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/wildemitter/-/wildemitter-1.2.1.tgz#9da3b5ca498e4378628d1783145493c70a10b774"
integrity sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==

word-wrap@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
Expand Down

0 comments on commit 35bd62c

Please sign in to comment.