・ユーザインターフェイス実装
音楽演奏についてmciSendCommandの使い方は前ページまででほぼ目的は達したのですが、 唯一、第三引数にMCI_NOTIFYを指定してmciSendCommandを発行したあとに送られる MM_MCINOTIFYメッセージの処理については書いてないのでここでまとめておきます。 これ以外はダイアログに作ったコントロールの処理がほとんどでお題とはちょっとかけ離れて しまうので適当に読み流してください。
ツールバーの表示(V)→その他のウィンドウ(E)→リソースビュー(R)でリソースビューを表示して リソースエディタにダイアログを出してください。以下ようにコントロールを配置して プロパティウィンドウでコントロールID等を設定してください。
コントロールの種類 | コントロールID | その他プロパティウィンドウでの設定 | |
1 | Edit Control | IDC_EDIT_PATH | |
2 | GroupBox | IDC_STATIC (デフォルト) | Captionプロパティを「デバイスタイプ」 |
3 | Radio Button | IDC_RADIO_MIDI | GroupプロパティをTrueにする |
4 | Radio Button | IDC_RADIO_WAV | |
5 | Radio Button | IDC_RADIO_MP3 | |
6 | Slider Control | IDC_SLIDER1 | |
7 | Button | IDC_BTN_PLAY | Captionプロパティを「Play」 |
8 | Button | IDC_BTN_PAUSE | Captionプロパティを「Pause」 |
9 | Button | IDC_BTN_STOP | Captionプロパティを「Stop」 |
これで準備が準備が出来たので実装していきます。
<メンバ変数の追加>
・ラジオボタンはDDX変数にしますので、クラスビューをアクティブにしてツールバーの プロジェクト(P)→変数の追加(B)でウィザードを起動して以下のように設定してください。
アクセス(A) ・・・protected 変数の種類(V) ・・・int 変数名 ・・・m_DevType コントロール変数(O) ・・・チェックを入れる コントロールのID(I) ・・・IDC_RADIO_MIDI コントロールの種類(Y)・・・RADIO カテゴリ(T) ・・・Value
OKしてください。
・ここで作成したCSound型の変数と タイマ識別子用変数を追加します。SoundsDlg.hヘッダファイルに
#include "Sound.h" class CSoundsDlg : public CDialog { ・ ・ protected: int m_DevType; CSound m_snd; UINT_PTR m_TimerID; ・ };
赤い部分を追加してください。
SoundsDlg.cpp実装ファイルでコンストラクタに
CSoundsDlg::CSoundsDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSoundsDlg::IDD, pParent)
, m_DevType(0)
, m_TimerID(0)
{
・・・
}
赤い部分を追加してください。
<ダイアログのメッセージハンドラ>
クラスビューでCSoundsDlgを選択して 右クリック→プロパティ(R)でCSoundsDlgクラスのプロパティウィンドウを表示してメッセージアイコンを クリックして一覧から以下のメッセージハンドラを作成してください。
メッセージ | メッセージハンドラ |
WM_HSCROLL | CSoundsDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) |
WM_TIMER | CSoundsDlg::OnTimer(UINT_PTR nIDEvent) |
ヘッダファイルにメンバ関数が宣言され、 実装ファイルにスケルトンができ、メッセージマップにハンドラが追加されているはずです。
<コントロールの通知メッセージハンドラ>
上記と同様CSoundsDlgクラスのプロパティウィンドウを 表示してください。イベントアイコンをクリックして以下の各コントロールの通知メッセージの ハンドラを作成してください。
コントロールID | 通知メッセージ | ハンドラ |
IDC_EDIT_PATH | EN_CHANGE | OnEnChangeEditPath() |
IDC_RADIO_MIDI | BN_CLICKED | OnBnClickedRadioMidi() |
IDC_RADIO_WAV | BN_CLICKED | OnBnClickedRadioWav() |
IDC_RADIO_MP3 | BN_CLICKED | OnBnClickedRadioMp3() |
IDC_BTN_PLAY | BN_CLICKED | OnBnClickedBtnPlay() |
IDC_BTN_PAUSE | BN_CLICKED | OnBnClickedBtnPause() |
IDC_BTN_STOP | BN_CLICKED | OnBnClickedBtnStop() |
ヘッダファイルにメンバ関数が宣言され、実装ファイルに スケルトンができ、メッセージマップにハンドラが追加されているはずです。
<MM_MCINOTIFYメッセージハンドラ>
mciSendCommand実行によってCSoundsDlgに送られる MM_MCINOTIFYメッセージのハンドラを作成しますが、これは自動的に作ってくれないので、 C++メンバ関数を宣言・定義して、さらにメッセージマップに自分で記述しないといけません。
SoundsDlg.hヘッダファイルにハンドラ関数を宣言します。
class CSoundsDlg : public CDialog
{
・・・・
public:
afx_msg LRESULT OnMciNotity(WPARAM wParam, LPARAM lParam);
・・・・
};
SoundsDlg.cpp実装ファイル
//メッセージマップ BEGIN_MESSAGE_MAP(CSoundsDlg, CDialog) ・・・ ON_MESSAGE(MM_MCINOTIFY, &CSoundsDlg::OnMciNotity) ・・・・ END_MESSAGE_MAP() //定義 LRESULT CSoundsDlg::OnMciNotity(WPARAM wParam, LPARAM lParam) { return 0; }
<その他>
以上でCSoundsDlgのすべてのメンバ変数の定義と インターフェイスが用意できたので以下にSoundsDlg.hヘッダファイル、SoundsDlg.cpp 実装ファイルの全内容を掲載するので実装してください。 (アプリケーションウィザードが作成した部分も含まれているので注意してください。)
// SoundsDlg.h : ヘッダーファイル // #pragma once #include "Sound.h" #include "afxcmn.h" // CSoundsDlg ダイアログ class CSoundsDlg : public CDialog { // コンストラクション public: CSoundsDlg(CWnd* pParent = NULL); // 標準コンストラクタ // ダイアログデータ enum { IDD = IDD_CONTROLSOUNDS_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート // 実装 protected: HICON m_hIcon; // 生成された、メッセージ割り当て関数 virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() ////////////////////////////////////////////////// //メンバ変数////////////////////////////////////// protected: CSound m_snd; //CSoundオブジェクト int m_DevType; // デバイスタイプDDX変数 UINT_PTR m_TimerID; // タイマ識別子 //メンバ関数////////////////////////////////////// public: afx_msg void OnEnChangeEditPath(); //Edit Control変更通知Messageハンドラ afx_msg void OnBnClickedRadioMidi(); //RadioボタンClick通知Messageハンドラ afx_msg void OnBnClickedRadioWav(); //同上 afx_msg void OnBnClickedRadioMp3(); //同上 afx_msg void OnBnClickedBtnPlay(); //PlayボタンClickハンドラ afx_msg void OnBnClickedBtnPause(); //Pauseボタン afx_msg void OnBnClickedBtnStop(); //Stopボタン afx_msg LRESULT OnMciNotity(WPARAM wParam, //MCI_NOTIFYコールバック関数 LPARAM lParam); afx_msg void OnHScroll(UINT nSBCode, //Slider Control UINT nPos, CScrollBar* pScrollBar); afx_msg void OnTimer(UINT_PTR nIDEvent); //タイマ protected: void CollectInitData(); //デバイス初期化データ取得 //デバイスの状態によりダイアログ内のコントロール状態変更 //引数eDevState : デバイスの状態 void ChangeCtrlState(DWORD_PTR dwDevState); virtual void OnCancel(); virtual void OnOK(); }; |
// SoundsDlg.cpp : 実装ファイル // #include "stdafx.h" #include "ControlSounds.h" #include "SoundsDlg.h" //#include "Mmsystem.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CSoundsDlg ダイアログ CSoundsDlg::CSoundsDlg(CWnd* pParent /*=NULL*/) : CDialog(CSoundsDlg::IDD, pParent) , m_DevType(0) , m_TimerID(0) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CSoundsDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Radio(pDX, IDC_RADIO_MIDI, m_DevType); } BEGIN_MESSAGE_MAP(CSoundsDlg, CDialog) ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_EN_CHANGE(IDC_EDIT_PATH, &CSoundsDlg::OnEnChangeEditPath) ON_BN_CLICKED(IDC_BTN_PLAY, &CSoundsDlg::OnBnClickedBtnPlay) ON_BN_CLICKED(IDC_BTN_PAUSE, &CSoundsDlg::OnBnClickedBtnPause) ON_BN_CLICKED(IDC_BTN_STOP, &CSoundsDlg::OnBnClickedBtnStop) ON_BN_CLICKED(IDC_RADIO_MIDI, &CSoundsDlg::OnBnClickedRadioMidi) ON_BN_CLICKED(IDC_RADIO_WAV, &CSoundsDlg::OnBnClickedRadioWav) ON_BN_CLICKED(IDC_RADIO_MP3, &CSoundsDlg::OnBnClickedRadioMp3) ON_MESSAGE(MM_MCINOTIFY, &CSoundsDlg::OnMciNotity) ON_WM_HSCROLL() ON_WM_TIMER() END_MESSAGE_MAP() // CSoundsDlg メッセージハンドラ BOOL CSoundsDlg::OnInitDialog() { CDialog::OnInitDialog(); // このダイアログのアイコンを設定します。アプリケーションのメインウィンドウが //ダイアログでない場合、Framework は、この設定を自動的に行います。 SetIcon(m_hIcon, TRUE); // 大きいアイコンの設定 SetIcon(m_hIcon, FALSE); // 小さいアイコンの設定 // TODO: 初期化をここに追加します。 m_DevType = 0; //ラジオボタンの初期値 ((CSliderCtrl*)GetDlgItem(IDC_SLIDER1))->SetRange(0, 1000); //Slider Controlの範囲 m_TimerID = 1; //タイマ識別子 SetTimer(m_TimerID, 1000, NULL); //タイマ開始 ChangeCtrlState(MCI_MODE_NOT_READY); //Radio, Slider, Button状態設定 UpdateData(FALSE); //Dialogデータ初期化(DDXデータをDialogに反映) return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。 } // ダイアログに最小化ボタンを追加する場合、アイコンを描画するための // 下のコードが必要です。ドキュメント/ビューモデルを使うMFC アプリケーションの場合、 // これは、Framework によって自動的に設定されます。 void CSoundsDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 描画のデバイスコンテキスト SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // クライアントの四角形領域内の中央 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // アイコンの描画 dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、 // システムがこの関数を呼び出します。 HCURSOR CSoundsDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } //////////////////////////////////////////////////////////////////////// //音楽ファイルOpen void CSoundsDlg::OnEnChangeEditPath() { CollectInitData(); } void CSoundsDlg::OnBnClickedRadioMidi() { CollectInitData(); } void CSoundsDlg::OnBnClickedRadioWav() { CollectInitData(); } void CSoundsDlg::OnBnClickedRadioMp3() { CollectInitData(); } //デバイス初期化用データ収集&デバイスOpen void CSoundsDlg::CollectInitData() { CString strPath; UpdateData(TRUE); //ファイルパス取得 strPath.GetBuffer(MAX_PATH); GetDlgItem(IDC_EDIT_PATH)->GetWindowText(strPath); strPath.ReleaseBuffer(); //現在のデバイスClose m_snd.Close(); //デバイスOpen if (m_snd.Open(strPath, (CSound::DEV_TYPE)m_DevType)) ChangeCtrlState(MCI_MODE_STOP); else ChangeCtrlState(MCI_MODE_NOT_READY); } //Playボタンクリック void CSoundsDlg::OnBnClickedBtnPlay() { if (!m_snd.Play(m_hWnd)) return; ChangeCtrlState(MCI_MODE_PLAY); } //Pauseボタンクリック void CSoundsDlg::OnBnClickedBtnPause() { CString strCaption; //ボタンのCaption取得 strCaption.GetBuffer(5); GetDlgItem(IDC_BTN_PAUSE)->GetWindowTextA(strCaption); strCaption.ReleaseBuffer(); //CaptionによってPauseとResumeを選択 if (!strCaption.Compare(_T("Pause"))) //Pause実行 { if (!m_snd.Pause()) return; if (m_snd.Status() == MCI_MODE_PAUSE) ChangeCtrlState(MCI_MODE_PAUSE); else ChangeCtrlState(MCI_MODE_STOP); } else if (!strCaption.Compare(_T("Resume"))) //Resume実行 { if (!m_snd.Resume()) { if (!m_snd.Play(m_hWnd)) return; //Resumeが効かない場合はPlayで代用 } ChangeCtrlState(MCI_MODE_PLAY); } } //Stopボタンクリック void CSoundsDlg::OnBnClickedBtnStop() { if (!m_snd.Stop()) return; ChangeCtrlState(MCI_MODE_STOP); ((CSliderCtrl*)GetDlgItem(IDC_SLIDER1))->SetPos(0); } //MCI_PLAY後送られるMM_MCINOTIFYメッセージコールバック関数 //m_snd.Play(m_hWnd)で指定されている LRESULT CSoundsDlg::OnMciNotity(WPARAM wParam, LPARAM lParam) { //最後まで演奏終了時 if (wParam == MCI_NOTIFY_SUCCESSFUL) { m_snd.Seek(0); //現在位置を最初に戻す ChangeCtrlState(MCI_MODE_STOP); } return 0; } //Slider Control通知メッセージ //Slideをドラッグまたはキー入力で移動の場合 void CSoundsDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (pScrollBar->GetDlgCtrlID() == IDC_SLIDER1) { if (nSBCode != TB_THUMBTRACK) //ツマミ-ドラッグ以外 { //現在のスライダの位置取得 DWORD dwPos = static_cast<DWORD>(((CSliderCtrl*)pScrollBar)->GetPos()); switch (m_snd.Status()) { case MCI_MODE_PLAY: //現在のデバイスがPlay中の時 m_snd.Seek(dwPos); m_snd.Play(m_hWnd); break; case MCI_MODE_PAUSE: //現在のデバイスがPauseの時 m_snd.Seek(dwPos); m_snd.Play(m_hWnd); m_snd.Pause(); break; case MCI_MODE_STOP: //現在のデバイスがStopの時 m_snd.Seek(dwPos); break; default: //デバイスがOpenできない時など ((CSliderCtrl*)pScrollBar)->SetPos(0); } } } CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } //タイマ void CSoundsDlg::OnTimer(UINT_PTR nIDEvent) { if (m_snd.Status() == MCI_MODE_PLAY) //Play中の時 { int iCurPos = m_snd.CurrentPosition(); //デバイスの現在位置取得 if (iCurPos > -1) ((CSliderCtrl*)GetDlgItem(IDC_SLIDER1))->SetPos(iCurPos); //Slider移動 } CDialog::OnTimer(nIDEvent); } //デバイスの状態によりダイアログ内のコントロール状態変更 //引数eDevState : デバイスの状態 void CSoundsDlg::ChangeCtrlState(DWORD_PTR dwDevState) { int iID[] = {IDC_SLIDER1, IDC_BTN_PLAY, IDC_BTN_PAUSE, IDC_BTN_STOP}; switch (dwDevState) { case MCI_MODE_PLAY: //Play状態 GetDlgItem(iID[0])->EnableWindow(TRUE); GetDlgItem(iID[1])->EnableWindow(FALSE); GetDlgItem(iID[2])->EnableWindow(TRUE); GetDlgItem(iID[2])->SetWindowText(_T("Pause")); GetDlgItem(iID[3])->EnableWindow(TRUE); break; case MCI_MODE_PAUSE: //Pause状態 GetDlgItem(iID[0])->EnableWindow(TRUE); GetDlgItem(iID[1])->EnableWindow(FALSE); GetDlgItem(iID[2])->EnableWindow(TRUE); GetDlgItem(iID[2])->SetWindowText(_T("Resume")); GetDlgItem(iID[3])->EnableWindow(TRUE); break; case MCI_MODE_STOP: //Stop状態 GetDlgItem(iID[0])->EnableWindow(TRUE); ((CSliderCtrl*)GetDlgItem(iID[0]))->SetPos(0); GetDlgItem(iID[1])->EnableWindow(TRUE); GetDlgItem(iID[2])->EnableWindow(FALSE); GetDlgItem(iID[2])->SetWindowText(_T("Pause")); GetDlgItem(iID[3])->EnableWindow(FALSE); break; default: GetDlgItem(iID[0])->EnableWindow(FALSE); ((CSliderCtrl*)GetDlgItem(iID[0]))->SetPos(0); GetDlgItem(iID[1])->EnableWindow(FALSE); GetDlgItem(iID[2])->EnableWindow(FALSE); GetDlgItem(iID[2])->SetWindowText(_T("Pause")); GetDlgItem(iID[3])->EnableWindow(FALSE); } } void CSoundsDlg::OnCancel() { m_snd.Close(); KillTimer(m_TimerID); CDialog::OnCancel(); } void CSoundsDlg::OnOK() { m_snd.Close(); KillTimer(m_TimerID); CDialog::OnOK(); } |
これでできたのでビルドして実行してください。エディットボックスに 対応した形式の音楽ファイルパスを入力して、その形式のラジオボタンを選択して Playボタンを押下すると演奏が始まるはずです。
最後にコードだけ書いてもなんなので各メンバ関数の解説だけしておきます。
参考文献 : MCIコマンドによる音楽ファイル再生