2007年11月18日星期日

Midi文件解码器(8052)

Midi文件解码器(8052)

下面的程序是可以播放midi音乐的。并且有3个灯用来和音乐中的小节同步(三个等依次为 强,次强,弱)。该程序只对单轨音乐进行解码。从开始做到调通灯光,我花了1周的时间。

希望有用的到的朋友。

 

#i nclude "reg52.h"
/**********************************************************************
     Name:  Midi_Player
     Version: 1.0
     hardware: 8052
     Author:  jiangkeqiang
     Email:  brave_man.student#sina.com.cn
     comment: hoping you can benifit from these code
/*********************************************************************/
#define BUFFER_LEN 528

void send_data(unsigned char *dat,unsigned int len);
unsigned long read4byte();
unsigned short read2byte();
unsigned char read1byte();
unsigned int get_wait_time(unsigned long wait);
unsigned char do_event();
unsigned long GetVLE ();
void led_blink();
unsigned long get_different(unsigned long begin,unsigned long end);
void loadsongtobuffer();


unsigned char code song[] =//this is a midi file
{
0x4d,0x54,0x68,0x64,0x00,0x00,0x00,0x06,0x00,0x00,
0x00,0x01,0x00,0x78,0x4d,0x54,0x72,0x6b,0x00,0x00,
0x3a,0x71,0x00,0xff,0x58,0x04,0x01,0x02,0x18,0x08,
0x00,0xff,0x59,0x02,0x00,0x00,0x00,0xff,0x51,0x03,
0x03,0xa9,0x80,0x00,0xc0,0x49,0x00,0xb0,0x07,0x7f,
0x00,0xc1,0x21,0x00,0xb1,0x07,0x54,0x00,0xc2,0x00,
0x00,0xb2,0x07,0x5f,0x00,0xc3,0x2e,0x00,0xb3,0x07,
0x5b,0x00,0x0a,0x62,0x00,0xc4,0x19,0x00,0xb4,0x07,
0x64,0x00,0x0a,0x34,0x00,0xc5,0x68,0x00,0xb5,0x07
};//由于减少没用信息,这里的内容不足一首歌曲。使用时

//只需要把这里替换成你想换的歌曲

//unsigned long idata time_wait=0xffff;
unsigned long idata  overflow_count=0;

unsigned short idata tempo;
//unsigned long idata  byte_of_track;
unsigned char idata  com_data[4];
unsigned char idata  last_command;
unsigned char idata  paizi=4;
unsigned char idata  jie_len;
unsigned char idata  ledsqu[6];
unsigned char idata  ledindex;

unsigned char xdata song_buffer[BUFFER_LEN];
unsigned char xdata * idata ptr=song_buffer;

unsigned short idata counter=0;
//unsigned long led_count=0;
bit next_is_begin = 0;
bit load = 1;

//0---2/4
//1---3/4
//2---4/4
//3---6/8
unsigned short base_count=0;//1/4音符需要的overflow_count数

void timer1_ISR (void) interrupt 3
{
//中断一次经历1/1800秒
 TF1 = 0;            /* Clear the interrupt request */
 TH1 = 0xFB;
 TL1 = 0x73;
 overflow_count++;
// led_count++;
}
void close_led()
{
 P1 = 0xFF;
}

void led_blink()
{
//2/4拍是"强、弱";
//3/4拍是"强、弱、弱";
//4/4拍是"强、弱、次强、弱",
//8/6拍是"强、弱、弱、次强、弱、弱",
 unsigned char max;
 
 max = paizi;
 
 if(paizi == 5)
 {
  max = 3;//3/8拍
 }
 
 if(max == 0)//程序不认识该节拍
 {
  //关灯
  P1 = 0xFF;
  return;
 }
 

 if(ledindex >= max)
  ledindex =0 ;
  
 if(ledsqu[ledindex] == 0)
 {
  P1 = 0xFB;
 }
 else if(ledsqu[ledindex] == 1)
 {
  P1 = 0xEF;
 }
 else if(ledsqu[ledindex] == 2)
 {
  P1 = 0xBF; 
 }
 
 ledindex ++;
}

void ChangeSpeed (unsigned long mydata)
{
 base_count = mydata*1800/1000000;
}

unsigned int get_wait_time(unsigned long wait)
{
 if(wait == 0)
  return 0; 
 else
  return 1.00*base_count*wait/tempo;
}


void com_init()
{
 //8位UART.可变波特率
 SCON |= 0x40;
 
 //使用Timer2
 TCLK = 1;
 
 //24M focs,31250Hz 
 RCAP2H = 0xFF;
 RCAP2L = 0xE8;
 
 //run timer2
 TR2 = 1;
}
void timer1_init()
{
 TH1 = 0x00;
 TL1 = 0x00;
 
 TMOD |= 0x10;
 
 //enable timer1 interrupts
 ET1 = 1;
 //globle interrupts enable
 EA = 1;
 
 //run timer1
 TR1 = 1;
}

void send_char(unsigned char c)
{

 SBUF = c;
 
 while(!TI);
 
 TI=0;

}

