WAP之家:为您提供最全最新的WAP技术,CP.SP.3G等行业资讯。 WAP之家交流论坛全新开放 点击进入>>
WAP资讯 | 3G动态 | SP动态 | 运营商动态 | 内容商动态 | 制造商动态 | 论坛讨论>> 每次自动访问
WAP技术 | WAP源码 | 手机编程 | 手机源码 | 无线技术 | J2ME技术 | 手机软件 添加到收藏夹
IVR技术 | SP资料 | SMS MMS技术 | 商业方案 | IVR下载 | 书籍教程 | 工具软件 语言:繁體中文

WAP之家技术文章SMS MMSMMS技术MIDI文件结构分析及生成方法

MIDI文件结构分析及生成方法
作者:佚名  来源:本站整理  发布时间:2005-7-13 10:51:00
如下说明字符组合而成。

音高:

  高音 c d e f g a b
  中音 1 2 3 4 5 6 7
  低音 c d e f g a b

  若某音升半音,则在其后加#号;降半音,在其后加b字符。

  音长: -(延长四分音符的一拍)、_(8分音符,后可带符点)、=(16分音符,后可带符点)、.(附点音符,后不可带符点)、:(32分音符,后可带符点)、;(64分音符,后不可带符点)。

  说明:在书写时,请先写完整的音高,再写音长,如简谱中的"3-",则应该为"3#-"。

  pn:表示设置音色,取值1-256之间。
  {}:歌词或注释。
  |: 表示小节分隔符。
  \: 后继音均降八度
  /: 后继音均升八度
  sn:音量大小,n数值越大,音量越大。
  其它的字符,视为非法字符。

  以下为歌曲《解放军的天》片断midi文本文件。

  [midi]


  f,2/4,150,2


  [1]


  p53


  /3=3=3=2= 3_.2= | 1_1=e= g | 3=3=3=2= 3_3=2= | 1_1=e= g |


  \6_. 5=  6_.5= | 6_c_ 3_5_| 6=6=6=6= 6_6_ | 5=6=c_ c_3_|


  2_.3= 5_c_ | 6=5=3_ 5 | 6_. / 1= 2_.1=| 2_0_ 3_.2= |


  1_0_ 2_2_ | \5_.6= /1_3_ | 3=1=a_ 1 \ |


  [2]


  p53 \


  1_c_ 5_g_ | 1_c_ 5_g_ | 1_c_ 5_g_ | 1_c_ 5_g_ |


  a_6_ 4_6_ | 1_5_ 3_ 5_ | a_6_ 4_6_ | 1_5_ 3_ 5_ |


  1_3_ 5_1_ | 1_6_ 4_6_ | 2_5_ 1_5_ | g_5_ 1_5_ |


  1_5_ 3_5_ | 1_6_ 4_6_| 3_2_ 1 |


