国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

MIDI文件結構分析及生成方法

2019-11-18 20:09:00
字體:
來源:轉載
供稿:網友

從網上找的,已經將用BC寫的改成了VC的,由于對音樂的理解比樂盲還差,對于程序中轉換是否有問題我也不得而知,反正用VC生成的MIDI文件聽起來慘不忍睹。

 對于制作MIDI音樂來說,比播放MIDI文件本身更復雜得多。我們得了解一些樂理常識和MIDI文件結構。

一、MIDI文件結構分析   MIDI文件包含首部塊(Header Chunk)和音軌塊(Track Chunk)兩部分。其格式一般如下:

  MThd <數據長度> <Header數據>    //首部塊
  .......
  Mtrk <數據長度> <Track數據>     //音軌塊
  Header Chunk 結構為:
  char MidiId[4];
  long length;
  int  foarmt;
  int  TrackNum;
  int  division;

其中:

  MidiId稱為MIDI文件頭標志,一般將其設置為MThd;

  length為文件首部數據長度(除它本身和文件頭標志占用的字節以外),通常它設置為6,即format,TrackNum和division共占用的字節數據長度;

  format表示MIDI文件存放的格式,當前只有3種格式:

  0 表示MIDI文件只有一個Track Chunk;
  1 表示MIDI文件只有一個或多個Track Chunk;
  2 表示MIDI文件只有一個或多個各處獨立的Track Chunk。
  division指定計數的方法,一種隨時間計數(最高位設置為0時),另一種使用制式的時間碼(最高位設置為1時)。這里,主要介紹隨時間計數的一種格式。其各位意義如下:

  ┌─┬─────────┐
  │0 │ 每一拍的計數值   │
  └─┴─────────┘
  b15     b14  ̄ b0

  其最高位一定要設置為0,其它的15位表示每一拍的計數值。如該數據為96(以八分音符為一拍),則表示一個四分音符延時數應該為192。

  另外,在MIDI文件中,long和int型數據均將高字節值存放入低地址上,如一個long型數據為0x45678,則在文件中,存放的結果為:0x00,0x04,0x56,0x78。而在內存中,int,long的變量值通常將崐高字節值存放高地址上。因此,存放數據時,應該作一下調整。

  Track Chunk為用來播放歌曲的數據信息。每一個Track Chunk是一組簡單的MIDI碼(包括一些非MIDI碼)的集合。它又由頭部信息和崐若干個Mtrk event組合而成。

  頭部結構和意義為:

  char TrackChunkId[4];      //Track Chunk標志MTrk
  long TrackChunkMsgLength;  //該Track Chunk信息長度

  而Mtrk event是由時間計數值(dela-time)和event(MIDI碼信崐息)組合成的。即:

  <Mtrk event>=<dela-time> <event>

  <dela-time>使用可變長度的形式存儲數據,它代表處理event之前要計數時間值。 它在音樂中,即表示拍數。通常音樂開始演奏時,總是將計數時間值設置為0。為了能連續處理兩個event,我們可以將deta-time設置為0。如:3和5同時演奏2拍(每一拍計數值為24),可以設置如下:

  deta-time    event
  0         開始演奏3
  0         開始演奏5
  48        停止3演奏
  0         停止5演奏

  event表示MIDI碼信息集,如0x9n表示開始發音,0x8n表示關閉發音等等(下有說明)。

  上述的dela-time使用可變長度的形式表示數據值。可變長度形崐式是MIDI文件中對于大于8位的數據打用的一種存儲方式,它把每一個數據定義為7位,剩下的最高位作為數據長度的識別。當這一位為0時,表示數據是最后一個,若為1,則表示還有下一個。

  如:數值0x3fff,可變長度形式便為0xff,0x7f;0x4000則應該為0x81,0x80,0x00。此數據的轉換可以參閱WriteLenghtToBuf()函數。

