備忘錄_20160105(定位) 修改 回首頁

程式 2022-05-04 21:35:38 1651671338 100
嘗試語音辨識 try speech recognition-step03

嘗試語音辨識 try speech recognition-step03

fourier transform

偵測聲音,切割並繪製頻率振幅圖
20220501_fourier_transform.odt


●20220501.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 class="divWrapper">
      <div>
        <a target="_blank" href="https://www.youtube.com/watch?v=spUNpyF58BY">可視化的傅里葉變換</a><br>
        <a target="_blank" href="https://zh.wikipedia.org/zh-tw/歐拉公式">尤拉公式/歐拉公式</a><br>
      </div>
      <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)
        {
          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.1)
                      {
                        /*
                        output(
                          "min="+fMin+", max="+fMax
                          +", channel="+iChannel
                          +", data length="+faData.length);
                        */
                        
                        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 
          console.log("samples="+faCurrentWord.length);
          var iFreqCount=iSampleRate/2;
          var faFreq=new Array(iFreqCount);
          for(var iIdxT=0; iIdxT<iFreqCount; iIdxT++)
          {
            var fFrequence=(iSampleRate/faCurrentWord.length)*iIdxT;
            fFrequence=(iSampleRate/2)*(iIdxT/iFreqCount);
            faFreq[iIdxT]=0;
            for(var t=0; t<faCurrentWord.length; t++)
            {
              var fTime=t/iSampleRate;
              faFreq[iIdxT]
                =faFreq[iIdxT]
                +(faCurrentWord[t]
                *Math.cos(-2*Math.PI*fFrequence*fTime));
            }
            faFreq[iIdxT]=faFreq[iIdxT]/faCurrentWord.length;
          }
          
          var oCanvas=document.createElement("canvas");
          oCanvas.setAttribute("width", 800);
          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="yellow";
          oCtx.fill();
          
          oCtx.strokeStyle="red";
          oCtx.strokeWidth=1;
          oCtx.beginPath();
          var fMaxValue=0.2; // -0.1 ~ +0.1
          
          var fMinGot=Math.min(...faFreq);
          var fMaxGot=Math.max(...faFreq);
          fMaxValue=Math.max(Math.abs(fMinGot),Math.abs(fMaxGot))*2;

          var iIdxFreq=0;
          x=iIdxFreq/(faFreq.length-1)*iCanvasWidth;
          y=(fMaxGot-faFreq[iIdxFreq])/(fMaxGot-fMinGot)*iCanvasHeight;
          oCtx.moveTo(x,y)

          for(var iIdxFreq=1; iIdxFreq<faFreq.length; iIdxFreq++)
          {
            x=iIdxFreq/(faFreq.length-1)*iCanvasWidth;
            y=(fMaxGot-faFreq[iIdxFreq])/(fMaxGot-fMinGot)*iCanvasHeight;
            oCtx.lineTo(x,y)
          }
          oCtx.stroke();
          
          var oDiv2=document.createElement("div");
          oDiv2.innerHTML
            ="y-amplitude("+fMinGot+"~"+fMaxGot+"), x-frequency(~"+(iSampleRate/2)+"Hz)<br>";
          gebi("divOutput").appendChild(oDiv2);
          
          gebi("divOutput").appendChild(oCanvas);
          gebi("divOutput").appendChild(document.createElement("br"));
          
        }
      }
      
    </script>
    
  </body>
</html>