Files
Client/GameTools/SoundLib/SoundManager.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
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>
2025-11-29 20:17:20 +09:00

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