◆音楽再生 その3

[ ホーム ] [ 上へ ]

・ユーザインターフェイス実装

音楽演奏について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コマンドによる音楽ファイル再生