mp3ファイルを、DirectSound にてストリーム再生

プロジェクトダウンロード(VC++2005)

必要な環境 ※DirectSound の初期化・終了には、DSQuickLib を使用しています。

【プログラム主要部分1(MP3Stream_acm.cpp)】

// MP3Stream_acm.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "MP3Stream_acm.h"
#include <mmsystem.h>
#include <dsound.h>
#include "DSQuickLib.h"
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>    // stat 関数を使う為インクルード
#include <crtdbg.h>
#include <TCHAR.h>
#include <vfw.h>
#include <mmreg.h>
#include <msacm.h>
#include "MP3StreamSound.h"

#pragma comment (lib, "msacm32.lib")
#pragma comment (lib, "winmm.lib")
#pragma comment (lib, "vfw32.lib")


LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2);

//    グローバル変数
CDSoundEnv    *g_pDSoundEnv = NULL;
static char * g_lpData = NULL;
static long    g_cfileSize;
CRITICAL_SECTION    g_csSema;
CMP3StreamSound    *g_pMP3StreamSound = NULL;

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = (WNDPROC)WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = NULL;
    wcex.lpszClassName    = _T("mp3 realtime audio stream");
    wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&wcex);

    InitializeCriticalSection(&g_csSema);
    HWND hWnd;
    hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wcex.lpszClassName,_T("mp3 realtime audio stream"),
                WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
                CW_USEDEFAULT,0,640,480,NULL,NULL,hInstance,NULL);
    if( !hWnd )
        return FALSE;

    RECT    bounds,client;
    GetWindowRect(hWnd,&bounds);
    GetClientRect(hWnd,&client);
    MoveWindow(hWnd,bounds.left,bounds.top,
        640 * 2 - client.right,
        480 * 2 - client.bottom,
        false );

    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );

    g_pDSoundEnv = new CDSoundEnv(2,44100,16);
    if (g_pDSoundEnv){
        if (SUCCEEDED(g_pDSoundEnv->Initialize(hWnd,DSSCL_PRIORITY))){
            struct    _stat    status;
            if (0 == _tstat(_T("Sample.mp3"),&status)){
                g_cfileSize = status.st_size;
                g_lpData = new char[g_cfileSize];
                FILE    *fp = fopen(_T("Sample.mp3"),_T("rb"));
                if (fp != NULL){
                    fread(g_lpData,g_cfileSize,1,fp);
                    fclose(fp);
                }
                //    mci:カスタムIOProc のインストール
                mmioInstallIOProc(mmioFOURCC('M', 'M', 'S', ' '), (LPMMIOPROC)IOProc,
                                    MMIO_INSTALLPROC | MMIO_GLOBALPROC);
                g_pMP3StreamSound = new CMP3StreamSound(g_pDSoundEnv,_T("test.MMS+"));
                g_pMP3StreamSound->Prepare();
                g_pMP3StreamSound->Rewind();
                g_pMP3StreamSound->Play();
            }
        }
    }
    BOOL    bFlag1 = false;
    BOOL    bFlag2 = false;
    BOOL    bFlag3 = false;
    BOOL    bFlag4 = false;
    BOOL    bPlaying = true;
    MSG        msg;
    while(true){
        if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){
            if(msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }else{
            g_pDSoundEnv->PollStatus();

            //    再生のコントロール

            //    キー 1
            if (::GetAsyncKeyState('1')){
                if (!bFlag1 && g_pMP3StreamSound){
                    bPlaying = true;
                    g_pMP3StreamSound->Play();
                }
                bFlag1 = true;
            }else{
                bFlag1 = false;
            }

            //    キー 2
            if (::GetAsyncKeyState('2')){
                if (!bFlag2 && g_pMP3StreamSound){
                    g_pMP3StreamSound->Pause();
                    bPlaying = false;
                }
                bFlag2 = true;
            }else{
                bFlag2 = false;
            }

            //    キー 3
            if (::GetAsyncKeyState('3')){
                if (!bFlag3 && g_pMP3StreamSound){
                    g_pMP3StreamSound->FadeOut(180);
                    bPlaying = false;
                }
                bFlag3 = true;
            }else{
                bFlag3 = false;
    
            }

            //    キー 4
            if (::GetAsyncKeyState('4')){
                if (!bFlag4 && g_pMP3StreamSound){
                    g_pMP3StreamSound->Play(0,0,0,-1,0,60);
                    bPlaying = true;
                }
                bFlag4 = true;
            }else{
                bFlag4 = false;
    
            }

            if (!g_pMP3StreamSound->IsPlaying() && bPlaying){
                Sleep(100);
                g_pMP3StreamSound->Rewind();
                g_pMP3StreamSound->Play();
            }
        }
    }
    if (g_pMP3StreamSound)
        delete    g_pMP3StreamSound;
    if (g_pDSoundEnv)
        delete    g_pDSoundEnv;


    EnterCriticalSection(&g_csSema);
    if (g_lpData != NULL){
        mmioInstallIOProc(mmioFOURCC('M', 'M', 'S', ' '), NULL,MMIO_REMOVEPROC);
        delete    g_lpData;
        g_lpData = NULL;
    }
    LeaveCriticalSection(&g_csSema);
    return (int)msg.wParam;
}

