genesis-3d_engine/Engine/addons/soundsystem/SoundSystemOpenAL.cc

566 lines
18 KiB
C++
Raw Permalink Normal View History

/****************************************************************************
Copyright (c) 2011-2013,WebJet Business Division,CYOU
http://www.genesis-3d.com.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#if __USE_AUDIO__ || __GENESIS_EDITOR__
#include "stdneb.h"
#include "SoundSystemOpenAL.h"
#include "Framework.h"
#include "SoundBufferOpenAL.h"
#include "SoundSystemSourceOpenAL.h"
#include "SoundSystemDSPOpenAL.h"
#include "io/memoryreader.h"
#include "io/assignregistry.h"
#include "Vorbis/vorbisfile.h"
#include "OpenAL/alext.h"
namespace Sound
{
__ImplementClass(Sound::SoundSystemOpenAL, 'SDAL', Sound::SoundSystem);
//------------------------------------------------------------------------------
SoundSystemOpenAL::SoundSystemOpenAL() {}
//------------------------------------------------------------------------------
SoundSystemOpenAL::~SoundSystemOpenAL() {}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::InitSoundSystem()
{
#ifndef __OSX__
alc_init();
#endif
ALFWInit();
//initialize mpg123
int iMpg123_ret = NULL;
iMpg123_ret = mpg123_init();
if ( MPG123_OK != iMpg123_ret )
{
return false;
}
bool ret = ALFWInitOpenAL();
if (!ret)
{
ALFWShutdown();
}
//By default AL_SOURCE_DISTANCE_MODEL is disable, so all sources use the
//same distance model that set to context.But we need every source have
//its own distance model, so we enable AL_SOURCE_DISTANCE_MODEL.
alEnable(AL_SOURCE_DISTANCE_MODEL);
if (alGetError() != AL_NO_ERROR)
{
ALFWShutdown();
return false;
}
return ret;
}
//------------------------------------------------------------------------------
void SoundSystemOpenAL::ReleaseSoundSystem()
{
//Must release all allocated resource before sound system shutdown.
SizeT size = mSoundSourceContainer.Size();
for (SizeT i = 0; i < size; i++)
{
mSoundSourceContainer[i]->InternalReleaseSource();
}
mSoundSourceContainer.Clear();
size = mSoundBufferContainer.Size();
for (SizeT i = 0; i < size; i++)
{
mSoundBufferContainer[i]->InternalReleaseBuffer();
}
mSoundBufferContainer.Clear();
ALFWShutdownOpenAL();
ALFWShutdown();
#ifndef __OSX__
alc_deinit();
#endif
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::CreateSoundBuffer(const char* nameOrData, SizeT dataSize,
SoundBuffer **soundBuffer,
GENESISOUND_MODE mode,
GENESIS_FILE_FORMAT fileFormat)
{
n_warning("Enter the CreateSoundBuffer happing\n");
GPtr< SoundBufferOpenAL > tempSoundBuffer = SoundBufferOpenAL::Create();
tempSoundBuffer->SetBufferMode(mode);
tempSoundBuffer->SetFileFormat(fileFormat);
bool bOpenMemory = (GENESISSOUND_OPENMEMORY == (mode & GENESISSOUND_OPENMEMORY));
bool bCreateStream = (GENESISSOUND_CREATESTREAM == (mode & GENESISSOUND_CREATESTREAM));
SoundBuffer::BufferInfo bufferInfo;
if (bCreateStream)
{
//Create file stream
GPtr<IO::MemoryReader> pMemoryData = IO::MemoryReader::Create();
pMemoryData->SetAccessMode( IO::Stream::ReadWriteAccess );
pMemoryData->Open();
pMemoryData->Write(nameOrData, dataSize);
pMemoryData->Seek(0,IO::Stream::Begin);
if ( fileFormat & GENESIS_FILE_FORMAT_OGG )
{
// Open Ogg Stream
ov_callbacks sCallbacks;
OggVorbis_File *sOggVorbisFile = new OggVorbis_File();
vorbis_info *psVorbisInfo;
sCallbacks.read_func = ReadOggMemoryStream;
sCallbacks.seek_func = SeekOggMemoryStream;
sCallbacks.close_func = CloseOggMemoryStream;
sCallbacks.tell_func = TellOggMemoryStream;
if (ov_open_callbacks(pMemoryData.get(), sOggVorbisFile, NULL, NULL, sCallbacks))
{
pMemoryData->Close();
ov_clear(sOggVorbisFile);
return false;
}
psVorbisInfo = ov_info(sOggVorbisFile, -1);
if ( !psVorbisInfo )
{
pMemoryData->Close();
ov_clear(sOggVorbisFile);
return false;
}
bufferInfo.frequency = psVorbisInfo->rate;
bufferInfo.channels = psVorbisInfo->channels;
SetBufferInfo(bufferInfo);
if ( !bufferInfo.format )
{
pMemoryData->Close();
ov_clear(sOggVorbisFile);
return false;
}
// Allocate a buffer to be used to store decoded data for all Buffers
unsigned char* pDecodeBuffer = (unsigned char*)malloc(bufferInfo.bufferSize);
if (!pDecodeBuffer)
{
pMemoryData->Close();
ov_clear(sOggVorbisFile);
return false;
}
tempSoundBuffer->SetBufferFormat(bufferInfo.format);
tempSoundBuffer->SetBufferFrequency(bufferInfo.frequency);
tempSoundBuffer->SetBufferChannelCount(bufferInfo.channels);
tempSoundBuffer->SetDecodeBufferSize(bufferInfo.bufferSize);
tempSoundBuffer->SetDecodeBuffer(pDecodeBuffer);
// TODO Move to the soundBuffer
ALuint *g_Buffers = new ALuint[NUM_BUFFERS];
// Generate some AL Buffers for streaming
alGenBuffers(NUM_BUFFERS, g_Buffers);
tempSoundBuffer->SetQueueBuffers(g_Buffers);
// set vorbis
tempSoundBuffer->SetOggHandle(sOggVorbisFile);
tempSoundBuffer->SetMemoryStream(pMemoryData);
}
else if( fileFormat & GENESIS_FILE_FORMAT_MP3 )
{
n_warning("In the mp3 fileformate");
int iMpg123_ret = NULL;
//open a default mpg123 decoder
mpg123_handle *mpg123 ;
mpg123 = mpg123_new(mpg123_decoders()[0], &iMpg123_ret);
n_warning("Is mpg123_handle OK ??? %s\n", NULL == mpg123 ? "FAILED":"SUCCESS");
if ( NULL == mpg123)
{
mpg123_exit();
return false;
}
tempSoundBuffer->SetMpg123Handle(mpg123);
int iMpg123_ret2;
iMpg123_ret = mpg123_replace_reader_handle(mpg123, ReadMP3MemoryStream, LseekMP3MemoryStream, CleanupMP3MemoryStream);
iMpg123_ret2 = mpg123_open_handle( mpg123, pMemoryData.get() );
n_warning("Is open handle OK ??? %s\n", MPG123_OK != iMpg123_ret || iMpg123_ret2!= MPG123_OK ? "FAILED":"SUCCESS");
if ( MPG123_OK != iMpg123_ret || MPG123_OK != iMpg123_ret2)
{
pMemoryData->Close();
CleanupMpg123(mpg123);
return false;
}
tempSoundBuffer->SetMemoryStream(pMemoryData);
//get mp3 format infomation
long lSampleRate;
int iEncoding, iChannels;
iMpg123_ret = mpg123_getformat(mpg123, &lSampleRate, &iChannels, &iEncoding);
n_warning("Is read format OK ??? %s\n", MPG123_OK != iMpg123_ret ? "FAILED":"SUCCESS");
if ( MPG123_OK != iMpg123_ret)
{
pMemoryData->Close();
CleanupMpg123(mpg123);
return false;
}
bufferInfo.frequency = lSampleRate;
bufferInfo.channels = iChannels;
SetBufferInfo(bufferInfo);
if (!bufferInfo.format)
{
pMemoryData->Close();
CleanupMpg123(mpg123);
return false;
}
tempSoundBuffer->SetBufferChannelCount(bufferInfo.channels);
tempSoundBuffer->SetBufferFormat(bufferInfo.format);
tempSoundBuffer->SetBufferFrequency(bufferInfo.frequency);
// Allocate a buffer to be used to store decoded data for all Buffers
unsigned char *pDecodeBuffer = (unsigned char *)malloc(bufferInfo.bufferSize);
if (!pDecodeBuffer)
{
pMemoryData->Close();
CleanupMpg123(mpg123);
return false;
}
tempSoundBuffer->SetDecodeBuffer(pDecodeBuffer);
tempSoundBuffer->SetDecodeBufferSize(bufferInfo.bufferSize);
ALuint *g_Buffers = new ALuint[NUM_BUFFERS];
alGenBuffers(NUM_BUFFERS, g_Buffers);
tempSoundBuffer->SetQueueBuffers(g_Buffers);
}
}
else
{
ALuint uiBuffer = NULL;
// Generate an AL Buffer.
//Buffer's release is in function'bool SoundBufferOpenAL::Release()'.
alGenBuffers( 1, &uiBuffer );
bool ret = ALFWLoadWaveToBuffer(nameOrData, dataSize, uiBuffer, 0, bOpenMemory);
if ( !ret )
return false;
tempSoundBuffer->SetBuffer(&uiBuffer);
}
mSoundBufferContainer.Append( tempSoundBuffer.upcast<SoundBuffer>() );
*soundBuffer = tempSoundBuffer.get();
return true;
}
//------------------------------------------------------------------------------
void SoundSystemOpenAL::SetBufferInfo( SoundBuffer::BufferInfo& bufferInfo )
{
if (bufferInfo.channels == 1)
{
bufferInfo.format = AL_FORMAT_MONO16;
// Set BufferSize to 250ms (Frequency * 2 (16bit) divided by 4 (quarter of a second))
bufferInfo.bufferSize = bufferInfo.frequency >> 1;
// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
bufferInfo.bufferSize -= (bufferInfo.bufferSize % 2);
}
else if (bufferInfo.channels == 2)
{
bufferInfo.format = AL_FORMAT_STEREO16;
// Set BufferSize to 250ms (Frequency * 4 (16bit stereo) divided by 4 (quarter of a second))
bufferInfo.bufferSize = bufferInfo.frequency;
// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
bufferInfo.bufferSize -= (bufferInfo.bufferSize % 4);
}
else if (bufferInfo.channels == 4)
{
bufferInfo.format = alGetEnumValue("AL_FORMAT_QUAD16");
// Set BufferSize to 250ms (Frequency * 8 (16bit 4-channel) divided by 4 (quarter of a second))
bufferInfo.bufferSize = bufferInfo.frequency * 2;
// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
bufferInfo.bufferSize -= (bufferInfo.bufferSize % 8);
}
else if (bufferInfo.channels == 6)
{
bufferInfo.format = alGetEnumValue("AL_FORMAT_51CHN16");
// Set BufferSize to 250ms (Frequency * 12 (16bit 6-channel) divided by 4 (quarter of a second))
bufferInfo.bufferSize = bufferInfo.frequency * 3;
// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
bufferInfo.bufferSize -= (bufferInfo.bufferSize % 12);
}
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::PlaySound(SoundBuffer* soundBuffer, SoundSystemSource **ssSource, bool paused)
{
ALuint uiSource = NULL;
// Generate a Source to playback the Buffer
alGenSources( 1, &uiSource );
GPtr< SoundSystemSourceOpenAL > tempSoundSource = SoundSystemSourceOpenAL::Create();
GENESISOUND_MODE mode = soundBuffer->GetBufferMode();
GENESIS_FILE_FORMAT fileFormat = soundBuffer->GetFileFormat();
if (mode & GENESISSOUND_CREATESTREAM)
{
if ( fileFormat & GENESIS_FILE_FORMAT_MP3 )
{
size_t ulBytesWritten = NULL;
int iMpg123_ret = NULL;
mpg123_handle *mpg123 = soundBuffer->GetMpg123Handle();
unsigned char *pDecodeBuffer = soundBuffer->GetDecodeBuffer();
unsigned long ulBufferSize = soundBuffer->GetDecodeBufferSize();
ALuint * queueBuffers = soundBuffer->GetQueueBuffers();
ALuint ulFormat = soundBuffer->GetBufferFormat();
unsigned long ulFrequency = soundBuffer->GetBufferFrequency();
for(int iLoop = 0; iLoop < NUM_BUFFERS; iLoop++)
{
iMpg123_ret = mpg123_read(mpg123, pDecodeBuffer, ulBufferSize,&ulBytesWritten);
if (iMpg123_ret == MPG123_OK)
{
alBufferData(queueBuffers[iLoop], ulFormat, pDecodeBuffer, ulBytesWritten, ulFrequency);
alSourceQueueBuffers(uiSource, 1, &queueBuffers[iLoop]);
}
else
{
tempSoundSource->SetDecode(false);
break;
}
}
}
else if ( fileFormat & GENESIS_FILE_FORMAT_OGG )
{
size_t ulBytesWritten = NULL;
OggVorbis_File *oggHandle = soundBuffer->GetOggHandle();
unsigned char *pDecodeBuffer = soundBuffer->GetDecodeBuffer();
unsigned long ulBufferSize = soundBuffer->GetDecodeBufferSize();
ALuint * queueBuffers = soundBuffer->GetQueueBuffers();
ALuint ulFormat = soundBuffer->GetBufferFormat();
unsigned long ulFrequency = soundBuffer->GetBufferFrequency();
unsigned long ulChannels = soundBuffer->GetBufferChannelCount();
// Fill all the Buffers with decoded audio data from the OggVorbis file
for (int iLoop = 0; iLoop < NUM_BUFFERS; iLoop++)
{
ulBytesWritten = tempSoundSource->DecodeOggVorbis(oggHandle, (char*)pDecodeBuffer, ulBufferSize, ulChannels);
if (ulBytesWritten)
{
alBufferData(queueBuffers[iLoop], ulFormat, pDecodeBuffer, ulBytesWritten, ulFrequency);
alSourceQueueBuffers(uiSource, 1, &queueBuffers[iLoop]);
}
else
{
tempSoundSource->SetDecode(false);
break;
}
}
}
}
else
{
// Attach Source to Buffer
void *vpBuffer = soundBuffer->GetBuffer();
ALuint *pBuffer = NULL;
pBuffer = (ALuint*)(vpBuffer);
alSourcei( uiSource, AL_BUFFER, *pBuffer);
}
tempSoundSource->SetSource(&uiSource);
tempSoundSource->SetSoundBuffer(soundBuffer);
mSoundSourceContainer.Append( tempSoundSource.upcast<SoundSystemSource>() );
*ssSource = tempSoundSource.get();
tempSoundSource->SetPaused(paused);
return true;
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::ReleaseSingleSource(SoundSystemSource *source)
{
bool ret = false;
SizeT size = mSoundSourceContainer.Size();
for (SizeT i = size - 1; i >= 0; --i)
{
if (mSoundSourceContainer[i].get() == source)
{
ret = mSoundSourceContainer[i]->InternalReleaseSource();
if (!ret)
{
return ret;
}
mSoundSourceContainer.EraseIndex(i);
break;
}
}
return ret;
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::ReleaseSingleBuffer(SoundBuffer *buffer)
{
bool ret = false;
SizeT size = mSoundBufferContainer.Size();
for (SizeT i = size - 1; i >= 0; --i)
{
if (mSoundBufferContainer[i].get() == buffer)
{
ret = mSoundBufferContainer[i]->InternalReleaseBuffer();
if (!ret)
return ret;
mSoundBufferContainer.EraseIndex(i);
break;
}
}
return ret;
}
//------------------------------------------------------------------------------
void SoundSystemOpenAL::CleanupMpg123(mpg123_handle *mh)
{
mpg123_delete(mh);
mpg123_exit();
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::SetListenerAttrs(int listener,
const Math::vector& pos,
const Math::vector& v,
const Math::vector& forward,
const Math::vector& up)
{
ALfloat listenerPos[] = {pos.x(), pos.y(), pos.z()};
ALfloat listenerVel[] = {v.x(), v.y(), v.z()};
ALfloat listenerOri[] = {forward.x(), forward.y(), forward.z(), up.x(), up.y(), up.z()};
// Position
alListenerfv(AL_POSITION,listenerPos);
if (alGetError() != AL_NO_ERROR)
{
return false;
}
// Velocity
alListenerfv(AL_VELOCITY,listenerVel);
if (alGetError() != AL_NO_ERROR)
{
return false;
}
// Orientation
alListenerfv(AL_ORIENTATION,listenerOri);
if (alGetError() != AL_NO_ERROR)
{
return false;
}
return true;
}
//------------------------------------------------------------------------------
bool SoundSystemOpenAL::createDSPByType( ALuint type, GPtr<SoundSystemDSP>& dsp )
{
dsp = SoundSystemDSPOpenAL::Create();
bool result = dsp->init(type);
return result;
}
}
//------------------------------------------------------------------------------
ssize_t ReadMP3MemoryStream(void *handle, void *buf, size_t sz)
{
ssize_t ret;
IO::MemoryStream *pData = static_cast< IO::MemoryStream* >(handle);
ret = pData->Read(buf, sz);
return ret;
}
//------------------------------------------------------------------------------
off_t LseekMP3MemoryStream(void *handle, off_t offset, int whence)
{
IO::MemoryStream *pData = static_cast< IO::MemoryStream* >(handle);
switch (whence)
{
case 0:
pData->Seek(offset, IO::Stream::Begin);
break;
case 2:
pData->Seek(offset, IO::Stream::End);
break;
default:
pData->Seek(offset, IO::Stream::Current);
}
off_t ret = pData->GetPosition();
return ret;
}
//------------------------------------------------------------------------------
void CleanupMP3MemoryStream(void *handle)
{
}
//------------------------------------------------------------------------------
size_t ReadOggMemoryStream( void *ptr, size_t size, size_t nmemb, void *datasource )
{
ssize_t ret;
IO::MemoryStream* pData = static_cast< IO::MemoryStream* >(datasource);
ret = pData->Read(ptr, nmemb);
return ret;
}
//------------------------------------------------------------------------------
int SeekOggMemoryStream( void *datasource, ogg_int64_t offset, int whence )
{
IO::MemoryStream *pData = static_cast< IO::MemoryStream* >(datasource);
switch (whence)
{
case SEEK_SET:
pData->Seek((int)offset, IO::Stream::Begin);
break;
case SEEK_CUR:
pData->Seek((int)offset, IO::Stream::Current);
break;
case SEEK_END:
pData->Seek((int)offset, IO::Stream::End);
break;
}
return NULL;
}
//------------------------------------------------------------------------------
int CloseOggMemoryStream( void *datasource )
{
return NULL;
}
//------------------------------------------------------------------------------
long TellOggMemoryStream( void *datasource )
{
IO::MemoryStream *pData = static_cast< IO::MemoryStream* >(datasource);
size_t position = pData->GetPosition();
return position;
}
#endif // __USE_AUDIO__ || __GENESIS_EDITOR__