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>

解读输出如下:

 

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

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个基于STM32的MIDI文件解析器的代码示例: ```C // 定义MIDI文件头结构体 typedef struct { uint32_t chunkID; uint32_t chunkSize; uint16_t formatType; uint16_t trackCount; uint16_t timeDivision; } MidiFileHeader; // 定义MIDI轨道事件结构体 typedef struct { uint32_t deltaTime; uint8_t status; uint8_t data1; uint8_t data2; } MidiTrackEvent; // 定义MIDI文件解析函数 void parseMidiFile(uint8_t* fileData, uint32_t fileSize) { // 解析MIDI文件MidiFileHeader* header = (MidiFileHeader*)fileData; // 检查MIDI文件标识 if (header->chunkID != 0x4D546864) { // 不是有效的MIDI文件 return; } // 获取轨道数据起始位置 uint8_t* trackData = fileData + sizeof(MidiFileHeader); // 解析每个轨道 for (int i = 0; i < header->trackCount; i++) { // 解析轨道头 uint32_t deltaTime = 0; uint8_t* eventData = trackData; // 解析轨道事件 while (eventData < (fileData + fileSize)) { // 解析delta time uint32_t delta = 0; uint8_t byte = 0; do { byte = *eventData++; delta = (delta << 7) | (byte & 0x7F); } while (byte & 0x80); deltaTime += delta; // 解析事件类型 uint8_t status = *eventData++; // 处理MIDI事件 if (status == 0xFF) { // Meta事件 uint8_t metaType = *eventData++; uint8_t metaLength = *eventData++; // 处理不同的Meta事件类型 switch (metaType) { case 0x2F: // End of Track return; // 跳出当前轨道解析 case 0x51: // Set Tempo uint32_t microsecondsPerQuarterNote = (eventData[0] << 16) | (eventData[1] << 8) | eventData[2]; // 处理设置的时间分辨率 break; // 处理其他的Meta事件类型 default: // 忽略其他的Meta事件 break; } // 跳过Meta事件数据 eventData += metaLength; } else if ((status & 0xF0) == 0x90) { // Note On事件 uint8_t note = *eventData++; uint8_t velocity = *eventData++; // 处理Note On事件 } else if ((status & 0xF0) == 0x80) { // Note Off事件 uint8_t note = *eventData++; uint8_t velocity = *eventData++; // 处理Note Off事件 } else { // 其他MIDI事件 // 处理其他的MIDI事件类型 } } // 更新下一个轨道的起始位置 trackData += sizeof(uint32_t) + *(uint32_t*)trackData; } } ``` 这段代码实现了一个简单的MIDI文件解析器,可以解析MIDI文件的头部信息和每个轨道的事件。它使用了MIDI文件头结构体和轨道事件结构体来存储解析出来的数据。在解析过程中,它会处理不同类型的MIDI事件,如Note On、Note Off、Meta事件等,并根据需要执行相应的操作。你可以根据自己的需要修改这段代码以实现更复杂的功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值