void send_data(unsigned char *dat,unsigned int len)
{
 unsigned int i;
 for(i=0;i<len;i++)
  send_char(dat[i]);
}
/*
unsigned long get_different(unsigned long begin,unsigned long end)
{
 if(end>=begin)
  return (end-begin);
 else
  return (0xFFFFFFFF-begin+end);
}
*/
unsigned long get_end(unsigned long begin,unsigned long wait)
{
 unsigned long end;
 
 end = begin + wait;
 
 return end;
}

void begin_play()
{
 unsigned long begin1=0;
 unsigned long begin2=0;
 unsigned long wait1;
 unsigned long wait2;
 unsigned long end1;
 unsigned long end2;
 unsigned char flag=0;
 unsigned char thing_index=0x00;
 unsigned short idata jie_click; 

 //默认是4/4拍
  paizi = 4;
 ledsqu[0] =0;
  ledsqu[1] =2;       
  ledsqu[2] =1;      
  ledsqu[3] =2;
 ledindex = 0;
 //亮红灯。

 //给base_count附上初值,假设每拍0.5s
 ChangeSpeed (1000000*60/tempo);
 jie_click = 4*tempo;
 jie_len = 4;
 
 wait1 = GetVLE();
 counter += wait1;
 wait1 = get_wait_time(wait1);
 end1 = get_end(begin1,wait1);

 wait2 = get_wait_time(tempo/2);
 end2 = get_end(begin2,wait2);
 


 while(1)
 {
  while(1)
  {
   if(end1 <= overflow_count )
   {
    thing_index |= 1;    
    begin1 = end1;
   }
   if(end2 <= overflow_count)
   {
    thing_index |= 2;
    flag = !flag;
    begin2 = end2;
   }  

   if(thing_index)
    break;   
       
  }


  if(thing_index&0x01)
  { 

   next_is_begin = 0;
   if( do_event() ==0)
   {
    close_led();
    break;
   }
  
   wait1 = GetVLE();   
   if(counter % (tempo*jie_len) == 0)
   {
    counter = 0;
    ledindex = 0;
    led_blink();
    flag = 0;
   
    begin2 = begin1;
    wait2 = get_wait_time(tempo/2);
    end2 = get_end(begin2,wait2); 
    
    thing_index = 0x01;//不再进行亮灯程序
   }
    
   counter += wait1;
   if(next_is_begin == 1)
   {
    counter = 0;
    ledindex = 0;
    led_blink();   
   }

   
   wait1 = get_wait_time(wait1);
   end1 = get_end(begin1,wait1);   
  }      
  if(thing_index&0x02)
  {
   if(flag == 0)
    led_blink();
   else
    close_led();
   wait2 = get_wait_time(tempo/2);
   end2 = get_end(begin2,wait2);    
  }  
  thing_index = 0;
 }
}