//
//    関数:WndProc
//    説明:ウインドウに渡されたイベントのハンドラ
//
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{

    switch (message){
        case WM_PAINT:
            {
                HDC    hDC;
                PAINTSTRUCT    paintStruct;
                hDC = BeginPaint(hWnd,&paintStruct);
                RECT    rc;
                GetClientRect(hWnd,&rc);
                DrawText(hDC,_T("このアプリケーションはオーディオ再生のみ行います。"),-1,&rc,DT_NOCLIP);
                rc.top += 30;
                DrawText(hDC,_T("映像は再生されません。"),-1,&rc,DT_NOCLIP);
                rc.top += 30;
                rc.left += 40;
                DrawText(hDC,_T("key\'1\' -- Play MP3 Stream"),-1,&rc,DT_NOCLIP);
                rc.top += 20;
                DrawText(hDC,_T("key\'2\' -- Pause MP3 Stream"),-1,&rc,DT_NOCLIP);
                rc.top += 20;
                DrawText(hDC,_T("key\'3\' -- Fadeout MP3 Stream"),-1,&rc,DT_NOCLIP);
                rc.top += 20;
                DrawText(hDC,_T("key\'4\' -- Fadein MP3 Stream"),-1,&rc,DT_NOCLIP);

                ::EndPaint(hWnd,&paintStruct);
            }
            return DefWindowProc(hWnd, message, wParam, lParam);
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return    0;
}


//
//    mmio: IOProc
//
LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM
lParam1, LPARAM lParam2)
{
  static BOOL alreadyOpened = FALSE;

  switch (uMessage) {
  case MMIOM_OPEN:
      if (alreadyOpened)
          return 0;
      alreadyOpened = TRUE;

      lpMMIOInfo->lDiskOffset = 0;
      return 0;

   case MMIOM_CLOSE:
       return 0;

   case MMIOM_READ:{
            LPARAM    dataRead = 0;
               EnterCriticalSection(&g_csSema);
            if (g_lpData != NULL){
                dataRead = lParam2;
                memcpy((void *)lParam1, g_lpData+lpMMIOInfo->lDiskOffset, lParam2);
                lpMMIOInfo->lDiskOffset += lParam2;
                dataRead = lParam2;
            }
            LeaveCriticalSection(&g_csSema);
            return (dataRead);
        }
   case MMIOM_SEEK:
       switch (lParam2) {
       case SEEK_SET:
           lpMMIOInfo->lDiskOffset = lParam1;
           break;

       case SEEK_CUR:
           lpMMIOInfo->lDiskOffset += lParam1;
           break;

       case SEEK_END:
           lpMMIOInfo->lDiskOffset = g_cfileSize - lParam1;
           break;
       }
       return lpMMIOInfo->lDiskOffset;

   default:
       return -1;
   }
}

 

 acm ライブラリを用いて解凍したオーディオを、DirectSound を使ってストリーム再生 しています。
 オーディオの解凍は1秒ぶんずつ行っていて、メモリ消費を抑えています。

 ストリーム再生には、リスト2・3のCMP3StreamSound というクラスの機能を使っています。

 ファイルの読み書きには、mmio を用いていますので、カスタム入出力プロシージャを 使用可能です。このサンプルでは、mp3ファイル全体を一旦メモリにロードして再生しています。
(カスタム入出力プロシージャを使用しなければ、ファイルより再生を行います。)

 

【プログラム主要部分2・クラスCMP3StreamSound の宣言(MP3StreamSound.h)】


#pragma once
#include <vfw.h>
#include <mmreg.h>
#include <msacm.h>
#include "DSQuickLib.h"

#define    MP3SOUNDSTREAM_NUM_BLOCKS    2

#define    MP3SOUNDSTREAM_NUM_HISTORY    16

typedef    struct _playhistory{
    LONG    lFilePos;
    LONG    lPcmPos;
}   MP3PLAYHISTORY;

class CMP3StreamSound : public CSoundObject
{
public:
    CMP3StreamSound(CDSoundEnv *pEnv, TCHAR *pFilename);
    virtual ~CMP3StreamSound(void);
    virtual void DoThread();
    void    PollStatus(FLOAT timeElapsed);
    HRESULT    Prepare();

    virtual HRESULT Play( DWORD dwPriority = 0, DWORD dwFlags = 0, LONG lVol = 0, LONG lFreq = -1, LONG lPan = 0 , LONG lFade = 0);
    virtual HRESULT Stop();
    virtual HRESULT Rewind();
    virtual void    FadeOut(LONG lFadeLen);
    HRESULT    Pause();
    virtual BOOL    IsPlaying();
protected:
    HRESULT     Create();
    HRESULT     FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB);
    HRESULT     SetNotify();
    HRESULT     MP3StreamReadAndDecode(void *buffer, LONG lSize);
    HRESULT     CheckMP3Format();
    HRESULT     CheckMP3Header(LPBYTE pID);
    CDSoundEnv  *m_pDSoundEnv;
    DWORD       m_dwDSBufferSize;

    //    Datas about the file
    TCHAR   *m_pFilename;
    HMMIO   m_hMMIO;
    LONG    m_lStreamStartOffset;
    LONG    m_lFileSize;
    LONG    m_lFilePos;
    BOOL    m_bSourceStreamActive;
    LONG    m_lPCMSizeDetected;

    MPEGLAYER3WAVEFORMAT    m_wfMP3;
    INT     m_iFrameSize;
    BYTE    *m_pMP3Frame;    //    1フレーム分のMP3データ
    INT     m_iSourceBufferSize;

    HACMSTREAM    m_hAcmStream;
    BOOL          m_bAcmHeaderPrepared;     //
    ACMSTREAMHEADER m_acmStreamHeader;

    //    Datas about converted audio
    WAVEFORMATEX    m_wfPcm;
    DWORD           m_dwSizeConverted;

    BYTE        *m_pPcmBuffer;              //  PCM Buffer (target to convert)
    BYTE        *m_pPcmBufferCurrent;       //  CurrentPos in m_pPcmBuffer
    LONG        m_dwPcmBufferSizeRemain;    //  Size of PCM Buffer
    DWORD       m_dwPcmBufferSize;

    //    Managing Sound Buffer
    HANDLE      m_hNotificationEvent[MP3SOUNDSTREAM_NUM_BLOCKS];
    HANDLE      m_hTerminator;

    DWORD       m_dwThreadID;
    HANDLE      m_hThread;

    //    Play History (Pause 解除用)
    MP3PLAYHISTORY    m_pHistory[MP3SOUNDSTREAM_NUM_HISTORY];
    LONG        m_lHistoryRead;
    LONG        m_lHistoryWrite;
    LPDIRECTSOUNDBUFFER    m_lpDSBuffer;
    LONG        m_lVol;
    LONG        m_lFreq;
    LONG        m_lPan;
    LONG        m_lCurrentPosition;
    
    BOOL        m_bThreadDone;
    BOOL        m_bThreadActive;

    DWORD       m_dwNumNotification;
    DWORD       m_dwNotifySize;
    DWORD       m_dwBufferLengthInSec;

    DWORD       m_dwLastPlayPos;
    DWORD       m_dwPlayProgress;
    DWORD       m_dwNextWriteOffset;

    DWORD       m_dwPhase;
    DWORD       m_dwPhaseAfterReplay;
    FLOAT       m_fFadeTime;
    FLOAT       m_fFadeLen;
    CRITICAL_SECTION    m_CriticalSection;

    HRESULT     RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored );

    HRESULT     Reset();
    HRESULT     HandleNotificationEvent();
};

 

【プログラム主要部分3・クラスCMP3StreamSoundの実装(MP3StreamSound.cpp)】


#include "StdAfx.h"
#include <mmsystem.h>
#include <dxerr9.h>
#include <dsound.h>
#include <crtdbg.h>

#include "DSQuickLib.h"
#include "MP3StreamSound.h"

