MIDI関数をクラス化する
30ではMIDI用の関数を作りましたが、これはどうやって使えばよいでしょうか。
Viewのメンバ関数でもかまいませんが、クラス化すれば今後も使えるのでクラスしてみます。
クラスの説明を覚えている人には簡単な作業だと思いますが、MIDIコードの動作確認の意味を含めて、作り方と、できたコードをいかに載せておきます。
まず、新しいクラスの制作です。
挿入 > クラスの新規作成 > Genericクラス でしたね。
クラス名は、CMidiにしておきましょう。CMidiにすると、システムと混乱しそうなので
間に i を いれてみました。
CMidiのメンバ関数、
BOOL MciMidiOpen();
BOOL MciMidiPlay();
BOOL MciMidiClose();
BOOL MciMidiSeekToStart();
BOOL MciMidiStop();
BOOL MidiPlay(LPCTSTR pName,BOOL bLoop);
を制作します。
ヘッダファイルは以下のようになります。
// Midi.h: CMidi クラスのインターフェイス
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_MIDI_H__6BA09337_9C3D_11D3_9566_A4929119997E__INCLUDED_)
#define AFX_MIDI_H__6BA09337_9C3D_11D3_9566_A4929119997E__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMidi
{
public:
CMidi();
virtual ~CMidi();
//-------------------------------------------------- ここから
MCI_PLAY_PARMS prm;
DWORD dwFlags;
CString m_sMidiName;
int m_wMidiDeviceID;
BOOL m_bMidiLoop; //演奏をループするかどうか
BOOL MciMidiOpen();
BOOL MciMidiPlay();
BOOL MciMidiClose();
BOOL MciMidiSeekToStart();
BOOL MciMidiStop();
BOOL MidiPlay(LPCTSTR pName,BOOL bLoop,DWORD idwFlags,DWORD cb);
BOOL OniMidiNotify(WPARAM wParam,LPARAM lParam);
//-------------------------------------------------- ここまで
};
#endif // !defined(AFX_MIDI_H__6BA09337_9C3D_11D3_9566_A4929119997E__INCLUDED_)
MCI_PLAY_PARMS prmや DWORD dwFlags がメンバ変数として外に出ているのはループ再生するための処理のためです。
基本的なソースは前回と同じですが、MidiPlayとループに関する部分が一部違っています。
プロジェクトの名前は、vc6になっているので自分のプロジェクトに変えてください。
#include <mmsystem.h>
//////////////////////////////////////////////////////////
// MIDI関係ルーチン
///////////////////////////////////////////////////////
//Midiデバイスをオープンするコード(成功したらTRUEを返す)
BOOL CiMidi::MciMidiOpen()
{
MCI_OPEN_PARMS prm;
DWORD dwFlags=0;
//デバイスにMidiを指定
dwFlags|=MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID;
prm.lpstrDeviceType=(LPCSTR)MCI_DEVTYPE_SEQUENCER;
//演奏するファイル名を指定
dwFlags|=MCI_OPEN_ELEMENT;
prm.lpstrElementName=m_sMidiName;
//オープン
dwFlags|=MCI_WAIT;
MCIERROR ret=mciSendCommand(0,MCI_OPEN,dwFlags,(DWORD)&prm);
if(ret==0){
m_wMidiDeviceID=prm.wDeviceID;
}else{ //エラー
char buf[256];
mciGetErrorString(ret,buf,sizeof(buf));
TRACE("MciMidiOpen:%d(%s)\r\n",ret,buf);
m_wMidiDeviceID=0;
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////
//Midiデバイスをクローズするコード(成功したらTRUEを返す)
BOOL CiMidi::MciMidiClose()
{
if(m_wMidiDeviceID==0) return TRUE;
MCI_GENERIC_PARMS prm;
DWORD flag=MCI_WAIT;
MCIERROR ret=mciSendCommand(m_wMidiDeviceID,MCI_CLOSE,flag,(DWORD)&prm);
if(ret){
char buf[256];
mciGetErrorString(ret,buf,sizeof(buf));
TRACE("MciMidiClose:%d(%s)\r\n",ret,buf);
}
m_wMidiDeviceID=0;
return ret==0;
}
BOOL CiMidi::MciMidiPlay()
{
if(m_wMidiDeviceID==0) return FALSE;
//演奏開始
MCIERROR ret=mciSendCommand(m_wMidiDeviceID,MCI_PLAY,dwFlags,(DWORD)&prm);
if(ret){ //エラー
char buf[256];
mciGetErrorString(ret,buf,sizeof(buf));
TRACE("MciMidiPlay:%d(%s)\r\n",ret,buf);
MciMidiClose(); //デバイスを閉じておく
}
return ret==0;
}
/////////////////////////////////////////////////////////
//Midiデバイスを停止する(成功したらTRUEを返す)
BOOL CiMidi::MciMidiStop()
{
if(m_wMidiDeviceID==0) return TRUE;
MCI_GENERIC_PARMS prm;
MCIERROR ret=mciSendCommand(m_wMidiDeviceID,MCI_STOP,0,(DWORD)&prm);
if(ret){
char buf[256];
mciGetErrorString(ret,buf,sizeof(buf));
TRACE("MciMidiStop:%d(%s)\r\n",ret,buf);
}
return ret==0;
}
//////////////////////////////////////////////////////////////
//Midiデバイスの再生位置を先頭に移動する(成功したらTRUEを返す)
BOOL CiMidi::MciMidiSeekToStart()
{
if(m_wMidiDeviceID==0) return FALSE;
MCI_SEEK_PARMS prm;
MCIERROR
ret=mciSendCommand(m_wMidiDeviceID,MCI_SEEK,MCI_SEEK_TO_START,(DWORD)&prm);
if(ret){
char buf[256];
mciGetErrorString(ret,buf,sizeof(buf));
TRACE("MciMidiSeekToStart:%d(%s)\r\n",ret,buf);
}
return ret==0;
}
///////////////////////////////////////////////////
//MIDIを演奏する
//引数:曲のファイル名、ループさせるかどうか
BOOL CiMidi::MidiPlay(LPCTSTR pName,BOOL bLoop,DWORD idwFlags,DWORD cb)
{
prm.dwCallback = cb;
dwFlags = idwFlags;
m_sMidiName=pName; //演奏するMidiファイル名
m_bMidiLoop=bLoop; //曲をループさせるかどうか
if(m_wMidiDeviceID){
MciMidiStop();
MciMidiClose();
}
if(!MciMidiOpen()) return FALSE;
return MciMidiPlay();
}
ループさせる部分はちょっと違っています。引数が多くなっていますので注意してください。
//Midiの演奏が終わった時に呼ばれる
BOOL CiMidi::OniMidiNotify(WPARAM wParam,LPARAM lParam)
{
if(LOWORD(lParam)!=m_wMidiDeviceID) return FALSE;
switch(wParam){
case MCI_NOTIFY_SUCCESSFUL:
break;
case MCI_NOTIFY_ABORTED:
TRACE("MidiAbort:%d %d\n",wParam,lParam);
return TRUE;
default:
TRACE("MidiNotSuccess:%d %d\n",wParam,lParam);
MciMidiClose();
return TRUE;
}
if(m_bMidiLoop){ //演奏をループさせる
MciMidiStop();
MciMidiSeekToStart();
MciMidiPlay();
}else{
MciMidiStop();
MciMidiClose();
}
return TRUE;
}
最後に行儀よく、デストラクタでMIDIをクローズしておきます。
CiMidi::~CiMidi()
{
MciMidiClose();
}
クラスの定義は以上で終わりです。
次にこのクラスの使い方です。
MIDIを演奏するときの呼び出し方は、
CMidi imidi;
imidi.MidiPlay("m23.mid", // ファイル名
TRUE, // ループ演奏なら TRUE
MCI_NOTIFY, // 決まっている
(DWORD)GetSafeHwnd()); // 決まっている
となります。これは、imidi というCMidiクラスのオブジェクトを使った場合です。使うときに変更する変数は、ファイル名とループするかしないか、の2個所です。ボタンか、キーハンドラか、メニューから試しに呼んでみましょう。ちゃんとなりましたか?
ずいぶんとすっきりしました。
さらにループ用に次の1行と、
BEGIN_MESSAGE_MAP(CMidixView, CFormView)
//{{AFX_MSG_MAP(CMidixView)
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
//}}AFX_MSG_MAP
// 標準印刷コマンド
ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)
ON_MESSAGE(MM_MCINOTIFY,CMidixView::OnMidiNotify) ーーーーーーーーーここ
END_MESSAGE_MAP()
次のメンバ関数をVIEWクラス(か、ほかの使用するクラス)に追加してください。
//Midiの演奏が終わった時に呼ばれる
afx_msg LRESULT CMidixView::OnMidiNotify(WPARAM wParam,LPARAM lParam)
{
return imidi.OniMidiNotify(wParam,lParam);
// return TRUE;
}
上の関数は、ちゃんとヘッダファイルにも宣言をわすれずに。
同じく鳴らしてみましょう。ループすればOK。
あとで使える実用的なクラスができました。
ソースをカットしてHPエディタに貼り付けるとどうしてもインデントがうまくいかずなんとなく見にくいソースになってしまいます。
なんとかならないかなぁ。