◆音楽再生 その2

[ ホーム ] [ 上へ ] [ 音楽再生 その3 ]

・音楽プレーヤ作成

目的がmciSendCommandの使い方なので、できるだけ簡単にするために VC++ 2005 MFCダイアログ形式のアプリで作ります。 また、対応できる形式はWave、MIDI、MP3とします。Visual Studioのツールバーの ファイル(F)→新規作成(N)→プロジェクト(P)で新しいプロジェクト ダイアログボックスで以下のように設定してください。

プロジェクトの種類(P) ・・・ Visual C++
テンプレート(T)       ・・・ MFCアプリケーション
プロジェクト名        ・・・ 任意    (今回はSoundsで説明します)

OKをクリックするとアプリケーションウィザードが始まります。次をクリックしてください。

<アプリケーションの種類>

・アプリケーションの種類・・・ダイアログベースにチェック
                              ドキュメントビューカーキテクチャのチェックをはずす
                              ユニコードライブラリを使用するのチェックをはずす
・プロジェクト形式      ・・・MFC標準
・MFCの使用法           ・・・共有DLLでMFC使用
(次をクリック)

<ユーザインターフェイス機能>

・システムメニューのみにチェック
・ダイアログタイトル・・・任意

<高度な機能>

・すべてのチェックをはずす

<生成されてクラス>

CWinAppの派生クラス・・・CSoundsApp
CDialogの派生クラス・・・CSoundsDlg
ヘッダファイル・実装ファイルを確認して完了をクリック

・mciSendCommandラッパクラス作成

前項でスケルトンができたのでとりあえずビルドしてください。 2つのクラスができてますが今はまだ手をつけません。まず、音楽再生を扱う mciSendCommandのラッパクラスを先に作成します。ツールバーの プロジェクト(P)→クラスの追加(C)でC++クラスCSoundという クラス名で作成してください。

Sound.hとSound.cppができているので以下のように書いてください。

//Sound.h ヘッダファイル///////////////////////
//
#pragma once

#include "Mmsystem.h"

//MIDI WAV MP3の音源再生用クラス
class CSound
{
public:
    enum DEV_TYPE {MIDI, WAV, MP3}; //デバイスの種類

    //メンバ変数///////////////////////////////////////////
protected:
    MCI_OPEN_PARMS m_mop; //デバイスID取得等

    //メンバ関数//////////////////////////////////
private:
    CSound(const CSound &obj); //コピーコンストラクタ

public:
    CSound();          //コンストラクタ
    virtual ~CSound(); //デストラクタ

    //デバイスOpen pPath : 音源のパス
    //             eType : 音源の種類(MIDI, WAV, MP3)
    BOOL Open(LPCTSTR pPath, CSound::DEV_TYPE eType);

    //デバイスの状態取得
    //戻値 MCI_MODE_NOT_READY, MCI_MODE_PAUSE, MCI_MODE_PLAY, MCI_MODE_STOP,
    //     MCI_MODE_OPEN, MCI_MODE_RECORD, MCI_MODE_SEEK
    DWORD_PTR Status() const;

    //現在の位置取得現在の位置を0-1000で返す(失敗時-1)
    int CurrentPosition() const;

    //再生 hWnd : MM_MCINOTIFYメッセージの送信先Windowハンドル
    //    iFrom : 0-1000までの値
    //    iTo   : 0-1000までの値
    BOOL Play(HWND hWnd = 0, int iFrom = -1, int iTo = -1);

    //再生停止
    BOOL Stop();

    //デバイスClose
    BOOL Close();

    //現在位置の移動dwPos : 0-1000の範囲で現在位置を変更)
    BOOL Seek(DWORD dwPos = 0);

    //一時停止
    BOOL Pause();

    //再開
    BOOL Resume();
};
//Sound.cpp 実装ファイル///////////////////////
//

#include "StdAfx.h"
#include "Sound.h"

//#include "Mmsystem.h"

#pragma comment(lib, "Winmm.lib")

//コンストラクタ
CSound::CSound()
{
    ::ZeroMemory((PVOID)&m_mop, sizeof(m_mop));
}

//コピーコンストラクタ
CSound::CSound(const CSound & obj)
{
}

//デストラクタ
CSound::~CSound()
{
    Close();
}

