#include #include"smf.h" // システムエクスクルーシブイベントを読み込んでバッファに書き込む // DWORD deltaTime 相対時間 // BYTE *p 読み込み元データの先頭アドレス // LPSTR pMidiBuf 書き込み先バッファの先頭アドレス // DWORD *written 書き込み先の先頭からのバイト数 DWORD SmfPlay::Sysexc(DWORD deltaTime,BYTE *p,LPSTR pMidiBuf,DWORD *written) { DWORD offset=1; // ステータスのバイト数 DWORD len; // イベントのバイト数 offset+=Decode(p+offset,&len); // イベントサイズ(可変長)とイベントデータ(可変長)のバイト数を取得 len++; // ステータスのバイト数を加算 DWORD nParam=(len/4) + (DWORD)((len%4)?1:0); if((*written)+sizeof(MIDIEVENT)+(nParam-1)*sizeof(DWORD) > m_bufsize) return 0; // バッファ不足 MIDIEVENT *pLongEvent; pLongEvent=(MIDIEVENT*)malloc(sizeof(MIDIEVENT)+(nParam-1)*sizeof(DWORD)); memset(pLongEvent,0,sizeof(MIDIEVENT)+(nParam-1)*sizeof(DWORD)); pLongEvent->dwDeltaTime=deltaTime; pLongEvent->dwStreamID=0; pLongEvent->dwEvent=MEVT_F_LONG | len; pLongEvent->dwParms[0]=p[0]; for(DWORD k=0;kdwParms[index] |= (p[offset+k]< m_bufsize) return 0; // バッファ不足 MIDISTRMEVENT shortEvent; shortEvent.dwDeltaTime=deltaTime; shortEvent.dwStreamID=0; DWORD offset; if(p[0]==0xFF && p[1]==0x51){ // セットテンポイベント shortEvent.dwEvent=(MEVT_TEMPO<<24) | (p[3]<<16) | (p[4]<<8) | p[5]; offset=6; }else{ // MIDIイベント if((p[0] & 0xF0)==0xC0 || (p[0] & 0xF0)==0xD0){ shortEvent.dwEvent=MEVT_F_SHORT | (p[1]<<8) | p[0]; offset=2; }else{ shortEvent.dwEvent=MEVT_F_SHORT | (p[2]<<16) | (p[1]<<8) | p[0]; offset=3; } } memcpy(pMidiBuf+(*written),&shortEvent,sizeof(MIDISTRMEVENT)); (*written)+=sizeof(MIDISTRMEVENT); // *written を書き換える事に注意して下さい return offset; // 読み込んだデータのバイト数 } // 終了イベントとして End of Track を送る // DWORD deltaTime 相対時間 // BYTE *p 読み込み元データの先頭アドレス // LPSTR pMidiBuf 書き込み先バッファの先頭アドレス // DWORD *written 書き込み先の先頭からのバイト数 DWORD SmfPlay::EndOfTrack(DWORD deltaTime,BYTE *p,LPSTR pMidiBuf,DWORD *written) { if((*written)+sizeof(MIDISTRMEVENT) > m_bufsize) return 0; // バッファ不足 MIDISTRMEVENT shortEvent; shortEvent.dwDeltaTime=deltaTime; shortEvent.dwStreamID=0; shortEvent.dwEvent= MEVT_F_CALLBACK | (p[2]<<16) | (p[1]<<8) | p[0]; memcpy(pMidiBuf+(*written),&shortEvent,sizeof(MIDISTRMEVENT)); (*written)+=sizeof(MIDISTRMEVENT); // *written を書き換える事に注意して下さい return 3; // 読み込んだデータのバイト数 } // データを読み込んでバッファに書き込む // BYTE *p 次に読み込みを開始するアドレス // MIDIHDR *pMidiHdr 現在のバッファのアドレス DWORD SmfPlay::TrackChunk(BYTE *p,MIDIHDR *pMidiHdr) { LPSTR pMidiBuf=pMidiHdr->lpData; memset(pMidiBuf,0,m_bufsize); // バッファの全内容クリア DWORD offset=0,written=0; DWORD befWri=0,*befEvent; // befWri は前回書き込んだバイト数 while(1){ BYTE nt; // 読み込んだ時間のバイト数 DWORD deltaTime; nt=Decode(p+offset,&deltaTime); // デルタタイムを読み込む // 前回のイベントのアドレス取得 befEvent=&((MIDISTRMEVENT*)(pMidiBuf+written-befWri))->dwEvent; befWri=written; DWORD ne; // 読み込んだイベントのバイト数 if(p[offset+nt]==0xF0 || p[offset+nt]==0xF7){ // システムエクスクルーシブイベント ne=Sysexc(deltaTime,p+offset+nt,pMidiBuf,&written); }else if(p[offset+nt]==0xFF){ // メタイベント if(p[offset+nt+1]==0x2F && p[offset+nt+2]==0x00){ // End of Track ne=EndOfTrack(deltaTime,p+offset+nt,pMidiBuf,&written); if(ne) offset+=(nt+ne); // 成功 else (*befEvent) |= MEVT_F_CALLBACK; // バッファが尽きた break; // while }else{ ne=Meta(deltaTime,p+offset+nt,pMidiBuf,&written); } }else{ // MIDIイベント ne=ShortEvent(deltaTime,p+offset+nt,pMidiBuf,&written); } if(ne){ // 成功 offset+=(nt+ne); befWri=written-befWri; continue; // while }else{ // バッファが尽きた (*befEvent) |= MEVT_F_CALLBACK; // 前回のイベントにコールバックフラグ設定 break; // while } } pMidiHdr->dwFlags=0; pMidiHdr->dwOffset=0; memset(pMidiHdr->dwReserved,0,sizeof(pMidiHdr->dwReserved)); pMidiHdr->lpNext=0; pMidiHdr->reserved=0; pMidiHdr->dwBytesRecorded=written; // 4の倍数でなければならない return offset; // データセクションのバイト数 } // マルチメディア関数のエラーをメッセージボックスで通知します void SmfPlay::MsgBox(MMRESULT result,char *caption) { TCHAR text[512]; if(result!=MMSYSERR_NOERROR){ mciGetErrorString(result,text,sizeof(text)/sizeof(text[0])); MessageBox(NULL,text,caption,MB_OK); } } // データを読み込んで出力する(再生はしない) void SmfPlay::StreamOut(void) { static bufnum=0; // バッファの番号 BYTE *p=m_pMidi; DWORD midiSize=22+(p[18]<<24)+(p[19]<<16)+(p[20]<<8)+p[21]; if(m_offset==midiSize) return; // 全データを読み込んだ // 全バッファ使用中なら何もしない if(m_pMidiHdr[bufnum].dwFlags & MHDR_INQUEUE) return; // バッファは再生のためにキューに入れられている m_offset+=TrackChunk(p+m_offset,&m_pMidiHdr[bufnum]); // データを読み込んでバッファに書き込む MMRESULT result=midiOutPrepareHeader((HMIDIOUT)m_hStream,&m_pMidiHdr[bufnum],sizeof(MIDIHDR)); MsgBox(result,"prepare"); result=midiStreamOut(m_hStream,&m_pMidiHdr[bufnum],sizeof(MIDIHDR)); MsgBox(result,"out"); bufnum=(bufnum+1)%m_bufsum; } // 再生開始します(一時停止を解除します) // 最後まで演奏した場合は、Stopメソッドを呼び出します void SmfPlay::Play(void) { if(m_pMidi==NULL) return; // MIDIの読み込みに失敗している if(m_condition==PLAY) return; // 再生中は何もしない MMRESULT result=midiStreamRestart(m_hStream); MsgBox(result,"restart"); if(m_condition==PAUSE){ // 一時停止していた場合は一時停止解除だけして終了 m_condition=PLAY; return; } m_condition=PLAY; BYTE *p=m_pMidi; DWORD midiSize=22+(p[18]<<24)+(p[19]<<16)+(p[20]<<8)+p[21]; if(m_offset==midiSize) return; // 全データを読み込んだ // まだ読み込んでいないバッファが有る StreamOut(); } // 停止します(再生開始位置を先頭にシークする) void SmfPlay::Stop(void) { if(m_pMidi==NULL) return; // MIDIの読み込みに失敗している MMRESULT result=midiStreamStop(m_hStream); MsgBox(result,"stop"); m_condition=STOP; m_offset=22; StreamOut(); // 必要な時に直ぐ再生できるよう予めバッファリングしておく(再生はしない) } // 一時停止します void SmfPlay::Pause(void) { if(m_pMidi==NULL) return; // MIDIの読み込みに失敗している if(m_condition!=PLAY) return; // 再生中ではない MMRESULT result=midiStreamPause(m_hStream); MsgBox(result,"pause"); m_condition=PAUSE; } // midiStreamPosition 関数を呼び出します MMRESULT SmfPlay::GetCurrentPosition(LPMMTIME lpmmt,UINT cbmmt) { if(m_pMidi==NULL) return 1; // MIDIの読み込みに失敗している // 1 は未定義の外部エラー return midiStreamPosition(m_hStream,lpmmt,cbmmt); } // 終了時刻(秒単位)を取得します double SmfPlay::GetEndTime(void) { if(m_pMidi==NULL) return 0; // MIDIの読み込みに失敗している return m_endTime; } // コールバックスレッド DWORD WINAPI SmfPlay::ThreadProc(void *lpParameter) // staticメソッド { SmfPlay *sp=(SmfPlay*)lpParameter; // スレッドを作成したオブジェクト MSG msg; BOOL bRet; while((bRet=GetMessage(&msg,NULL,0,0))!=0){ // スレッドはオブジェクト消滅まで終了させない if(bRet==-1) break; MIDIHDR *pMidiHdr; MIDISTRMEVENT *pEvent; BYTE *p; DWORD midiSize; if(msg.message==MM_MOM_POSITIONCB){ pMidiHdr=(MIDIHDR*)msg.lParam; pEvent=(MIDISTRMEVENT*)&(pMidiHdr->lpData[pMidiHdr->dwOffset]); // メッセージを送ったイベント if((pEvent->dwEvent & 0x00FFFFFF)==0x00002FFF){ // End of Track sp->Stop(); // 停止して、再生開始位置を先頭にシークする continue; } p=sp->m_pMidi; midiSize=22+(p[18]<<24)+(p[19]<<16)+(p[20]<<8)+p[21]; if(sp->m_offset==midiSize) continue; // 全データを読み込んだ // まだ読み込んでいないバッファが有る sp->StreamOut(); } } return 0; } // 初期化します void SmfPlay::Initialize(void) { m_bufsum=2; // バッファの個数(マルチバッファリングするので複数個以上必要) m_pMidiHdr=(MIDIHDR*)calloc(m_bufsum,sizeof(MIDIHDR)); m_bufsize=24000; // バッファのバイト数(64KB未満 & 4バイトの倍数でなければならない) for(BYTE k=0;k