二、常見MIDI碼說明

  MIDI碼是制定音樂交換的信息碼,它使用串行非同步傳送,因此數據碼是用多碼形式。第一個MIDI碼是狀態碼,剩余的都是數據碼,其長度視狀態而定。

  以下是一些常見的MIDI碼。

  1、開始發音(0x9n)

  格式為:0x9n note speed

  它一共占用3個字節,n表示通道號,取值0-15。MIDI可以同時演奏16個通道,用此指定在哪一個通道上發音(以下n相同)。

  note表示音高數值,即音階碼值。如C4(中音1)為60,它的取值在0xc和0x6c之間(具體碼值,可參考「參考書籍1」)。

  speed表示按鍵時的速度,用此表示音的力度。若沒有力度感,可以將其設置為64,若為0,表示關閉發音。

  如:在第2通道上開始演奏3,則MIDI碼便為0x91,63,40。

  MIDI規范還規定,若連續向同一通道上發送多個音,則可以不指出狀態碼。如上述同時演奏3,5,MIDI碼便為:0x91,63,40,65,40。

  2、關閉發音(0x8n)。

  格式:0x8n note speed

  說明同上。通常它用0x9n,note,0來代替。

  3、切換音色(0xcn)。

  格式:0xcn,PRogram

  program表示音色代碼,0 ̄255之間,如Acou Piano 1(電鋼1值為0),Synth Bass 1(電貝司1值為64)等(詳見「參考書籍1」)。

  4、設置音量大小

  格式:0xbn ,07,size

  0xbn,39,size

  7,表示設置主音量的高字節值;39表示設置主音量的低字節值。

  5、設置時間記號

  格式:0xff 0x58 04 nn dd cc bb

  nn和dd直接對應到譜號的數字,dd使用2的指數。如3/8,則nn=3,dd=3。cc是代表第次節拍器打后的時間是幾個MIDI clock。bb通常設置為8表示多少個MIDI clock等于1/4 拍。

  6、設置演奏速度

  格式:0xff 0x51 03 tt tt tt

  tt tt tt 表示第一拍定義多少個Miscro Seconds。它即是用來崐變演奏的速度。

  7、寫歌詞

  格式:0xff 0x05 len text

  len表示歌詞的長度,text表示歌詞文本碼。

  8、磁道結束

  格式:0xff,0x2f,00

  它表示結束點。每外track chunk后都應該有此MIDI碼。

三、MIDI信息文本文件制作

  為了能制成符合規范的MIDI文件,我們在此規定MIDI信息文本制作格式如下:

  [MIDI]
  <調號>,<節拍>,<每分鐘節拍數>,<音軌個數>
  [1]
  .....
  [n]
  ....

  說明:

  1、調號,占用一個字符,必須為A、B、C、D、E、F、G,否則視為C調;

  2、節拍,取值如下:2/4,3/4,4/4,3/8,6/8....等。

  3、每分鐘節拍數:表示每分鐘演奏的節拍總數,取值在40-200崐之間,否則視為120。

  4、音軌個數表示此歌曲聲部數。如三聲部,可將其設置為3。

  5、[n]后表示此音軌的音樂信息。有如下說明字符組合而成。

音高:

  高音 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 *TextFileName,
   char *MidiFileName);
  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 */
  /* 文件. */
  /* 入口參數: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)(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)。",
   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)(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);
  }

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 成安县| 康马县| 萨迦县| 清水河县| 威远县| 南开区| 佳木斯市| 富顺县| 武城县| 泰安市| 嘉定区| 绥阳县| 阿拉善右旗| 福贡县| 吉林省| 鲁山县| 永福县| 姚安县| 锡林郭勒盟| 新疆| 兴仁县| 克什克腾旗| 商城县| 遂川县| 夏津县| 江城| 简阳市| 大英县| 邳州市| 阳原县| 南江县| 芮城县| 射洪县| 福海县| 惠州市| 简阳市| 兴山县| 都昌县| 嘉善县| 体育| 资溪县|