//デバイスのオープン
//引数 pPath : 音源ファイルのパス    
//     eType : デバイスの種類(MIDI, WAV, MP3)    
//戻値         成功時TRUE
BOOL CSound::Open(LPCTSTR pPath, CSound::DEV_TYPE eType)
{
    LPCTSTR pType[] = {_T("Sequencer"), _T("WaveAudio"), _T("MPEGVideo")};

    //現在のデバイスClose
    Close();

    //ファイルパス文字数がの時
    if (!lstrlen(pPath))
        return FALSE;

    //mciSendCommandでMCI_OPENコマンド発行
    m_mop.lpstrDeviceType = pType[eType]; //"Sequencer" "WaveAudio" "MPEGVideo"
    m_mop.lpstrElementName = pPath;

    if (::mciSendCommand(NULL, MCI_OPEN,
                         MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
                         reinterpret_cast<DWORD_PTR>(&m_mop))
        )
        return FALSE;

    //現在位置の単位をミリ秒単位に設定
    MCI_SET_PARMS   msp;
    ::ZeroMemory((PVOID)&msp, sizeof(msp));

    msp.dwTimeFormat = MCI_FORMAT_MILLISECONDS;

    ::mciSendCommand(m_mop.wDeviceID, MCI_SET,
                     MCI_SET_TIME_FORMAT, (DWORD_PTR)&msp);

    return TRUE;
}

//デバイスの状態取得
//戻値 MCI_MODE_NOT_READY, MCI_MODE_PAUSE, MCI_MODE_PLAY, MCI_MODE_STOP,    
//     MCI_MODE_OPEN, MCI_MODE_RECORD, MCI_MODE_SEEKまたは 0
DWORD_PTR CSound::Status() const 
{
    MCI_STATUS_PARMS  msp;   
    ::ZeroMemory((PVOID)&msp, sizeof(msp));

    msp.dwItem = MCI_STATUS_MODE;    
    if (::mciSendCommand(m_mop.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&msp))
        return 0;

    return msp.dwReturn;
}

//現在位置の取得
//戻値0-1000の範囲で現在演奏位置を返す(失敗時は-1)
int CSound::CurrentPosition() const
{
    MCI_STATUS_PARMS   msp_cur;   
    MCI_STATUS_PARMS   msp_total;   

    //現在の位置をミリ秒単位で取得
    ::ZeroMemory((PVOID)&msp_cur, sizeof(msp_cur));
    msp_cur.dwItem = MCI_STATUS_POSITION;    

    if (::mciSendCommand(m_mop.wDeviceID, 
                         MCI_STATUS, 
                         MCI_STATUS_ITEM, 
                         (DWORD_PTR)&msp_cur)
       )
        return -1;

    //全体の長さをミリ秒単位で取得
    ::ZeroMemory((PVOID)&msp_total, sizeof(msp_total));
    msp_total.dwItem = MCI_STATUS_LENGTH;    

    if (::mciSendCommand(m_mop.wDeviceID, 
                         MCI_STATUS, 
                         MCI_STATUS_ITEM, 
                         (DWORD_PTR)&msp_total)
       )
        return -1;

    double dPercent = (double)msp_cur.dwReturn / msp_total.dwReturn;    
    return (int)(dPercent * 1000);    
}

//演奏
//引数 hWnd : 終了時/中止時などに発生するMM_MCINOTIFYメッセージの送信先ウィンドウハンドル
//    iFrom : 開始位置(0-1000の範囲)    
//    iTo   : 終了位置(0-1000の範囲)    
//戻値        mciSendCommandの実行が成功時TRUE
BOOL CSound::Play(HWND hWnd /*= 0*/, int iFrom /*= -1*/, int iTo /*= -1*/)
{
    MCI_STATUS_PARMS  msp;   
    MCI_PLAY_PARMS    mpp;   
    BOOL              bFrom = ((iFrom < 0) || (iFrom > 1000)) ? FALSE : TRUE;    
    BOOL              bTo = ((iTo < 0) || (iTo > 1000)) ? FALSE : TRUE;    

    ::ZeroMemory((PVOID)&mpp, sizeof(mpp));

    if (bFrom || bTo)    
    {
        //全体の長さをミリ秒単位で取得
        ::ZeroMemory((PVOID)&msp, sizeof(msp));
        msp.dwItem = MCI_STATUS_LENGTH;    

        if (::mciSendCommand(m_mop.wDeviceID, 
                             MCI_STATUS, 
                             MCI_STATUS_ITEM, 
                             (DWORD_PTR)&msp)
           )
        {
            bFrom = FALSE; bTo = FALSE;    
        }

        //開始位置の特定
        if (bFrom)
        {
            double dPercent = (double)iFrom / 1000;    
            mpp.dwFrom = static_cast<DWORD>(msp.dwReturn * dPercent);    
        }

        //終了位置の特定
        if (bTo)
        {
            double dPercent = (double)iTo / 1000;    
            mpp.dwTo = static_cast<DWORD>(msp.dwReturn * dPercent);    
        }
    }

    mpp.dwCallback = (DWORD_PTR)hWnd;    

    return !::mciSendCommand(m_mop.wDeviceID, MCI_PLAY,    
                             MCI_NOTIFY | (bFrom ? MCI_FROM : 0) | (bTo ? MCI_TO :0),    
                             reinterpret_cast<DWORD_PTR>(&mpp));
}