DWORD WINAPI MP3StreamSound_DoThread(LPVOID pVoid);
#define SAFE_RELEASE(o)      {if (o){    (o)->Release(); (o) = NULL;    }}
#define SAFE_DELETE(o)       {if (o){    delete (o); (o) = NULL;    }}
#define SAFE_DELETE_ARRAY(p) {if(p) { delete[] (p);   (p)=NULL; } }
#define SAFE_CLOSEHANDLE(h)  {if(h) { CloseHandle(h);   (h)=NULL; } }

CMP3StreamSound::CMP3StreamSound(CDSoundEnv *pEnv, TCHAR *pFilename)
{
    m_pDSoundEnv = pEnv;
    size_t len = _tcslen(pFilename);
    ++len;
    m_pFilename = new TCHAR[len];
    _tcscpy_s(m_pFilename,len,pFilename);
    m_hMMIO = NULL;
    m_lStreamStartOffset = 0;
    m_lFileSize = 0;
    m_lFilePos = 0;
    m_bSourceStreamActive = FALSE;

    m_dwDSBufferSize = 0L;
    m_hThread = NULL;
    m_dwThreadID = 0;
    for (int i = 0; i < MP3SOUNDSTREAM_NUM_BLOCKS ; ++i)
        m_hNotificationEvent[i] = NULL;
    m_hTerminator = NULL;
    m_lpDSBuffer = NULL;

    m_dwNumNotification = MP3SOUNDSTREAM_NUM_BLOCKS;    //  バッファの分割数・作成途上
    m_dwBufferLengthInSec = MP3SOUNDSTREAM_NUM_BLOCKS;  //  デフォルトのバッファ長1ブロック1秒

    InitializeCriticalSection(&m_CriticalSection);

    m_dwPhase = 0;
    m_bThreadActive = FALSE;
    m_bThreadDone = FALSE;

    m_iSourceBufferSize = 1441;                     //  
    m_pMP3Frame = new BYTE[m_iSourceBufferSize];    //  規格上 MP3 での最大フレームサイズ

    m_dwSizeConverted = 0;
    m_pPcmBuffer = NULL;
    m_pPcmBufferCurrent = NULL;
    m_dwPcmBufferSizeRemain = 0;
    m_lPCMSizeDetected = -1;

    m_lHistoryRead = 0;
    m_lHistoryWrite = 0;
    m_hAcmStream = NULL;
    ZeroMemory((BYTE*)&m_acmStreamHeader,sizeof(ACMSTREAMHEADER));
    m_bAcmHeaderPrepared = FALSE;

    pEnv->AddSoundObject(this);
}


