// 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;
}
}
#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;
}