Index: ps/trunk/source/soundmanager/SoundManager.cpp =================================================================== --- ps/trunk/source/soundmanager/SoundManager.cpp (revision 13355) +++ ps/trunk/source/soundmanager/SoundManager.cpp (revision 13356) @@ -1,696 +1,673 @@ /* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "SoundManager.h" #include "soundmanager/data/SoundData.h" #include "soundmanager/items/CSoundItem.h" #include "soundmanager/items/CBufferItem.h" #include "soundmanager/items/CStreamItem.h" #include "soundmanager/js/SoundPlayer.h" #include "soundmanager/js/AmbientSound.h" #include "soundmanager/js/MusicSound.h" #include "soundmanager/js/Sound.h" #include "lib/external_libraries/libsdl.h" #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/Profiler2.h" CSoundManager* g_SoundManager = NULL; #define SOURCE_NUM 64 #if CONFIG2_AUDIO class CSoundManagerWorker { public: CSoundManagerWorker() { m_Items = new ItemsList; m_DeadItems = new ItemsList; m_Shutdown = false; m_Enabled = false; int ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this); ENSURE(ret == 0); } ~CSoundManagerWorker() { delete m_Items; CleanupItems(); delete m_DeadItems; } /** * Called by main thread, when the online reporting is enabled/disabled. */ void SetEnabled(bool enabled) { CScopeLock lock(m_WorkerMutex); if (enabled != m_Enabled) { m_Enabled = enabled; } } /** * Called by main thread to request shutdown. * Returns true if we've shut down successfully. * Returns false if shutdown is taking too long (we might be blocked on a * sync network operation) - you mustn't destroy this object, just leak it * and terminate. */ bool Shutdown() { { CScopeLock lock(m_WorkerMutex); m_Shutdown = true; m_Enabled = false; ItemsList::iterator lstr = m_Items->begin(); while (lstr != m_Items->end()) { delete *lstr; - lstr++; + ++lstr; } } pthread_join(m_WorkerThread, NULL); return true; } void addItem( ISoundItem* anItem ) { CScopeLock lock(m_WorkerMutex); m_Items->push_back( anItem ); } void CleanupItems() { CScopeLock lock(m_DeadItemsMutex); AL_CHECK ItemsList::iterator deadItems = m_DeadItems->begin(); while (deadItems != m_DeadItems->end()) { delete *deadItems; - deadItems++; + ++deadItems; AL_CHECK } m_DeadItems->clear(); } private: static void* RunThread(void* data) { debug_SetThreadName("CSoundManagerWorker"); g_Profiler2.RegisterCurrentThread("soundmanager"); static_cast(data)->Run(); return NULL; } void Run() { // Wait until the main thread wakes us up while ( true ) { g_Profiler2.RecordRegionLeave("semaphore wait"); // Handle shutdown requests as soon as possible if (GetShutdown()) return; // If we're not enabled, ignore this wakeup if (!GetEnabled()) continue; int pauseTime = 500; if ( g_SoundManager->InDistress() ) pauseTime = 50; { CScopeLock lock(m_WorkerMutex); ItemsList::iterator lstr = m_Items->begin(); ItemsList* nextItemList = new ItemsList; while (lstr != m_Items->end()) { AL_CHECK if ((*lstr)->IdleTask()) { if ( (pauseTime == 1000) && (*lstr)->IsFading() ) pauseTime = 100; nextItemList->push_back(*lstr); } else { CScopeLock lock(m_DeadItemsMutex); m_DeadItems->push_back(*lstr); } - lstr++; + ++lstr; AL_CHECK } delete m_Items; m_Items = nextItemList; AL_CHECK } SDL_Delay( pauseTime ); } } bool GetEnabled() { CScopeLock lock(m_WorkerMutex); return m_Enabled; } bool GetShutdown() { CScopeLock lock(m_WorkerMutex); return m_Shutdown; } private: // Thread-related members: pthread_t m_WorkerThread; CMutex m_WorkerMutex; CMutex m_DeadItemsMutex; // Shared by main thread and worker thread: // These variables are all protected by m_WorkerMutex ItemsList* m_Items; ItemsList* m_DeadItems; bool m_Enabled; bool m_Shutdown; + + CSoundManagerWorker(CSoundManager* UNUSED(other)){}; }; #endif void CSoundManager::ScriptingInit() { JAmbientSound::ScriptingInit(); JMusicSound::ScriptingInit(); JSound::ScriptingInit(); JSoundPlayer::ScriptingInit(); } #if CONFIG2_AUDIO void CSoundManager::CreateSoundManager() { g_SoundManager = new CSoundManager(); } void CSoundManager::SetEnabled(bool doEnable) { if ( g_SoundManager && !doEnable ) { SAFE_DELETE(g_SoundManager); } else if ( ! g_SoundManager && doEnable ) { CSoundManager::CreateSoundManager(); } } void CSoundManager::al_ReportError(ALenum err, const char* caller, int line) { LOGERROR(L"OpenAL error: %hs; called from %hs (line %d)\n", alGetString(err), caller, line); } void CSoundManager::al_check(const char* caller, int line) { ALenum err = alGetError(); if (err != AL_NO_ERROR) al_ReportError(err, caller, line); } CSoundManager::CSoundManager() { m_CurrentEnvirons = 0; m_ALSourceBuffer = NULL; m_CurrentTune = 0; m_SourceCOunt = 0; m_Gain = 1; m_MusicGain = 1; m_AmbientGain = 1; m_ActionGain = 1; m_BufferCount = 50; m_BufferSize = 65536; + m_SoundEnabled = true; m_MusicEnabled = true; m_MusicPaused = false; m_AmbientPaused = false; m_ActionPaused = false; m_DistressTime = 0; m_DistressErrCount = 0; m_Enabled = AlcInit() == INFO::OK; m_ItemsMap = new ItemsMap; InitListener(); m_Worker = new CSoundManagerWorker(); m_Worker->SetEnabled( true ); } CSoundManager::~CSoundManager() { if (m_Worker->Shutdown()) delete m_Worker; delete m_ItemsMap; if ( m_ALSourceBuffer != NULL ) delete[] m_ALSourceBuffer; alcDestroyContext(m_Context); alcCloseDevice(m_Device); } Status CSoundManager::AlcInit() { Status ret = INFO::OK; m_Device = alcOpenDevice(NULL); if(m_Device) { ALCint attribs[] = {ALC_STEREO_SOURCES, 16, 0}; m_Context = alcCreateContext(m_Device, &attribs[0]); if(m_Context) { alcMakeContextCurrent(m_Context); m_ALSourceBuffer = new ALSourceHolder[SOURCE_NUM]; ALuint* sourceList = new ALuint[SOURCE_NUM]; alGenSources( SOURCE_NUM, sourceList); ALCenum err = alcGetError(m_Device); if ( err == ALC_NO_ERROR ) { for ( int x=0; x 10 ) { m_DistressTime = 0; // Coming out of distress mode m_DistressErrCount = 0; return false; } return true; } void CSoundManager::SetDistressThroughShortage() { CScopeLock lock(m_DistressMutex); // Going into distress for normal reasons m_DistressTime = timer_Time(); } void CSoundManager::SetDistressThroughError() { CScopeLock lock(m_DistressMutex); // Going into distress due to unknown error m_DistressTime = timer_Time(); m_DistressErrCount++; } ALuint CSoundManager::GetALSource( ISoundItem* anItem) { for ( int x=0; xIsOneShot()) { if (itemData->GetBufferCount() == 1) answer = new CSoundItem(itemData); else answer = new CBufferItem(itemData); } else { answer = new CStreamItem(itemData); } if ( answer && m_Worker ) m_Worker->addItem( answer ); } return answer; } void CSoundManager::IdleTask() { AL_CHECK if (m_CurrentTune) m_CurrentTune->EnsurePlay(); AL_CHECK if (m_CurrentEnvirons) m_CurrentEnvirons->EnsurePlay(); AL_CHECK if (m_Worker) m_Worker->CleanupItems(); AL_CHECK } -ISoundItem* CSoundManager::ItemForEntity( entity_id_t source, CSoundData* sndData) +ISoundItem* CSoundManager::ItemForEntity( entity_id_t UNUSED(source), CSoundData* sndData) { - ISoundItem* currentItem = NULL; - if ( false ) - { - ItemsMap::iterator itemFound = m_ItemsMap->find( source ); - if ( itemFound != m_ItemsMap->end() ) - { - currentItem = itemFound->second; - if ( currentItem->CanAttach( sndData ) ) - { - currentItem->Attach( sndData ); - LOGERROR(L"did REUSE items source = %d", m_SourceCOunt); - } - else - { - m_ItemsMap->erase( itemFound ); - currentItem->StopAndDelete(); - LOGERROR(L"item UNREUSABLE for data = %d", m_SourceCOunt); - currentItem = NULL; - } - } - } - if ( currentItem == NULL ) - { - currentItem = ItemForData( sndData ); - if ( currentItem ) - m_ItemsMap->insert(std::make_pair( source, currentItem)); - } + ISoundItem* currentItem = ItemForData( sndData ); return currentItem; } void CSoundManager::InitListener() { ALfloat listenerPos[]={0.0,0.0,0.0}; ALfloat listenerVel[]={0.0,0.0,0.0}; ALfloat listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0}; alListenerfv(AL_POSITION,listenerPos); alListenerfv(AL_VELOCITY,listenerVel); alListenerfv(AL_ORIENTATION,listenerOri); alDistanceModel(AL_LINEAR_DISTANCE); } void CSoundManager::PlayActionItem(ISoundItem* anItem) { if (anItem) { if (m_Enabled && (m_ActionGain > 0)) { anItem->SetGain( m_ActionGain ); anItem->Play(); AL_CHECK } } } void CSoundManager::PlayGroupItem(ISoundItem* anItem, ALfloat groupGain ) { if (anItem) { if (m_Enabled && (m_ActionGain > 0)) { anItem->SetGain(m_ActionGain * groupGain); anItem->PlayAndDelete(); AL_CHECK } } } void CSoundManager::SetMusicEnabled (bool isEnabled) { if (m_CurrentTune && !isEnabled) { m_CurrentTune->FadeAndDelete(1.00); m_CurrentTune = 0L; } m_MusicEnabled = isEnabled; } void CSoundManager::SetMusicItem(ISoundItem* anItem) { AL_CHECK if (m_CurrentTune) { m_CurrentTune->FadeAndDelete(2.00); m_CurrentTune = 0L; } IdleTask(); if (anItem) { if (m_MusicEnabled && m_Enabled) { m_CurrentTune = anItem; m_CurrentTune->SetIsManaged( true ); m_CurrentTune->SetGain(0); m_CurrentTune->PlayLoop(); m_CurrentTune->FadeToIn( m_MusicGain, 1.00); } else { anItem->StopAndDelete(); } } AL_CHECK } void CSoundManager::SetAmbientItem(ISoundItem* anItem) { if (m_CurrentEnvirons) { m_CurrentEnvirons->FadeAndDelete(3.00); m_CurrentEnvirons = 0L; } IdleTask(); if (anItem) { if (m_Enabled && (m_AmbientGain > 0)) { m_CurrentEnvirons = anItem; m_CurrentEnvirons->SetIsManaged( true ); m_CurrentEnvirons->SetGain(0); m_CurrentEnvirons->PlayLoop(); m_CurrentEnvirons->FadeToIn( m_AmbientGain, 2.00); } } AL_CHECK } void CSoundManager::Pause(bool pauseIt) { PauseMusic(pauseIt); PauseAmbient(pauseIt); PauseAction(pauseIt); } void CSoundManager::PauseMusic (bool pauseIt) { if (m_CurrentTune && pauseIt && !m_MusicPaused ) { m_CurrentTune->FadeAndPause( 1.0 ); } else if ( m_CurrentTune && m_MusicPaused && !pauseIt ) { m_CurrentTune->SetGain(0); m_CurrentTune->Resume(); m_CurrentTune->FadeToIn( m_MusicGain, 1.0); } m_MusicPaused = pauseIt; } void CSoundManager::PauseAmbient (bool pauseIt) { if (m_CurrentEnvirons && pauseIt) m_CurrentEnvirons->Pause(); else if ( m_CurrentEnvirons ) m_CurrentEnvirons->Resume(); m_AmbientPaused = pauseIt; } void CSoundManager::PauseAction (bool pauseIt) { m_ActionPaused = pauseIt; } #endif // CONFIG2_AUDIO Index: ps/trunk/source/soundmanager/SoundManager.h =================================================================== --- ps/trunk/source/soundmanager/SoundManager.h (revision 13355) +++ ps/trunk/source/soundmanager/SoundManager.h (revision 13356) @@ -1,155 +1,157 @@ /* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_SOUNDMANAGER_H #define INCLUDED_SOUNDMANAGER_H #include "lib/config2.h" #if CONFIG2_AUDIO #include "lib/file/vfs/vfs_path.h" #include "soundmanager/items/ISoundItem.h" #include "simulation2/system/Entity.h" #include "soundmanager/data/SoundData.h" #include "soundmanager/items/ISoundItem.h" #include "ps/Profiler2.h" #include #include #define AL_CHECK CSoundManager::al_check(__func__, __LINE__); typedef std::vector ItemsList; typedef std::map ItemsMap; class CSoundManagerWorker; struct ALSourceHolder { /// Title of the column ALuint ALSource; ISoundItem* SourceItem; }; class CSoundManager { protected: ALuint m_ALEnvironment; ALCcontext* m_Context; ALCdevice* m_Device; ISoundItem* m_CurrentTune; ISoundItem* m_CurrentEnvirons; CSoundManagerWorker* m_Worker; ItemsMap* m_ItemsMap; CMutex m_DistressMutex; float m_Gain; float m_MusicGain; float m_AmbientGain; float m_ActionGain; bool m_Enabled; long m_SourceCOunt; long m_BufferSize; int m_BufferCount; bool m_MusicEnabled; bool m_SoundEnabled; bool m_MusicPaused; bool m_AmbientPaused; bool m_ActionPaused; long m_DistressErrCount; long m_DistressTime; ALSourceHolder* m_ALSourceBuffer; public: CSoundManager(); virtual ~CSoundManager(); ISoundItem* LoadItem(const VfsPath& itemPath); ISoundItem* ItemForData(CSoundData* itemData); ISoundItem* ItemForEntity( entity_id_t source, CSoundData* sndData); static void ScriptingInit(); static void CreateSoundManager(); static void SetEnabled(bool doEnable); static void al_ReportError(ALenum err, const char* caller, int line); static void al_check(const char* caller, int line); void SetMusicEnabled (bool isEnabled); void setSoundEnabled( bool enabled ); ALuint GetALSource(ISoundItem* anItem); void ReleaseALSource(ALuint theSource); ISoundItem* ItemFromData(CSoundData* itemData); ISoundItem* ItemFromWAV(VfsPath& fname); ISoundItem* ItemFromOgg(VfsPath& fname); ISoundItem* GetSoundItem(unsigned long itemRow); unsigned long Count(); void IdleTask(); void SetMemoryUsage(long bufferSize, int bufferCount); long GetBufferCount(); long GetBufferSize(); void SetMusicItem(ISoundItem* anItem); void SetAmbientItem(ISoundItem* anItem); void PlayActionItem(ISoundItem* anItem); void PlayGroupItem(ISoundItem* anItem, ALfloat groupGain); void SetMasterGain(float gain); void SetMusicGain(float gain); void SetAmbientGain(float gain); void SetActionGain(float gain); bool InDistress(); void SetDistressThroughShortage(); void SetDistressThroughError(); void Pause(bool pauseIt); void PauseMusic (bool pauseIt); void PauseAmbient (bool pauseIt); void PauseAction (bool pauseIt); protected: void InitListener(); virtual Status AlcInit(); +private: + CSoundManager(CSoundManager* UNUSED(other)){}; }; #else // !CONFIG2_AUDIO #define AL_CHECK class CSoundManager { public: static void ScriptingInit(); }; #endif // !CONFIG2_AUDIO extern CSoundManager* g_SoundManager; #endif // INCLUDED_SOUNDMANAGER_H Index: ps/trunk/source/soundmanager/data/OggData.cpp =================================================================== --- ps/trunk/source/soundmanager/data/OggData.cpp (revision 13355) +++ ps/trunk/source/soundmanager/data/OggData.cpp (revision 13356) @@ -1,140 +1,143 @@ /* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "OggData.h" #if CONFIG2_AUDIO #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "soundmanager/SoundManager.h" #include "ps/CLogger.h" COggData::COggData() { m_OneShot = false; + m_Format = 0; + m_Frequency = 0; + m_BuffersUsed = 0; } COggData::~COggData() { ogg->Close(); alDeleteBuffers(m_BuffersUsed, m_Buffer); } void COggData::SetFormatAndFreq(int form, ALsizei freq) { m_Format = form; m_Frequency = freq; } bool COggData::InitOggFile(const VfsPath& itemPath) { int buffersToStart = g_SoundManager->GetBufferCount(); if ( OpenOggNonstream( g_VFS, itemPath, ogg) == INFO::OK ) { m_FileFinished = false; SetFormatAndFreq(ogg->Format(), ogg->SamplingRate() ); SetFileName( itemPath ); AL_CHECK alGenBuffers(buffersToStart, m_Buffer); if(alGetError() != AL_NO_ERROR) { LOGERROR( L"- Error creating initial buffer !!\n"); return false; } else { m_BuffersUsed = FetchDataIntoBuffer(buffersToStart, m_Buffer); if (m_FileFinished) { m_OneShot = true; if (m_BuffersUsed < buffersToStart) { alDeleteBuffers(buffersToStart - m_BuffersUsed, &m_Buffer[m_BuffersUsed]); } } AL_CHECK } return true; } return false; } ALsizei COggData::GetBufferCount() { return m_BuffersUsed; } bool COggData::IsFileFinished() { return m_FileFinished; } void COggData::ResetFile() { ogg->ResetFile(); m_FileFinished = false; } bool COggData::IsOneShot() { return m_OneShot; } int COggData::FetchDataIntoBuffer(int count, ALuint* buffers) { long bufferSize = g_SoundManager->GetBufferSize(); u8* pcmout = new u8[bufferSize + 5000]; int buffersWritten = 0; for (int i = 0; (i < count) && !m_FileFinished; i++) { memset( pcmout, 0, bufferSize + 5000 ); Status totalRet = ogg->GetNextChunk( pcmout, bufferSize); m_FileFinished = ogg->atFileEOF(); if (totalRet > 0) { buffersWritten++; alBufferData(buffers[i], m_Format, pcmout, (ALsizei)totalRet, (int)m_Frequency); } } delete[] pcmout; return buffersWritten; } ALuint COggData::GetBuffer() { return m_Buffer[0]; } ALuint* COggData::GetBufferPtr() { return m_Buffer; } #endif // CONFIG2_AUDIO Index: ps/trunk/source/soundmanager/data/ogg.cpp =================================================================== --- ps/trunk/source/soundmanager/data/ogg.cpp (revision 13355) +++ ps/trunk/source/soundmanager/data/ogg.cpp (revision 13356) @@ -1,331 +1,333 @@ /* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "ogg.h" #if CONFIG2_AUDIO #include "lib/external_libraries/openal.h" #include "lib/external_libraries/vorbis.h" #include "lib/byte_order.h" #include "lib/file/io/io.h" #include "lib/file/file_system.h" #include "lib/file/vfs/vfs_util.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" static Status LibErrorFromVorbis(int err) { switch(err) { case 0: return INFO::OK; case OV_HOLE: return ERR::AGAIN; case OV_EREAD: return ERR::IO; case OV_EFAULT: return ERR::LOGIC; case OV_EIMPL: return ERR::NOT_SUPPORTED; case OV_EINVAL: return ERR::INVALID_PARAM; case OV_ENOTVORBIS: return ERR::NOT_SUPPORTED; case OV_EBADHEADER: return ERR::CORRUPTED; case OV_EVERSION: return ERR::INVALID_VERSION; case OV_ENOTAUDIO: return ERR::_1; case OV_EBADPACKET: return ERR::_2; case OV_EBADLINK: return ERR::_3; case OV_ENOSEEK: return ERR::_4; default: return ERR::FAIL; } } //----------------------------------------------------------------------------- class VorbisFileAdapter { public: VorbisFileAdapter(const PFile& openedFile) : file(openedFile) , size(FileSize(openedFile->Pathname())) , offset(0) { } static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context) { - VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + VorbisFileAdapter* adapter = static_cast(context); const off_t sizeRequested = numItems*itemSize; const off_t sizeRemaining = adapter->size - adapter->offset; const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining); io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset); if(io::Run(op) == INFO::OK) { adapter->offset += sizeToRead; return sizeToRead; } errno = EIO; return 0; } static int Seek(void* context, ogg_int64_t offset, int whence) { - VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + VorbisFileAdapter* adapter = static_cast(context); off_t origin = 0; switch(whence) { case SEEK_SET: origin = 0; break; case SEEK_CUR: origin = adapter->offset; break; case SEEK_END: origin = adapter->size+1; break; NODEFAULT; } adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size); return 0; } static int Close(void* context) { - VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + VorbisFileAdapter* adapter = static_cast(context); adapter->file.reset(); return 0; // return value is ignored } static long Tell(void* context) { - VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + VorbisFileAdapter* adapter = static_cast(context); return adapter->offset; } private: PFile file; off_t size; off_t offset; }; //----------------------------------------------------------------------------- class VorbisBufferAdapter { public: VorbisBufferAdapter(const shared_ptr& buffer, size_t size) : buffer(buffer) , size(size) , offset(0) { } static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context) { - VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; + VorbisBufferAdapter* adapter = static_cast(context); + const off_t sizeRequested = numItems*itemSize; const off_t sizeRemaining = adapter->size - adapter->offset; const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining); memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead); adapter->offset += sizeToRead; return sizeToRead; } static int Seek(void* context, ogg_int64_t offset, int whence) { - VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; + VorbisBufferAdapter* adapter = static_cast(context); off_t origin = 0; switch(whence) { case SEEK_SET: origin = 0; break; case SEEK_CUR: origin = adapter->offset; break; case SEEK_END: origin = adapter->size+1; break; NODEFAULT; } adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size); return 0; } static int Close(void* context) { - VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; + VorbisBufferAdapter* adapter = static_cast(context); adapter->buffer.reset(); return 0; // return value is ignored } static long Tell(void* context) { - VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; + VorbisBufferAdapter* adapter = static_cast(context); return adapter->offset; } private: shared_ptr buffer; off_t size; off_t offset; }; //----------------------------------------------------------------------------- template class OggStreamImpl : public OggStream { public: OggStreamImpl(const Adapter& adapter) : adapter(adapter) { m_fileEOF = false; + info = NULL; } Status Close() { ov_clear( &vf ); return 0; } Status Open() { ov_callbacks callbacks; callbacks.read_func = Adapter::Read; callbacks.close_func = Adapter::Close; callbacks.seek_func = Adapter::Seek; callbacks.tell_func = Adapter::Tell; const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks); if(ret != 0) WARN_RETURN(LibErrorFromVorbis(ret)); const int link = -1; // retrieve info for current bitstream info = ov_info(&vf, link); if(!info) WARN_RETURN(ERR::INVALID_HANDLE); return INFO::OK; } virtual ALenum Format() { return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; } virtual ALsizei SamplingRate() { return info->rate; } virtual bool atFileEOF() { return m_fileEOF; } virtual Status ResetFile() { ov_time_seek( &vf, 0 ); m_fileEOF = false; return INFO::OK; } virtual Status GetNextChunk(u8* buffer, size_t size) { // we may have to call ov_read multiple times because it // treats the buffer size "as a limit and not a request" size_t bytesRead = 0; for(;;) { const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN); const int wordSize = sizeof(i16); const int isSigned = 1; int bitstream; // unused const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream); if(ret == 0) { // EOF m_fileEOF = true; return (Status)bytesRead; } else if(ret < 0) WARN_RETURN(LibErrorFromVorbis(ret)); else // success { bytesRead += ret; if(bytesRead == size) return (Status)bytesRead; } } } private: Adapter adapter; OggVorbis_File vf; vorbis_info* info; bool m_fileEOF; }; //----------------------------------------------------------------------------- Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream) { PFile file(new File); RETURN_STATUS_IF_ERR(file->Open(pathname, L'r')); shared_ptr > tmp(new OggStreamImpl(VorbisFileAdapter(file))); RETURN_STATUS_IF_ERR(tmp->Open()); stream = tmp; return INFO::OK; } Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream) { shared_ptr contents; size_t size; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size)); shared_ptr > tmp(new OggStreamImpl(VorbisBufferAdapter(contents, size))); RETURN_STATUS_IF_ERR(tmp->Open()); stream = tmp; return INFO::OK; } #endif // CONFIG2_AUDIO