-
Notifications
You must be signed in to change notification settings - Fork 579
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #951 from th-ch/audio-context-source
Use same audio context/source everywhere
- Loading branch information
Showing
5 changed files
with
139 additions
and
66 deletions.
There are no files selected for viewing
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
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
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
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; | ||
} |
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
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