JSFiddle - React, Tailwind, and code Playground
by soulwire
HTML
<button id="start">
Start
</button>
JavaScript
class AdvancedSpeechDetector {
constructor(callback) {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.analyser = this.audioContext.createAnalyser();
this.microphone = null;
this.scriptProcessor = null;
this.callback = callback;
this.analyser.fftSize = 1024; // Frequency resolution
this.smoothingTimeConstant = 0.3; // Smoother transitions
}
async start() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.microphone = this.audioContext.createMediaStreamSource(stream);
this.scriptProcessor = this.audioContext.createScriptProcessor(2048, 1, 1);
this.microphone.connect(this.analyser);
this.analyser.connect(this.scriptProcessor);
this.scriptProcessor.connect(this.audioContext.destination);
this.scriptProcessor.onaudioprocess = (event) => {
const inputData = event.inputBuffer.getChannelData(0);
const volume = this.calculateRMS(inputData);
const zcr = this.calculateZeroCrossingRate(inputData);
const speechPresence = this.analyzeFrequency();
// Decide if speech is present based on multiple factors
const isSpeaking = volume > 0.02 && zcr > 200 && speechPresence;
this.callback(isSpeaking);
};
} catch (error) {
console.error("Error accessing microphone:", error);
}
}
stop() {
if (this.scriptProcessor) this.scriptProcessor.disconnect();
if (this.microphone) this.microphone.disconnect();
}
calculateRMS(samples) {
let sum = 0;
for (let i = 0; i < samples.length; i++) {
sum += samples[i] * samples[i];
}
return Math.sqrt(sum / samples.length);
}
calculateZeroCrossingRate(samples) {
let crossings = 0;
for (let i = 1; i < samples.length; i++) {
if ((samples[i - 1] < 0 && samples[i] > 0) || (samples[i - 1] > 0 && samples[i] < 0)) {
crossings++;
}
}
return crossings;
}
...