//  CheckMP3Header
//  MP3 フレームヘッダを解析する。
//  MP3 フォーマットでは、オーディオデータは、
//  1152 サンプル毎にフレーム単位でエンコードされる。
//  各フレームの先頭には32 ビットのフレームヘッダが格納され
//  ている。
//  
//  フレームヘッダのフォーマットは以下のとおり
//  Bit :AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
//  Byte:+---0----+----1----+----2----+----3----+
//  A フレーム同期ビット
//  B ID 00:MPEG2.5 01:reserved 10:MPEG2 11:MPEG1
//  C レイヤ 11:レイヤT 10:レイヤU 01:レイヤV 00:reserved
//  D 保護ビット 1:誤り検出訂正符号付加せず 0:同情報付加
//  E ビットレート ・・・テーブル参照
//  F サンプリング周波数・・・テーブル参照
//  G パディングビット・・・ 1:余分なスロット※を含む 0:同含まない
//  H 私用ビット・・・規格上使用せず
//  I モード 00:ステレオ 01:ジョイントステレオ 10:デュアルチャンネル 11:シングルチャンネル
//  J モード拡張・・・ジョイントステレオの実装方式
//  K 著作権 1:保護あり 0:保護なし
//  L オリジナル/コピー 1:オリジナル 0:コピー
//  M エンファシス 使用されるべきエンファシスの種類
//    
HRESULT    CMP3StreamSound::CheckMP3Header(LPBYTE pID){
    INT iBitRate;
    INT iFS;
    INT iFrameSize = 0;
    INT iPadding = 0;
    INT iChannel = 0;
    INT iVersion;
    static INT iLayer3BitrateTable[][16] = {
        {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
        {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
    };
    static INT iFSTable[][3] = {
        {44100, 48000, 32000},
        {22050, 24000, 16000}
    };
    if ((pID[0] != 0xff) || (0xe0 != (pID[1] & 0xe0))){
        _RPT0(_CRT_WARN,_T("MPEGフレームヘッダが不正です\n"));
        return E_FAIL;
    }

    //    MPEG バージョンのチェック
    switch ((pID[1] >> 3) & 0x03) {
    case 3:
        iVersion = 1;
        break;
    case 2:
        iVersion = 2;
        break;
    default:
        _RPT0(_CRT_WARN,_T("MPEGバージョンが不正です\n"));
        return E_FAIL;
    }

    //    レイヤ3のみに対応
    if ((pID[1] >> 1 & 0x03) != 1){
        _RPT0(_CRT_WARN,_T("MP3 フォーマットではありません\n"));
        return E_FAIL;
    }
    
    //    オーディオフォーマットの取得
    iBitRate = iLayer3BitrateTable[iVersion - 1][pID[2] >> 4 & 15];
    iFS = iFSTable[iVersion - 1][pID[2] >> 2 & 0x03];
    iPadding = pID[2] >> 1 & 0x01;
    iChannel = ((pID[3] >> 6 & 3) == 3) ?(1):(2);

    iFrameSize = (WORD)((1152 * iBitRate * 1000 / iFS) / 8) + iPadding;

    m_iFrameSize = iFrameSize;

    m_wfMP3.wfx.wFormatTag      = WAVE_FORMAT_MPEGLAYER3;
    m_wfMP3.wfx.nChannels       = iChannel;
    m_wfMP3.wfx.nSamplesPerSec  = iFS;
    m_wfMP3.wfx.nAvgBytesPerSec = (iBitRate * 1000) / 8;
    m_wfMP3.wfx.nBlockAlign     = 1;
    m_wfMP3.wfx.wBitsPerSample  = 0;
    m_wfMP3.wfx.cbSize          = MPEGLAYER3_WFX_EXTRA_BYTES;

    m_wfMP3.wID             = MPEGLAYER3_ID_MPEG;
    m_wfMP3.fdwFlags        = iPadding ? MPEGLAYER3_FLAG_PADDING_ON : MPEGLAYER3_FLAG_PADDING_OFF;
    m_wfMP3.nBlockSize      = iFrameSize;
    m_wfMP3.nFramesPerBlock = 1;
    m_wfMP3.nCodecDelay     = 0x571;
    return    S_OK;
}

//
//    MP3ファイルのフォーマットを確認する。
//
HRESULT    CMP3StreamSound::CheckMP3Format(){
    if (m_hMMIO == NULL){
        return    E_FAIL;
    }
    BYTE    id3[10];
    m_lFileSize = mmioSeek(m_hMMIO,0,SEEK_END);
    mmioSeek(m_hMMIO,0,SEEK_SET);
    mmioRead(m_hMMIO,(HPSTR)id3,sizeof(id3));
    //    MPEG2 形式かチェック
    if (id3[0] == 'I' && id3[1] == 'D' && id3[2] == '3'){
        //    id3v2 形式
        LONG lOffset = 10;
        //    tag サイズの取得・・・BIG ENDIAN
        lOffset += id3[6] << 21;
        lOffset += id3[7] << 14;
        lOffset += id3[8] << 7;
        lOffset += id3[9];
        m_lStreamStartOffset = lOffset;
        m_lFileSize -= lOffset;
    }else{
        //    id3v1 形式
        m_lStreamStartOffset = 0;
        mmioSeek(m_hMMIO,128,SEEK_END);
        mmioRead(m_hMMIO,(HPSTR)id3,3);
        if (id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G'){
            m_lFileSize -= 128;
        }
    }

    //    MP3 フォーマットの確認
    mmioSeek(m_hMMIO,m_lStreamStartOffset,SEEK_SET);
    m_lFilePos = m_lStreamStartOffset;
    mmioRead(m_hMMIO,(HPSTR)id3,4);    //    ストリームヘッダの読み込み
    CheckMP3Header(id3);
    mmioSeek(m_hMMIO,m_lFilePos,SEEK_SET);
    m_bSourceStreamActive = TRUE;
    return    S_OK;
}

CMP3StreamSound::~CMP3StreamSound(void)
{
    m_bThreadDone = TRUE;
    if (m_hTerminator != NULL){
        SetEvent(m_hTerminator);
        while(m_bThreadActive){
            Sleep(10);
        }
        SAFE_CLOSEHANDLE(m_hThread);
    }
    for (int i = 0; i < MP3SOUNDSTREAM_NUM_BLOCKS ; ++i)
        SAFE_CLOSEHANDLE(m_hNotificationEvent[i]);
    if (m_hAcmStream != NULL){
        if (m_bAcmHeaderPrepared){
            acmStreamUnprepareHeader(m_hAcmStream,&m_acmStreamHeader,0);
            m_bAcmHeaderPrepared = FALSE;
        }
        acmStreamClose(m_hAcmStream,0);
    }

    DeleteCriticalSection(&m_CriticalSection);
    SAFE_CLOSEHANDLE(m_hTerminator);
    SAFE_RELEASE(m_lpDSBuffer);
    SAFE_DELETE_ARRAY(m_pMP3Frame);
    SAFE_DELETE_ARRAY(m_pPcmBuffer);
    if (m_hMMIO != NULL){
        mmioClose(m_hMMIO,0);
        m_hMMIO = NULL;
    }
    SAFE_DELETE_ARRAY(m_pFilename);
    m_pDSoundEnv->RemoveSoundObject(this);
}

//
//    再生準備
//
HRESULT    CMP3StreamSound::Prepare(){
    m_hMMIO = mmioOpen(m_pFilename,NULL,MMIO_READ);
    if (m_hMMIO == NULL){
        _RPT0(_CRT_WARN,_T("ファイルが開けませんでした\n"));
        return    E_FAIL;
    }
    CheckMP3Format();

    //    再生フォーマットの決定
    m_wfPcm.cbSize = sizeof(WAVEFORMATEX);
    m_wfPcm.wFormatTag = WAVE_FORMAT_PCM;
    m_wfPcm.nSamplesPerSec = m_wfMP3.wfx.nSamplesPerSec;
    if (0 != acmFormatSuggest(NULL, (WAVEFORMATEX*)&m_wfMP3, &m_wfPcm, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG|ACM_FORMATSUGGESTF_NSAMPLESPERSEC)){
        goto    e_Fail;
    }

    DWORD    nAlign;
    nAlign = m_wfPcm.nBlockAlign;
    m_dwNotifySize = nAlign * (DWORD)m_wfPcm.nSamplesPerSec;
    m_dwNotifySize *= m_dwBufferLengthInSec;
    m_dwNotifySize /= m_dwNumNotification;
    m_dwNotifySize -= m_dwNotifySize % nAlign;

    for (int i = 0; i < MP3SOUNDSTREAM_NUM_BLOCKS; ++i)
        m_hNotificationEvent[i] =  CreateEvent( NULL, FALSE, FALSE, NULL );

    m_dwSizeConverted = 0;

    if (FAILED(Create())){
        goto    e_Fail;
    }

    m_hTerminator =  CreateEvent( NULL, FALSE, FALSE, NULL );
    m_hThread = CreateThread(NULL,0,::MP3StreamSound_DoThread,(LPVOID)this,0,&m_dwThreadID);

    m_dwPhase = STREAMSOUND_STANDBY;
    return    S_OK;
e_Fail:
    return    E_FAIL;
}



//
//    ストリームバッファの構築
//
HRESULT    CMP3StreamSound::Create(){
    LPDIRECTSOUND8    pDS;
    DSBUFFERDESC dsbd;
    HRESULT    hr;


    if( m_pDSoundEnv == NULL || m_hMMIO == NULL)
        return CO_E_NOTINITIALIZED;

    m_dwDSBufferSize = m_dwNumNotification * m_dwNotifySize;
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
    dsbd.dwSize          = sizeof(DSBUFFERDESC);
    dsbd.dwFlags         = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN |
                           DSBCAPS_CTRLPOSITIONNOTIFY | 
                           DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE ;
    dsbd.dwBufferBytes   = m_dwDSBufferSize;
    dsbd.guid3DAlgorithm = GUID_NULL;
    dsbd.lpwfxFormat     = &m_wfPcm;
    pDS = m_pDSoundEnv->GetDirectSound();
    hr = pDS->CreateSoundBuffer( &dsbd, &m_lpDSBuffer, NULL );
    SAFE_RELEASE(pDS);
    if (FAILED(hr)){
        _RPT1(_CRT_WARN,_T("CreateSoundBuffer 失敗 0x%x"),hr);
        goto    e_Fail;
    }
    SetNotify();

    m_dwLastPlayPos     = 0;
    m_dwPlayProgress    = 0;
    m_dwNextWriteOffset = 0;
    FillBufferWithSound(m_lpDSBuffer);

    return    S_OK;
e_Fail:
    return    hr;
}

//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::MP3StreamReadAndDecode()
// Desc: サウンドファイルをバッファに転送する。 
//-----------------------------------------------------------------------------
HRESULT    CMP3StreamSound::MP3StreamReadAndDecode(void *buffer,LONG lSize){
    LONG    lWritten = 0;
    HRESULT hr = E_FAIL;
    INT        bits = (m_wfPcm.wBitsPerSample *m_wfPcm.nChannels) / 8;
    BYTE    *pwrite = (BYTE*)buffer;

    if (m_pPcmBuffer == NULL){
        m_dwPcmBufferSize = 1152 * bits;
        m_pPcmBuffer = new BYTE[m_dwPcmBufferSize];
    }
    while (lSize > lWritten){
        //    バッファに残りがあれば書き込む
        if (m_dwPcmBufferSizeRemain > 0){
            LONG    lSizeWrite = lSize - lWritten;
            if (lSizeWrite > m_dwPcmBufferSizeRemain){
                lSizeWrite = m_dwPcmBufferSizeRemain;
            }
            ::CopyMemory((BYTE*)pwrite,m_pPcmBufferCurrent,lSizeWrite);
            pwrite += lSizeWrite;
            lWritten += lSizeWrite;
            m_pPcmBufferCurrent += lSizeWrite;
            m_dwPcmBufferSizeRemain -= lSizeWrite;
            if (m_dwPcmBufferSizeRemain <= 0){
                m_pPcmBufferCurrent = NULL;
                m_dwPcmBufferSizeRemain = 0;
            }else{
                continue;
            }
        }
        if (!m_bSourceStreamActive){
            //    再生が終了していたら残りを0 で埋める
            LONG lSizeWrite = lSize - lWritten;
            ZeroMemory(pwrite,lSizeWrite);
            lWritten += lSizeWrite;
            break;
        }


        if (m_pPcmBuffer == NULL){
            m_bSourceStreamActive = FALSE;
            continue;
        }
        m_pPcmBufferCurrent = m_pPcmBuffer;
        m_dwPcmBufferSizeRemain = 0;
        BYTE    id[4];
        MMRESULT    mmr;
        mmioRead(m_hMMIO,(HPSTR)id,4);    //    フレームヘッダ読み込み
        CheckMP3Header(id);
        if (!m_bAcmHeaderPrepared || m_iFrameSize > m_iSourceBufferSize){
            if (m_bAcmHeaderPrepared){
                acmStreamUnprepareHeader(m_hAcmStream,&m_acmStreamHeader,0);
                acmStreamClose(m_hAcmStream,0);
                m_bAcmHeaderPrepared = FALSE;
            }
            mmr = acmStreamOpen(&m_hAcmStream,NULL,(LPWAVEFORMATEX)&m_wfMP3,&m_wfPcm, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
            if (mmr != 0){
                _RPT1(_CRT_WARN,_T("acmがオープンできませんでした エラーコード%d\n"),mmr);
                m_bSourceStreamActive = FALSE;
                continue;
            }

            if (m_pMP3Frame == NULL){
                _RPT0(_CRT_WARN,_T("メモリが確保できませんでした"));
                m_bSourceStreamActive = FALSE;
                continue;
            }
            if (m_iFrameSize > m_iSourceBufferSize){
                SAFE_DELETE_ARRAY(m_pMP3Frame);
                m_pMP3Frame = new BYTE[m_iFrameSize];
                m_iSourceBufferSize = m_iFrameSize;
            }
            
            if (m_pPcmBuffer != NULL){
                ZeroMemory(&m_acmStreamHeader, sizeof(ACMSTREAMHEADER));
                m_acmStreamHeader.cbStruct    = sizeof(ACMSTREAMHEADER);
                m_acmStreamHeader.pbSrc       = m_pMP3Frame;
                m_acmStreamHeader.cbSrcLength = m_iSourceBufferSize;
                m_acmStreamHeader.pbDst       = m_pPcmBuffer;
                m_acmStreamHeader.cbDstLength = m_dwPcmBufferSize;
                acmStreamPrepareHeader(m_hAcmStream,&m_acmStreamHeader,0);
                m_bAcmHeaderPrepared = TRUE;
            }else{
                m_bSourceStreamActive = FALSE;
                continue;
            }
        }
        //    History の保存
        m_pHistory[m_lHistoryWrite].lFilePos = m_lFilePos;
        m_pHistory[m_lHistoryWrite].lPcmPos = m_dwSizeConverted;
        m_lHistoryWrite = (m_lHistoryWrite + 1) % MP3SOUNDSTREAM_NUM_HISTORY;
        if (m_lHistoryWrite == m_lHistoryRead){
            m_lHistoryRead = (m_lHistoryRead + 1) % MP3SOUNDSTREAM_NUM_HISTORY;
        }
        CopyMemory(m_pMP3Frame,id,4);
        mmioRead(m_hMMIO,(HPSTR)m_pMP3Frame+4,m_iFrameSize - 4);
        DWORD dwLengthSaved = m_acmStreamHeader.cbSrcLength;
        m_acmStreamHeader.cbSrcLength = m_iFrameSize;

        acmStreamConvert(m_hAcmStream, &m_acmStreamHeader, 0);
        m_dwPcmBufferSizeRemain = m_acmStreamHeader.cbDstLengthUsed;
        m_dwSizeConverted += m_acmStreamHeader.cbDstLengthUsed;
        m_acmStreamHeader.cbSrcLength = dwLengthSaved;

        m_lFilePos += m_iFrameSize;
        if ((m_lFilePos - m_lStreamStartOffset) >= m_lFileSize){
            m_bSourceStreamActive = FALSE;
            m_lPCMSizeDetected = m_dwSizeConverted;
        }
    }
    return    S_OK;
}


//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::FillBufferWithSound()
// Desc: サウンドファイルをバッファに転送する。 
//-----------------------------------------------------------------------------
HRESULT CMP3StreamSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB )
{
    HRESULT hr; 
    VOID*   pDSLockedBuffer      = NULL; // ロックされたバッファメモリへのポインタ
    DWORD   dwDSLockedBufferSize = 0;    // ロックされたバッファメモリのサイズ

    if( pDSB == NULL )
        return CO_E_NOTINITIALIZED;

    if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) {
        _RPT0(_CRT_WARN,  _T("RestoreBuffer"));
        return    hr;
    }

    //    再生開始点を、再生進行カウンタにコピー
    m_dwPlayProgress = m_dwSizeConverted;

    //    サウンドバッファのロック
    if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, 
                                 &pDSLockedBuffer, &dwDSLockedBufferSize, 
                                 NULL, NULL, 0L ) ) ){
        _RPT1(_CRT_WARN, _T("Lock"), hr );
        return    hr;
    }
    if (m_bAcmHeaderPrepared){
        acmStreamUnprepareHeader(m_hAcmStream,&m_acmStreamHeader,0);
        acmStreamClose(m_hAcmStream,0);
        m_bAcmHeaderPrepared = FALSE;
    }
    MP3StreamReadAndDecode((BYTE*) pDSLockedBuffer,dwDSLockedBufferSize);

    pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
    return S_OK;
}

