Move git root from Client/ to src/ to track all source code: - Client: Game client source (moved to Client/Client/) - Server: Game server source - GameTools: Development tools - CryptoSource: Encryption utilities - database: Database scripts - Script: Game scripts - rylCoder_16.02.2008_src: Legacy coder tools - GMFont, Game: Additional resources 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
419 lines
9.4 KiB
C++
419 lines
9.4 KiB
C++
|
|
#include "SoundManager.h"
|
|
|
|
#include "DirectSound.h"
|
|
#include "StreamBuffer.h"
|
|
#include "StreamHandler.h"
|
|
|
|
#include "DirectMusic.h"
|
|
#include "MusicBuffer.h"
|
|
#include "SoundObj_None.h"
|
|
|
|
#include <assert.h>
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <functional>
|
|
|
|
#pragma comment( lib, "winmm.lib" )
|
|
|
|
|
|
//#define REPORT_SOUNDMANAGER
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
struct CompareResource : public less< CSoundManager::Resource * >
|
|
{
|
|
bool operator()( const CSoundManager::Resource * pLhs, const CSoundManager::Resource * pRhs ) const
|
|
{
|
|
DWORD eval_Left = pLhs->used * ( pLhs->time / 60000 ); //사용 횟수 한 번 더 많은 것과 사용 중지한 시간이 1분 늦은 것의 효과가 같다.
|
|
DWORD eval_Right = pRhs->used * ( pRhs->time / 60000 );
|
|
return ( eval_Left < eval_Right );
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSoundManager & CSoundManager::GetInstance()
|
|
{
|
|
static CSoundManager SndManager;
|
|
return SndManager;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSoundManager::CSoundManager()
|
|
: m_DSound( CDirectSound::GetInstance() )
|
|
, m_DMusic( CDirectMusic::GetInstance() )
|
|
|
|
, m_bDSoundEnable( false )
|
|
, m_bDMusicEnable( false )
|
|
|
|
// , m_pStreamUpdater( new CStreamUpdater )
|
|
|
|
, m_pResources( new RESOURCES )
|
|
, ReportFunc( NULL )
|
|
{
|
|
m_pComparer = new CompareResource;
|
|
m_pUsingList = new RESLIST( *m_pComparer );
|
|
m_pUnuseList = new RESLIST( *m_pComparer );
|
|
m_pDeletedList = new RESLIST( *m_pComparer );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
|
|
CSoundManager::~CSoundManager()
|
|
{
|
|
Destroy();
|
|
|
|
// delete m_pStreamUpdater;
|
|
delete m_pComparer;
|
|
delete m_pResources;
|
|
delete m_pUsingList;
|
|
delete m_pUnuseList;
|
|
delete m_pDeletedList;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
|
|
void CSoundManager::Create( HWND hWnd, DWORD dwCoopLevel )
|
|
{
|
|
Destroy();
|
|
|
|
try
|
|
{
|
|
m_bDSoundEnable = true;
|
|
m_DSound.Create( hWnd, dwCoopLevel );
|
|
}
|
|
catch( std::exception & )
|
|
{
|
|
//DirectSound 초기화에 실패하면 DSound를 Disable 상태로 세팅
|
|
m_bDSoundEnable = false;
|
|
}
|
|
|
|
try
|
|
{
|
|
m_bDMusicEnable = true;
|
|
m_DMusic.Create( hWnd );
|
|
}
|
|
catch( std::exception & e )
|
|
{
|
|
//DirectMusic 초기화에 실패하면 DMusic을 Disable 상태로 세팅
|
|
MessageBox( NULL, "알림!! DirectMusic의 초기화에 실패하여 BGM이 출력되지 않을 것입니다.", e.what(), MB_OK );
|
|
m_bDMusicEnable = false;
|
|
}
|
|
|
|
// m_pStreamUpdater->Create();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
|
|
void CSoundManager::Destroy()
|
|
{
|
|
// m_pStreamUpdater->Destroy();
|
|
|
|
for( RESOURCES::iterator it = m_pResources->begin(); it != m_pResources->end(); it++ )
|
|
{
|
|
if( it->second )
|
|
{
|
|
if( it->second->pSndBuf )
|
|
{
|
|
if( it->second->pSndBuf->GetType() == SNDOBJTYPE_MUSICBUFFER )
|
|
{
|
|
m_DMusic.DeleteMusicBuffer( *((CMusicBuffer*)it->second->pSndBuf) );
|
|
}
|
|
else
|
|
{
|
|
delete it->second->pSndBuf;
|
|
}
|
|
it->second->pSndBuf = NULL;
|
|
}
|
|
|
|
delete it->second;
|
|
it->second = NULL;
|
|
}
|
|
}
|
|
|
|
m_pResources->clear();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CDirectSound & CSoundManager::GetDirectSound()
|
|
{
|
|
return m_DSound;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
ISoundObject & CSoundManager::GetBuffer( const char * szFilename,
|
|
bool b3DSound,
|
|
bool bStream,
|
|
int nBuf,
|
|
DWORD dwAddFlag )
|
|
{
|
|
typedef RESOURCES::iterator RES_ITER;
|
|
typedef RESLIST::iterator LIST_ITER;
|
|
|
|
//먼저 Resources에서 주어진 이름으로 된 버퍼가 있는지 찾는다.
|
|
RES_ITER it = m_pResources->find( szFilename );
|
|
|
|
if( it == m_pResources->end() )
|
|
{
|
|
//없으면 Resources에 새로 추가하고, using list에도 추가한 다음 버퍼를 리턴한다.
|
|
Resource & res = AddResource( szFilename, b3DSound, bStream, nBuf, dwAddFlag );
|
|
Caching();
|
|
|
|
return *res.pSndBuf;
|
|
}
|
|
else
|
|
{
|
|
//있으면 using 리스트로 옮기고, 사용 빈도 변수를 증가시킨다.
|
|
Resource & res = *(it->second);
|
|
MoveResourceTo( res, m_pUsingList );
|
|
res.used++;
|
|
|
|
if( res.pSndBuf == NULL )
|
|
{
|
|
CreateSoundBuffer( res, szFilename, b3DSound, bStream, nBuf, dwAddFlag );
|
|
}
|
|
|
|
Caching();
|
|
|
|
return *res.pSndBuf;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void CSoundManager::Update()
|
|
{
|
|
// m_pStreamUpdater->Update();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSoundManager::RESLIST * CSoundManager::GetListOfState( eState state )
|
|
{
|
|
RESLIST * pList = m_pUsingList;
|
|
|
|
switch( state )
|
|
{
|
|
case STATE_UNUSE: pList = m_pUnuseList;
|
|
break;
|
|
case STATE_DELETED: pList = m_pDeletedList;
|
|
break;
|
|
}
|
|
|
|
return pList;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSoundManager::eState CSoundManager::GetStateOfList( RESLIST * pList )
|
|
{
|
|
if( pList == m_pUsingList )
|
|
return STATE_USING;
|
|
else if( pList == m_pUnuseList )
|
|
return STATE_UNUSE;
|
|
else if( pList == m_pDeletedList )
|
|
return STATE_DELETED;
|
|
else
|
|
assert( false );
|
|
|
|
return STATE_USING;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void CSoundManager::CreateSoundBuffer( Resource & res,
|
|
const char * szFilename,
|
|
bool b3DSound,
|
|
bool bStream,
|
|
int nBuf,
|
|
DWORD dwAddFlag )
|
|
{
|
|
if( bStream )
|
|
{
|
|
if( m_bDMusicEnable )
|
|
{
|
|
CMusicBuffer * p = &m_DMusic.NewMusicBuffer();
|
|
p->Create( szFilename, b3DSound );
|
|
res.pSndBuf = p;
|
|
|
|
// CStreamBuffer * p = new CStreamBuffer();
|
|
// p->Create( m_DSound.GetDS(), szFilename, b3DSound, nBuf, dwAddFlag );
|
|
// m_pStreamUpdater->Add( *p );
|
|
}
|
|
else
|
|
{
|
|
res.pSndBuf = new CSoundObj_None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_bDSoundEnable )
|
|
{
|
|
CSoundBuffer * p = new CSoundBuffer();
|
|
p->Create( m_DSound.GetDS(), szFilename, b3DSound, nBuf, dwAddFlag );
|
|
res.pSndBuf = p;
|
|
}
|
|
else
|
|
{
|
|
res.pSndBuf = new CSoundObj_None;
|
|
}
|
|
}
|
|
|
|
res.state = STATE_USING;
|
|
res.time = timeGetTime();
|
|
|
|
//-------리포트---------------//
|
|
#ifdef REPORT_SOUNDMANAGER
|
|
char szBuffer[1024];
|
|
sprintf( szBuffer, "**%s 생성됨 ==> %s ==> %d 만큼의 메모리 사용. [[현재 메모리:%d]]\r\n", ( bStream ? "스트림버퍼" : "스태틱버퍼" ), szFilename, res.pSndBuf->GetMemoryUse(), m_dwCurrentByte );
|
|
if( ReportFunc )
|
|
ReportFunc( szBuffer );
|
|
#endif
|
|
//----------------------------//
|
|
|
|
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
CSoundManager::Resource & CSoundManager::AddResource( const char * szFilename,
|
|
bool b3DSound,
|
|
bool bStream,
|
|
int nBuf,
|
|
DWORD dwAddFlag )
|
|
{
|
|
typedef RESOURCES::iterator RES_ITER;
|
|
typedef RESLIST::iterator LIST_ITER;
|
|
|
|
Resource * data = new Resource;
|
|
data->used = 0;
|
|
|
|
CreateSoundBuffer( *data, szFilename, b3DSound, bStream, nBuf, dwAddFlag );
|
|
|
|
pair<RES_ITER, bool> result = m_pResources->insert( RESOURCES::value_type( szFilename, data ) );
|
|
assert( result.second == true );
|
|
|
|
pair<LIST_ITER, bool> result2 = m_pUsingList->insert( data );
|
|
assert( result2.second == true );
|
|
|
|
return *data;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void CSoundManager::MoveResourceTo( Resource & res, RESLIST * pDest )
|
|
{
|
|
typedef RESLIST::iterator LIST_ITER;
|
|
|
|
RESLIST * pList = GetListOfState( res.state );
|
|
|
|
LIST_ITER it_res = pList->find( &res );
|
|
if( it_res != pList->end() )
|
|
{
|
|
pList->erase( it_res );
|
|
pDest->insert( &res );
|
|
}
|
|
|
|
res.state = GetStateOfList( pDest );
|
|
res.time = timeGetTime();
|
|
|
|
/* if( res.pSndBuf )
|
|
{
|
|
if( res.pSndBuf->GetEventNotify() != 0 )
|
|
{
|
|
if( res.pSndBuf->GetType() == SNDOBJTYPE_STREAMBUFFER )
|
|
{
|
|
m_pStreamUpdater->Remove( *((CStreamBuffer*)res.pSndBuf) );
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void CSoundManager::Caching()
|
|
{
|
|
typedef RESLIST::iterator LIST_ITER;
|
|
|
|
DWORD curTime = timeGetTime();
|
|
|
|
//using 리스트를 모두 돌며, Play중인지 검사한다.
|
|
for( LIST_ITER it = m_pUsingList->begin(); it != m_pUsingList->end(); it++ )
|
|
{
|
|
Resource & res = **it;
|
|
|
|
if( res.pSndBuf->IsAllFree() )
|
|
{
|
|
if( curTime - res.time > 5000 )
|
|
{
|
|
//Play중이 아닌 것중에서 5초가 지난 것들은 시간을 기록해두고, unusing 리스트에 추가한다.
|
|
res.time = timeGetTime();
|
|
LIST_ITER it2 = it;
|
|
it2++;
|
|
MoveResourceTo( res, m_pUnuseList );
|
|
it = it2; //UsingList에서 삭제된 것이 있으므로 it의 위치가 제대로 되도록 함.
|
|
}
|
|
}
|
|
}
|
|
|
|
//Unused list는 10개 이하까지만 허용한다.
|
|
while( m_pUnuseList->size() > 10 )
|
|
{
|
|
unsigned ss = m_pUnuseList->size();
|
|
|
|
Resource * pRes = *(m_pUnuseList->begin());
|
|
MoveResourceTo( *pRes, m_pDeletedList );
|
|
|
|
if( pRes->pSndBuf->GetType() == SNDOBJTYPE_MUSICBUFFER )
|
|
{
|
|
m_DMusic.DeleteMusicBuffer( *((CMusicBuffer*)pRes->pSndBuf) );
|
|
}
|
|
else
|
|
{
|
|
delete pRes->pSndBuf;
|
|
}
|
|
pRes->pSndBuf = NULL;
|
|
|
|
//--------리포트--------------//
|
|
#ifdef REPORT_SOUNDMANAGER
|
|
char szBuffer[1024];
|
|
sprintf( szBuffer, "**버퍼폐기됨 ==> %s ==> [[현재 메모리:%d]]\r\n", it2->first.c_str(), m_dwCurrentByte );
|
|
if( ReportFunc )
|
|
ReportFunc( szBuffer );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|