#include #include"smf.h" // 固定長から可変長数値に変換 BYTE SmfFormat::Encode(BYTE *variable,DWORD fixed) { if(fixed<128){ variable[0]=(BYTE)fixed; return 1; // 戻り値は可変長数値のバイト数 }else if(fixed<16384){ variable[0]=(BYTE)((fixed>>7) | 0x80); variable[1]=(BYTE)( fixed & 0x7F); return 2; }else if(fixed<2097152){ variable[0]=(BYTE)((fixed>>14) | 0x80); variable[1]=(BYTE)((fixed>> 7) | 0x80); variable[2]=(BYTE)( fixed & 0x7F); return 3; }else if(fixed<268435456){ // 最大値 268435455 variable[0]=(BYTE)((fixed>>21) | 0x80); variable[1]=(BYTE)((fixed>>14) | 0x80); variable[2]=(BYTE)((fixed>> 7) | 0x80); variable[3]=(BYTE)( fixed & 0x7F); return 4; // 最大4バイト } return 0; // Error } // 可変長から固定長数値に変換 BYTE SmfFormat::Decode(BYTE *variable,DWORD *fixed) { (*fixed)=0; for(BYTE k=0;;k++){ // 最大4バイト if(k>=4) return 0; // Error (*fixed)+=(variable[k] & 0x7F); if(!(variable[k] & 0x80)) break; (*fixed)<<=7; } return k+1; // 可変長数値のバイト数 } // 相対時間から絶対時間に変換し、ランニングステータスを補完する // BYTE *pData 変換前のデータのアドレス // DWORD *nSumEvent イベントの数 MIDI** SmfFormat::ConvertStage1(BYTE *pData,DWORD *nSumEvent) { BYTE *p=pData; WORD nTrack=(p[10]<<8)+p[11]; MIDI **midiList=(MIDI**)calloc(nTrack,sizeof(MIDI*)); // 線形リスト for(DWORD k=0;klastEndTime) lastEndTime=sumTime; // 一番遅い End of Track の時間 pMidi->pNext=NULL; // 各トラックの最後 offset+=2; // ステータスを含めないイベントのバイト数だけ進める break; // while }else{ // 他のメタイベント len=1; // イベントタイプのバイト数だけ進める len+=Decode(p+offset+1,&dataSize); // イベントサイズ(可変長)のバイト数だけ進める len+=dataSize; // イベントデータ(可変長)のバイト数だけ進める } }else if(status==0xF0 || status==0xF7){ // システムエクスクルーシブイベント len=Decode(p+offset,&dataSize); // イベントサイズ(可変長)のバイト数だけ進める len+=dataSize; // イベントデータ(可変長)のバイト数だけ進める }else{ // MIDIイベント if((status & 0xF0)==0xC0 || (status & 0xF0)==0xD0) len=1; else len=2; } pMidi->pNext=(MIDI*)malloc(sizeof(MIDI)+len); pMidi=pMidi->pNext; // 線形リストを一つ進める pMidi->dwSumTime=sumTime; pMidi->dwEvent[0]=status; memcpy(&pMidi->dwEvent[1],p+offset,len); (*nSumEvent)++; offset+=len; // ステータスを含めないイベントのバイト数だけ進める } } // 一番時間が遅い End of Track を最後のトラックに書き込む for(MIDI *pMidi=midiList[nTrack-1];pMidi->pNext!=NULL;pMidi=pMidi->pNext); // トラックの最後を探す pMidi->pNext=(MIDI*)malloc(sizeof(MIDI)+2); pMidi=pMidi->pNext; pMidi->dwSumTime=lastEndTime; pMidi->dwEvent[0]=0xFF; pMidi->dwEvent[1]=0x2F; pMidi->dwEvent[2]=0x00; pMidi->pNext=NULL; // 新しい(トラックの)最後 (*nSumEvent)++; return midiList; } // 時間の早い順にポインタ配列で線形リストの各リストを指す // MIDI **midiList ConvertStage1メソッドの戻り値 MIDI** SmfFormat::ConvertStage2(BYTE *pData,MIDI **midiList,DWORD nSumEvent) { WORD nTrack=(pData[10]<<8)+pData[11]; MIDI **list=(MIDI**)calloc(nTrack,sizeof(MIDI*)); for(DWORD n=0;npNext; // 線形リストの先頭はダミー } // 線形リストの有効な各要素を指すポインタ配列 MIDI **midiArray=(MIDI**)calloc(nSumEvent,sizeof(MIDI*)); for(DWORD k=0;kdwSumTime < list[min]->dwSumTime) min=n; } midiArray[k]=list[min]; list[min]=list[min]->pNext; // 選択されたトラックの線形リストを次に進める } free(list); ComputeEndTime(pData,midiArray,nSumEvent); // 終了時刻(秒単位)を計算する return midiArray; } // 終了時刻(秒単位)を計算する // ConvertStage2メソッド以降に呼び出して下さい void SmfFormat::ComputeEndTime(BYTE *pData,MIDI **midiArray,DWORD nSumEvent) { m_endTime=0; WORD division=(pData[12]<<8)+pData[13]; DWORD befTempo=500000; // 一つ前のテンポ(現在有効なテンポ) // 500000 はセットテンポイベントが1つも指定されなかった場合の仮定値 DWORD befTime=0; // 一つ前のセットテンポイベントが発生した時の絶対チック for(DWORD k=0;kdwEvent[0]==0xFF){ if(p->dwEvent[1]==0x51 && p->dwEvent[2]==0x03){ // セットテンポイベント deltaTime=p->dwSumTime - befTime; m_endTime+=deltaTime*(befTempo/1000000.0)/division; // 相対時間を加算していく befTempo=(p->dwEvent[3]<<16)+(p->dwEvent[4]<<8)+p->dwEvent[5]; befTime=p->dwSumTime; }else if(p->dwEvent[1]==0x2F && p->dwEvent[2]==0x00){ // End of Track deltaTime=p->dwSumTime - befTime; m_endTime+=deltaTime*(befTempo/1000000.0)/division; // 相対時間を加算していく } } } // deltaTime : チック // befTempo : マイクロ秒 / 4分音符 // division : チック / 4分音符 } // 絶対時間から相対時間に変換し、フォーマット0データを作成する // MIDI **midiArray ConvertStage2メソッドの戻り値 BYTE* SmfFormat::ConvertStage3(BYTE *pData,MIDI **midiArray,DWORD nSumEvent) { BYTE *midi0=(BYTE*)malloc(22); BYTE info[18]={'M','T','h','d',0,0,0,6,0,0,0,1,pData[12],pData[13],'M','T','r','k'}; memcpy(midi0,info,18); DWORD written=22; // フォーマット0データのバイト数 DWORD befTime=0; // 一つ前のイベントの絶対時間 for(DWORD k=0;kdwSumTime - befTime; befTime=midiArray[k]->dwSumTime; BYTE varlenTime[4]; BYTE num=Encode(varlenTime,deltaTime); midi0=(BYTE*)realloc(midi0,written+num); memcpy(midi0+written,varlenTime,num); // デルタタイム written+=num; BYTE status=midiArray[k]->dwEvent[0]; DWORD eventSize,len; if(status==0xFF){ // メタイベント num=Decode(&midiArray[k]->dwEvent[2],&len); eventSize=2+num+len; }else if(status==0xF0 || status==0xF7){ // システムエクスクルーシブイベント num=Decode(&midiArray[k]->dwEvent[1],&len); eventSize=1+num+len; }else{ // MIDIイベント if((status & 0xF0)==0xC0 || (status & 0xF0)==0xD0) eventSize=2; else eventSize=3; } midi0=(BYTE*)realloc(midi0,written+eventSize); memcpy(midi0+written,midiArray[k]->dwEvent,eventSize); // イベント written+=eventSize; } DWORD trackDataSize=written-22; // トラックチャンクのデータサイズ midi0[18]=(BYTE)(trackDataSize>>24); midi0[19]=(BYTE)(trackDataSize>>16); midi0[20]=(BYTE)(trackDataSize>> 8); midi0[21]=(BYTE) trackDataSize; return midi0; } // フォーマット1をフォーマット0(ランニングステータス補完)に変換する // BYTE *pData 変換前のデータのアドレス // 戻り値は変換後のデータのアドレス BYTE* SmfFormat::Convert(BYTE *pData) { WORD nTrack=(pData[10]<<8)+pData[11]; // 変換前のトラック数 // 相対時間から絶対時間に変換し、ランニングステータスを補完する DWORD nSumEvent; MIDI **midiList=ConvertStage1(pData,&nSumEvent); // 時間の早い順にポインタ配列で線形リストの各リストを指す MIDI **midiArray=ConvertStage2(pData,midiList,nSumEvent); // 絶対時間から相対時間に変換し、フォーマット0に変換する BYTE *pSmf0=ConvertStage3(pData,midiArray,nSumEvent); // 不要になったバッファを解放する for(DWORD k=0;kpNext; // 次のリストのアドレスを一時保存 free(pMidi); pMidi=pNext; } } if(midiList) free(midiList); if(midiArray) free(midiArray); // ポインタ配列は線形リストを指している return pSmf0; } // 標準MIDIファイル(SMF)を作成する(ランニングステータスを補完したフォーマット0) // char *fileName 拡張子も付けて下さい void SmfFormat::CreateSMF(char *fileName) { if(m_pMidi==NULL) return; // MIDIの読み込みに失敗している DWORD nByte=22; // MIDIのバイト数 nByte+=(m_pMidi[18]<<24)+(m_pMidi[19]<<16)+(m_pMidi[20]<<8)+m_pMidi[21]; HANDLE fh=CreateFile(fileName,GENERIC_WRITE,0,NULL, CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); if(fh==INVALID_HANDLE_VALUE) MessageBox(NULL,"失敗しました\n同名のファイルが存在します",fileName,MB_OK); DWORD dwWriteSize; WriteFile(fh,m_pMidi,nByte,&dwWriteSize,NULL); CloseHandle(fh); } // データチェックと初期化 BYTE* SmfFormat::Initialize(BYTE *pData) { if(strncmp((char*)pData,"MThd",4)){ MessageBox(NULL,"標準MIDI(SMF)を指定して下さい","非対応データ",MB_OK); return NULL; } DWORD dataSize=(pData[4]<<24)+(pData[5]<<16)+(pData[6]<<8)+pData[7]; if(dataSize!=6){ MessageBox(NULL,"標準MIDI(SMF)を指定して下さい","非対応データ",MB_OK); return NULL; } WORD format=(pData[8]<<8)+pData[10]; if(format==2){ MessageBox(NULL,"指定したMIDIはフォーマット2です","非対応フォーマット",MB_OK); return NULL; }else if(format>2){ MessageBox(NULL,"フォーマット0または1のみ対応しています","非対応フォーマット",MB_OK); return NULL; } if(strncmp((char*)&pData[14],"MTrk",4)){ MessageBox(NULL,"標準MIDI(SMF)を指定して下さい","非対応データ",MB_OK); return NULL; } return Convert(pData); // ランニングステータスを補完したフォーマット0データを作成する } // リソース SmfFormat::SmfFormat(char *rscName,char *rscType) { m_pMidi=NULL; HRSRC hrs=FindResource(NULL,rscName,rscType); if(hrs==NULL){ MessageBox(NULL,"失敗しました\n指定したリソースは存在しません",rscName,MB_OK); return; } HGLOBAL hg=LoadResource(NULL,hrs); BYTE *pData=(BYTE*)LockResource(hg); BYTE *p=Initialize(pData); if(p) m_pMidi=p; // 作成したデータを指す } // ファイル SmfFormat::SmfFormat(char *fileName) { m_pMidi=NULL; HANDLE fh=CreateFile(fileName,GENERIC_READ,0,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(fh==INVALID_HANDLE_VALUE){ MessageBox(NULL,"失敗しました\nファイルが開けません",fileName,MB_OK); return; } DWORD fileSize=GetFileSize(fh,NULL); BYTE *pData=(BYTE*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,fileSize); DWORD readSize; ReadFile(fh,pData,fileSize,&readSize,NULL); CloseHandle(fh); BYTE *p=Initialize(pData); if(p) m_pMidi=p; // 作成したデータを指す HeapFree(GetProcessHeap(),0,pData); } // データ(SMFのデータ形式に従っている必要があります) SmfFormat::SmfFormat(BYTE *pData) { m_pMidi=NULL; BYTE *p=Initialize(pData); if(p) m_pMidi=p; // 作成したデータを指す } SmfFormat::~SmfFormat(void) { if(m_pMidi==NULL) return; // MIDIの読み込みに失敗している free(m_pMidi); }