備忘錄_20160105(定位)
修改
回首頁
程式 2022-05-04 21:40:30 1651671630 100
嘗試語音辨識 try speech recognition-step04
嘗試語音辨識 try speech recognition-step04
Cooley–Tukey FFT algorithm
偵測聲音,切割並繪製頻率振幅圖
偵測聲音,切割並繪製時間頻率圖(test)
20220506_Cooley–Tukey FFT algorithm - Wikipedia.pdf
●20220506.php
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*
{
font-family: "WenQuanYi Zen Hei","文泉驛正黑","Heiti TC","黑體-繁","LiHei Pro","儷黑 Pro","PingFang TC","Droid Sans","Roboto","Microsoft JhengHei","微軟正黑體",sans-serif;
/* 【linux的字型】【ios字型】【android字型】【微軟正黑體】【無襯線字=黑體】 */
}
html, body
{
margin: 0;
border: 0;
padding: 0;
width: 100%;
height: 100%;
}
a:link, a:visited
{
color: blue;
text-decoration: none;
}
#divWrapper
{
padding: 1em;
}
</style>
</head>
<body>
<div id="divWrapper">
<div>
<a target="_blank" href="https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm">Cooley–Tukey FFT algorithm</a><br>
<a target="_blank" href="https://en.wikipedia.org/wiki/Bit-reversal_permutation">Bit-reversal permutation</a><br>
<a target="_blank" href="https://zh.wikipedia.org/zh-tw/复数_(数学)">複數 (數學)</a><br>
<a target="_blank" href="https://zh.wikipedia.org/zh-tw/快速傅里叶变换">快速傅立葉變換</a><br>
</div>
<div>
<div id="divColor">●●●</div>
<button type="button" onclick="goRecord();">錄音</button>
<button type="button" onclick="goStop();">停止</button>
</div>
<div id="divOutput"></div>
</div>
<script>
var iSampleRate=8000;
var oMediaRecorder=null;
var oaWords=null;
var iIntervalId=null;
var booStart=false;
var oLastData=null;
var booLastDataOkay=false;
if(!String.prototype.ltrim) { String.prototype.ltrim = function() { return this.replace(/^\s+/,''); }; }
if(!String.prototype.rtrim) { String.prototype.rtrim = function() { return this.replace(/\s+$/,''); }; }
if(!String.prototype.trim ) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,''); }; }
function gebi(strId)
{
return document.getElementById(strId);
}
window.addEventListener(
"load",
function()
{
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
{
navigator.mediaDevices.getUserMedia({ audio: true})
.then
(
function(oStream)
{
oMediaRecorder=new MediaRecorder(oStream);
oMediaRecorder.ondataavailable=function(oEvent)
{
console.log(oEvent.data);
oLastData=[];
oLastData.push(oEvent.data);
getBuffer();
}
oMediaRecorder.onstop=function(oEvent)
{
}
}
)
.catch
(
function(oErr)
{
alert("遇到錯誤!"+oErr);
}
);
}
else
{
alert("您的瀏覽器不支援 getUserMedia!");
}
}
);
function goRecord()
{
gebi("divOutput").innerHTML="";
booLastDataOkay=false;
oaWords=[];
oMediaRecorder.start();
booStart=true;
iIntervalId=window.setInterval(requestBlob, 100);
}
function goStop()
{
booStart=false;
window.clearInterval(iIntervalId);
console.log(oMediaRecorder.state);
oMediaRecorder.stop();
drawWords();
}
function requestBlob()
{
if(booStart==true)
{
var r=Math.floor(Math.random()*256);
var g=Math.floor(Math.random()*256);
var b=Math.floor(Math.random()*256);
gebi("divColor").style.color="rgb("+r+","+g+","+b+")";
oMediaRecorder.stop();
oMediaRecorder.start();
}
}
function getBuffer()
{
oLastData[0].arrayBuffer()
.then
(
oBuffer=>
{
var oAC=new (window.AudioContext || window.webkitAudioContext)({sampleRate:iSampleRate});
oAC.decodeAudioData(oBuffer)
.then
(
function(oDecodedData)
{
//for(var iChannel=0; iChannel<oDecodedData.numberOfChannels; iChannel++)
for(var iChannel=0; iChannel<1; iChannel++)
{
var faData=new Float32Array(oDecodedData.getChannelData(iChannel)); // -1 ~ +1
var fMin=Math.min(...faData);
var fMax=Math.max(...faData);
if(((fMax-fMin)/2.0)>0.05)
{
if(booLastDataOkay==false)
{
oaWords.push([Array.from(faData)]);
}
else
{
oaWords[oaWords.length-1].push(Array.from(faData));
}
booLastDataOkay=true;
}
else
{
/* output("silence"); */
booLastDataOkay=false;
}
}
//output("......");
}
)
.catch
(
function(oErr)
{
//output(oErr);
}
);
}
)
.catch
(
oErr=>{ output(oErr); }
);
}
function output(strMessage)
{
gebi("divOutput").innerHTML=gebi("divOutput").innerHTML+"<br>\r\n"+strMessage;
}
function drawWords()
{
for(var iIdxWord=0; iIdxWord<oaWords.length; iIdxWord++)
{
var faCurrentWord=oaWords[iIdxWord][0];
for(var iTmp1=1; iTmp1<oaWords[iIdxWord].length; iTmp1++)
{
faCurrentWord=faCurrentWord.concat(oaWords[iIdxWord][iTmp1]);
}
// 時域
var oCanvas=document.createElement("canvas");
oCanvas.setAttribute("width", 100*oaWords[iIdxWord].length);
oCanvas.setAttribute("height", 200);
var oCtx=oCanvas.getContext("2d");
var iCanvasWidth=oCanvas.width;
var iCanvasHeight=oCanvas.height;
oCtx.beginPath();
oCtx.rect(0,0,iCanvasWidth,iCanvasHeight);
oCtx.fillStyle="black";
oCtx.fill();
oCtx.strokeStyle="white";
oCtx.beginPath();
var fMaxValue=2; // -1 ~ +1
var iIdxData=0;
var x=iIdxData/(faCurrentWord.length-1)*iCanvasWidth;
var y=(fMaxValue-(faCurrentWord[iIdxData]+(fMaxValue/2)))/fMaxValue*iCanvasHeight;
oCtx.moveTo(x,y);
for(var iIdxData=1; iIdxData<faCurrentWord.length; iIdxData++)
{
x=iIdxData/(faCurrentWord.length-1)*iCanvasWidth;
y=(fMaxValue-(faCurrentWord[iIdxData]+(fMaxValue/2)))/fMaxValue*iCanvasHeight;
oCtx.lineTo(x,y)
}
oCtx.stroke();
var oDiv3=document.createElement("div");
oDiv3.innerHTML="y-amplitude(-1~1), x-time(seconds)";
gebi("divOutput").appendChild(oDiv3);
gebi("divOutput").appendChild(oCanvas);
gebi("divOutput").appendChild(document.createElement("br"));
// 頻域
// Fourier Cosine Transform
// 先準備陣列
// 人耳 20Hz ~ 20000Hz
var o2={};
o2.faSrc=faCurrentWord;
iterative_fast_fourier_transform(o2);
console.log(o2); // nmomtf
// nmomtf
// 準備陣列,兩邊是幾乎對稱的
var faFreq=[];
for(var iIdxData=0; iIdxData<(o2.oaDst.length/2); iIdxData++)
{
faFreq.push(o2.oaDst[iIdxData].v1);
}
for(var iIdxData=(o2.oaDst.length/2); iIdxData<o2.oaDst.length; iIdxData++)
{
faFreq[o2.oaDst.length-1-iIdxData]+=o2.oaDst[iIdxData].v1;
}
// 正規化,先取絕對值,然後再取對數
for(var iIdx1=0; iIdx1<faFreq.length; iIdx1++)
{
faFreq[iIdx1]=Math.abs(faFreq[iIdx1]);
}
var fRefValue=Math.max(...faFreq);
for(var iIdx1=0; iIdx1<faFreq.length; iIdx1++)
{
faFreq[iIdx1]=20*Math.log(faFreq[iIdx1]/fRefValue)/Math.log(10);
}
var fMinVal=Math.min(...faFreq);
var fMaxVal=Math.max(...faFreq);
console.log("check faFreq..."+fMinVal+" ~ "+fMaxVal); // nmomtf
console.log(faFreq);
// 畫圖
var oCanvas=document.createElement("canvas");
oCanvas.setAttribute("width", 200);
oCanvas.setAttribute("height", 200);
var oCtx=oCanvas.getContext("2d");
var iCanvasWidth=oCanvas.width;
var iCanvasHeight=oCanvas.height;
oCtx.beginPath();
oCtx.rect(0,0,iCanvasWidth,iCanvasHeight);
oCtx.fillStyle="black";
oCtx.fill();
oCtx.strokeStyle="blue";
oCtx.beginPath();
var fMaxValue=fMaxVal-fMinVal; // fMinVal ~ fMaxVal
for(var iIdxData=0; iIdxData<faFreq.length; iIdxData++)
{
x=iIdxData/(faFreq.length-1)*iCanvasWidth;
y=(fMaxValue-(faFreq[iIdxData]-fMinVal))/fMaxValue*iCanvasHeight;
oCtx.moveTo(x,y);
oCtx.lineTo(x,iCanvasHeight);
}
oCtx.stroke();
var oDiv1=document.createElement("div");
oDiv1.innerHTML="y-logValue("+fMinVal+"~"+fMaxVal+"), x-frequency(Hz)";
gebi("divOutput").appendChild(oDiv1);
gebi("divOutput").appendChild(oCanvas);
gebi("divOutput").appendChild(document.createElement("br"));
}
}
</script>
<script>
function cn_add(oCN1,oCN2)
{
return getOComplexNumber(oCN1.v1+oCN2.v1, oCN1.v2+oCN2.v2);
}
function cn_sub(oCN1,oCN2)
{
return getOComplexNumber(oCN1.v1-oCN2.v1, oCN1.v2-oCN2.v2);
}
function cn_mul(oCN1,oCN2)
{
return getOComplexNumber(
oCN1.v1*oCN2.v1-oCN1.v2*oCN2.v2,
oCN1.v1*oCN2.v2+oCN1.v2*oCN2.v1);
}
function getOComplexNumber(v1,v2)
{
var o1={};
o1.v1=v1;
o1.v2=v2;
return o1;
}
function getIReverseBits(iExponent, iValue)
{
var ia1=iValue.toString(2).padStart(iExponent,"0").split("").reverse();
var iFinalValue=parseInt(ia1.join(""),2);
return iFinalValue;
}
function bit_reverse_copy(oArrays)
{
// 補滿到2的指數數量
var iN=oArrays.faSrc.length;
var iExponent=Math.log2(iN);
if(Number.isInteger(iExponent)==false)
{
iExponent=Math.floor(iExponent)+1;
}
iN=Math.pow(2,iExponent);
var iBeginValue=oArrays.faSrc.length;
for(var i=iBeginValue; i<iN; i++)
{
oArrays.faSrc.push(0);
}
// 開始複製陣列
oArrays.oaDst=new Array(iN);
for(iK=0; iK<iN; iK++)
{
oArrays.oaDst[getIReverseBits(iExponent,iK)]=getOComplexNumber(oArrays.faSrc[iK],0);
}
}
function iterative_fast_fourier_transform(oArrays)
{
bit_reverse_copy(oArrays);
var n=oArrays.oaDst.length;
var sEnd=Math.log2(n); // nmomtf
for(var s=1; s<=sEnd; s++)
{
var m=Math.pow(2,s);
var wm=getOComplexNumber(Math.cos(-2*Math.PI/m),Math.sin(-2*Math.PI/m));
for(var k=0; k<n; k+=m)
{
w=getOComplexNumber(1,0);
for(var j=0; j<(m/2); j++)
{
var t=cn_mul(w,oArrays.oaDst[k+j+m/2]);
var u=oArrays.oaDst[k+j];
oArrays.oaDst[k+j]=cn_add(u,t);
oArrays.oaDst[k+j+m/2]=cn_sub(u,t);
w=cn_mul(w,wm);
}
}
}
}
</script>
</body>
</html>