MIDI文件结构分析及生成方法

 

 

  /**************************************************
  /* 作用:将符合MIDI文本文件的text文件转换成MIDI */
  /* 文件. */
  /* 入口参数:TextFileName 文本文件名 */
  /* MidiFileName MIDI文件名 */
  /* 出口参数:见 ERRORCODE 说明 */
  /*************************************************/
  int MIDI::ChangeTextToMidi(char *TextFileName,
  char *MidiFileName)
  { int tracks,ntrack,delaytime;
  int speed,IsFirst,nn,dd;
  unsigned char buf[80],*msgbuf,c;
  FILE *TextFp,*MidiFp;
  long FileSize;
  char SpeedVal;
  TextFp=fopen(TextFileName,"r");
  if (TextFp==NULL)
  {sprintf(ErrorMsg,
  "文本文件[%s]不能打开。\n",TextFileName);
   return(TextFileNotOpen);
   }
  fseek(TextFp,0,SEEK_END); /*测试文件大小*/
  FileSize=ftell(TextFp);
  TextFileBuf=(char *)malloc(FileSize);/*为文件分配内存*/
  if (TextFileBuf==NULL)
  { sprintf(ErrorMsg,
  "文本文件[%s]太大,没有足够的内存处理。\n",
  TextFileName);
   fclose(TextFp);
   return(TextFileToBig);
  }
  memset(TextFileBuf,0,FileSize);
  MidiFileBuf=(char *) malloc(FileSize*4);
  if ( MidiFileBuf==NULL)
  { sprintf(ErrorMsg,"不能为MIDI文件分配内存。\n");
  fclose(TextFp);
  free(TextFileBuf);
  return(MallocError);
  }
  MidiFp=fopen(MidiFileName,"wb");
  if (MidiFp==NULL)
   { sprintf(ErrorMsg,
  "Midi文件[%s]不能建立。\n",MidiFileName);
   fclose(TextFp);
   free(MidiFileBuf);
   free(TextFileBuf);
   return(MidiFileCanNotCreate);
   }
  MidiFileOldBuf=MidiFileBuf;
  TextFileOldBuf=TextFileBuf;
  fseek(TextFp,0,SEEK_SET);
  fread(TextFileBuf,FileSize,1,TextFp);
  fclose(TextFp);
  JumpNullChar(TextFileBuf);
  c=strnicmp(TextFileBuf,"[MIDI]",6);
  if (c)
  {sprintf(ErrorMsg,
  "文本文件[%s]不是MIDI文本文件。\n",MidiFileName);
  fcloseall();
  free(TextFileOldBuf);
  free(MidiFileOldBuf);
  return(NotMIDITextFile);
  }
  TextFileBuf+=6;
  JumpNullChar(TextFileBuf);
  sscanf(TextFileBuf,"%c,%d/%d,%d,%d", //取调号等信息
  c,nn,dd,speed,tracks);
  buf[0]=c;buf[1]=0; SetOneVal(buf); //设置该调1的键值
  if (nn<1 || nn> 7) nn=4;
  if (dd<2 || dd>16) dd=4;
  while(*TextFileBuf!='\n') TextFileBuf++;
  JumpNullChar(TextFileBuf);
  if (speed<60 || speed >200) speed=120;
  JumpNullChar(TextFileBuf);
  if (tracks<1 || tracks>16) tracks=1;
  JumpNullChar(TextFileBuf);
  ntrack=1;
  WriteMHToFile(6,1,tracks,speed,MidiFp);
  WriteTimeSignature(nn,dd); //设置时间记录格式
  SetPaiNum(nn);
  WriteSpeed(speed); //设置演奏速度
  while(ntrack<=tracks *TextFileBuf!=0)
  {sprintf(buf,"[%d]",ntrack);
  TextFileBuf=strstr(TextFileBuf,buf);//查找该磁道起始位置
  if (TextFileBuf==NULL) //没有找到
  { sprintf(ErrorMsg,
  "在文件[%s]中,第%d磁道音乐信息没找到。\n.",
   TextFileName,ntrack);
   free(MidiFileOldBuf);
   free(TextFileOldBuf);
   fcloseall();
   return(NotFoundTrack);
  }
  if (ntrack!=1) MidiFileBuf=MidiFileOldBuf;
  SpeedVal=0;
  TextFileBuf+=strlen(buf);
  IsFirst=1;
  while(*TextFileBuf!=0 *TextFileBuf!='[')
  { JumpNullChar(TextFileBuf);
  c=*(TextFileBuf++);
  if ( (c>='0' c<='7')
  || (c>='a' c<='g')
  || (c>='A' c<='G')
  )
  {JumpNullChar(TextFileBuf);
   if (*TextFileBuf=='b' || *TextFileBuf=='#')
   { c=GetToneNum(c,*TextFileBuf);/*取出实际的音符*/
   TextFileBuf++;
   JumpNullChar(TextFileBuf);
   }
   else c=GetToneNum(c,' ');
  switch(*(TextFileBuf++))
   { case '-': //延长一拍
   delaytime=2*FOURPAINUM;
   JumpNullChar(TextFileBuf);
   while(*TextFileBuf=='-')
   { TextFileBuf++;
   delaytime+=FOURPAINUM;
   JumpNullChar(TextFileBuf);
   }
   break;
   case '_': //8分音符
   delaytime=FOURPAINUM/2;
   JumpNullChar(TextFileBuf);
   if(*TextFileBuf=='.')
   {TextFileBuf++;
   delaytime=delaytime*3/2;
   }
   break;
   case '=': //16分音符
   delaytime=FOURPAINUM/4;
   JumpNullChar(TextFileBuf);
   if(*TextFileBuf=='.')
   {delaytime=delaytime*3/2;
   TextFileBuf++;}
   break;
   case '.': //附点音符
   delaytime=FOURPAINUM*3/2;
   break;
   case ':': //32分音符
   delaytime=FOURPAINUM/16;
   JumpNullChar(TextFileBuf);
   if(*TextFileBuf=='.')
   {delaytime=delaytime*3/2;
   TextFileBuf++;}
   break;
   case ';': //64分音符
   delaytime=FOURPAINUM/32;
   if(*TextFileBuf=='.')
   { delaytime=delaytime*3/2;
   TextFileBuf++;}
   break;
   default:
   delaytime=FOURPAINUM;
   TextFileBuf--;
   break;
   }

   if (IsFirst)
   {WriteNoteOn(ntrack,c,
  GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),
  delaytime);
   IsFirst=0;}
   else
   NoteOn(c,
  GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),
  delaytime);
   SpeedVal=(SpeedVal+delaytime) //下一音符所处的节拍
  %(PaiNum*FOURPAINUM*4/dd);
  }
  else
  {switch(c)
  { case 'S':
  case 's':
  case 'p':
  case 'P': /*设置音色*/
   sscanf(TextFileBuf,"%d",IsFirst);
   while(*TextFileBuf>='0' *TextFileBuf<='9')
  TextFileBuf++;
  if (c=='P'||c=='p') //若为P,表示改变音色
   ChangePrommgram(ntrack,(char)IsFirst);
   else //否则,表示设置音量大小
   WriteSoundSize(ntrack,(unsigned int)IsFirst);
   IsFirst=1;
   break;
  case '{': /*写歌词*/
   msgbuf=buf;
   while(*TextFileBuf!='}'
   *TextFileBuf!='\n'
   *TextFileBuf!=0
   *TextFileBuf!='[')
   *(msgbuf++)=*(TextFileBuf++);
   *msgbuf=0;
   IsFirst=1;
   WriteTextMsg(buf);
   if (*TextFileBuf=='}') TextFileBuf++;
   break;
  case '\\': //降八度
   OneVal-=12;
   break;
  case '/': //升八度
   OneVal+=12;
   break;
  case '[':
  case 0:
   TextFileBuf--;
   break;
  default:
   sprintf(ErrorMsg,"文本文件[%s]出现非法字符(%c)。",
   TextFileName,c);
   free(MidiFileOldBuf);
   free(TextFileOldBuf);
   fcloseall();
   return(InvalideChar);
   }
  }
  }
  WriteTrackEndMsg(); //设置磁道结束信息
  WriteTrackMsgToFile(MidiFp); //将磁道音乐信息定入文件中
  ntrack++;
  }
  free(MidiFileOldBuf);
  free(TextFileOldBuf);
  fclose(MidiFp);
  sprintf(ErrorMsg,"MIDI文件[%s]转换成功。",MidiFileName);
  return(ChangeOK);
  }
 /*****************************************************/
  /*作用:将长整型数据变成可变长度,存入buf处 */
  /*入口参数:n 数据 buf 结果保存入 */
  /****************************************************/
  void MIDI::WriteLenghtToBuf(unsigned long n,char *buf)
  { unsigned char b[4]={0};
  int i;
  b[3]=(unsigned char)(n0x7f);
  i=2;
  while(n>>7)
  { n>>=7;
   b[i--]=(char)( (n0x7f)|0x80);
   }
  for (i=0;i<4;i++)
  if (b[i]) *(buf++)=b[i];
  *buf=0;
  }
  long MIDI::NewLong(long n) //将长整型数据改成高位在前
  { union LENGHT l={0};
  char i;
  l.length=n;
  SWAP(l.b[3],l.b[0]);
  SWAP(l.b[2],l.b[1]);
  return(l.length);
  }
  //开始演奏音乐
  void MIDI::WriteNoteOn(char channel, //通道号
  char note, //音符值
  char speed, //按键速度
  unsigned long delaytime) //延时数
  { unsigned char buf[5];
  int i;
  channel--;
  *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0x90|channel0x7f;//Write Channel
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=speed;
  WriteLenghtToBuf(delaytime*MIDICLOCK,buf);
  i=0;
  while(buf[i]>=0x80) //Write Delay Time
   *(MidiFileBuf++)=buf[i++];
  *(MidiFileBuf++)=buf[i];
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=0;
  }
  void MIDI::NoteOn(char note,
  char speed,
  unsigned long delaytime) //发音
  { unsigned char buf[5];
  int i;
  *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=speed;
  WriteLenghtToBuf(delaytime*MIDICLOCK,buf);
  i=0;
  while(buf[i]>0x80)
   *(MidiFileBuf++)=buf[i++];
  *(MidiFileBuf++)=buf[i];
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=0;
  }
  void MIDI::ChangePrommgram(char channel,char n) //改变音色
  { *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0xc0|(channel-1)0x7f;
  *(MidiFileBuf++)=n;
  }
  void MIDI::WriteTextMsg(char *msg) //向内存写入一文本信息
  { char bufmsg[100]={0xff,5,0,0,0};
  int len;
  *(MidiFileBuf++)=0;
  bufmsg[2]=(char)strlen(msg);
  strcpy(bufmsg[3],msg);
  strcpy(MidiFileBuf,bufmsg);
  MidiFileBuf+=strlen(bufmsg)+3;
  }
  void MIDI::WriteTrackEndMsg() //磁道结束信息
  { *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0xff;
  *(MidiFileBuf++)=0x2f;
  *(MidiFileBuf++)=0;
  }
  char MIDI::GetToneNum(char n,char flag)
  /*入口参数: n 音高
   flag 升降记号
  返回值: 该乐音的实际标号值*/
  { static char val[7]={9 ,11,0,2,4,5,7};
  static char one[7]={0,2,4,5,7,9,11};
  int i;
  i=OneVal;
  if (n<='7' n>='1') i=i+one[n-'1'];
  else
   if (n>='a' n<='g')
  i=i+val[n-'a']-12; //低音,降12个半音
   else
   if (n>='A' n<='G') //高音,升12个半音
   i=i+val[n-'A']+12;
   else //否则,识为休止符
   i=0;
  if (flag=='b') i--;
  else if (flag=='#') i++;
  return(i);
  }

   if (IsFirst)
   {WriteNoteOn(ntrack,c,
  GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),
  delaytime);
   IsFirst=0;}
   else
   NoteOn(c,
  GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),
  delaytime);
   SpeedVal=(SpeedVal+delaytime) //下一音符所处的节拍
  %(PaiNum*FOURPAINUM*4/dd);
  }
  else
  {switch(c)
  { case 'S':
  case 's':
  case 'p':
  case 'P': /*设置音色*/
   sscanf(TextFileBuf,"%d",IsFirst);
   while(*TextFileBuf>='0' *TextFileBuf<='9')
  TextFileBuf++;
  if (c=='P'||c=='p') //若为P,表示改变音色
   ChangePrommgram(ntrack,(char)IsFirst);
   else //否则,表示设置音量大小
   WriteSoundSize(ntrack,(unsigned int)IsFirst);
   IsFirst=1;
   break;
  case '{': /*写歌词*/
   msgbuf=buf;
   while(*TextFileBuf!='}'
   *TextFileBuf!='\n'
   *TextFileBuf!=0
   *TextFileBuf!='[')
   *(msgbuf++)=*(TextFileBuf++);
   *msgbuf=0;
   IsFirst=1;
   WriteTextMsg(buf);
   if (*TextFileBuf=='}') TextFileBuf++;
   break;
  case '\\': //降八度
   OneVal-=12;
   break;
  case '/': //升八度
   OneVal+=12;
   break;
  case '[':
  case 0:
   TextFileBuf--;
   break;
  default:
   sprintf(ErrorMsg,"文本文件[%s]出现非法字符(%c)。",
   TextFileName,c);
   free(MidiFileOldBuf);
   free(TextFileOldBuf);
   fcloseall();
   return(InvalideChar);
   }
  }
  }
  WriteTrackEndMsg(); //设置磁道结束信息
  WriteTrackMsgToFile(MidiFp); //将磁道音乐信息定入文件中
  ntrack++;
  }
  free(MidiFileOldBuf);
  free(TextFileOldBuf);
  fclose(MidiFp);
  sprintf(ErrorMsg,"MIDI文件[%s]转换成功。",MidiFileName);
  return(ChangeOK);
  }
 /*****************************************************/
  /*作用:将长整型数据变成可变长度,存入buf处 */
  /*入口参数:n 数据 buf 结果保存入 */
  /****************************************************/
  void MIDI::WriteLenghtToBuf(unsigned long n,char *buf)
  { unsigned char b[4]={0};
  int i;
  b[3]=(unsigned char)(n0x7f);
  i=2;
  while(n>>7)
  { n>>=7;
   b[i--]=(char)( (n0x7f)|0x80);
   }
  for (i=0;i<4;i++)
  if (b[i]) *(buf++)=b[i];
  *buf=0;
  }
  long MIDI::NewLong(long n) //将长整型数据改成高位在前
  { union LENGHT l={0};
  char i;
  l.length=n;
  SWAP(l.b[3],l.b[0]);
  SWAP(l.b[2],l.b[1]);
  return(l.length);
  }
  //开始演奏音乐
  void MIDI::WriteNoteOn(char channel, //通道号
  char note, //音符值
  char speed, //按键速度
  unsigned long delaytime) //延时数
  { unsigned char buf[5];
  int i;
  channel--;
  *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0x90|channel0x7f;//Write Channel
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=speed;
  WriteLenghtToBuf(delaytime*MIDICLOCK,buf);
  i=0;
  while(buf[i]>=0x80) //Write Delay Time
   *(MidiFileBuf++)=buf[i++];
  *(MidiFileBuf++)=buf[i];
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=0;
  }
  void MIDI::NoteOn(char note,
  char speed,
  unsigned long delaytime) //发音
  { unsigned char buf[5];
  int i;
  *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=speed;
  WriteLenghtToBuf(delaytime*MIDICLOCK,buf);
  i=0;
  while(buf[i]>0x80)
   *(MidiFileBuf++)=buf[i++];
  *(MidiFileBuf++)=buf[i];
  *(MidiFileBuf++)=note;
  *(MidiFileBuf++)=0;
  }
  void MIDI::ChangePrommgram(char channel,char n) //改变音色
  { *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0xc0|(channel-1)0x7f;
  *(MidiFileBuf++)=n;
  }
  void MIDI::WriteTextMsg(char *msg) //向内存写入一文本信息
  { char bufmsg[100]={0xff,5,0,0,0};
  int len;
  *(MidiFileBuf++)=0;
  bufmsg[2]=(char)strlen(msg);
  strcpy(bufmsg[3],msg);
  strcpy(MidiFileBuf,bufmsg);
  MidiFileBuf+=strlen(bufmsg)+3;
  }
  void MIDI::WriteTrackEndMsg() //磁道结束信息
  { *(MidiFileBuf++)=0;
  *(MidiFileBuf++)=0xff;
  *(MidiFileBuf++)=0x2f;
  *(MidiFileBuf++)=0;
  }
  char MIDI::GetToneNum(char n,char flag)
  /*入口参数: n 音高
   flag 升降记号
  返回值: 该乐音的实际标号值*/
  { static char val[7]={9 ,11,0,2,4,5,7};
  static char one[7]={0,2,4,5,7,9,11};
  int i;
  i=OneVal;
  if (n<='7' n>='1') i=i+one[n-'1'];
  else
   if (n>='a' n<='g')
  i=i+val[n-'a']-12; //低音,降12个半音
   else
   if (n>='A' n<='G') //高音,升12个半音
   i=i+val[n-'A']+12;
   else //否则,识为休止符
   i=0;
  if (flag=='b') i--;
  else if (flag=='#') i++;
  return(i);
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值