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

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

テキストボックスに「ドレミファソラシド」を入力してその結果をMIDIファイルとして保存する事ができるサンプルプログラムです。C++版のサンプルは簡易版ですがMIDIファイルフォーマットの勉強として少しは参考になるかと思います。

サンプルの画面

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

[Midi.cpp]

//--------------------------------------------------------------------------------------
//  ■ミニミニMIDIコンパイラー(C/C++版「MIDIセーバー」)
//
//
//--------------------------------------------------------------------------------------

// 「ミニミニMIDIコンパイラー」は「MIDIセーバー」(Delphi用)をC/C++用に移植したものです。
// 私自身、C/C++の知識が乏しい(約3ヶ月の初心者)為、移植する際に面倒なものを削除しました。
// なので「MIDIセーバー」と比べるとかなり単純な仕様になっています。

// 完全版のサンプルが欲しい方はDelphi版をご利用下さい。

// 完全版の内容-------->

// MIDIフォーマットの解説<---Delphiがなくても参考になると思います。(xxx.pas参照)
// 64種類の鍵盤の使用 
// 128種類の楽器の使用
// 和音(音を重ねる)
// テンポの変更
// 音の強さの変更
// 休符の使用
// 実際の楽譜からMIDIデータの作成
// ドラム/ベースなどのリズムマップ音の利用
// 著作権情報の書きこみ
// テキスト情報の書きこみ
// など・・・

//<-------------------

#include <windows.h>

#define IDD_DIALOG 101
#define IDB_DO     1000
#define IDB_RE     1001
#define IDB_MI     1002
#define IDB_FA     1003
#define IDB_SO     1004
#define IDB_RA     1005
#define IDB_SI     1006
#define IDB_DOM    1007
#define IDB_WRITE  1008
#define IDB_CLAER  1009
#define IDE_EDIT   1011

//14bytes
typedef struct
{
 DWORD ChunckType; // SMFのID               'MThd' $6468544D
 DWORD DataLength; // データの長さ          ここからの構造体の大きさ   常に$06000000
 WORD  Format;     // SMFフォーマットの種類 「0,1,2のどれか」 0---シングルトラック 1---マルチトラック 2---通常使わない
 WORD  TrackCount; // トラック数  
 WORD  TimeType;   // 時間の種類            小節/拍/クロック(ティック)でタイミングを表す形式と絶対時間でタイミングを表す形式がある
}TSMFHEDAER;

//8bytes
typedef struct
{
 DWORD ChunckType; // トラックチャンクのID   'MTrk'($6B72544D)
 DWORD DataLength; // これ以後のデータの長さ 
}TSMFTRACKCHUNCK;

BYTE DataCode[10000]; //演奏データ とりあえず10kまで
int  CodeCount;

//コールバックプロシージャ
LRESULT CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//////// インテルオーダー word編
WORD IntelOder_Word(WORD Buffer)
{
 WORD work;
 work =(Buffer & 0x00FF) << 8 ;
 return (work | ((Buffer & 0xFF00) >> 8)) ;
}

//////// インテルオーダー Dword編
DWORD IntelOder_DWord(DWORD Buffer)
{
 DWORD work;
 work   =(Buffer & 0x000000FF) << 24 ;
 work   =work | ((Buffer & 0x0000FF00) << 8) ;
 work   =work | ((Buffer & 0x00FF0000) >> 8) ;
 return (work | ((Buffer & 0xFF000000) >> 24))  ;
}

///////////////////////////////////////////////////////////////////////////////
//
//  WinMain  
//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{    
 DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG), 0, (DLGPROC)DialogProc);
 return (0);
}

