AxibugEmuOnline/virtuanessrc097-master/DirectSound.cpp

614 lines
26 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// DirectSound class
//
#include "DebugOut.h"
#include "DirectSound.h"
#include "COM.h"
CDirectSound DirectSound;
#define COMUSE TRUE
//
// WaveFileのロードとメモリへの保持
//
CWaveData::CWaveData()
{
m_pBuffer = NULL;
m_pwfex = NULL;
m_dwSize = 0;
m_pData = NULL;
}
CWaveData::~CWaveData()
{
Free();
}
BOOL CWaveData::Load( LPCSTR szFileName )
{
Free();
FILE* fp = NULL;
if( !(fp = ::fopen( szFileName, "rb" )) ) {
return FALSE;
}
LONG size;
::fseek( fp, 0, SEEK_END );
size = ::ftell( fp );
::fseek( fp, 0, SEEK_SET );
if( size < 0 ) {
return FALSE;
}
if( !(m_pBuffer = ::malloc( size )) ) {
return FALSE;
}
if( ::fread( m_pBuffer, size, 1, fp ) != 1 ) {
Free();
return FALSE;
}
FCLOSE( fp );
LPWAVEFORMATEX pWaveHeader;
BYTE *pbWaveData;
DWORD cbWaveSize;
DWORD *pdw, *pdwEnd;
DWORD dwRiff, dwLength, dwType;
// WAVEデータの解析
pWaveHeader = NULL;
pbWaveData = NULL;
cbWaveSize = 0;
pdw = (DWORD *)m_pBuffer;
dwRiff = *pdw++;
dwLength = *pdw++;
dwType = *pdw++;
if( dwRiff != mmioFOURCC( 'R','I','F','F' ) ) {
Free();
return FALSE;
}
if( dwType != mmioFOURCC( 'W','A','V','E' ) ) {
Free();
return FALSE;
}
pdwEnd = (DWORD*)((BYTE*)pdw+dwLength-sizeof(DWORD));
while( pdw < pdwEnd ) {
dwType = *pdw++;
dwLength = *pdw++;
switch( dwType ) {
case mmioFOURCC( 'f','m','t',' ' ):
if( pWaveHeader == NULL ) {
if( dwLength < sizeof(WAVEFORMAT) ) {
Free();
return FALSE;
}
pWaveHeader = (WAVEFORMATEX *)pdw;
}
break;
case mmioFOURCC( 'd','a','t','a' ):
if( (pbWaveData == NULL)||(!cbWaveSize) ) {
pbWaveData = (BYTE *)pdw;
cbWaveSize = dwLength;
}
}
if( pWaveHeader && (pbWaveData != NULL) && cbWaveSize)
break;
pdw = (DWORD *)((BYTE*)pdw + ((dwLength+1)&~1));
}
if( pdwEnd <= pdw ) {
Free();
return FALSE;
}
if( pWaveHeader->wFormatTag != WAVE_FORMAT_PCM ) {
Free();
return FALSE;
}
m_pwfex = pWaveHeader;
m_dwSize = dwLength;
m_pData = pbWaveData;
return TRUE;
}
void CWaveData::Free()
{
FREE( m_pBuffer );
}
DWORD CWaveData::GetSize()
{
if( !m_pBuffer )
return 0;
return m_dwSize;
}
WAVEFORMATEX* CWaveData::GetFormat()
{
if( !m_pBuffer )
return 0;
return m_pwfex;
}
LPVOID CWaveData::GetData()
{
if( !m_pBuffer )
return 0;
return m_pData;
}
//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////
CDirectSound::SAMPLERATE CDirectSound::m_SampleRateTable[] = {
11025, 8,
22050, 8,
32000, 8, //
44100, 8,
48000, 8,
96000, 8,
192000, 8,
11025, 16,
22050, 16,
32000, 16, //
44100, 16,
48000, 16,
96000, 16,
192000, 16,
0, 0
};
INT CDirectSound::m_BufferSizeTable[] = {
2, 3, 4, 5, 6, 7, 8, 9, 10, 0
};
CDirectSound::CDirectSound()
{
m_lpDS = NULL;
m_lpDSPrimary = NULL;
m_lpDSStream = NULL;
#if 1
m_SampleRate.Rate = 22050;
#else
m_SampleRate.Rate = 44100;
#endif
// m_SampleRate.Bits = 8;
m_SampleRate.Bits = 16;
m_BufferSize = 1;
// m_BufferSize = 2;
m_bStreamPlay = FALSE;
m_bStreamPause = FALSE;
for( INT i = 0; i < ESF_FILE_MAX; i++ ) {
m_pEsfDSBuffer[ i ] = NULL;
}
#if COMUSE
COM::AddRef();
#endif
}
CDirectSound::~CDirectSound()
{
ReleaseDSound();
#if COMUSE
COM::Release();
#endif
}
// DirectSoundの初期化
BOOL CDirectSound::InitialDSound( HWND hWnd )
{
DSBUFFERDESC dsbdesc;
m_hWnd = hWnd;
try {
// DirectSoundオブジェクトの作成
#if !COMUSE
if( DirectSoundCreate( NULL, &m_lpDS, NULL ) != DS_OK ) {
m_lpDS = NULL;
throw "CDirectSound:DirectSoundCreate failed.";
}
#else
// COM的利用
// COM::AddRef();
if( ::CoCreateInstance( CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (LPVOID*)&m_lpDS) != S_OK ) {
m_lpDS = NULL;
throw "CDirectSound:CoCreateInstance failed.";
}
if( m_lpDS->Initialize( NULL ) != DS_OK )
throw "CDirectSound:IDirectSound->Initialize failed.";
#endif
// 優先協調モードの設定
if( m_lpDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) != DS_OK )
throw "CDirectSound:SetCooperativeLevel failed.";
// スピーカの設定
// m_lpDS->SetSpeakerConfig( DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE ) );
// プライマリバッファの作成
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
// dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME
// | DSBCAPS_PRIMARYBUFFER;
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbdesc.dwBufferBytes = 0;
dsbdesc.lpwfxFormat = NULL;
if( m_lpDS->CreateSoundBuffer( &dsbdesc, &m_lpDSPrimary, NULL ) != DS_OK )
throw "CDirectSound:CreateSoundBuffer failed.";
} catch( char *str ) {
ReleaseDSound();
::MessageBox( hWnd, str, "ERROR", MB_ICONERROR|MB_OK );
return FALSE;
}
return TRUE;
}
// DirectSoundの開放
void CDirectSound::ReleaseDSound()
{
ReleaseEsfBuffer();
ReleaseBuffer();
// DirectSoundオブジェクトの開放
RELEASE( m_lpDSPrimary );
if( m_lpDS ) {
RELEASE( m_lpDS );
#if COMUSE
// COM::Release();
#endif
}
m_hWnd = NULL;
}
// DirectSoundバッファの作成
BOOL CDirectSound::InitialBuffer()
{
DSBUFFERDESC dsbdesc;
WAVEFORMATEX pcmwf;
try {
if( !m_lpDSPrimary )
throw "CDirectSound:DirectSound object uninitialized.";
// プライマリバッファのWaveフォーマットを設定(常にモノラル)
ZEROMEMORY( &pcmwf, sizeof(WAVEFORMATEX) );
pcmwf.wFormatTag = WAVE_FORMAT_PCM;
pcmwf.nChannels = 1;
pcmwf.nSamplesPerSec = m_SampleRate.Rate;
pcmwf.nBlockAlign = (WORD)m_SampleRate.Bits/8;
pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
pcmwf.wBitsPerSample = (WORD)m_SampleRate.Bits;
if( m_lpDSPrimary->SetFormat( &pcmwf ) != DS_OK )
throw "CDirectSound:SetFormat failed.";
// ストリームセカンダリバッファ作成
if( m_BufferSize < 2 )
m_BufferSize = 2;
// バッファサイズ等の計算
m_dwDSBlockNum = m_BufferSize * 10;
m_dwDSBlockSize = pcmwf.nAvgBytesPerSec * m_BufferSize / 60;
m_dwDSBlockSize-= m_dwDSBlockSize % pcmwf.nBlockAlign;
m_dwDSBufferSize = m_dwDSBlockSize * m_dwDSBlockNum;
m_dwDSLastBlock = 0;
ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE
| DSBCAPS_GETCURRENTPOSITION2
| DSBCAPS_GLOBALFOCUS;
dsbdesc.dwBufferBytes = m_dwDSBufferSize;
dsbdesc.lpwfxFormat = &pcmwf;
if( m_lpDS->CreateSoundBuffer( &dsbdesc, &m_lpDSStream, NULL ) != DS_OK )
throw "CDirectSound:CreateSoundBuffer failed.";
LPBYTE lpPtr;
DWORD dwBytes;
if( m_lpDSStream->Lock( 0, m_dwDSBufferSize, (LPVOID*)&lpPtr, &dwBytes, NULL, NULL, 0 ) != DS_OK ) {
throw "CDirectSound:Lock failed.";
} else {
FillMemory( lpPtr, dwBytes, (BYTE)(m_SampleRate.Bits==8?128:0) );
m_lpDSStream->Unlock( lpPtr, dwBytes, NULL, NULL );
}
} catch( char *str ) {
ReleaseBuffer();
::MessageBox( m_hWnd, str, "ERROR", MB_ICONERROR|MB_OK );
return FALSE;
}
return TRUE;
}
// DirectSoundバッファの開放
void CDirectSound::ReleaseBuffer()
{
StreamStop();
RELEASE( m_lpDSStream );
}
// サンプリングレートの設定
BOOL CDirectSound::SetSamplingRate( DWORD rate, DWORD bits )
{
INT i;
i = 0;
while( m_SampleRateTable[i].Rate != 0 ) {
if( m_SampleRateTable[i].Rate == rate
&& m_SampleRateTable[i].Bits == bits ) {
m_SampleRate.Rate = rate;
m_SampleRate.Bits = bits;
return TRUE;
}
i++;
}
return FALSE;
}
// サンプリングレートの取得
void CDirectSound::GetSamplingRate( DWORD& rate, DWORD& bits )
{
rate = m_SampleRate.Rate;
bits = m_SampleRate.Bits;
}
// ストリーミング再生
void CDirectSound::StreamPlay()
{
if( !m_lpDS || !m_lpDSStream )
return;
if( !m_bStreamPlay ) {
// Buffer clear
LPBYTE lpPtr;
DWORD dwBytes;
if( m_lpDSStream->Lock( 0, m_dwDSBufferSize, (LPVOID*)&lpPtr, &dwBytes, NULL, NULL, 0 ) != DS_OK ) {
throw "CDirectSound:Lock failed.";
} else {
FillMemory( lpPtr, dwBytes, (BYTE)(m_SampleRate.Bits==8?128:0) );
m_lpDSStream->Unlock( lpPtr, dwBytes, NULL, NULL );
}
m_dwDSLastBlock = 0xFFFFFFFF;
m_bStreamPlay = TRUE;
m_bStreamPause = FALSE;
m_lpDSStream->SetCurrentPosition( 0 );
m_lpDSStream->Play( 0, 0, DSBPLAY_LOOPING );
}
}
// ストリーミング停止
void CDirectSound::StreamStop()
{
if( !m_lpDS || !m_lpDSStream )
return;
if( m_bStreamPlay ) {
m_bStreamPlay = FALSE;
m_bStreamPause = FALSE;
m_lpDSStream->Stop();
// 完全停止まで待つ
DWORD dwStatus;
do {
m_lpDSStream->GetStatus( &dwStatus );
} while( dwStatus & DSBSTATUS_PLAYING );
m_lpDSStream->SetCurrentPosition( 0 );
}
}
// ストリーミングポーズ
void CDirectSound::StreamPause()
{
// DEBUGOUT( "CDirectSound::StreamPause\n" );
if( !m_lpDS || !m_lpDSStream )
return;
if( m_bStreamPlay ) {
if( !m_bStreamPause ) {
m_bStreamPause = TRUE;
m_lpDSStream->Stop();
}
}
}
// ストリーミングレジューム
void CDirectSound::StreamResume()
{
// DEBUGOUT( "CDirectSound::StreamResume\n" );
if( !m_lpDS || !m_lpDSStream )
return;
if( m_bStreamPlay ) {
if( m_bStreamPause ) {
m_bStreamPause = FALSE;
m_lpDSStream->Play( 0, 0, DSBPLAY_LOOPING );
}
}
}
// ストリーミング
BOOL CDirectSound::GetStreamLockPosition( LPDWORD lpdwStart, LPDWORD lpdwSize )
{
static BOOL bLockHalf = FALSE;
DWORD dwPlayPos, dwWritePos;
if( m_lpDSStream->GetCurrentPosition( &dwPlayPos, &dwWritePos ) == DS_OK ) {
if( (dwWritePos / m_dwDSBlockSize) != m_dwDSLastBlock ) {
m_dwDSLastBlock = dwWritePos / m_dwDSBlockSize;
dwWritePos = (((dwWritePos/m_dwDSBlockSize)+1)%m_dwDSBlockNum) * m_dwDSBlockSize;
// ロックすべき場所
*lpdwStart = dwWritePos;
*lpdwSize = m_dwDSBlockSize;
return TRUE;
}
}
return FALSE;
}
BOOL CDirectSound::StreamLock( DWORD dwWriteCursor, DWORD dwWriteBytes, LPVOID* lplpvPtr1, LPDWORD lpdwBytes1, LPVOID* lplpvPtr2, LPDWORD lpdwBytes2, DWORD dwFlags )
{
if( m_lpDSStream->Lock( dwWriteCursor, dwWriteBytes, lplpvPtr1, lpdwBytes1, lplpvPtr2, lpdwBytes2, dwFlags ) == DS_OK )
return TRUE;
return FALSE;
}
BOOL CDirectSound::StreamUnlock( LPVOID lpvPtr1, DWORD dwBytes1, LPVOID lpvPtr2, DWORD dwBytes2 )
{
if( m_lpDSStream->Unlock( lpvPtr1, dwBytes1, lpvPtr2, dwBytes2 ) == DS_OK )
return TRUE;
return FALSE;
}
BOOL CDirectSound::LoadEsf( LPCSTR szFileName, INT no )
{
if( no < 0 || no > ESF_FILE_MAX-1 )
return FALSE;
if( m_EsfWaveFile[no].Load( szFileName ) ) {
return CreateESFBuffer( no, m_EsfWaveFile[no].GetFormat(), m_EsfWaveFile[no].GetData(), m_EsfWaveFile[no].GetSize() );
} else {
DEBUGOUT( "CDirectSound::LoadEsf error. [%s]\n", szFileName );
}
return FALSE;
}
BOOL CDirectSound::EsfPlay( INT no )
{
if( !m_lpDS )
return FALSE;
if( !m_pEsfDSBuffer[no] )
return FALSE;
m_pEsfDSBuffer[no]->SetCurrentPosition( 0 );
if( m_pEsfDSBuffer[no]->Play( 0, 0, 0 ) == DSERR_BUFFERLOST ) {
if( m_pEsfDSBuffer[no]->Restore() == DS_OK ) {
CreateESFBuffer( no, m_EsfWaveFile[no].GetFormat(), m_EsfWaveFile[no].GetData(), m_EsfWaveFile[no].GetSize() );
m_pEsfDSBuffer[no]->Play( 0, 0, 0 );
}
}
return TRUE;
}
BOOL CDirectSound::EsfPlayLoop( INT no )
{
if( !m_lpDS )
return FALSE;
if( !m_pEsfDSBuffer[no] )
return FALSE;
// 既に再生中か?
DWORD dwStatus;
if( m_pEsfDSBuffer[no]->GetStatus( &dwStatus ) == DS_OK ) {
if( dwStatus == DSBSTATUS_PLAYING ) {
return TRUE;
}
}
m_pEsfDSBuffer[no]->SetCurrentPosition( 0 );
if( m_pEsfDSBuffer[no]->Play( 0, 0, DSBPLAY_LOOPING ) == DSERR_BUFFERLOST ) {
if( m_pEsfDSBuffer[no]->Restore() == DS_OK ) {
CreateESFBuffer( no, m_EsfWaveFile[no].GetFormat(), m_EsfWaveFile[no].GetData(), m_EsfWaveFile[no].GetSize() );
m_pEsfDSBuffer[no]->Play( 0, 0, DSBPLAY_LOOPING );
}
}
return TRUE;
}
BOOL CDirectSound::EsfStop( INT no )
{
if( !m_lpDS )
return FALSE;
if( !m_pEsfDSBuffer[no] )
return FALSE;
m_pEsfDSBuffer[no]->Stop();
m_pEsfDSBuffer[no]->SetCurrentPosition( 0 );
return TRUE;
}
void CDirectSound::EsfAllStop()
{
if( !m_lpDS )
return;
for( INT i = 0; i < ESF_FILE_MAX; i++ ) {
EsfStop( i );
}
}
BOOL CDirectSound::CreateESFBuffer( INT no, WAVEFORMATEX* pwfex, LPVOID pData, DWORD dwSize )
{
DSBUFFERDESC dsbdesc;
LPVOID lpPtr0, lpPtr1;
DWORD dwBytes0, dwBytes1;
// 既に使っていたら開放しておく
if( m_pEsfDSBuffer[no] ) {
RELEASE( m_pEsfDSBuffer[no] );
}
// DirectSound セカンダリバッファ作成
ZEROMEMORY( &dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE;
dsbdesc.dwBufferBytes = dwSize;
dsbdesc.lpwfxFormat = pwfex;
if( m_lpDS->CreateSoundBuffer( &dsbdesc, &m_pEsfDSBuffer[no], NULL ) != DS_OK ) {
m_pEsfDSBuffer[no] = NULL;
return FALSE;
}
// 作成したセカンダリバッファにウェーブデータをコピー
m_pEsfDSBuffer[no]->Lock( 0L, dwSize, &lpPtr0, &dwBytes0, &lpPtr1, &dwBytes1, 0 );
::CopyMemory( lpPtr0, pData, dwBytes0 );
if( dwBytes1 ) {
::CopyMemory( lpPtr1, (LPBYTE)pData + dwBytes1, dwBytes1 );
}
m_pEsfDSBuffer[no]->Unlock( &lpPtr0, dwBytes0, &lpPtr1, dwBytes1 );
return TRUE;
}
void CDirectSound::ReleaseEsfBuffer()
{
EsfAllStop();
for( INT i = 0; i < ESF_FILE_MAX; i++ ) {
RELEASE( m_pEsfDSBuffer[i] );
m_EsfWaveFile[i].Free();
}
}