Last active
December 25, 2022 13:37
-
-
Save diyism/9a7458fcb8f6c738244abd144fb6965b to your computer and use it in GitHub Desktop.
firefox chrome speak chinese chrome voice recognition
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 html/js code works in the linux chrome and even in ios safari, but won't work in android chrome, it's weird(google is stingy), | |
#and other author's demo also doesn't work in android chrome: | |
# https://bensonruan.com/voice-to-text-with-chrome-web-speech-api/ | |
# https://mdn.github.io/dom-examples/web-speech-api/speech-color-changer/ | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>firefox/chrome speak chinese</title> | |
</head> | |
<body> | |
<script> | |
//on ready of speechSynthesis voices list | |
//window.speechSynthesis.onvoiceschanged=function() | |
//{ | |
var is_linux_firefox=window.navigator.userAgent.indexOf('Linux')!==-1 && window.navigator.userAgent.indexOf('Firefox')!==-1; | |
//window.voice=window.speechSynthesis.getVoices().find(function(ele) {return ele.lang==='zh-CN';}); | |
window.utter=new window.SpeechSynthesisUtterance(); | |
//window.utter.voice=window.voice; | |
//linux firefox needs "apt install speech-dispatcher speech-dispatcher-espeak-ng", while linux chrome needs be online | |
//android firefox and android chrome don't need be online | |
window.utter.lang=is_linux_firefox?'cmn':'zh-CN'; | |
window.utter.pitch=0.92; | |
window.utter.rate=1.1; | |
window.speak=function(txt) {window.utter.text=txt;window.speechSynthesis.speak(window.utter);} | |
//}; | |
</script> | |
<button onclick="speak('你好啊 世界')">firefox/chrome发声</button> | |
<button onclick="start_recog()">chrome语音识别</button> | |
<div style="width:200px;height: 10px;border: 1px solid black;position: relative;margin: 0 auto;"><div id='voiceVolume' style="width: 0px;height: 10px;background: green;"></div> </div> | |
<div id="recog_text"></div> | |
<script> | |
var recognition = new webkitSpeechRecognition(); | |
recognition.continuous = true; | |
recognition.interimResults = true; | |
recognition.lang = "cmn-Hans-CN"; | |
document.getElementById('recog_text').innerHTML=document.getElementById('recog_text').innerHTML+'jack<br>'; | |
recognition.onresult = function(e) { | |
//recognition.stop(); | |
console.log(e.results[0][0].transcript); | |
document.getElementById('recog_text').innerHTML=document.getElementById('recog_text').innerHTML+e.results[0][0].transcript+'<br>'; | |
}; | |
recognition.onerror = function(e) { | |
//recognition.stop(); | |
} | |
recognition.onend = function() { | |
recognition.start(); | |
} | |
function start_recog() | |
{ | |
recognition.start(); | |
recordingStarted=true; | |
} | |
</script> | |
<script> | |
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) { | |
var processor = audioContext.createScriptProcessor(512); | |
processor.onaudioprocess = volumeAudioProcess; | |
processor.clipping = false; | |
processor.lastClip = 0; | |
processor.volume = 0; | |
processor.clipLevel = clipLevel || 0.98; | |
processor.averaging = averaging || 0.95; | |
processor.clipLag = clipLag || 750; | |
// this will have no effect, since we don't copy the input to the output, | |
// but works around a current Chrome bug. | |
processor.connect(audioContext.destination); | |
processor.checkClipping = | |
function(){ | |
if (!this.clipping) | |
return false; | |
if ((this.lastClip + this.clipLag) < window.performance.now()) | |
this.clipping = false; | |
return this.clipping; | |
}; | |
processor.shutdown = | |
function(){ | |
this.disconnect(); | |
this.onaudioprocess = null; | |
}; | |
return processor; | |
} | |
function volumeAudioProcess( event ) { | |
var buf = event.inputBuffer.getChannelData(0); | |
var bufLength = buf.length; | |
var sum = 0; | |
var x; | |
// Do a root-mean-square on the samples: sum up the squares... | |
for (var i=0; i<bufLength; i++) { | |
x = buf[i]; | |
if (Math.abs(x)>=this.clipLevel) { | |
this.clipping = true; | |
this.lastClip = window.performance.now(); | |
} | |
sum += x * x; | |
} | |
// ... then take the square root of the sum. | |
var rms = Math.sqrt(sum / bufLength); | |
// Now smooth this out with the averaging factor applied | |
// to the previous sample - take the max here because we | |
// want "fast attack, slow release." | |
this.volume = Math.max(rms, this.volume*this.averaging); | |
} | |
var meter = null; | |
var WIDTH = 500; | |
var recordingStarted = false; | |
navigator.getUserMedia({audio: true}, startUserMedia, function(e) {}); | |
function startUserMedia(stream) { | |
const ctx = new AudioContext(); | |
const analyser = ctx.createAnalyser(); | |
const streamNode = ctx.createMediaStreamSource(stream); | |
streamNode.connect(analyser); | |
// Create a new volume meter and connect it. | |
meter = createAudioMeter(ctx); | |
streamNode.connect(meter); | |
drawLoop(); | |
} | |
// Create pitch bar | |
function drawLoop( time ) { | |
var pitchVolume = meter.volume*WIDTH*1.4; | |
var width = 0; | |
// Pitch detection minimum volume | |
var minimum_volume = 50; | |
// Get width if Recording started | |
if(recordingStarted){ | |
if(pitchVolume < minimum_volume){ | |
width = 0; | |
}else if(pitchVolume >= minimum_volume && pitchVolume < (minimum_volume+20) ){ | |
width = 10; | |
}else if(pitchVolume >= (minimum_volume+20) && pitchVolume < (minimum_volume+40)){ | |
width = 20; | |
}else if(pitchVolume >= (minimum_volume+40) && pitchVolume < (minimum_volume+60)){ | |
width = 30; | |
}else if(pitchVolume >= (minimum_volume+60) && pitchVolume < (minimum_volume+80)){ | |
width = 40; | |
}else if(pitchVolume >= (minimum_volume+80) && pitchVolume < (minimum_volume+100)){ | |
width = 50; | |
}else if(pitchVolume >= (minimum_volume+100) && pitchVolume < (minimum_volume+120)){ | |
width = 60; | |
}else if(pitchVolume >= (minimum_volume+120) && pitchVolume < (minimum_volume+140)){ | |
width = 70; | |
}else if(pitchVolume >= (minimum_volume+140) && pitchVolume < (minimum_volume+160)){ | |
width = 80; | |
}else if(pitchVolume >= (minimum_volume+160) && pitchVolume < (minimum_volume+180)){ | |
width = 90; | |
}else if(pitchVolume >= (minimum_volume+180)){ | |
width = 100; | |
} | |
} | |
// Update width | |
document.getElementById('voiceVolume').style.width = width+'%'; | |
rafID = window.requestAnimationFrame( drawLoop ); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment