OpenAL이란?
○ 게임이나 또 다른 오디오 어플리케이션을 위해 고안된 3D Audio API로 플랫폼 독립적인 라이브러리다.
○ 사용법이 그리 어렵지 않기 때문에 누구나 조금만 보면 금방 사용할 수 있다.
○ 공식 사이트 : http://connect.creativelabs.com/openal/default.aspx
● OpenAL 사용을 위한 사전 작업
○ OpenAL을 사용하기 위해서는 먼저 사이트로부터 oalinst.exe 를 다운받아 설치해줘야 한다.
○ oalinst.exe 다운로드 :
http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx
● 공통된 작업
○ 생성 : OpenAL을 사용하기 위한 초기화 작업이다. 부가적인 코드는 모두 생략했다.
bool OpenAlInterface::Create()
{
ALFWInit();
if( AL_FALSE == ALFWInitOpenAL() )
{
ALFWShutdown();
return false;
}
return true;
}
○ 해제 : OpenAL을 해제한다. 어플리케이션이 끝나기 전에 반드시 해줘야 한다.
void OpenAlInterface::Destroy()
{
UnloadWave(); // 로드된 Wave데이터가 있다면 해제 시켜주는 함수다.
ALFWShutdownOpenAL();
ALFWShutdown();
}
// 로드된 Wave데이터가 있다면 해제 시켜주는 함수다.
void OpenAlInterface::UnloadWave()
{
// Stream Data 해제
if( m_pStreamData )
{
delete m_pStreamData;
m_pStreamData = 0;
}
// Stream Play에 사용된 Wave Loader 해제
if( m_pWaveLoader )
{
m_pWaveLoader->DeleteWaveFile(m_waveID);
delete m_pWaveLoader;
m_pWaveLoader = 0;
}
// OpenAL은 각 사운드를 ID로 관리한다.
// 하나의 Sound Data가 생성되면 그 Sound Data를 대표하는 하나의 ID가 보여되고,
// 그 ID를 통해 접근 할 수 있다.
// 현재 로드된 Sound Data가 있다면 해제한다.
if( m_uiSourceID )
{
alDeleteSources(m_uiSourceSize, &m_uiSourceID);
m_uiSourceID = 0;
m_uiSourceSize = 1;
}
// Sound Data가 채워질 버퍼를 대표하는 ID들의 리스트를 해제한다.
if( m_pBuffersID )
{
alDeleteBuffers(m_uiBufferSize, m_pBuffersID);
delete[] m_pBuffersID;
m_pBuffersID = 0;
m_uiBufferSize = 1;
}
}
● Static Play를 위한 작업 : Static Play에 필요한 기본적인 함수들이다.
// Wave 파일을 로드한다.
bool OpenAlInterface::LoadWaveStatic(std::string strFilename)
{
m_pBuffersID = new unsigned int[m_uiBufferSize];
alGenBuffers(m_uiBufferSize, m_pBuffersID);
alGenSources(m_uiSourceSize, &m_uiSourceID);
if( AL_FALSE == ALFWLoadWaveToBuffer(strFilename.c_str(), m_pBuffersID[0]) )
return false;
// Source ID의 Sound Data가 채워질 BufferID를 설정한다.
alSourcei(m_uiSourceID, AL_BUFFER, m_pBuffersID[0]);
return true;
}
bool OpenAlInterface::UpdateStatic()
{
if( m_iState != AL_PLAYING )
return false;
Sleep(100);
// 현재 Source의 상태를 플레이 중인지 아닌지를 얻는다.
alGetSourcei( m_uiSourceID, AL_SOURCE_STATE, &m_iState);
return true;
}
// 플레이 시키는 함수
void OpenAlInterface::PlayStatic()
{
// Play Source
alSourcePlay( m_uiSourceID );
m_iState = AL_PLAYING;
}
// 스톱 시키는 함수
void OpenAlInterface::StopStatic()
{
alSourceStop(m_uiSourceID);
m_iState = AL_INITIAL;
}
● Stream Play를 위한 작업 : 다음은 Stream Play를 위한 함수들이다. 아무래도 기본적인 Static Play보다는 좀더 복잡하다.
bool OpenAlInterface::LoadWaveStream(std::string strFilename, unsigned int uiBufferSize)
{
// static에서는 BufferID가 하나만 잇으면 되엇지만
// Stream Play시는 적어도 4개 정도의 Buffer가 필요하다.
m_uiBufferSize = uiBufferSize;
m_pBuffersID = new unsigned int[m_uiBufferSize];
alGenBuffers(m_uiBufferSize, m_pBuffersID);
alGenSources(m_uiSourceSize, &m_uiSourceID);
m_pWaveLoader = new CWaves();
// WaveLoader를 통해 WaveFile을 오픈한다.
// WaveLoader는 직접 만들어도 되고 OpenAL 샘플에서 제공하는 것을 가져다 써도 된다.
if( WR_OK != m_pWaveLoader->OpenWaveFile(strFilename.c_str(), &m_waveID) )
return false;
// WaveLoader의 임무 중 하나는 이렇게 Wave의 정보를 얻는대 있다.
m_pWaveLoader->GetWaveSize(m_waveID, &m_ulStreamDataSize);
m_pWaveLoader->GetWaveFrequency(m_waveID, &m_ulStreamFrequency);
m_pWaveLoader->GetWaveALBufferFormat(m_waveID, &alGetEnumValue, &m_ulStreamFormat);
m_pWaveLoader->GetWaveFormatExHeader(m_waveID, &m_waveFormatEx);
m_uiStreamBufferSize = m_waveFormatEx.nAvgBytesPerSec >> 2;
m_uiStreamBufferSize -= (m_uiStreamBufferSize % m_waveFormatEx.nBlockAlign);
if( 0 == m_ulStreamFormat )
return false;
m_pStreamData = malloc(m_uiStreamBufferSize);
if( 0 == m_pStreamData )
return false;
// Audio Data의첫번째부터읽도록세팅
m_pWaveLoader->SetWaveDataOffset(m_waveID, 0);
// Buffer를audio data로채운다.
unsigned long ulBytesWritten;
for(int index=0 ; index<m_uiBufferSize ; ++index)
{
if( WR_OK == m_pWaveLoader->ReadWaveData(m_waveID, m_pStreamData, m_uiStreamBufferSize, &ulBytesWritten) )
{
alBufferData(m_pBuffersID[index], m_ulStreamFormat, m_pStreamData, ulBytesWritten, m_ulStreamFrequency);
alSourceQueueBuffers(m_uiSourceID, 1, &m_pBuffersID[index]);
}
}
m_bStreamPlay = false;
return true;
}
// 다음은 매 틱마다 플레이시켜주기 위한 함수다.
bool OpenAlInterface::UpdateStream()
{
if( false == m_bStreamPlay )
return false;
if( m_iState != AL_PLAYING )
return false;
Sleep(20);
int iBuffersProcessed = 0;
alGetSourcei(m_uiSourceID, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
unsigned long ulBytesWritten;
while( iBuffersProcessed )
{
unsigned int uiBuffer = 0;
alSourceUnqueueBuffers(m_uiSourceID, 1, &uiBuffer);
m_pWaveLoader->ReadWaveData(m_waveID, m_pStreamData, m_uiStreamBufferSize, &ulBytesWritten);
if( ulBytesWritten )
{
alBufferData(uiBuffer, m_ulStreamFormat, m_pStreamData, ulBytesWritten, m_ulStreamFrequency);
alSourceQueueBuffers(m_uiSourceID, 1, &uiBuffer);
}
iBuffersProcessed--;
}
alGetSourcei(m_uiSourceID, AL_SOURCE_STATE, &m_iState);
if( m_iState != AL_PLAYING )
{
int iQueuedBuffers;
alGetSourcei(m_uiSourceID, AL_BUFFERS_QUEUED, &iQueuedBuffers);
if( iQueuedBuffers )
{
alSourcePlay(m_uiSourceID);
m_iState = AL_PLAYING;
}
else
{
StopStream();
}
}
return true;
}
// 플레이 시켜주는 함수.
void OpenAlInterface::PlayStream()
{
alSourcePlay(m_uiSourceID);
m_bStreamPlay = true;
m_iState = AL_PLAYING;
}
// 스톱 시켜주는 함수
void OpenAlInterface::StopStream()
{
alSourceStop(m_uiSourceID);
alSourcei(m_uiSourceID, AL_BUFFER, 0);
m_bStreamPlay = false;
m_iState = AL_INITIAL;
}