TOP > カテゴリ > フォーマット変換 >

MIDIファイルを作成する(Perl)

WEB上で楽器やリズムを指定してテキストボックスに「音符データ」を入力するとMIDIファイルが作成できるサンプルプログラムです。但し、10年以上前に作成したもので修正していませんのでWEB上でMIDIファイルは作成できますが、MIDIファイルが再生されません。(適宜、修正して下さい)

サンプルの画面

サンプルプログラムの実行画面です。

[midsl.pl]

#====================================================================
# midsl.pl  MIDIファイル作成ライブラリ
#
# このCGIを使用したいかなる損害に対しても作者は一切の責任を負いません
#====================================================================

###################################
#
# midiファイルの生成
#
###################################
sub SaveToFile # (休符の状態,楽器,リズム,音程データ,テンポデータ)
{
 local($Instrument,$Versity,$SetTemp,@music,@temp,$count,$datfile,$position,$Rhythm,$rest);
 
  $datfile="midsl.dat";
  
  $rest=$_[0];                    # 休符の状態
  $Rhythm=$_[2];                  # 曲全体のリズム
  $Instrument=$_[1];              # 楽器
  @music = split(/,/, $_[3]);     # 音程データ    <-二つ合わせてはじめて音符になる 
  @temp  = split(/,/, $_[4]);     # テンポデータ  <-二つ合わせてはじめて音符になる 
 
  $SetTemp=0x10;    # 音符
  $count=@music;    # 配列数取得
  $Versity=0x7f;    # 音符のボリューム

  # ファイルにMIDIファイルを書きこむ
  if (open(MIDIOUT, "> $datfile")) 
  {
    binmode(MIDIOUT);     # バイナリモード
    print (MIDIOUT "");   # 最初に空にする。
   
   # 1トラック(初期情報)の書き込み-------------------------------------------->

       &Write_SMFHeader(2,16);         # SMFヘッダー
       &Write_SMFTrack();              # SMFチャンク
       print (MIDIOUT pack("C",0x00)); # ???
       &Write_Rhythm($Rhythm);         # リズム
       print (MIDIOUT pack("C",16));   # ???
       &Write_EndOfTrack(MIDIOUT);     # 終了サイン
       &Write_SMFTrack_DataLength(18,&GetPosition()-22); # SMFチャンクのサイズ書きこみ
 
   # 2トラックの書き込み------------------------------------------------------>
 
       &Write_SMFTrack();                 # SMFチャンク
       &Write_Instrument($Instrument);    # 楽器
 
       # はじめに8分休符をとる
       if ($rest eq "on"){ Write_NoteOn_Off(0x90,0x80,60,0x00,0x08); }
        
       # 音符データの書きこみ
       for ($i=0;$i<$count;$i++)
       {
         # 休符
         if (@music[$i] ==777)
         {
          &Write_NoteOn_Off(0x90,0x80,@music[$i],0,&GetTemp(@temp[$i]));
         } 
         else
         {
          &Write_NoteOn_Off(0x90,0x80,@music[$i],$Versity,&GetTemp(@temp[$i]));
         }
       }
       
       # 最後にも8分休符をとる
       if ($rest eq "on"){ Write_NoteOn_Off(0x90,0x80,60,0x00,0x08); }
    
       # ???
       print (MIDIOUT pack("C",0x00));
    
       # 終了サイン
       &Write_EndOfTrack(MIDIOUT);
    
       # SMFチャンクのサイズ書きこみ
       &Write_SMFTrack_DataLength(37,&GetPosition()-41);
      
    close(MIDIOUT);
   }
}

###################################
#
# SMFヘッダーの書き込み
#
###################################
sub Write_SMFHeader # (トラック数,テンポ)
{
 local($TrackCount)=$_[0];
 local($Temp)=$_[1];
 
 print (MIDIOUT pack("N",0x4D546864));  # SMF ID
 print (MIDIOUT pack("N",6));           # データの長さ
 print (MIDIOUT pack("n",1));           # フォーマット
 print (MIDIOUT pack("n",$TrackCount)); # トラック数
 print (MIDIOUT pack("n",$Temp));       # 時間単位
}

###################################
#
# SMFトラックチャンクの書き込み
#
###################################
sub Write_SMFTrack 
{
 print (MIDIOUT pack("N",0x4D54726B)); # トラックチャンクのID 
 print (MIDIOUT pack("N",0));          # チャンクデータの長さ
}

###################################
#
# チャンクの長さの書き込み
#
###################################
sub Write_SMFTrack_DataLength # (書きこみ位置,データサイズ)
{
 local($StratPos)=$_[0];
 local($DataSize)=$_[1];

 seek(MIDIOUT,$StratPos,0);
 print (MIDIOUT pack("N",$DataSize));
 seek(MIDIOUT,0,2);
}