//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::RestoreBuffer()
// Desc: 失われたバッファをリストアする
//-----------------------------------------------------------------------------
HRESULT CMP3StreamSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored )
{
    HRESULT hr;

    if( pDSB == NULL )
        return CO_E_NOTINITIALIZED;
    if( pbWasRestored )
        *pbWasRestored = FALSE;

    DWORD dwStatus;
    if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ){
        _RPT1(_CRT_WARN,_T("GetStatus %x\n"), hr );
        return    hr;
    }

    if( dwStatus & DSBSTATUS_BUFFERLOST ){
        while(true){
            hr = pDSB->Restore();
            if( hr != DSERR_BUFFERLOST )
                break;
            Sleep( 10 );
        }

        if( pbWasRestored != NULL )
            *pbWasRestored = TRUE;

        m_dwPlayProgress = 0L;

        return S_OK;
    }else{
        return S_FALSE;
    }
}

//
//    CMP3StreamSound::SetNotify()
//    Notification イベントの実装
//
HRESULT    CMP3StreamSound::SetNotify()
{
    HRESULT    hr;
    DSBPOSITIONNOTIFY*  pPosNotify     = NULL; 
    LPDIRECTSOUNDNOTIFY pDSNotify      = NULL;
    DWORD    dwC;
    if( FAILED( hr = m_lpDSBuffer->QueryInterface( IID_IDirectSoundNotify, 
                                                (VOID**)&pDSNotify ) ) )
    {
        _RPT1(_CRT_WARN,_T("QueryInterface 失敗 0x%x"),hr);
        goto    e_Fail;
    }

    pPosNotify = new DSBPOSITIONNOTIFY[ m_dwNumNotification];
    for( dwC = 0; dwC < m_dwNumNotification; dwC++ )
    {
        pPosNotify[dwC].dwOffset     = (m_dwNotifySize * dwC) + m_dwNotifySize - 1;
        pPosNotify[dwC].hEventNotify = m_hNotificationEvent[dwC];             
    }

    if( FAILED( hr = pDSNotify->SetNotificationPositions( m_dwNumNotification,
                                                        pPosNotify ) ) )
    {
        _RPT0(_CRT_WARN,  _T("SetNotificationPositions") );
        goto    e_Fail;
    }
    SAFE_RELEASE( pDSNotify );
    SAFE_DELETE_ARRAY( pPosNotify );
    return    S_OK;

e_Fail:
    SAFE_RELEASE( pDSNotify );
    SAFE_DELETE_ARRAY( pPosNotify );
    return    hr;
}