unsigned char do_event()
{
 unsigned char MIDIByte,MetaEvent,MIDICommand,MIDIChannel;
 unsigned long DataLength,Counter1;
 unsigned long mydata;
 unsigned char n1,n2;
 
 
 MIDIByte = read1byte();
 if((MIDIByte & 0x80) == 0x00)
 {
  MIDIByte = last_command;
  ptr--;
  load = 0;
 }
 else
 {
  last_command = MIDIByte;
 }
 
 if (MIDIByte == 0xFF)
 {
     MetaEvent  = read1byte();
     DataLength = GetVLE();
     if (MetaEvent == 0x2F) //End of track}
      return 0;
     else if (MetaEvent == 0x51)
     { //Tempo change
       mydata = read1byte();
       mydata = (mydata << 8) + read1byte();
       mydata = (mydata << 8) + read1byte();
      
       ChangeSpeed (mydata);
     }
     else if(MetaEvent == 0x58)
     {
      n1 = read1byte();
      n2 = read1byte();
      mydata = read1byte();
      mydata = read1byte();
      
      if(n1 == 6 && n2 ==3)//6/8拍
      {
       jie_len = 6;     
       paizi = 6;
       ledsqu[0] =0;
       ledsqu[1] =2;       
       ledsqu[2] =2;      
       ledsqu[3] =1;
       ledsqu[4] =2;       
       ledsqu[5] =2;             
      }
      else if(n1 == 3 && n2 ==3)//3/8拍
      {
       jie_len = 3;          
       paizi = 5;
       ledsqu[0] =0;
       ledsqu[1] =2;       
       ledsqu[2] =2;      
      }      
      else if(n1 == 4 && n2 == 2)//4/4拍
      {
       jie_len = 4;         
       paizi = 4;
       ledsqu[0] =0;
       ledsqu[1] =2;       
       ledsqu[2] =1;      
       ledsqu[3] =2;       
      }
      else if(n1 ==3 && n2 == 2)//3/4拍
      {
       jie_len = 3;           
       paizi = 3;
       ledsqu[0] =0;
       ledsqu[1] =2;       
       ledsqu[2] =2;      
      }
      else if( (n1 == 2 && n2 == 2) || (n1 == 2 && n2 == 1) )//2/4拍,2/2拍
      {
       jie_len = 2;      
       paizi = 2;
       ledsqu[0] =0;
       ledsqu[1] =2;       
      }
      else if(n1 == 1 && n2 == 2)//1/4拍
      {
       jie_len = 1;           
       paizi = 1;
       ledsqu[0] =0;
      }
      else
      {
       paizi = 0;
      }
      ledindex = 0;
      counter = 0;
      next_is_begin = 1;//下下一个event开始计拍
     }
     else
     {
        //Others (text events, track sequence numbers etc. - ignore
        for (Counter1 = 1; Counter1 <= DataLength; Counter1++)
          read1byte();
     }    
 }
 
   /*
     CHANNEL COMMANDS
     ================
     Upper nibble contains command, lower contains channel
   */
   MIDIChannel = MIDIByte & 0xF;
 MIDICommand = MIDIByte >> 4;
 if (MIDICommand == 8 || MIDICommand == 9 || MIDICommand == 0x0a)
 { //Note off
  n1 = read1byte();
      n2 = read1byte();
      /*This allows the use of a wavetable General MIDI instrument (such
      as the Roland SCC1 (or an emulation thereof) or the FM synthesizer*/
      if(n2 > 127)
       n2 = 127;
  com_data[0] = MIDIByte;
  com_data[1] = n1;
  com_data[2] = n2;
  send_data(com_data,3);
     }
 if(MIDICommand == 0x0b || MIDICommand == 0x0e)     
 {
  n1 = read1byte();
      n2 = read1byte();
      /*This allows the use of a wavetable General MIDI instrument (such
      as the Roland SCC1 (or an emulation thereof) or the FM synthesizer*/
     
  com_data[0] = MIDIByte;
  com_data[1] = n1;
  com_data[2] = n2;
  send_data(com_data,3); 
 }
  if(MIDICommand == 0x0c || MIDICommand == 0x0d)
  {
   n1 = read1byte();
   com_data[0] = MIDIByte;
   com_data[1] = n1;
   send_data(com_data,2); 
  }
  /*
    SYSTEM COMMANDS
    ===============
    These are ignored.
  */
 if (MIDICommand == 0xF)
 {
      if (MIDIChannel == 0)
      {
   DataLength = GetVLE();      
         for (Counter1 = 1; Counter1 <= DataLength; Counter1++)
           read1byte();
      }
      if (MIDIChannel == 2) {read2byte();} //Song Position Pointer
      if (MIDIChannel == 3) { read1byte(); } // Song Select
   }
 
 return 1;
}

int read_track_header()
{
//0x4d,0x54,0x72,0x6b,0x00,0x00,
//0x00,0xa4,0x00,0xff,0x03,0x08,0x75,0x6e,0x74,0x69,
//0x74,0x6c,0x65,0x64,0x00,0xff,0x58,0x04,0x04,0x02,
 unsigned long x;

 x = read4byte();
 if(x != 0x4d54726b)
  return 0;
 read4byte();
}


int read_midi_header()
{
//0x4d,0x54,0x68,0x64,0x00,0x00,0x00,0x06,0x00,0x00,
//0x00,0x01,0x00,0x78, 
 unsigned long x;
 unsigned short y;
 
 x = read4byte();
 if(x != 0x4d546864)
  return 0;
  
 x = read4byte();
 if(x != 0x00000006)
  return 0;
   
 //format
 y = read2byte();
 if(y != 0x00)
  return 0;
 
 //number of tracks  
 y = read2byte();
 if(y != 0x01)
  return 0;
 tempo = read2byte();
  
 return 1;
}
unsigned char read1byte()
{
 unsigned char ret;
 
 if( (ptr-song_buffer) % BUFFER_LEN ==0) && load==1 )
 {
  loadsongtobuffer();
  ptr = song_buffer;
 }
 
 ret = *ptr++;
 load = 1;
 return ret;
}
unsigned short read2byte()
{
 unsigned short ret;
 ret = read1byte();
 ret = ret << 8;
 ret |= read1byte();
 
 return ret;
}
unsigned long read4byte()
{
 unsigned long ret;
 ret = read1byte();
 ret = ret<<8;
 
 ret |= read1byte();
 ret = ret<<8;
 
 ret |= read1byte();
 ret = ret<<8;
 
 ret |= read1byte();
 
 return ret; 
}
unsigned long GetVLE ()
{
  unsigned char ByteRead;
  long Result;

  //Assume zero
  Result = 0;
  do {
    //Read first byte
    ByteRead = read1byte();
    //Store 7bit part
    Result = (Result << 7) + (ByteRead & 0x7F);
  } while ((ByteRead & 0x80) != 0);
  return Result;

static unsigned char *song_ptr=song;//改变量为了调试.
void loadsongtobuffer()
{
 int i;
 for(i=0;i<BUFFER_LEN;i++)
 {
  song_buffer[i] = *song_ptr++;
 }
 
}


main()
{
 com_init();
 timer1_init();
 

 if(read_midi_header() == 0)
  goto end;
 
 if(read_track_header() == 0)
  goto end;
  
 begin_play();
end: 
 for(;;);
}