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年前に作成した「いにしえ」の産物です。予めご了承下さい。
スポンサーリンク
関連記事
前の記事: | WAVEファイルを作成する(C++) |
次の記事: | MIDIファイルを作成する(Perl) |
公開日:2015年02月19日
記事NO:00243