//----------------------------------------------------
//    CMP3StreamSound::DoThread()
//    Streaming のスレッドを実行!
//----------------------------------------------------
void    CMP3StreamSound::DoThread(){
    DWORD   dwEvent;
    HRESULT    hr;
    int    i;
    m_bThreadActive = TRUE;
    HANDLE    hEvents[MP3SOUNDSTREAM_NUM_BLOCKS + 1];
    for (i = 0; i < MP3SOUNDSTREAM_NUM_BLOCKS ; ++i)
        hEvents[i] = m_hNotificationEvent[i];
    hEvents[i] = m_hTerminator;
    while(!m_bThreadDone){
        dwEvent = WaitForMultipleObjects( MP3SOUNDSTREAM_NUM_BLOCKS + 1, hEvents, FALSE, INFINITE);
        if (dwEvent >= WAIT_OBJECT_0 && dwEvent < WAIT_OBJECT_0 + MP3SOUNDSTREAM_NUM_BLOCKS){
            //_RPT1(_CRT_WARN, _T("Notify:%d\n"),dwEvent - WAIT_OBJECT_0);
            EnterCriticalSection(&m_CriticalSection);
            hr = HandleNotificationEvent();
            LeaveCriticalSection(&m_CriticalSection);
            if (FAILED(hr)){
                m_lpDSBuffer->Stop();
                m_dwPhase = STREAMSOUND_STOP;
            }
        }
        if (dwEvent == WAIT_OBJECT_0 + MP3SOUNDSTREAM_NUM_BLOCKS){
        }
    }
    m_bThreadActive = FALSE;
}

//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::HandleNotificationEvent()
// Desc: Notification のイベントハンドラ
//-----------------------------------------------------------------------------
HRESULT CMP3StreamSound::HandleNotificationEvent(  )
{
    HRESULT hr;
    DWORD   dwCurrentPlayPos;
    DWORD   dwPlayDelta;
    VOID*   pDSLockedBuffer = NULL;
    VOID*   pDSLockedBuffer2 = NULL;
    DWORD   dwDSLockedBufferSize;
    DWORD   dwDSLockedBufferSize2;

    if( m_lpDSBuffer == NULL )
        return CO_E_NOTINITIALIZED;

    // バッファのリストア
    BOOL bRestored;
    if( FAILED( hr = RestoreBuffer( m_lpDSBuffer, &bRestored ) ) ){
        _RPT0(_CRT_WARN,_T("RestoreBuffer"));
        return    hr;
    }

    if( bRestored )
    {
        if( FAILED( hr = FillBufferWithSound( m_lpDSBuffer ) ) ){
            _RPT0(_CRT_WARN,_T("FillBufferWithSound"));
            return    hr;
        }
        return S_OK;
    }

    // DirectSoundBuffer をロック
    if( FAILED( hr = m_lpDSBuffer->Lock( m_dwNextWriteOffset, m_dwNotifySize, 
                                         &pDSLockedBuffer, &dwDSLockedBufferSize, 
                                         &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) ){
        _RPT0(_CRT_WARN,_T("Lock"));
        return    hr;
    }

    //    m_dwDSBufferSize および m_dwNextWriteOffset はともに、
    //    m_dwNotifySize の倍数であるため、 pDSLockedBuffer2 は有効になる事は無い。
    if( pDSLockedBuffer2 != NULL ){
        return E_UNEXPECTED; 
    }

    if( FAILED( hr = MP3StreamReadAndDecode((void*)pDSLockedBuffer,dwDSLockedBufferSize))){
        m_lpDSBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
        _RPT0(_CRT_WARN,_T("Read"));
        return    hr;        
    }

    m_lpDSBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );

    if( FAILED( hr = m_lpDSBuffer->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) ){
        _RPT0(_CRT_WARN,_T("GetCurrentPosition"));
        return    hr;        
    }
    //    前回の再生地点からの進行量をチェックする。
    if( dwCurrentPlayPos < m_dwLastPlayPos )
        dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos;
    else
        dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;

    m_dwPlayProgress += dwPlayDelta;
    //_RPT2(_CRT_WARN,_T("Progress:%x total %x\n"),dwPlayDelta,m_dwPlayProgress);
    m_dwLastPlayPos = dwCurrentPlayPos;

    //    再生地点をチェックし、完了時点でバッファをストップする。

    if( m_dwPlayProgress >= m_dwSizeConverted/* + m_dwNotifySize*/)
    {
        m_dwPhase = STREAMSOUND_STOP;
        m_lpDSBuffer->Stop();
    }
    //  次回ロックするアドレスの更新
    m_dwNextWriteOffset += dwDSLockedBufferSize; 
    m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer

    return S_OK;
}