四、程序实现

  以下为midi文件生成的全部源程序,经borland c++3.1编译、连接通过。

  #include <stdlib.h>
  #include <stdio.h>
  #include <io.h>
  #include <string.h>
  #define c1 60 //c调1的键名值
  #define fourpainum 64 //1/4音符计数
  #define midiclock 24 //每1/64音符的midiclock数
  #define jumpnullchar(x) \ //跳过空字符
   { \
   while(*x==' ' \
   ||*x=='\t' \
   ||*x=='\n' \
   ||*x=='|') \
   x++; \
   };
  enum errorcode{ //处理错误信息
   changeok, //转换成功
   textfilenotopen, //文本文件不能打开
   midifilecannotcreate, //指定的midi文件不能建立
   textfiletobig, //文本文件太大
   mallocerror, //内存分配错误
   invalidechar, //在文本文件中出现了非法字符
   notfoundtrack, //没有找到指定的磁道信息
   notmiditextfile, //文本文件不是midi文本文件
   };
  void swap(char *x,char *y) //两数据交换
  { char i;
   i=*x;
   *x=*y;
   *y=i;
  }
  union lenght
  { long length;
  char b[4];
  } ;
  struct mh { //midi文件头
  char midiid[4]; //midi文件标志mthd
  long length; //头部信息长度
  int format; //存放的格式
  int ntracks; //磁道数目
  int perpainum; //每节计算器值
  };
  struct th //音轨头
  { char trackid[4]; //磁道标志mtrk
   long length; //信息长度
  } ;
  class midi
  {
  public:
  char errormsg[100]; //错误信息
  private:
  unsigned char *textfilebuf,
   *textfileoldbuf;
  unsigned char *midifilebuf,
   *midifileoldbuf;
  char oneval; //某调时,1的健值
  char painum; //第一小节节拍总数
  char onepaitonenum; //用几分音符作为一基本拍
  public:
  //将符全midi书定格式的文本文件生成midi文件
  int changetexttomidi(char *text952.htm,
   char *midi952.htm);
  char *geterrormsg() //获取错误信息
   { return(errormsg);}
  private:
  char getcurpaispeed(int n); //取当前拍的按下强度
  void writesoundsize(char ntrack,unsigned int );
  void setonepaitonenum(int n)
   { onepaitonenum=n; };
  void setoneval(char *m) ; //取m大调或小调时,1的实际键值
  char gettonenum(char c, //取记名对应的键值
   char flag) ;
  void writemhtofile(long length, //建立midi文件头
   int format,
   int ntracks,
   int perpainum,
   file *fp);
  void writethtofile(long lenght,
   file *fp); //建立midi磁道头
  void writetrackmsgtofile(file *fp);
  //将磁道音乐信息定入文件中
  void writespeed(int speed);
  void setpainum(int n)
   { painum=n;}
  long newlong(long n); //新的long值
  int newint(int n) //新的int值
   { return(n<<8|n>>8);}
  //将n改为可变长度,内入buf处
  void writelenghttobuf(unsigned long n,
   char *buf);
  void changeprommgram(char channel, //设置音色
   char promgram);
  void noteon (char n, //演奏乐音
   char speed,
   unsigned long delaytime);
  void writenoteon(char,char,char ,unsigned long) ;
  void writetextmsg(char *msg); //定一串文本信息
  void writetimesignature(char n, //设置时间信息
   char d);
  void writetrackendmsg(); //设置磁道结束信息
  };

  /**************************************************
  /* 作用:将符合midi文本文件的text文件转换成midi */
  /* 文件. */
  /* 入口参数:text952.htm 文本文件名 */
  /* midi952.htm midi文件名 */
  /* 出口参数:见 errorcode 说明 */
  /*************************************************/
  int midi::changetexttomidi(char *text952.htm,
  char *midi952.htm)
  { int tracks,ntrack,delaytime;
  int speed,isfirst,nn,dd;
  unsigned char buf[80],*msgbuf,c;
  file *textfp,*midifp;
  long filesize;
  char speedval;
  textfp=fopen(text952.htm,"r");
  if (textfp==null)
  {sprintf(errormsg,
  "文本文件[%s]不能打开。\n",text952.htm);
   return(textfilenotopen);
   }
  fseek(textfp,0,seek_end); /*测试文件大小*/
  filesize=ftell(textfp);
  textfilebuf=(char *)malloc(filesize);/*为文件分配内存*/
  if (textfilebuf==null)
  { sprintf(errormsg,
  "文本文件[%s]太大,没有足够的内存处理。\n",
  text952.htm);
   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(midi952.htm,"wb");
  if (midifp==null)
   { sprintf(errormsg,
  "midi文件[%s]不能建立。\n",midi952.htm);
   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",midi952.htm);
  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.",
   text952.htm,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)。",
   text952.htm,c);
   free(midifileoldbuf);
   free(textfileoldbuf);
   fcloseall();
   return(invalidechar);
   }
  }
  }
  writetrackendmsg(); //设置磁道结束信息
  writetrackmsgtofile(midifp); //将磁道音乐信息定入文件中
  ntrack++;
  }
  free(midifileoldbuf);
  free(textfileoldbuf);
  fclose(midifp);
  sprintf(errormsg,"midi文件[%s]转换成功。",midi952.htm);
  return(changeok);
  }
 /*****************************************************/
  /*作用:将长整型数据变成可变长度,存入buf处 */
  /*入口参数:n 数据 buf 结果保存入 */
  /****************************************************/
  void midi::writelenghttobuf(unsigned long n,char *buf)
  { unsigned char b[4]={0};
  int i;
  b[3]=(unsigned char)(n&0x7f);
  i=2;
  while(n>>7)
  { n>>=7;
   b[i--]=(char)( (n&0x7f)|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|channel&0x7f;//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)。",
   text952.htm,c);
   free(midifileoldbuf);
   free(textfileoldbuf);
   fcloseall();
   return(invalidechar);
   }
  }
  }
  writetrackendmsg(); //设置磁道结束信息
  writetrackmsgtofile(midifp); //将磁道音乐信息定入文件中
  ntrack++;
  }
  free(midifileoldbuf);
  free(textfileoldbuf);
  fclose(midifp);
  sprintf(errormsg,"midi文件[%s]转换成功。",midi952.htm);
  return(changeok);
  }
 /*****************************************************/
  /*作用:将长整型数据变成可变长度,存入buf处 */
  /*入口参数:n 数据 buf 结果保存入 */
  /****************************************************/
  void midi::writelenghttobuf(unsigned long n,char *buf)
  { unsigned char b[4]={0};
  int i;
  b[3]=(unsigned char)(n&0x7f);
  i=2;
  while(n>>7)
{ n>>=7;
   b[i--]=(char)( (n&0x7f)|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|channel&0x7f;//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);
  }

上一页  [1] [2] 

[] [返回上一页] [打 印]
文章评论

用户名: 查看更多评论

分 值:100分 85分 70分 55分 40分 25分 10分 0分

内 容:

         (注“”为必填内容。) 验证码: 验证码,看不清楚?请点击刷新验证码