///////////////////////////////////////////////////////////////////////////////
//
//  DialogProc  
//
LRESULT CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  int      _size,i,j;
  CHAR     *buffer;
  BYTE     *MIDIData;
  HANDLE   hFile;
  DWORD    Work;
  WORD Instrument;     //楽器
  BYTE Versity;        //べロシティ(音の強さ)
  BYTE NoteNumber;     //ノートナンバー(音階)
  BYTE _Zero;          //ゼロ
  BYTE _Max;           //マックス
  WORD _End;           //終了値
  BYTE _NoteOn;        //ノートオン
  BYTE _NoteOff;       //ノートオフ
  BYTE _SetTemp;       //テンポ
  DWORD _FileSize;
  TSMFTRACKCHUNCK SMFTRACKCHUNCK;
  TSMFHEDAER      SMFHEDAER;
  
  switch (uMsg) 
    {
     case WM_INITDIALOG:  
         SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,NULL);
         return false;
     case WM_COMMAND:   
         switch (LOWORD(wParam)) 
         {
             //ユーザーが指定した音符データの取得----->

                case IDB_DO:
                   //10kまで
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sド",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x3C;
                   CodeCount++;
                   break;
                case IDB_RE:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sレ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    DataCode[CodeCount]=0x3E;
                   CodeCount++;
                   free(buffer);
                   break;
                case IDB_MI:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sミ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x40;
                   CodeCount++;
                    break;
                 case IDB_FA:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sファ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x41;
                   CodeCount++;
                   break;
                 case IDB_SO:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sソ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x43;
                   CodeCount++;
                   break;
                 case IDB_RA:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sラ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x45;
                   CodeCount++;
                   break;
                 case IDB_SI:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sシ",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x47;
                   CodeCount++;
                   break;
                case IDB_DOM:
                   if (CodeCount>10000) { MessageBox(0,"これ以上音符データを書き込めせん。","エラー",MB_OK); break;}
                   buffer=(CHAR*)malloc(10000);
                   ZeroMemory(buffer,10000);
                   _size= SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXTLENGTH ,0,0)+1;
                   if (_size!=1) SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_GETTEXT,_size,(LPARAM)buffer);
                   wsprintf(buffer,"%sド↑",buffer);    
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,(LPARAM)buffer);
                    free(buffer);
                   DataCode[CodeCount]=0x48;
                   CodeCount++;
                   break;

             //MIDIファイル生成----->

                case IDB_WRITE:
                
                     if (CodeCount==0) 
                     {
                      MessageBox(0,"音符データが入力されていません。","エラー",MB_OK); 
                      break;
                     }

                    hFile = CreateFile("Test.mid", GENERIC_WRITE, 0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
                    if (hFile!=NULL)
                    {
                      //初期設定
                     ZeroMemory(&SMFTRACKCHUNCK,sizeof(TSMFTRACKCHUNCK));
                     ZeroMemory(&SMFHEDAER,sizeof(TSMFHEDAER));
                                
                      _Zero    =0x00;    _Max     =0xFF;      
                      _End     =0x002F;  _NoteOn  =0x90;      
                      _NoteOff =0x80;  
                      
                      //テンポ
                      
                      //0.125秒(16分音符)
                      //_SetTemp =0x04;
                      //0.25秒(8分音符)
                      //_SetTemp =0x08;
                      //0.5秒(4分音符)
                      _SetTemp =0x10;
                      //1秒(2分音符)
                      //_SetTemp =0x20;
                      //2秒(全音符)
                      //_SetTemp =0x40;

                      //楽器の選択(0-127)  
                      //0は   AcousticGrandPiano
                      //127は Gunshot
                      //その他は「MIDI規格1.0」のサウンド音色(128種類) 一覧表を参照
                      Instrument =0xC0 | (0 * 0x100)  ;
                      
                      //音の強さ(0-127)
                      Versity=127;
                      
                      //SMFヘッダーの準備
                      SMFHEDAER.ChunckType =0x6468544D;         //SMFのID
                      SMFHEDAER.DataLength =IntelOder_DWord(6); //データの長さ
                      SMFHEDAER.Format     =IntelOder_Word(1);  //フォーマット
                      SMFHEDAER.TrackCount =IntelOder_Word(1);  //トラック数
                      SMFHEDAER.TimeType   =0x1000;             //時間単位
                                      
                      //SMFチャンク準備
                      SMFTRACKCHUNCK.ChunckType = 0x6B72544D;  //SMFトラックチャンクのID
                      SMFTRACKCHUNCK.DataLength =      0 ;     //これ以後のデータの長さ
                     
                      //構造体の書きこみ
                      WriteFile(hFile,&SMFHEDAER,14,&Work,NULL);
                      WriteFile(hFile,&SMFTRACKCHUNCK,8,&Work,NULL);
                      //楽器の書き込み
                      WriteFile(hFile,&_Zero,1,&Work,NULL);
                      WriteFile(hFile,&Instrument,2,&Work,NULL);
                      WriteFile(hFile,&_Zero,1,&Work,NULL);
                          
                      //メモリ確保
                      MIDIData=(BYTE*)malloc(CodeCount*8);
                      ZeroMemory(MIDIData,CodeCount*8);    j=0;
                      
                      //データの書きこみ
                      for (i=0;i<CodeCount;i++)
                      {                     
                        NoteNumber=DataCode[i];
                            
                         //ノートオンメッセージの書き込み
                        MIDIData[j]=_NoteOn;    j++;
                        MIDIData[j]=NoteNumber; j++;
                        MIDIData[j]=Versity;    j++;
                        MIDIData[j]=_SetTemp;   j++;
                    
                        //ノートオフメッセージの書き込み
                        MIDIData[j]=_NoteOff;   j++;
                        MIDIData[j]=NoteNumber; j++;
                        MIDIData[j]=Versity;    j++;
                        
                        //最後でなかったら
                        if (i!=CodeCount)
                        {  MIDIData[j]=_Zero; j++;}
                        else
                        {  MIDIData[j]=_SetTemp; j++;}
                      }
                      
                      WriteFile(hFile,MIDIData,j,&Work,NULL);
                      free(MIDIData);

                      //終了サインを書きこむ
                      WriteFile(hFile,&_Max,1,&Work,NULL);
                      WriteFile(hFile,&_End,2,&Work,NULL);
                      
                      //データサイズの書きこみ
                      _FileSize = GetFileSize(hFile, NULL)-22;
                      SetFilePointer(hFile,18,NULL,FILE_BEGIN);
                      _FileSize=IntelOder_DWord(_FileSize);
                      WriteFile(hFile,&_FileSize,4,&Work,NULL);
                      CloseHandle(hFile);
                    }
                   break;
                case IDB_CLAER:
                   SendMessage(GetDlgItem(hwnd,IDE_EDIT),WM_SETTEXT,0,NULL);
                   ZeroMemory(DataCode,10000);
                   CodeCount=0;
                   break;
                case 2:
                    EndDialog(hwnd, 2); 
                    break;    
                default: return false;    
         }
     default: return false; 
   } 
 return true;
}

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

create_midi_cpp.zip 12.7 KB (13,038 バイト)

注意事項

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





関連記事



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