mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-13 11:21:46 +00:00
Use same audio context/source everywhere
This commit is contained in:
@ -111,7 +111,6 @@
|
|||||||
"electron-unhandled": "^4.0.1",
|
"electron-unhandled": "^4.0.1",
|
||||||
"electron-updater": "^4.6.3",
|
"electron-updater": "^4.6.3",
|
||||||
"filenamify": "^4.3.0",
|
"filenamify": "^4.3.0",
|
||||||
"hark": "^1.2.3",
|
|
||||||
"html-to-text": "^8.2.1",
|
"html-to-text": "^8.2.1",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"mpris-service": "^2.1.2",
|
"mpris-service": "^2.1.2",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const applyCompressor = () => {
|
const applyCompressor = (e) => {
|
||||||
const audioContext = new AudioContext();
|
const audioContext = e.detail.audioContext;
|
||||||
|
|
||||||
const compressor = audioContext.createDynamicsCompressor();
|
const compressor = audioContext.createDynamicsCompressor();
|
||||||
compressor.threshold.value = -50;
|
compressor.threshold.value = -50;
|
||||||
@ -8,10 +8,11 @@ const applyCompressor = () => {
|
|||||||
compressor.attack.value = 0;
|
compressor.attack.value = 0;
|
||||||
compressor.release.value = 0.25;
|
compressor.release.value = 0.25;
|
||||||
|
|
||||||
const source = audioContext.createMediaElementSource(document.querySelector("video"));
|
e.detail.audioSource.connect(compressor);
|
||||||
|
|
||||||
source.connect(compressor);
|
|
||||||
compressor.connect(audioContext.destination);
|
compressor.connect(audioContext.destination);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = () => document.addEventListener('apiLoaded', applyCompressor, { once: true, passive: true });
|
module.exports = () =>
|
||||||
|
document.addEventListener("audioCanPlay", applyCompressor, {
|
||||||
|
passive: true,
|
||||||
|
});
|
||||||
|
|||||||
@ -1,53 +1,112 @@
|
|||||||
const hark = require("hark/hark.bundle.js");
|
|
||||||
|
|
||||||
module.exports = (options) => {
|
module.exports = (options) => {
|
||||||
let isSilent = false;
|
let isSilent = false;
|
||||||
let hasAudioStarted = false;
|
let hasAudioStarted = false;
|
||||||
|
|
||||||
document.addEventListener("apiLoaded", () => {
|
const smoothing = 0.1;
|
||||||
const video = document.querySelector("video");
|
const threshold = -100; // dB (-100 = absolute silence, 0 = loudest)
|
||||||
const speechEvents = hark(video, {
|
const interval = 2; // ms
|
||||||
threshold: -100, // dB (-100 = absolute silence, 0 = loudest)
|
const history = 10;
|
||||||
interval: 2, // ms
|
const speakingHistory = Array(history).fill(0);
|
||||||
});
|
|
||||||
const skipSilence = () => {
|
|
||||||
if (options.onlySkipBeginning && hasAudioStarted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSilent && !video.paused) {
|
document.addEventListener(
|
||||||
video.currentTime += 0.2; // in s
|
"audioCanPlay",
|
||||||
}
|
(e) => {
|
||||||
};
|
const video = document.querySelector("video");
|
||||||
|
const audioContext = e.detail.audioContext;
|
||||||
|
const sourceNode = e.detail.audioSource;
|
||||||
|
|
||||||
speechEvents.on("speaking", function () {
|
// Use an audio analyser similar to Hark
|
||||||
isSilent = false;
|
// https://github.com/otalk/hark/blob/master/hark.bundle.js
|
||||||
hasAudioStarted = true;
|
const analyser = audioContext.createAnalyser();
|
||||||
});
|
analyser.fftSize = 512;
|
||||||
|
analyser.smoothingTimeConstant = smoothing;
|
||||||
|
const fftBins = new Float32Array(analyser.frequencyBinCount);
|
||||||
|
|
||||||
speechEvents.on("stopped_speaking", function () {
|
sourceNode.connect(analyser);
|
||||||
if (
|
analyser.connect(audioContext.destination);
|
||||||
!(
|
|
||||||
video.paused ||
|
const looper = () => {
|
||||||
video.seeking ||
|
setTimeout(() => {
|
||||||
video.ended ||
|
const currentVolume = getMaxVolume(analyser, fftBins);
|
||||||
video.muted ||
|
|
||||||
video.volume === 0
|
let history = 0;
|
||||||
)
|
if (currentVolume > threshold && isSilent) {
|
||||||
) {
|
// trigger quickly, short history
|
||||||
isSilent = true;
|
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();
|
skipSilence();
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
video.addEventListener("play", function () {
|
video.addEventListener("seeked", function () {
|
||||||
hasAudioStarted = false;
|
hasAudioStarted = false;
|
||||||
skipSilence();
|
skipSilence();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
video.addEventListener("seeked", function () {
|
{
|
||||||
hasAudioStarted = false;
|
passive: true,
|
||||||
skipSilence();
|
}
|
||||||
});
|
);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
preload.js
26
preload.js
@ -90,6 +90,32 @@ function listenForApiLoad() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onApiLoaded() {
|
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 }));
|
document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api }));
|
||||||
|
|
||||||
// Remove upgrade button
|
// Remove upgrade button
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@ -3527,13 +3527,6 @@ hard-rejection@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
||||||
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
|
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:
|
has-ansi@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||||
@ -6639,11 +6632,6 @@ widest-line@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width "^4.0.0"
|
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:
|
word-wrap@^1.2.3:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
|
|||||||
Reference in New Issue
Block a user