//----------------------------------------------------
//    CMP3StreamSound::Play()
//    Streaming の再生開始!
//----------------------------------------------------
HRESULT CMP3StreamSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVol, LONG lFreq, LONG lPan , LONG lFade){
    HRESULT hr;
    BOOL    bRestored;
    LPDIRECTSOUNDBUFFER pDSB = this->m_lpDSBuffer;
    FLOAT    fFadeTime;
    FLOAT    fVol1, fVol2;
    DWORD    dwCurrent;

    if( pDSB == NULL ){
        _RPT0(_CRT_WARN,_T("No Sound Buffer"));
        return    E_FAIL;
    }
    pDSB->Stop();
    EnterCriticalSection(&m_CriticalSection);
    switch(m_dwPhase){
        case    STREAMSOUND_STANDBY:
        case    STREAMSOUND_STOP:
        case    STREAMSOUND_RUN:
        case    STREAMSOUND_FADEIN:
        case    STREAMSOUND_FADEOUT:
            m_lVol = lVol;
            m_lPan = lPan;
            m_lFreq = lFreq;
            m_dwPhaseAfterReplay = STREAMSOUND_RUN;
            m_fFadeTime = 0.0f;
            m_fFadeLen = (FLOAT)lFade;
            if (lFade != 0){
                m_dwPhaseAfterReplay = STREAMSOUND_FADEIN;
            }
            m_dwPlayProgress = 0;
            if (m_dwPhase != STREAMSOUND_STANDBY){
                Reset();
                FillBufferWithSound(pDSB);
            }
            break;
        case    STREAMSOUND_PAUSE:
            if (!m_bSourceStreamActive && m_dwPlayProgress >= m_dwSizeConverted){
                m_dwPhase = STREAMSOUND_STOP;
                goto    exitPlay;
            }
            dwCurrent = m_dwPlayProgress;
            Reset();
            //    再生再開時のポジショニング
            if (m_lHistoryWrite != m_lHistoryRead){
                INT    i = m_lHistoryWrite;
                INT ix = -1;
                while(i != m_lHistoryRead){
                    if (m_pHistory[i].lPcmPos < dwCurrent)
                        break;
                    ix = i;
                    --i;
                    if (i < 0)
                        i += MP3SOUNDSTREAM_NUM_HISTORY;
                }
                if (ix < 0){
                    m_dwPhase = STREAMSOUND_STOP;
                    goto    exitPlay;
                }
                m_lFilePos = m_pHistory[ix].lFilePos;
                m_dwSizeConverted = m_pHistory[ix].lPcmPos;
                m_lHistoryRead = m_lHistoryWrite;
                mmioSeek(m_hMMIO,m_lFilePos,SEEK_SET);
                FillBufferWithSound(pDSB);
            }
            break;
        default:
            _RPT0(_CRT_WARN, _T("サウンドバッファが再生可能になっていません。") );
            hr = E_FAIL;

            goto    exitPlay;
            break;
    }
    pDSB->SetCurrentPosition(0);    

    // Restore the buffer if it was lost
    if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ){
        _RPT0(_CRT_WARN,_T("RestoreBuffer"));
        goto    exitPlay;
    }

    if( bRestored )
    {
        // The buffer was restored, so we need to fill it with new data
        if( FAILED( hr = FillBufferWithSound( pDSB ) ) ){
            _RPT0(_CRT_WARN,_T("FillBufferWithSound"));
            goto    exitPlay;
        }
    }

    fFadeTime = m_fFadeTime;
    if (fFadeTime > m_fFadeLen)
        fFadeTime = m_fFadeLen;
    switch(m_dwPhaseAfterReplay){
        case    STREAMSOUND_FADEIN:
            fVol1 = DSBVOLUME_MIN * (m_fFadeLen - fFadeTime) / m_fFadeLen;
            fVol2 = (fFadeTime / m_fFadeLen) * m_lVol;
            lVol = (LONG)(fVol1 + fVol2);
            pDSB->SetVolume( lVol );
            break;
        case    STREAMSOUND_FADEOUT:
            fVol1 = DSBVOLUME_MIN * fFadeTime / m_fFadeLen;
            fVol2 = ((m_fFadeLen - fFadeTime) / m_fFadeLen) * m_lVol;
            lVol = (LONG)(fVol1 + fVol2);
            pDSB->SetVolume( lVol );
            break;
        default:
            pDSB->SetVolume( m_lVol );
            break;
    }
    pDSB->SetPan( m_lPan );
    if( m_lFreq != -1 ){
        pDSB->SetFrequency( m_lFreq );
    }
    
    if (SUCCEEDED(hr = pDSB->Play( 0, dwPriority, dwFlags | DSBPLAY_LOOPING ))){
        m_dwPhase = m_dwPhaseAfterReplay;
    }
exitPlay:
    LeaveCriticalSection(&m_CriticalSection);
    return    hr;
}

//----------------------------------------------------
//    CMP3StreamSound::Stop()
//    Streaming の再生終了!
//----------------------------------------------------
HRESULT CMP3StreamSound::Stop(){
    HRESULT    hr = S_OK;
    switch(m_dwPhase){
        case    STREAMSOUND_RUN:
        case    STREAMSOUND_FADEIN:
        case    STREAMSOUND_FADEOUT:
            EnterCriticalSection(&m_CriticalSection);
            hr = m_lpDSBuffer->Stop();
            if (FAILED(hr)){
                _RPT0(_CRT_WARN,_T("Stop"));
            }
            m_dwPhase = STREAMSOUND_STOP;
            hr = Reset();
            LeaveCriticalSection(&m_CriticalSection);
            break;
        case    STREAMSOUND_PAUSE:
            EnterCriticalSection(&m_CriticalSection);
            m_dwPhase = STREAMSOUND_STOP;
            hr = Reset();
            LeaveCriticalSection(&m_CriticalSection);
            break;
    }
    return    hr;
}

