midi格式文件解析代码

<script>
    var midiSrc='/res/25469pm201.mid'//修改此处的midi音乐文件的地址为自己资源的地址
    /**
    *加载二进制文件
    */
    var buffer;
    var bufferLen;
    function loadMidi(src="/res/baga.mid"){
        var req = new XMLHttpRequest();
        req.open('GET', src);
        req.responseType = "arraybuffer";
        req.send();

        req.onreadystatechange = function () {
            if (req.readyState === 4) {
                var bufferTemp = req.response;
                var dataview = new DataView(bufferTemp);
                var ints = new Uint8Array(bufferTemp.byteLength);
                for (var i = 0; i < ints.length; i++) {
                    ints[i] = dataview.getUint8(i);
                }
                buffer=ints;
                bufferLen=buffer.length;
            }
        }
    }
    loadMidi(midiSrc);
    var bufferIdx=0;
    var tick=120;
    var trackType=0;
    var trackNum=0;
    var trackRoad=[];

    /**
    *读取midi文件
    */
    function ascToStr(asc){
      return String.fromCharCode(asc);
    }
    function arrToStr(arr){
      let strs='';
      for(let i in arr){
        strs= strs + ascToStr(arr[i]);
      }
      console.log('arrToStr',arr,strs);
      return strs;
    }
    function getByteNum(arr){
      let str='';
      let len=arr.length;
      for(let i=0;i<len;i++){
        let tmp=arr[i].toString(2);
        let tmpLen=tmp.length;
        if(tmpLen<8){
          for(let j=0;j<8-tmpLen;j++){
            tmp='0' + tmp;
          }
        }
        str = str+tmp;
      }
      return parseInt(str,2);
    }

    function headerChunk(){
      let strs=arrToStr([buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++]]);
      if(strs!=='MThd')return 'start not from MThd';

      let byteLength= buffer[bufferIdx++] +''+ buffer[bufferIdx++] +'' + buffer[bufferIdx++] + '' + buffer[bufferIdx++];
      console.log('MThd byteLength:',byteLength);
      trackType = getByteNum( [ buffer[bufferIdx++], buffer[bufferIdx++] ] );
      console.log('音轨类型:',trackType);
      trackNum=getByteNum([ buffer[bufferIdx++],buffer[bufferIdx++] ]);
      console.log('轨道数:',trackNum);
      tick = getByteNum([buffer[bufferIdx++],buffer[bufferIdx++]]);
      console.log('tick:',tick);
      console.log('bufferIdx',bufferIdx);
    }
    function getDynamicByte(){
      let num=0;
      let arr=[];
      let flag=true;
      while(flag){
        if(buffer[bufferIdx]<255){
          arr.push(buffer[bufferIdx]);
          if(buffer[bufferIdx]<128){
            flag=false;
          }
          bufferIdx++;
        }else{
          flag=false;
        }
      }
      let len=arr.length;
      for(let i=0;i<len;i++){
        if(arr[i]>=128){
          num+=Math.pow(128,len-i-1)*(arr[i]-128);
        }else{
          num+=arr[i];
        }
      }
      return num;
    }
    function printTxt(str='',num){
      for(let i=0;i<num;i++){
        str = str + ascToStr(buffer[bufferIdx++]);
      }
      console.log('printTxt',str,num);
    }
    function trackChunk(){
      let strs=arrToStr([buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++]]);
      if(strs!=='MTrk')return 'start not from MTrk';
      let len=getByteNum([ buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++],buffer[bufferIdx++] ]);
      console.log('音轨开头',strs,'长度',len);
      let j=bufferIdx+len;
      while(bufferIdx<j){
        //读取deltime
          let deltime=getDynamicByte();
          console.log('deltime',deltime);
        //meta-event 事件
        if(buffer[bufferIdx]==255){
            bufferIdx++;
          if(buffer[bufferIdx]===0){
            console.log('音序号:',getByteNum([ buffer[bufferIdx++], buffer[bufferIdx++] ]) );
          }else if(buffer[bufferIdx]===1){
            let num=getDynamicByte();
            printTxt('文字事件: ',num);
          }else if(buffer[bufferIdx]===2){
            let num=getDynamicByte();
            printTxt('版权: ',num);
          }else if(buffer[bufferIdx]===3){
            bufferIdx++;
            let num=getDynamicByte();
            printTxt('歌曲/音轨名称: ',num);
          }else if(buffer[bufferIdx]===4){
            bufferIdx++;
            let num=getDynamicByte();
            printTxt('乐器名称: ',num);
          }else if(buffer[bufferIdx]===5){
            let num=getDynamicByte();
            printTxt('歌词: ',num);
          }else if(buffer[bufferIdx]===6){
            let num=getDynamicByte();
            printTxt('标记: ',num);
          }else if(buffer[bufferIdx]===7){
            let num=getDynamicByte();
            printTxt('注释: ',num);
          }else if(buffer[bufferIdx]===47){
            printTxt('音轨结束: ',buffer[++bufferIdx]);
            bufferIdx++;
            j=bufferIdx;//强制退出循环
          }else if(buffer[bufferIdx]===81){
            let num=buffer[++bufferIdx];
            bufferIdx++;
            let dynamicArr=[];
            for(let i=0;i<num;i++){
              dynamicArr.push(buffer[bufferIdx++]);
            }
            let speed=getByteNum(dynamicArr);
            console.log('播放速度:',num,' 四分音符为',speed,dynamicArr);
          }else if(buffer[bufferIdx]===88){
            bufferIdx++;
            let num=buffer[bufferIdx++];
            console.log('指定节拍: 长度',num,',',buffer[bufferIdx++],'/',Math.pow(2,buffer[bufferIdx++]),'拍号',',节拍器时钟:',buffer[bufferIdx++],'(1个四分音符占的midi间隔),一个四分音符时值等于',buffer[bufferIdx++],'个三十二分音符');
            
          }
        }else{
          // console.log('bufferIdx',bufferIdx);
          if(buffer[bufferIdx]>=128 && buffer[bufferIdx]<=143){
            console.log('关闭音符 第',buffer[bufferIdx]-128,'音轨,音符',buffer[++bufferIdx],'力度',buffer[++bufferIdx]);
            bufferIdx++;
          }else if(buffer[bufferIdx]>=144 && buffer[bufferIdx]<=159){
            console.log('打开音符,第',buffer[bufferIdx]-144,'音轨,音符',buffer[++bufferIdx],'力度',buffer[++bufferIdx]);
            bufferIdx++;
          }else if(buffer[bufferIdx]>=160 && buffer[bufferIdx]<=175){
             console.log('触后音符,第',buffer[bufferIdx]-160,'音轨,音符',buffer[++bufferIdx],'力度',buffer[++bufferIdx]);
             bufferIdx++;
          }else if(buffer[bufferIdx]>=176 && buffer[bufferIdx]<=191){
            console.log('设置',buffer[bufferIdx]-176,'通道,',buffer[++bufferIdx],'号控制器值为',buffer[++bufferIdx]);
            bufferIdx++;
          }else if(buffer[bufferIdx]>=192 && buffer[bufferIdx]<=207){
            console.log('设置',buffer[bufferIdx]-192,'号通道音色值为',buffer[++bufferIdx]);
            bufferIdx++;
          }else if(buffer[bufferIdx]>=208 && buffer[bufferIdx]<=223){
            console.log('设置',buffer[bufferIdx]-192,'通道,音色值为',buffer[++bufferIdx],buffer[++bufferIdx]);
            bufferIdx++
          }else{
            console.log('其它事件,标识符号',buffer[bufferIdx++]);
          }
        }
      }
    }
    function analysisMidi(){
      console.log('buffer',buffer);
      headerChunk();
      let len=buffer.length;
      while(bufferIdx<len){
        trackChunk();
      }
    }
    setTimeout(function() {
      analysisMidi();
    }, 2000);
    
</script>

解读输出如下:

 

如果愿意支持本人的创作,请下载源代码吧!下载地址

Published by

风君子

独自遨游何稽首 揭天掀地慰生平