Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diyism/9a7458fcb8f6c738244abd144fb6965b to your computer and use it in GitHub Desktop.
Save diyism/9a7458fcb8f6c738244abd144fb6965b to your computer and use it in GitHub Desktop.
firefox chrome speak chinese chrome voice recognition
#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