HRESULT    CMP3StreamSound::Rewind(){
    HRESULT    hr;
    if (m_dwPhase == STREAMSOUND_STANDBY)
        return    S_OK;
    hr = Stop();
    if (FAILED(hr))
        return    hr;
    Reset();
    hr = FillBufferWithSound(m_lpDSBuffer);
    return    hr;
}

//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::Reset()
// Desc: バッファの再生ポジションを先頭に戻す
//-----------------------------------------------------------------------------
HRESULT CMP3StreamSound::Reset()
{
    HRESULT hr;

    if( m_lpDSBuffer == NULL || m_hMMIO == NULL )
        return CO_E_NOTINITIALIZED;

    m_dwLastPlayPos     = 0;
    m_dwPlayProgress    = 0;
    m_dwNextWriteOffset = 0;

    BOOL bRestored;
    if( FAILED( hr = RestoreBuffer( m_lpDSBuffer, &bRestored ) ) ){
        _RPT0( _CRT_WARN, _T("RestoreBuffer"));
        goto    exitReset;
    }

    //    m_pWaveFile->ResetFile();
    //    一旦AVI の再生ポジションを巻き戻しておく!
    //m_dwAviCurrentPos = 0;
    if (m_hMMIO != NULL){
        mmioSeek(m_hMMIO,m_lStreamStartOffset,SEEK_SET);
        m_lFilePos = m_lStreamStartOffset;
        m_bSourceStreamActive = TRUE;
    }

    hr = m_lpDSBuffer->SetCurrentPosition( 0L );  

    m_pPcmBufferCurrent = m_pPcmBuffer;
    m_dwPcmBufferSizeRemain = 0;
exitReset:
    return    hr;
}

//-----------------------------------------------------------------------------
// Name: CStreamSound::FadeOut()
// Desc: FadeOut 開始
//-----------------------------------------------------------------------------
void    CMP3StreamSound::FadeOut(LONG lFadeLen){
    EnterCriticalSection(&m_CriticalSection);
    FLOAT    t;
    switch(m_dwPhase){
        case    STREAMSOUND_RUN:
            m_fFadeTime = 0;
            m_fFadeLen = (FLOAT)lFadeLen;
            m_dwPhase = STREAMSOUND_FADEOUT;
            break;
        case    STREAMSOUND_FADEIN:
            t = m_fFadeTime / m_fFadeLen;
            if (t > 1.0f)
                t = 1.0f;
            m_fFadeLen = (FLOAT)lFadeLen;
            m_fFadeTime = m_fFadeLen - (t * m_fFadeLen);
            m_dwPhase = STREAMSOUND_FADEOUT;
            break;
        case    STREAMSOUND_PAUSE:
            switch(m_dwPhaseAfterReplay){
                case    STREAMSOUND_RUN:
                    m_fFadeTime = 0;
                    m_fFadeLen = (FLOAT)lFadeLen;
                    m_dwPhaseAfterReplay = STREAMSOUND_FADEOUT;
                    break;
                case    STREAMSOUND_FADEIN:
                    t = m_fFadeTime / m_fFadeLen;
                    if (t > 1.0f)
                        t = 1.0f;
                    m_fFadeLen = (FLOAT)lFadeLen;
                    m_fFadeTime = m_fFadeLen - (t * m_fFadeLen);
                    m_dwPhaseAfterReplay = STREAMSOUND_FADEOUT;
                    break;
                case    STREAMSOUND_FADEOUT:
                    break;
            }
            break;
    }
    LeaveCriticalSection(&m_CriticalSection);
}

//-----------------------------------------------------------------------------
// Name: CMP3StreamSound::PollStatus()
// Desc: FADE-OUT, FADE-IN の処理
//-----------------------------------------------------------------------------
void    CMP3StreamSound::PollStatus(FLOAT timeElapsed){
    FLOAT    fFadeTime;
    FLOAT    fVol1, fVol2;
    LONG    lVol;
    switch(m_dwPhase){
        case    STREAMSOUND_FADEIN:
            EnterCriticalSection(&m_CriticalSection);
            if (m_dwPhase == STREAMSOUND_FADEIN){
                fFadeTime = m_fFadeTime + timeElapsed;
                if (fFadeTime > m_fFadeLen){
                    m_dwPhase = STREAMSOUND_RUN;
                    fFadeTime = m_fFadeLen;
                }
                m_fFadeTime = fFadeTime;
                fVol1 = DSBVOLUME_MIN * (m_fFadeLen - fFadeTime) / m_fFadeLen;
                fVol2 = (fFadeTime / m_fFadeLen) * m_lVol;
                lVol = (LONG)(fVol1 + fVol2);
                m_lpDSBuffer->SetVolume( lVol );
            }
            LeaveCriticalSection(&m_CriticalSection);
            break;
        case    STREAMSOUND_FADEOUT:
            lVol = m_lVol;

            fFadeTime = m_fFadeTime + timeElapsed;
            if (fFadeTime > m_fFadeLen){
                Stop();
                break;
            }
            m_fFadeTime = fFadeTime;
            EnterCriticalSection(&m_CriticalSection);
            fVol1 = DSBVOLUME_MIN * fFadeTime / m_fFadeLen;
            fVol2 = ((m_fFadeLen - fFadeTime) / m_fFadeLen) * m_lVol;
            lVol = (LONG)(fVol1 + fVol2);
            m_lpDSBuffer->SetVolume( lVol );
            LeaveCriticalSection(&m_CriticalSection);
            if (lVol == DSBVOLUME_MIN){
                Stop();
            }
            break;
    }
}

HRESULT    CMP3StreamSound::Pause(){
    if( m_lpDSBuffer == NULL || m_hMMIO == NULL )
        return CO_E_NOTINITIALIZED;

    EnterCriticalSection(&m_CriticalSection);
    if (m_dwPhase == STREAMSOUND_RUN || m_dwPhase == STREAMSOUND_FADEIN || m_dwPhase == STREAMSOUND_FADEOUT){
        m_lpDSBuffer->Stop();
        m_dwPhaseAfterReplay = m_dwPhase;
        m_dwPhase = STREAMSOUND_PAUSE;
    }
    LeaveCriticalSection(&m_CriticalSection);

    return    S_OK;
}

BOOL    CMP3StreamSound::IsPlaying(){
    return    m_dwPhase == STREAMSOUND_RUN 
        || m_dwPhase == STREAMSOUND_FADEIN 
        || m_dwPhase == STREAMSOUND_FADEOUT;
}
DWORD WINAPI MP3StreamSound_DoThread(LPVOID pVoid){
    ((CMP3StreamSound*)pVoid)->DoThread();
    return    0L;
}

 

まったくもって説明不足ですね。

少しづつ書き足したいと思います。

CMP3StreamSound クラスについては、安定してきたらDSQuickLib に含めたいと考えています。

戻る