###################################
#
# 曲全体のリズムを書き込む
#
###################################
sub Write_Rhythm # (リズム)
{
  # テンポを変更するメタイベント----------------------
  # ■FF 51 03 xxx(3byte)
  # ※xxxは4分音符の長さをマイクロ秒単位で表現する 
  # --------------------------------------------------

  local($Rhythm)=$_[0];

  print (MIDIOUT pack("C",0xFF));
  print (MIDIOUT pack("C",0x51));
  print (MIDIOUT pack("C",0x03));
  
  if    ($Rhythm==250)
  { print (MIDIOUT pack("C",0x03)); print (MIDIOUT pack("C",0xA9)); print (MIDIOUT pack("C",0x80));}
  elsif ($Rhythm==240)
  { print (MIDIOUT pack("C",0x03)); print (MIDIOUT pack("C",0xD0)); print (MIDIOUT pack("C",0x90));}
  elsif ($Rhythm==230)
  { print (MIDIOUT pack("C",0x03)); print (MIDIOUT pack("C",0xF7)); print (MIDIOUT pack("C",0xA0));}
  elsif ($Rhythm==220)
  { print (MIDIOUT pack("C",0x04)); print (MIDIOUT pack("C",0x28)); print (MIDIOUT pack("C",0x74));}
  elsif ($Rhythm==210)
  { print (MIDIOUT pack("C",0x04)); print (MIDIOUT pack("C",0x57)); print (MIDIOUT pack("C",0x54));}
  elsif ($Rhythm==200)
  { print (MIDIOUT pack("C",0x04)); print (MIDIOUT pack("C",0x93)); print (MIDIOUT pack("C",0xE0));}
  elsif ($Rhythm==190)
  { print (MIDIOUT pack("C",0x04)); print (MIDIOUT pack("C",0xCE)); print (MIDIOUT pack("C",0x78));}
  elsif ($Rhythm==180)
  { print (MIDIOUT pack("C",0x05)); print (MIDIOUT pack("C",0x12)); print (MIDIOUT pack("C",0xD4));}
  elsif ($Rhythm==170)
  { print (MIDIOUT pack("C",0x05)); print (MIDIOUT pack("C",0x60)); print (MIDIOUT pack("C",0xF4));}
  elsif ($Rhythm==160)
  { print (MIDIOUT pack("C",0x05)); print (MIDIOUT pack("C",0xB8)); print (MIDIOUT pack("C",0xD8));}
  elsif ($Rhythm==150)
  { print (MIDIOUT pack("C",0x06)); print (MIDIOUT pack("C",0x1A)); print (MIDIOUT pack("C",0x80));}
  elsif ($Rhythm==140)
  { print (MIDIOUT pack("C",0x06)); print (MIDIOUT pack("C",0x80)); print (MIDIOUT pack("C",0x10));}
  elsif ($Rhythm==130)
  { print (MIDIOUT pack("C",0x06)); print (MIDIOUT pack("C",0xFF)); print (MIDIOUT pack("C",0x04));}
  elsif ($Rhythm==110)
  { print (MIDIOUT pack("C",0x08)); print (MIDIOUT pack("C",0x50)); print (MIDIOUT pack("C",0xE8));}
  elsif ($Rhythm==100)
  { print (MIDIOUT pack("C",0x09)); print (MIDIOUT pack("C",0x27)); print (MIDIOUT pack("C",0xC0));}
  elsif ($Rhythm==90)
  { print (MIDIOUT pack("C",0x0A)); print (MIDIOUT pack("C",0x25)); print (MIDIOUT pack("C",0xA8));}
  elsif ($Rhythm==80)
  { print (MIDIOUT pack("C",0x0B)); print (MIDIOUT pack("C",0x5E)); print (MIDIOUT pack("C",0x28));}
  elsif ($Rhythm==70)
  { print (MIDIOUT pack("C",0x0C)); print (MIDIOUT pack("C",0xF8)); print (MIDIOUT pack("C",0x50));}
  elsif ($Rhythm==60)
  { print (MIDIOUT pack("C",0x0F)); print (MIDIOUT pack("C",0x42)); print (MIDIOUT pack("C",0x40));}
  elsif ($Rhythm==50)
  { print (MIDIOUT pack("C",0x12)); print (MIDIOUT pack("C",0x4F)); print (MIDIOUT pack("C",0x80));}
  else
  { print (MIDIOUT pack("C",0x07)); print (MIDIOUT pack("C",0xA1)); print (MIDIOUT pack("C",0x20));}
}

###################################
#
# トラックに終了サインを書き込む
#
###################################
sub Write_EndOfTrack
{
 # トラックの終了コードのメタイベント----
 # ■FF 2F 00
 # --------------------------------------
 print (MIDIOUT pack("C",0xFF));
 print (MIDIOUT pack("C",0x2F));
 print (MIDIOUT pack("C",0x00));
}

###################################
#
# 楽器を書き込む
#
###################################
sub Write_Instrument # (楽器)
{
 local($Instrument)=$_[0];

 if ($Instrument<0)     {$Instrument=0;}
 elsif ($Instrument>127){$Instrument=0;}

 print (MIDIOUT pack("C",0x00));
 print (MIDIOUT pack("n",&IntelOder_Word(0xC0 | ($Instrument * 0x100))));  
 #print (MIDIOUT pack("C",0x00)); 
}