//演奏停止
BOOL CSound::Stop()
{
    if (::mciSendCommand(m_mop.wDeviceID, MCI_STOP, 0, 0))    
        return FALSE;

    return !::mciSendCommand(m_mop.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);    
}

//デバイスClose
BOOL CSound::Close()
{
    if (Status() == MCI_MODE_PLAY || Status() == MCI_MODE_PAUSE)    
        Stop();

    MCIERROR nRet = ::mciSendCommand(m_mop.wDeviceID, MCI_CLOSE, 0, 0);    
    ::ZeroMemory((PVOID)&m_mop, sizeof(m_mop));

    return !nRet;
}

//現在位置の検索移動
//引数 dwPos : 移動する位置を-1000までで設定    
//戻値         位置変更が成功TRUE
BOOL CSound::Seek(DWORD dwPos /*=0*/)
{
    DWORD             dwCurPos;       //移動すべき位置(ミリ秒単位)
    MCI_STATUS_PARMS  msp;   
    MCI_SEEK_PARMS    mseekp;   
    const DWORD       DIST = 200;     //Wait間隔
    const DWORD       MAXWAIT = 5000; //最大Wait時間

    //範囲外のとき
    if (dwPos < 0 || dwPos > 1000)    
        return FALSE;

    if (!dwPos) //dwPosがの時最初に移す
    {
        if (::mciSendCommand(m_mop.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0))    
            return FALSE;

    } else if (dwPos == 1000) //dwPosがの時最後に移す
    {
        if (::mciSendCommand(m_mop.wDeviceID, MCI_SEEK, MCI_SEEK_TO_END, 0))    
            return FALSE;

    } else if (dwPos > 1000) //0-1000の範囲にない時
    {
        return FALSE;
    } else
    {
        //全体の長さを取得(ミリ秒単位)
        ::ZeroMemory((PVOID)&msp, sizeof(msp));
        msp.dwItem = MCI_STATUS_LENGTH;    

        if (::mciSendCommand(m_mop.wDeviceID, 
                             MCI_STATUS, 
                             MCI_STATUS_ITEM, 
                             (DWORD_PTR)&msp)
           )
            return FALSE;

        //移動すべき位置をdwCurPosに取得
        dwCurPos = static_cast<DWORD>(msp.dwReturn) * dwPos / 1000;    

        //現在位置を移動
        ::ZeroMemory((PVOID)&mseekp, sizeof(mseekp));
        mseekp.dwTo = dwCurPos;    

        if (::mciSendCommand(m_mop.wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&mseekp))
            return FALSE;
    }

    //Seek完了までWait
    DWORD dwCnt = 0;    
    while ((Status() == MCI_MODE_SEEK) && (dwCnt < MAXWAIT))    
    {
        dwCnt += DIST;    
        ::Sleep(DIST);
    }

    return dwCnt < MAXWAIT ? TRUE : FALSE;    
}

//一時停止
BOOL CSound::Pause()
{
    if (Status() != MCI_MODE_PLAY)     return FALSE;    

    return !::mciSendCommand(m_mop.wDeviceID, MCI_PAUSE, 0, 0);    
}

//再開
BOOL CSound::Resume()
{
    if (Status() != MCI_MODE_PAUSE)    return FALSE;    

    return !::mciSendCommand(m_mop.wDeviceID, MCI_RESUME, 0, 0);    
}

CSoundクラスのメンバ関数がmciSendCommandの各コマンドの使い方に対応しているので 確認してください。このクラスを使って次に音楽再生プレーヤをつくります。