###################################
#
# ノートオン/ノートオフの書き込み
#
###################################
sub Write_NoteOn_Off # (ノートオン,ノートオフ,ノートナンバー,ベロシティ,テンポ)
{
  # ノートオフメッセージ------------------------------------------------------------
  # ■8n kk vv
  #  ・n  ---「チャンネルナンバー」 MIDIチャンネル($0から$Fまで)
  #  ・kk ---「ノートナンバー」     音程をあらわす(表を参照)
  #  ・vv ---「ぺロシティ」         鍵盤を弾くときの速さを表す値(1~127の127段階)
  #                                 ※0はノートオフと同じ動作=音を消す
  #---------------------------------------------------------------------------------

  # ノートオンメッセージ------------------------------------------------------------
  # ■9n kk vv
  #   ・n  ---「チャンネルナンバー」 MIDIチャンネル($0から$Fまで)
  #   ・kk ---「ノートナンバー」     音程をあらわす(表を参照)
  #   ・vv ---「ぺロシティ」         鍵盤を弾くときの速さを表す値(1~127の127段階)
  #                                  ※0はノートオフと同じ動作=音を消す
  #---------------------------------------------------------------------------------
  
  local($NoteOn)    =$_[0];
  local($NoteOff)   =$_[1];
  local($NoteNumber)=$_[2];
  local($Versity)   =$_[3];
  local($Temp)      =$_[4];
  
  # ノートオンメッセージの書き込み
  print (MIDIOUT pack("C",0x00));
  print (MIDIOUT pack("C",$NoteOn));
  print (MIDIOUT pack("C",$NoteNumber));
  print (MIDIOUT pack("C",$Versity));
  print (MIDIOUT pack("C",$Temp));
  
  # ノートオフメッセージの書き込み
  print (MIDIOUT pack("C",$NoteOff));
  print (MIDIOUT pack("C",$NoteNumber));  
  print (MIDIOUT pack("C",$Versity));
}

###################################
#
# テンポを返す
#
###################################
sub GetTemp # (テンポデータ)
{
 local($work)    =$_[0];
 
 if ($work==1)      {return (0x40);}
 elsif ($work==2)   {return (0x20);}
 elsif ($work==8)   {return (0x8);}
 elsif ($work==16)  {return (0x4);}
 elsif ($work==32)  {return (0x2);}
 elsif ($work==22)  {return (0x20+0x10);}
 elsif ($work==44)  {return (0x10+0x8);}
 elsif ($work==88)  {return (0x8+0x4);}
 elsif ($work==1616){return (0x4+0x2);}
 
 else               {return (0x10);}
}

###################################
#
# 現在のポジションを返す
#
###################################
sub GetPosition
{
 return (tell(MIDIOUT));
}  

###################################
#
# Litle endian<->Big Endian Word編
#
###################################
sub IntelOder_Word
{
 local($Buffer)=$_[0];
 local($work)   =($Buffer & 0x00FF) << 8 ;
 return ($work | (($Buffer & 0xFF00) >> 8)) ;
}  

###################################
#
#Litle endian<->Big Endian DWord編
#
###################################
# sub IntelOder_DWord
# {
#  local($Buffer)=$_[0];
#  local($work)      =($Buffer & 0x000000FF) << 24 ;
#  $work    =$work | (($Buffer & 0x0000FF00) << 8) ;
#  $work    =$work | (($Buffer & 0x00FF0000) >> 8) ;
#  return   ($work | (($Buffer & 0xFF000000) >> 24)) ;
# }

###################################
#
# 文字列を"0123456789,?/"だけにする
#
###################################
sub SetAscii
{
 local($buffer)=$_[0];
 local($count)=length($buffer);
 local($dest)="";
  
 for (local($i)=0;$i<$count;$i++)
 {
   local($work)=substr($buffer,$i,1);

   if ($work=~ /0|1|2|3|4|5|6|7|8|9|,|\/|\?/)
   {      
     $dest .= $work;
   }
 }
  return $dest;
}

###################################
#
# 文字列を数値に変換
#
###################################
sub StrToInt
{
 local($buffer)=$_[0];
 local($lensize)=length($buffer);
 local($dest)=0;
 
 local(@work) = unpack("C*", $buffer);
 for (local($i)=0;$i<$lensize;$i++)
 {
  $dest =$dest + (@work[$i]-48)*(&AndMask($lensize-($i+1)));
 }
 return  $dest;
}

#
# StrToInt用のマスク整数発行
#

sub AndMask
{
 local($buffer)=$_[0];
  
  if ($buffer==0){return (1);}
  else {return (10 ** $buffer);}
}


1; # return

サンプルプログラム一式のダウンロード

create_midi_perl.zip 20.6 KB (21,137 バイト)

注意事項

このサンプルは約10年前に作成した「いにしえ」の産物です。予めご了承下さい。





関連記事



公開日:2015年02月19日
記事NO:00244