Index: build/premake/premake4.lua =================================================================== --- build/premake/premake4.lua (revision 11718) +++ build/premake/premake4.lua (working copy) @@ -519,7 +519,10 @@ "ps/Network", "ps/GameSetup", "ps/XML", - "sound", + "soundmanager", + "soundmanager/data", + "soundmanager/items", + "soundmanager/js", "scripting", "maths", "maths/scripting", @@ -533,6 +536,8 @@ "boost", "enet", "libcurl", + "vorbis", + "openal" } setup_static_lib_project("engine", source_dirs, extern_libs, {}) @@ -553,7 +558,6 @@ end setup_static_lib_project("graphics", source_dirs, extern_libs, {}) - source_dirs = { "tools/atlas/GameInterface", "tools/atlas/GameInterface/Handlers" Index: source/ps/GameSetup/Config.cpp =================================================================== --- source/ps/GameSetup/Config.cpp (revision 11718) +++ source/ps/GameSetup/Config.cpp (working copy) @@ -23,8 +23,8 @@ #include "lib/timer.h" #include "lib/res/sound/snd_mgr.h" #include "Config.h" +#include "soundmanager/CSoundManager.h" - // (these variables are documented in the header.) CStrW g_CursorName = L"test"; @@ -81,9 +81,26 @@ CFG_GET_USER_VAL("particles", Bool, g_Particles); float gain = -1.0f; + float musicGain = -1.0f; + float ambientGain = -1.0f; + float actionGain = -1.0f; + int bufferCount = 50; + unsigned long bufferSize = 65536; + CFG_GET_USER_VAL("sound.mastergain", Float, gain); - if(gain >= 0.0f) - WARN_IF_ERR(snd_set_master_gain(gain)); + CFG_GET_USER_VAL("sound.musicgain", Float, musicGain); + CFG_GET_USER_VAL("sound.ambientgain", Float, ambientGain); + CFG_GET_USER_VAL("sound.actiongain", Float, actionGain); + + CFG_GET_USER_VAL("sound.bufferCount", Int, bufferCount); + CFG_GET_USER_VAL("sound.bufferSize", UnsignedLong, bufferSize); + + g_SoundManager->setMasterGain( gain ); + g_SoundManager->setMusicGain( musicGain ); + g_SoundManager->setAmbientGain( ambientGain ); + g_SoundManager->setActionGain( actionGain ); + + g_SoundManager->setMemoryUsage( bufferSize, bufferCount); } Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp (revision 11718) +++ source/ps/GameSetup/GameSetup.cpp (working copy) @@ -102,6 +102,7 @@ #include "tools/atlas/GameInterface/GameLoop.h" #include "tools/atlas/GameInterface/View.h" +#include "soundmanager/CSoundManager.h" #if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets #define MUST_INIT_X11 1 @@ -203,6 +204,8 @@ { PROFILE3("render"); + g_SoundManager->idleTask(); + ogl_WarnIfError(); g_Profiler2.RecordGPUFrameStart(); @@ -330,7 +333,8 @@ { // maths JSI_Vector3D::init(); - + + CSoundManager::ScriptingInit(); // graphics CGameView::ScriptingInit(); @@ -338,7 +342,7 @@ CRenderer::ScriptingInit(); // sound - JSI_Sound::ScriptingInit(); +// JSI_Sound::ScriptingInit(); // ps JSI_Console::init(); @@ -476,6 +480,9 @@ g_VFS->Mount(L"", modLoosePath / modName/"", flags, priority); g_VFS->Mount(L"", modArchivePath / modName/"", flags, priority); } + + OsPath apth = paths.RData()/"mods/public"; + g_SoundManager = new CSoundManager( apth ); // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. @@ -691,7 +698,7 @@ // resource // first shut down all resource owners, and then the handle manager. TIMER_BEGIN(L"resource modules"); - snd_shutdown(); + delete g_SoundManager; g_VFS.reset(); @@ -930,7 +937,7 @@ // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. - snd_disable(true); + g_SoundManager->setEnabled( false ); } g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); Index: source/ps/Game.cpp =================================================================== --- source/ps/Game.cpp (revision 11718) +++ source/ps/Game.cpp (working copy) @@ -45,6 +45,7 @@ #include "simulation2/components/ICmpPlayerManager.h" #include "gui/GUIManager.h" +#include "soundmanager/CSoundManager.h" extern bool g_GameRestarted; @@ -299,6 +300,7 @@ if (doInterpolate) { m_TurnManager->Interpolate(deltaTime); + g_SoundManager->idleTask(); } // TODO: maybe we should add a CCmpParticleInterface that passes the interpolation commands Index: source/soundmanager/items/CSoundItem.h =================================================================== --- source/soundmanager/items/CSoundItem.h (revision 0) +++ source/soundmanager/items/CSoundItem.h (revision 0) @@ -0,0 +1,48 @@ +/* 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 SoundTester_CSoundItem_h +#define SoundTester_CSoundItem_h + +#include "CSoundBase.h" +#include "soundmanager/data/CSoundData.h" + + +class CSoundItem :public CSoundBase +{ +protected: + +public: + CSoundItem (); + CSoundItem (CSoundData* sndData); + + virtual ~CSoundItem (); + void attach ( CSoundData* itemData ); + bool idleTask (); + +protected: + + +}; + + + + + + +#endif Index: source/soundmanager/items/CBufferItem.h =================================================================== --- source/soundmanager/items/CBufferItem.h (revision 0) +++ source/soundmanager/items/CBufferItem.h (revision 0) @@ -0,0 +1,40 @@ +/* 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 SoundTester_CBufferItem_h +#define SoundTester_CBufferItem_h + +#include "CSoundBase.h" + +class CBufferItem : public CSoundBase +{ +public: + CBufferItem (CSoundData* sndData); + virtual ~CBufferItem (); + + virtual void setLooping ( bool loops ); + virtual bool idleTask (); + +protected: + virtual void attach ( CSoundData* itemData ); + + +}; + + +#endif Index: source/soundmanager/items/CStreamItem.h =================================================================== --- source/soundmanager/items/CStreamItem.h (revision 0) +++ source/soundmanager/items/CStreamItem.h (revision 0) @@ -0,0 +1,38 @@ +/* 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 SoundTester_CStreamItem_h +#define SoundTester_CStreamItem_h + +#include "soundmanager/data/CSoundData.h" +#include "CSoundBase.h" + +class CStreamItem : public CSoundBase +{ +public: + CStreamItem (CSoundData* sndData); + virtual ~CStreamItem (); + + virtual void setLooping ( bool loops ); + virtual bool idleTask (); + +protected: + virtual void attach ( CSoundData* itemData ); + +}; + +#endif Index: source/soundmanager/items/ISoundItem.h =================================================================== --- source/soundmanager/items/ISoundItem.h (revision 0) +++ source/soundmanager/items/ISoundItem.h (revision 0) @@ -0,0 +1,59 @@ +/* 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 SoundTester_ISoundItem_h +#define SoundTester_ISoundItem_h + +#include +#include "lib/external_libraries/openal.h" +#include "maths/Vector3D.h" + + +class ISoundItem +{ + +public: + virtual ~ISoundItem(){}; + virtual bool getLooping () = 0; + virtual void setLooping (bool loop) = 0; + virtual bool isPlaying () = 0; + + + virtual std::string getName () = 0; + virtual bool idleTask () = 0; + + virtual void play () = 0; + virtual void stop () = 0; + + virtual void ensurePlay () = 0; + virtual void playAsMusic () = 0; + virtual void playAsAmbient () = 0; + + virtual void playAndDelete () = 0; + virtual void stopAndDelete () = 0; + virtual void fadeToIn ( ALfloat newVolume, double fadeDuration) = 0; + virtual void fadeAndDelete ( double fadeTime ) = 0; + virtual void playLoop () = 0; + + virtual void setCone (ALfloat innerCone, ALfloat outerCone, ALfloat coneGain) = 0; + virtual void setPitch (ALfloat pitch) = 0; + virtual void setGain (ALfloat gain) = 0; + virtual void setLocation (const CVector3D& position) = 0; +}; + + +#endif //SoundTester_ISoundItem_h \ No newline at end of file Index: source/soundmanager/items/CSoundBase.cpp =================================================================== --- source/soundmanager/items/CSoundBase.cpp (revision 0) +++ source/soundmanager/items/CSoundBase.cpp (revision 0) @@ -0,0 +1,265 @@ +/* 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 "CSoundBase.h" +#include "soundmanager/CSoundManager.h" +#include "soundmanager/data/CSoundData.h" + +#include + +#include "lib/timer.h" + + +CSoundBase::CSoundBase() +{ + resetVars(); +} + +CSoundBase::~CSoundBase() +{ + stop(); + if ( mALSource != 0 ) { + alDeleteSources( 1, &mALSource); + mALSource = 0; + } + if ( mSoundData != 0 ) { + CSoundData::releaseSoundData( mSoundData ); + mSoundData = 0; + } + if ( mName ) + delete mName; +} + +void CSoundBase::resetVars() +{ + mALSource = 0; + mSoundData = 0; + mLastPlay = false; + mLooping = false; + mStartFadeTime = 0; + mEndFadeTime = 0; + mStartVolume = 0; + mEndVolume = 0; + + resetFade(); + mName = new std::string( "sound name" ); +} + +void CSoundBase::resetFade() +{ + mStartFadeTime = 0; + mEndFadeTime = 0; + mStartVolume = 0; + mEndVolume = 0; + mShouldBePlaying = false; +} + +void CSoundBase::setGain(ALfloat gain) +{ + alSourcef(mALSource, AL_GAIN, gain); +} + +void CSoundBase::ensurePlay() +{ + if ( mShouldBePlaying && !isPlaying() ) + play(); +} + +void CSoundBase::setCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain) +{ + alSourcef( mALSource, innerCone, AL_CONE_INNER_ANGLE); + alSourcef( mALSource, outerCone, AL_CONE_OUTER_ANGLE); + alSourcef( mALSource, coneGain, AL_CONE_OUTER_GAIN); +} + +void CSoundBase::setPitch(ALfloat pitch) +{ + alSourcef( mALSource, AL_PITCH, pitch); +} + +void CSoundBase::setDirection(const CVector3D& direction) +{ + alSourcefv( mALSource, AL_DIRECTION, direction.GetFloatArray() ); +} + +bool CSoundBase::initOpenAL() +{ + alGetError(); /* clear error */ + alGenSources( 1, &mALSource); + long anErr = alGetError(); + if( anErr != AL_NO_ERROR) + { + printf("- Error creating sources %ld !!\n", anErr ); + } + else + { + ALfloat source0Pos[]={ -2.0, 0.0, 0.0}; + ALfloat source0Vel[]={ 0.0, 0.0, 0.0}; + + alSourcef( mALSource,AL_PITCH,1.0f); + alSourcef( mALSource,AL_GAIN,1.0f); + alSourcefv( mALSource,AL_POSITION,source0Pos); + alSourcefv( mALSource,AL_VELOCITY,source0Vel); + alSourcei( mALSource,AL_LOOPING,AL_FALSE); + return true; + } + return false; +} + +bool CSoundBase::isPlaying() +{ + int proc_state; + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + + return ( proc_state == AL_PLAYING ); +} + +void CSoundBase::setLastPlay( bool last ) +{ + mLastPlay = last; +} + +bool CSoundBase::idleTask() +{ + return true; +} + +void CSoundBase::setLocation (const CVector3D& position) +{ + // const float* loatArr = position.GetFloatArray(); +// debug_printf(L"do upload and play at location:%f, %f, %f\n\n", loatArr[0], loatArr[1], loatArr[2] ); + +// alSourcefv( mALSource,AL_POSITION, position.GetFloatArray() ); +} + +bool CSoundBase::handleFade() +{ + if ( mStartFadeTime != 0 ) { + double currTime = timer_Time(); + double pctDone = std::min( 1.0, (currTime - mStartFadeTime) / (mEndFadeTime - mStartFadeTime) ); + pctDone = std::max( 0.0, pctDone ); + ALfloat curGain = ((mEndVolume - mStartVolume ) * pctDone) + mStartVolume; + + if (curGain == 0 ) + stop(); + else if ( curGain == mEndVolume ) { + alSourcef( mALSource, AL_GAIN, curGain); + resetFade(); + } + else + alSourcef( mALSource, AL_GAIN, curGain); + } + return true; +} + +bool CSoundBase::getLooping() +{ + return mLooping; +} +void CSoundBase::setLooping( bool loops ) +{ + mLooping = loops; + alSourcei( mALSource, AL_LOOPING, loops ? AL_TRUE : AL_FALSE ); +} + +void CSoundBase::play() +{ + mShouldBePlaying = true; + if ( mALSource != 0 ) + alSourcePlay( mALSource ); +} +void CSoundBase::playAndDelete() +{ + setLastPlay( true ); + play(); +} + +void CSoundBase::fadeAndDelete( double fadeTime ) +{ + setLastPlay( true ); + fadeToIn( 0, fadeTime ); +} + +void CSoundBase::stopAndDelete() +{ + setLastPlay( true ); + stop(); +} + +void CSoundBase::playLoop() +{ + if ( mALSource != 0 ) { + setLooping( true ); + play(); + } +} + +void CSoundBase::fadeToIn( ALfloat newVolume, double fadeDuration) +{ + int proc_state; + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + if ( proc_state == AL_PLAYING ) { + mStartFadeTime = timer_Time(); + mEndFadeTime = mStartFadeTime + fadeDuration; + alGetSourcef( mALSource, AL_GAIN, &mStartVolume); + mEndVolume = newVolume; + } + +} + +void CSoundBase::playAsMusic() +{ + g_SoundManager->setMusicItem( this ); +} + +void CSoundBase::playAsAmbient() +{ + g_SoundManager->setAmbientItem( this ); +} + +void CSoundBase::stop() +{ + mShouldBePlaying = false; + if ( mALSource != 0 ) { + int proc_state; + alSourcei( mALSource, AL_LOOPING, AL_FALSE ); + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + if ( proc_state == AL_PLAYING ) + alSourceStop( mALSource ); + } +} + +const char* CSoundBase::Name() +{ + return mName->c_str(); +} + +std::string CSoundBase::getName() +{ + return std::string( mName->c_str() ); +} + +void CSoundBase::setNameFromPath( char* fileLoc ) +{ + std::string anst( fileLoc ); + size_t pos = anst.find_last_of("/"); + if(pos != std::wstring::npos) + mName->assign(anst.begin() + pos + 1, anst.end()); + else + mName->assign(anst.begin(), anst.end()); +} + Index: source/soundmanager/items/CSoundItem.cpp =================================================================== --- source/soundmanager/items/CSoundItem.cpp (revision 0) +++ source/soundmanager/items/CSoundItem.cpp (revision 0) @@ -0,0 +1,65 @@ +/* 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 "CSoundItem.h" +#include "soundmanager/data/CSoundData.h" + +#include + + +CSoundItem::CSoundItem() +{ + resetVars(); +} + +CSoundItem::CSoundItem(CSoundData* sndData) +{ + resetVars(); + if ( initOpenAL() ) + attach( sndData ); + + debug_printf(L"created SoundItem at: %ls\n\n", sndData->getFileName().c_str()); +} + +CSoundItem::~CSoundItem() +{ + ALuint al_buf; + + stop(); + alSourceUnqueueBuffers(mALSource, 1, &al_buf); +} + +bool CSoundItem::idleTask() +{ + handleFade(); + + if ( mLastPlay ) + { + int proc_state; + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + return ( proc_state != AL_STOPPED ); + } + return true; +} + +void CSoundItem::attach( CSoundData* itemData ) +{ + if ( itemData != NULL ) { + mSoundData = itemData->incrementCount(); + alSourcei( mALSource, AL_BUFFER, mSoundData->getBuffer() ); + } +} Index: source/soundmanager/items/CBufferItem.cpp =================================================================== --- source/soundmanager/items/CBufferItem.cpp (revision 0) +++ source/soundmanager/items/CBufferItem.cpp (revision 0) @@ -0,0 +1,85 @@ +/* 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 "CBufferItem.h" +#include "soundmanager/data/CSoundData.h" + +#include + +CBufferItem::CBufferItem(CSoundData* sndData) +{ + resetVars(); + if ( initOpenAL() ) + attach( sndData ); + + debug_printf(L"created BufferItem at: %ls\n\n", sndData->getFileName().c_str()); +} + + +CBufferItem::~CBufferItem() +{ + stop(); + int num_processed; + alGetSourcei( mALSource, AL_BUFFERS_PROCESSED, &num_processed); + + if (num_processed > 0) + { + ALuint al_buf[num_processed]; + alSourceUnqueueBuffers(mALSource, num_processed, al_buf); + } +} + + +bool CBufferItem::idleTask() +{ + handleFade(); + + if ( mLastPlay ) + { + int proc_state; + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + return ( proc_state != AL_STOPPED ); + } + + if ( getLooping() ) { + int num_processed; + alGetSourcei( mALSource, AL_BUFFERS_PROCESSED, &num_processed); + + for ( int i = 0; i < num_processed; i++ ) + { + ALuint al_buf; + alSourceUnqueueBuffers(mALSource, 1, &al_buf); + alSourceQueueBuffers(mALSource, 1, &al_buf); + } + } + + return true; +} + +void CBufferItem::attach( CSoundData* itemData ) +{ + if ( itemData != NULL ) { + mSoundData = itemData->incrementCount(); + alSourceQueueBuffers(mALSource, mSoundData->getBufferCount(),(const ALuint *) mSoundData->getBufferPtr()); + } +} + +void CBufferItem::setLooping( bool loops ) +{ + mLooping = loops; +} + Index: source/soundmanager/items/CSoundBase.h =================================================================== --- source/soundmanager/items/CSoundBase.h (revision 0) +++ source/soundmanager/items/CSoundBase.h (revision 0) @@ -0,0 +1,94 @@ +/* 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 SoundTester_CSoundBase_h +#define SoundTester_CSoundBase_h + +#include +#include "lib/external_libraries/openal.h" +#include "soundmanager/items/ISoundItem.h" +#include "soundmanager/data/CSoundData.h" + + +class CSoundBase :public ISoundItem +{ +protected: + + ALuint mALSource; + CSoundData* mSoundData; + + std::string* mName; + bool mLastPlay; + bool mLooping; + bool mShouldBePlaying; + + double mStartFadeTime; + double mEndFadeTime; + ALfloat mStartVolume; + ALfloat mEndVolume; + +public: + CSoundBase (); + + virtual ~CSoundBase (); + + virtual bool initOpenAL(); + virtual void resetVars(); + virtual void ensurePlay(); + + virtual void setGain (ALfloat gain); + virtual void setPitch(ALfloat pitch); + virtual void setDirection(const CVector3D& direction); + virtual void setCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain); + virtual void setLastPlay( bool last ); + + void play (); + void playAndDelete (); + bool idleTask (); + void playLoop (); + void stop (); + void stopAndDelete (); + void fadeToIn ( ALfloat newVolume, double fadeDuration); + + void playAsMusic (); + void playAsAmbient (); + + const char* Name(); + std::string getName(); + + virtual bool getLooping (); + virtual void setLooping ( bool loops ); + virtual bool isPlaying(); + virtual void setLocation (const CVector3D& position); + virtual void fadeAndDelete ( double fadeTime ); + +protected: + + void setNameFromPath( char* fileLoc ); + void resetFade(); + bool handleFade(); + + +}; + + + + + + +#endif Index: source/soundmanager/items/CStreamItem.cpp =================================================================== --- source/soundmanager/items/CStreamItem.cpp (revision 0) +++ source/soundmanager/items/CStreamItem.cpp (revision 0) @@ -0,0 +1,92 @@ +/* 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 "CStreamItem.h" +#include "soundmanager/data/COggData.h" + +#include + +CStreamItem::CStreamItem(CSoundData* sndData) +{ + resetVars(); + if ( initOpenAL() ) + attach( sndData ); + + debug_printf(L"created StreamItem at: %ls\n\n", sndData->getFileName().c_str()); +} + +CStreamItem::~CStreamItem() +{ + stop(); + + int num_processed; + alGetSourcei( mALSource, AL_BUFFERS_PROCESSED, &num_processed); + + if (num_processed > 0) + { + ALuint al_buf[num_processed]; + alSourceUnqueueBuffers(mALSource, num_processed, al_buf); + } +} + +bool CStreamItem::idleTask() +{ + handleFade(); + + int proc_state; + alGetSourceiv( mALSource, AL_SOURCE_STATE, &proc_state); + + if ( proc_state == AL_STOPPED ) { + if ( mLastPlay ) + return ( proc_state != AL_STOPPED ); + } + else { + COggData* tmp = (COggData*)mSoundData; + + if ( ! tmp->isFileFinished() ) { + int num_processed; + alGetSourcei( mALSource, AL_BUFFERS_PROCESSED, &num_processed); + + if (num_processed > 0) + { + ALuint al_buf[num_processed]; + alSourceUnqueueBuffers(mALSource, num_processed, al_buf); + int didWrite = tmp->fetchDataIntoBuffer( num_processed, al_buf); + alSourceQueueBuffers( mALSource, didWrite, al_buf); + } + } + else if ( getLooping() ) + { + tmp->resetFile(); + } + } + return true; +} + +void CStreamItem::attach( CSoundData* itemData ) +{ + if ( itemData != NULL ) { + mSoundData = itemData->incrementCount(); + alSourceQueueBuffers(mALSource, mSoundData->getBufferCount(), (const ALuint *)mSoundData->getBufferPtr()); + } +} + +void CStreamItem::setLooping( bool loops ) +{ + mLooping = loops; +} + Index: source/soundmanager/CSoundManager.cpp =================================================================== --- source/soundmanager/CSoundManager.cpp (revision 0) +++ source/soundmanager/CSoundManager.cpp (revision 0) @@ -0,0 +1,292 @@ +/* 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 "CSoundManager.h" +#include "soundmanager/items/CSoundItem.h" +#include "soundmanager/items/CBufferItem.h" +#include "soundmanager/items/CStreamItem.h" +#include "soundmanager/js/JAmbientSound.h" +#include "soundmanager/js/JMusicSound.h" +#include "soundmanager/js/JSound.h" +#include "soundmanager/data/CSoundData.h" + + +CSoundManager* g_SoundManager; + +void CSoundManager::ScriptingInit() +{ + JAmbientSound::ScriptingInit(); + JMusicSound::ScriptingInit(); + JSound::ScriptingInit(); +} + +CSoundManager::CSoundManager(OsPath& resourcePath) +{ + mResourcePath = resourcePath; + + mItems = new ItemsList; + mCurrentEnvirons = 0; + mCurrentTune = 0; + mGain = 1; + mMusicGain = 1; + mAmbientGain = 1; + mActionGain = 1; + mEnabled = true; + mBufferCount = 50; + mBufferSize = 65536; + + debug_printf(L"initiate manager at: %ls\n\n", resourcePath.string().c_str()); + + alc_init(); +} + +CSoundManager::~CSoundManager() +{ + ItemsList::iterator lstr = mItems->begin(); + while ( lstr != mItems->end() ) { + (*lstr)->stop(); + delete *lstr; + lstr++; + } + + alcDestroyContext( mContext ); + alcCloseDevice( mDevice ); + + delete mItems; + mItems = 0L; + mCurrentEnvirons = 0; + mCurrentTune = 0; +} + + +Status CSoundManager::alc_init() +{ + Status ret = INFO::OK; + + mDevice = alcOpenDevice(NULL); + if(mDevice) + { + mContext = alcCreateContext(mDevice, 0); // no attrlist needed + if(mContext) + alcMakeContextCurrent(mContext); + } + + // check if init succeeded. + // some OpenAL implementations don't indicate failure here correctly; + // we need to check if the device and context pointers are actually valid. + ALCenum err = alcGetError(mDevice); + if(err != ALC_NO_ERROR || !mDevice || !mContext) + { +#if OS_UNIX + ret = INFO::OK; +#else + ret = ERR::FAIL; +#endif + } + + const char* dev_name = (const char*)alcGetString(mDevice, ALC_DEVICE_SPECIFIER); + wchar_t buf[200]; + swprintf(buf, ARRAY_SIZE(buf), L"SND| alc_init: success, using %hs\n", dev_name); + + return ret; +} +void CSoundManager::setMemoryUsage( long bufferSize, int bufferCount ) +{ + mBufferCount = bufferCount; + mBufferSize = bufferSize; +} +long CSoundManager::getBufferCount() +{ + return mBufferCount; +} +long CSoundManager::getBufferSize() +{ + return mBufferSize; +} + + +void CSoundManager::setMasterGain( float gain) +{ + mGain = gain; +} +void CSoundManager::setMusicGain( float gain) +{ + mMusicGain = gain; +} +void CSoundManager::setAmbientGain( float gain) +{ + mAmbientGain = gain; +} +void CSoundManager::setActionGain( float gain) +{ + mActionGain = gain; +} + + +ISoundItem* CSoundManager::loadItem( OsPath* itemPath ) +{ + debug_printf(L"initiate item at: %ls\n\n", itemPath->string().c_str()); + + OsPath thePath = mResourcePath/ *itemPath; + + CSoundData* itemData = CSoundData::soundDataFromFile( thePath ); + ISoundItem* answer = NULL; + + if ( itemData != NULL ) { + if ( itemData->isOneShot() ) { + if ( itemData->getBufferCount() == 1 ) + answer = new CSoundItem( itemData ); + else + answer = new CBufferItem( itemData ); + } + else { + answer = new CStreamItem( itemData ); + } + + if ( answer != NULL ) + mItems->push_back( answer ); + } + + + return answer; +} + +unsigned long CSoundManager::count() +{ + return mItems->size(); +} + +void CSoundManager::idleTask() +{ + if ( mItems ) + { + ItemsList::iterator lstr = mItems->begin(); + ItemsList deadItemList; + ItemsList* nextItemList = new ItemsList; + + + while ( lstr != mItems->end() ) { + if ( (*lstr)->idleTask() ) + nextItemList->push_back( *lstr ); + else + deadItemList.push_back( *lstr ); + lstr++; + } + delete mItems; + mItems = nextItemList; + + ItemsList::iterator deadItems = deadItemList.begin(); + while ( deadItems != deadItemList.end() ) + { + delete *deadItems; + deadItems++; + } + } + if ( mCurrentTune ) + mCurrentTune->ensurePlay(); + if ( mCurrentEnvirons ) + mCurrentEnvirons->ensurePlay(); +} + +void CSoundManager::deleteItem( long itemNum ) +{ + ItemsList::iterator lstr = mItems->begin(); + lstr += itemNum; + + delete *lstr; + + mItems->erase( lstr ); +} + +ISoundItem* CSoundManager::getSoundItem( unsigned long itemRow ) +{ + return (*mItems)[itemRow]; +} + +void CSoundManager::InitListener() +{ + ALfloat listenerPos[]={0.0,0.0,4.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); +} +void CSoundManager::setEnabled( bool doEnable ) +{ + mEnabled = doEnable; +} + +void CSoundManager::playActionItem( ISoundItem* anItem ) +{ + if ( anItem ) + { + if ( mEnabled && ( mActionGain > 0 ) ) { + anItem->setGain( mGain * mActionGain ); + anItem->play(); + } + } +} +void CSoundManager::playGroupItem( ISoundItem* anItem, ALfloat groupGain) +{ + if ( anItem ) + { + if ( mEnabled && ( mActionGain > 0 ) ) { + anItem->setGain( mGain * groupGain ); + anItem->play(); + } + } +} +void CSoundManager::setMusicItem( ISoundItem* anItem ) +{ + if ( mCurrentTune ) { + mCurrentTune->fadeAndDelete(3.00); + mCurrentTune = 0L; + } + idleTask(); + if ( anItem ) + { + if ( mEnabled && ( mMusicGain > 0 ) ) { + mCurrentTune = anItem; + mCurrentTune->setGain( 0 ); + mCurrentTune->playLoop(); + mCurrentTune->fadeToIn( mGain * mMusicGain, 3.00 ); + } + } +} + +void CSoundManager::setAmbientItem( ISoundItem* anItem ) +{ + if ( mCurrentEnvirons ) { + mCurrentEnvirons->fadeAndDelete(3.00); + mCurrentEnvirons = 0L; + } + idleTask(); + + if ( anItem ) + { + if ( mEnabled && ( mAmbientGain > 0 ) ) { + mCurrentEnvirons = anItem; + mCurrentEnvirons->setGain( 0 ); + mCurrentEnvirons->playLoop(); + mCurrentEnvirons->fadeToIn( mGain * mAmbientGain, 3.00 ); + } + } +} + Index: source/soundmanager/data/COggData.h =================================================================== --- source/soundmanager/data/COggData.h (revision 0) +++ source/soundmanager/data/COggData.h (revision 0) @@ -0,0 +1,58 @@ +/* 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 SoundTester_COggData_h +#define SoundTester_COggData_h + +#include "CSoundData.h" +#include "vorbis/vorbisfile.h" + +class COggData : public CSoundData +{ + ALuint mFormat; + long mFrequency; + +public: + COggData (); + virtual ~COggData (); + + virtual bool InitOggFile( const wchar_t* fileLoc ); + virtual bool isFileFinished(); + virtual bool isOneShot(); + + virtual int fetchDataIntoBuffer( int count, ALuint* buffers); + virtual void resetFile(); + +protected: + OggVorbis_File m_vf; + int m_current_section; + bool mFileFinished; + bool mOneShot; + ALuint mBuffer[100]; + int mBuffersUsed; + + bool addDataBuffer( char* data, long length); + void setFormatAndFreq( int form, ALsizei freq); + ALsizei getBufferCount(); + ALuint getBuffer(); + ALuint* getBufferPtr(); +}; + + + +#endif Index: source/soundmanager/data/CSoundData.cpp =================================================================== --- source/soundmanager/data/CSoundData.cpp (revision 0) +++ source/soundmanager/data/CSoundData.cpp (revision 0) @@ -0,0 +1,146 @@ +/* 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 . + */ + + +#import "CSoundData.h" + + +#include +#include "COggData.h" + +DataMap* CSoundData::sSoundData = NULL; + +CSoundData::CSoundData() +{ + initProperties(); +} + +CSoundData::~CSoundData() +{ + if ( mALBuffer != 0 ) + alDeleteBuffers( 1, &mALBuffer ); +} + +void CSoundData::initProperties() +{ + mALBuffer = 0; + mRetentionCount = 0; +} + +void CSoundData::releaseSoundData( CSoundData* theData ) +{ + DataMap::iterator itemFind; + + if ( theData->decrementCount() ) { + if ( ( itemFind = CSoundData::sSoundData->find( theData->getFileName() ) ) != sSoundData->end() ) + { + CSoundData* dier = itemFind->second; + CSoundData::sSoundData->erase( itemFind ); + delete dier; + } + } +} + +CSoundData* CSoundData::soundDataFromFile( OsPath& itemPath ) +{ + if ( CSoundData::sSoundData == NULL ) + CSoundData::sSoundData = new DataMap; + + Path fExt = itemPath.Extension(); + DataMap::iterator itemFind; + CSoundData* answer; + + + debug_printf(L"creating data at: %ls\n\n", itemPath.string().c_str()); + + if ( ( itemFind = CSoundData::sSoundData->find( itemPath.string() ) ) != sSoundData->end() ) + { + debug_printf(L"data found in cache at: %ls\n\n", itemPath.string().c_str()); + answer = itemFind->second; + } + else + { + if ( fExt == ".ogg" ) + answer = soundDataFromOgg( itemPath ); +// else if ( fExt == ".wav" ) +// answer = soundDataFromWAV( itemPath ); + + if ( answer && answer->isOneShot() ) + (*CSoundData::sSoundData)[itemPath.string()] = answer; + + } + return answer; +} + +bool CSoundData::isOneShot() +{ + return true; +} + + +CSoundData* CSoundData::soundDataFromOgg(OsPath& itemPath ) +{ + CSoundData* answer = NULL; + COggData* oggAnswer = new COggData(); + if ( oggAnswer->InitOggFile( itemPath.string().c_str() ) ) { + answer = oggAnswer; + } + + return answer; +} + + +ALsizei CSoundData::getBufferCount() +{ + return 1; +} + +std::wstring CSoundData::getFileName() +{ + return mFileName; +} + + + + + + + + + +CSoundData* CSoundData::incrementCount() +{ + mRetentionCount++; + return this; +} + +bool CSoundData::decrementCount() +{ + mRetentionCount--; + + return ( mRetentionCount <= 0 ); +} + +ALuint CSoundData::getBuffer() +{ + return mALBuffer; +} +ALuint* CSoundData::getBufferPtr() +{ + return &mALBuffer; +} + Index: source/soundmanager/data/COggData.cpp =================================================================== --- source/soundmanager/data/COggData.cpp (revision 0) +++ source/soundmanager/data/COggData.cpp (revision 0) @@ -0,0 +1,161 @@ +/* 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 "COggData.h" + + +#include +#include +#include "soundmanager/CSoundManager.h" + +COggData::COggData() +{ + mOneShot = false; +} + +COggData::~COggData() +{ + alDeleteBuffers( mBuffersUsed, mBuffer ); + ov_clear(&m_vf); +} + +void COggData::setFormatAndFreq( int form, ALsizei freq) +{ + mFormat = form; + mFrequency = freq; +} + +bool COggData::InitOggFile( const wchar_t* fileLoc ) +{ + int buffersToStart = g_SoundManager->getBufferCount(); + +#ifdef _WIN32 + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); +#endif + +// fprintf(stderr, "ready to open ogg file at:%ls \r\r", fileLoc); + + char nameH[300]; + sprintf( nameH, "%ls", fileLoc ); + + FILE* f = fopen( nameH, "rb"); + m_current_section = 0; + int err = ov_open_callbacks(f, &m_vf, NULL, 0, OV_CALLBACKS_DEFAULT); + if ( err < 0) { + fprintf(stderr,"Input does not appear to be an Ogg bitstream :%d :%d.\n", err, ferror(f) ); + return false; + } + + mFileName = std::wstring(fileLoc); + + mFileFinished = false; + setFormatAndFreq( (m_vf.vi->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16 , (ALsizei)m_vf.vi->rate ); + + alGetError(); /* clear error */ + alGenBuffers( buffersToStart, mBuffer); + + if(alGetError() != AL_NO_ERROR) + { + printf("- Error creating initial buffer !!\n"); + return false; + } + else + { + mBuffersUsed = fetchDataIntoBuffer( buffersToStart, mBuffer); + if ( mFileFinished ) { + mOneShot = true; + if ( mBuffersUsed < buffersToStart ) { + debug_printf(L"one shot gave back %d buffers\n\n", buffersToStart - mBuffersUsed ); + alDeleteBuffers( buffersToStart - mBuffersUsed, &mBuffer[mBuffersUsed] ); + } + } + } + return true; +} + +ALsizei COggData::getBufferCount() +{ + return mBuffersUsed; +} + +bool COggData::isFileFinished() +{ + return mFileFinished; +} + +void COggData::resetFile() +{ + ov_time_seek( &m_vf, 0 ); + m_current_section = 0; + mFileFinished = false; +} + +bool COggData::isOneShot() +{ + return mOneShot; +} + +int COggData::fetchDataIntoBuffer( int count, ALuint* buffers) +{ + long bufferSize = g_SoundManager->getBufferSize(); + + char pcmout[bufferSize + 5000]; + int buffersWritten = 0; + + for(int i = 0; ( i < count ) && !mFileFinished; i++) { + char* readDest = pcmout; + long totalRet = 0; + while (totalRet < bufferSize ) + { + long ret=ov_read(&m_vf,readDest, 4096,0,2,1, &m_current_section); + if (ret == 0) { + mFileFinished=true; + break; + } else if (ret < 0) { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } else { + totalRet += ret; + readDest += ret; + } + } + if ( totalRet > 0 ) + { + buffersWritten++; + alBufferData( buffers[i], mFormat, pcmout, (ALsizei)totalRet, (int)mFrequency); + } + } + + return buffersWritten; +} + + +ALuint COggData::getBuffer() +{ + return mBuffer[0]; +} +ALuint* COggData::getBufferPtr() +{ + return mBuffer; +} + + + + + Index: source/soundmanager/data/CSoundData.h =================================================================== --- source/soundmanager/data/CSoundData.h (revision 0) +++ source/soundmanager/data/CSoundData.h (revision 0) @@ -0,0 +1,72 @@ +/* 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 SoundTester_CSoundData_h +#define SoundTester_CSoundData_h +#include "lib/os_path.h" + +#include "lib/external_libraries/openal.h" +#include "string" +#include "map" + +class CSoundData; +typedef std::map DataMap; + + + +class CSoundData +{ +public: + static CSoundData* soundDataFromFile( OsPath& itemPath ); + static CSoundData* soundDataFromOgg( OsPath& itemPath ); +// static CSoundData* soundDataFromWAV( OsPath& itemPath ); + + static void releaseSoundData( CSoundData* theData ); + + CSoundData (); + CSoundData (ALuint dataSource); + virtual ~CSoundData (); + + CSoundData* incrementCount(); + bool decrementCount(); + void initProperties(); + virtual bool isOneShot(); + + + virtual ALuint getBuffer(); + virtual ALsizei getBufferCount(); + std::wstring getFileName(); + virtual ALuint* getBufferPtr(); + +protected: + static DataMap* sSoundData; + + ALuint mALBuffer; + int mRetentionCount; + std::wstring mFileName; + + + +}; + + + + + + +#endif Index: source/soundmanager/CSoundManager.h =================================================================== --- source/soundmanager/CSoundManager.h (revision 0) +++ source/soundmanager/CSoundManager.h (revision 0) @@ -0,0 +1,99 @@ +/* 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 SoundTester_CSoundManager_h +#define SoundTester_CSoundManager_h + +#include "vector" +#include "map" +#include "lib/external_libraries/openal.h" + +#include "soundmanager/items/ISoundItem.h" + +typedef std::vector ItemsList; + + +class CSoundManager +{ +protected: + + ALuint mALEnvironment; + ALCcontext* mContext; + ALCdevice* mDevice; + ISoundItem* mCurrentTune; + ISoundItem* mCurrentEnvirons; + ItemsList* mItems; + OsPath mResourcePath; + float mGain; + float mMusicGain; + float mAmbientGain; + float mActionGain; + bool mEnabled; + long mBufferSize; + int mBufferCount; + +public: + CSoundManager (OsPath& resourcePath); + virtual ~CSoundManager (); + + ISoundItem* loadItem( OsPath* itemPath ); + + static void ScriptingInit(); + + + + ISoundItem* itemFromWAV ( OsPath& fname); + ISoundItem* itemFromOgg ( OsPath& fname); + + ISoundItem* getSoundItem ( unsigned long itemRow ); + unsigned long count (); + void idleTask (); + void deleteItem ( long itemNum ); + + 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); + + void setEnabled( bool doEnable ); +protected: + void InitListener(); + virtual Status alc_init(); + +}; + + + +extern CSoundManager* g_SoundManager; + + + + + + + +#endif Index: source/soundmanager/js/JAmbientSound.cpp =================================================================== --- source/soundmanager/js/JAmbientSound.cpp (revision 0) +++ source/soundmanager/js/JAmbientSound.cpp (revision 0) @@ -0,0 +1,99 @@ +/* Copyright (C) 2009 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 "JAmbientSound.h" +#include "maths/Vector3D.h" + +#include "lib/utf8.h" +#include "lib/res/sound/snd_mgr.h" +#include "lib/res/h_mgr.h" // h_filename +#include "ps/Filesystem.h" + +#include "soundmanager/CSoundManager.h" + +JAmbientSound::JAmbientSound(const VfsPath& pathname) +{ + mFileName = new OsPath( pathname.string().c_str() ); +} + +JAmbientSound::~JAmbientSound() +{ +} + + +// start playing the sound, all ambient sounds loop +bool JAmbientSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + ISoundItem* aSnd = g_SoundManager->loadItem( mFileName ); + + aSnd->playAsAmbient(); + + return true; +} + +// start playing the sound, all ambient sounds loop +bool JAmbientSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + ISoundItem* aSnd = g_SoundManager->loadItem( mFileName ); + + aSnd->playAsAmbient(); + return true; +} +bool JAmbientSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + g_SoundManager->setAmbientItem( 0L ); + + return true; +} + +// Script-bound functions + + +void JAmbientSound::ScriptingInit() +{ + AddMethod("toString", 0); + AddMethod("play", 0); + AddMethod("loop", 0); + AddMethod("free", 0); + + CJSObject::ScriptingInit("AmbientSound", &JAmbientSound::Construct, 1); +} + +CStr JAmbientSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + std::ostringstream stringStream; + stringStream << "[object AmbientSound: "; + stringStream << mFileName->string().c_str(); + + return stringStream.str(); +} + +JSBool JAmbientSound::Construct(JSContext* cx, uintN argc, jsval* vp) +{ +// JSU_REQUIRE_MIN_PARAMS(1); + + CStrW filename; + if (! ToPrimitive(cx, JS_ARGV(cx, vp)[0], filename)) + return JS_FALSE; + + JAmbientSound* newObject = new JAmbientSound(filename); + newObject->m_EngineOwned = false; + + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); + + return JS_TRUE; +} Index: source/soundmanager/js/JSound.cpp =================================================================== --- source/soundmanager/js/JSound.cpp (revision 0) +++ source/soundmanager/js/JSound.cpp (revision 0) @@ -0,0 +1,171 @@ +/* Copyright (C) 2009 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 "JSound.h" +#include "maths/Vector3D.h" + +#include "lib/utf8.h" +#include "ps/Filesystem.h" + +#include "soundmanager/CSoundManager.h" + + +JSound::JSound(const VfsPath& pathname) +{ + OsPath itsPth = OsPath( pathname.string().c_str() ); + mSndItem = g_SoundManager->loadItem( &itsPth ); +} + +JSound::~JSound() +{ + if ( mSndItem ) { + mSndItem->fadeAndDelete(0.2); + mSndItem = 0; + } +} + +bool JSound::clearSoundItem() +{ + mSndItem = 0L; +} + +bool JSound::SetGain(JSContext* cx, uintN UNUSED(argc), jsval* argv) +{ + if (! mSndItem ) + return false; + + float gain; + if (! ToPrimitive(cx, argv[0], gain)) + return false; + + mSndItem->setGain( gain ); + return true; +} + +bool JSound::SetPitch(JSContext* cx, uintN UNUSED(argc), jsval* argv) +{ + if (! mSndItem ) + return false; + + float pitch; + if (! ToPrimitive(cx, argv[0], pitch)) + return false; + + mSndItem->setPitch( pitch ); + return true; +} + +bool JSound::SetPosition(JSContext* cx, uintN argc, jsval* argv) +{ + if (! mSndItem ) + return false; + + ENSURE(argc >= 1); // FIXME + + CVector3D pos; + // absolute world coords + if (!ToPrimitive(cx, argv[0], pos)) + return false; + + mSndItem->setLocation( pos ); + + return true; +} + + +bool JSound::Fade(JSContext* cx, uintN argc, jsval* argv) +{ + if (! mSndItem ) + return false; + +// ENSURE(argc >= 3); // FIXME + float initial_gain, final_gain; + float length; + if (! (ToPrimitive(cx, argv[0], initial_gain) + && ToPrimitive(cx, argv[1], final_gain) + && ToPrimitive(cx, argv[2], length))) + return false; + + mSndItem->setGain( initial_gain ); + mSndItem->fadeToIn( final_gain, length ); + + return true; +} + +bool JSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + if (! mSndItem ) + return false; + + mSndItem->play(); + + return true; +} + +bool JSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + if (! mSndItem ) + return false; + + mSndItem->playLoop(); + + return true; +} + +bool JSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + if ( mSndItem ) { + mSndItem->fadeAndDelete(0.2); + mSndItem = 0; + } + + return true; +} + +void JSound::ScriptingInit() +{ + AddMethod("toString", 0); + AddMethod("play", 0); + AddMethod("loop", 0); + AddMethod("free", 0); + AddMethod("setGain", 0); + AddMethod("setPitch", 0); + AddMethod("setPosition", 0); + AddMethod("fade", 0); + + CJSObject::ScriptingInit("Sound", &JSound::Construct, 1); +} + +CStr JSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + return "[object Sound: " + ( mSndItem ? mSndItem->getName() : "(null)" ) + "]"; +} + +JSBool JSound::Construct(JSContext* cx, uintN argc, jsval* vp) +{ +// JSU_REQUIRE_MIN_PARAMS(1); + + CStrW filename; + if (! ToPrimitive(cx, JS_ARGV(cx, vp)[0], filename)) + return JS_FALSE; + + JSound* newObject = new JSound(filename); + newObject->m_EngineOwned = false; + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); + + return JS_TRUE; +} Index: source/soundmanager/js/JMusicSound.cpp =================================================================== --- source/soundmanager/js/JMusicSound.cpp (revision 0) +++ source/soundmanager/js/JMusicSound.cpp (revision 0) @@ -0,0 +1,85 @@ +/* Copyright (C) 2009 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 "JMusicSound.h" +#include "maths/Vector3D.h" + +#include "lib/utf8.h" +#include "ps/Filesystem.h" + +#include "soundmanager/CSoundManager.h" + + +JMusicSound::JMusicSound(const VfsPath& pathname) +{ + mFileName = new OsPath( pathname.string().c_str() ); +} + +JMusicSound::~JMusicSound() +{ +// disposing of music now handled by SoundManager +} + +bool JMusicSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + ISoundItem* aSnd = g_SoundManager->loadItem( mFileName ); + aSnd->playAsMusic(); + + return true; +} + +// request the sound be played until free() is called. returns immediately. +bool JMusicSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + ISoundItem* aSnd = g_SoundManager->loadItem( mFileName ); + aSnd->playAsMusic(); + + return true; +} + +void JMusicSound::ScriptingInit() +{ + AddMethod("toString", 0); + AddMethod("play", 0); + AddMethod("loop", 0); + + CJSObject::ScriptingInit("MusicSound", &JMusicSound::Construct, 1); +} + +CStr JMusicSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) +{ + std::ostringstream stringStream; + stringStream << "[object MusicSound: "; + stringStream << mFileName->string().c_str(); + + return stringStream.str(); +} + +JSBool JMusicSound::Construct(JSContext* cx, uintN argc, jsval* vp) +{ +// JSU_REQUIRE_MIN_PARAMS(1); + + CStrW filename; + if (! ToPrimitive(cx, JS_ARGV(cx, vp)[0], filename)) + return JS_FALSE; + + JMusicSound* newObject = new JMusicSound(filename); + newObject->m_EngineOwned = false; + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); + + return JS_TRUE; +} Index: source/soundmanager/js/SMSoundGroup.cpp =================================================================== --- source/soundmanager/js/SMSoundGroup.cpp (revision 0) +++ source/soundmanager/js/SMSoundGroup.cpp (revision 0) @@ -0,0 +1,295 @@ +/* Copyright (C) 2010 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 . + */ + +/** +* ========================================================================= +* File : SoundGroup.cpp +* Project : 0 A.D. +* Description : Loads up a group of sound files with shared properties, +* and provides a simple interface for playing them. +* ========================================================================= +*/ + +#include "precompiled.h" +#include "soundmanager/CSoundManager.h" +#include "SMSoundGroup.h" + +#include + +#include "lib/rand.h" + +#include "ps/XML/Xeromyces.h" +#include "ps/CLogger.h" +#include "ps/Filesystem.h" +#include "ps/Util.h" + + +static const bool DISABLE_INTENSITY = true; // disable for now since it's broken + +void CSMSoundGroup::SetGain(float gain) +{ + gain = std::min(gain, 1.0f); + m_Gain = gain; +} + +void CSMSoundGroup::SetDefaultValues() +{ + m_index = 0; + m_Flags = 0; + m_Intensity = 0; + m_CurTime = 0.0f; + + // sane defaults; will probably be replaced by the values read during LoadSoundGroup. + SetGain(0.7f); + m_Pitch = 1.0f; + m_Priority = 60; + m_PitchUpper = 1.1f; + m_PitchLower = 0.9f; + m_GainUpper = 1.0f; + m_GainLower = 0.8f; + m_ConeOuterGain = 0.0f; + m_ConeInnerAngle = 360.0f; + m_ConeOuterAngle = 360.0f; + m_Decay = 3.0f; + m_IntensityThreshold = 3; + // WARNING: m_TimeWindow is currently unused and uninitialized +} + +CSMSoundGroup::CSMSoundGroup() +{ + SetDefaultValues(); +} + +CSMSoundGroup::CSMSoundGroup(const VfsPath& pathnameXML) +{ + SetDefaultValues(); + LoadSoundGroup(pathnameXML); +} + +CSMSoundGroup::~CSMSoundGroup() +{ + // clean up all the handles from this group. + ReleaseGroup(); +} + +static float RandFloat(float min, float max) +{ + return float(rand(min*100.0f, max*100.0f) / 100.0f); +} + +void CSMSoundGroup::UploadPropertiesAndPlay(ISoundItem* hSound, const CVector3D& position) +{ + hSound->setLocation( position ); + + if( TestFlag(eRandPitch) ) + hSound->setPitch( RandFloat( m_PitchLower, m_PitchUpper ) ); + else + hSound->setPitch( m_Pitch ); + + ALfloat theGain = m_Gain; + if( TestFlag(eRandGain) ) + theGain = RandFloat( m_GainLower, m_GainUpper); + + hSound->setCone( m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain); + + + g_SoundManager->playGroupItem( hSound, theGain ); +} + + +static void HandleError(const std::wstring& message, const VfsPath& pathname, Status err) +{ + if(err == ERR::AGAIN) + return; // open failed because sound is disabled (don't log this) + LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err)); +} + +void CSMSoundGroup::PlayNext(const CVector3D& position) +{ + // if no sounds, return + if (filenames.size() == 0) + return; + + m_index = (size_t)rand(0, (size_t)filenames.size()); + UploadPropertiesAndPlay( snd_group[m_index], position); +} + +void CSMSoundGroup::Reload() +{ + m_index = 0; // reset our index + + snd_group.clear(); + + for(size_t i = 0; i < filenames.size(); i++) + { + OsPath thePath = OsPath( m_filepath/filenames[i] ); + ISoundItem* temp = g_SoundManager->loadItem( &thePath ); + snd_group.push_back(temp); + } + + if(TestFlag(eRandOrder)) + random_shuffle(snd_group.begin(), snd_group.end()); +} + +void CSMSoundGroup::ReleaseGroup() +{ + + for(size_t i = 0; i < filenames.size(); i++) + { + snd_group[i]->fadeAndDelete(0.2); + } + snd_group.clear(); +} + +void CSMSoundGroup::Update(float TimeSinceLastFrame) +{ +} + +bool CSMSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML) +{ +// LOGERROR(L"loading new sound group '%ls'", pathnameXML.string().c_str()); + + CXeromyces XeroFile; + if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK) + return false; + + // Define elements used in XML file + #define EL(x) int el_##x = XeroFile.GetElementID(#x) + #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) + EL(soundgroup); + EL(gain); + EL(looping); + EL(omnipresent); + EL(pitch); + EL(priority); + EL(randorder); + EL(randgain); + EL(randpitch); + EL(conegain); + EL(coneinner); + EL(coneouter); + EL(sound); + EL(gainupper); + EL(gainlower); + EL(pitchupper); + EL(pitchlower); + EL(path); + EL(threshold); + EL(decay); + EL(replacement); + #undef AT + #undef EL + + XMBElement root = XeroFile.GetRoot(); + + if (root.GetNodeName() != el_soundgroup) + { + LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str()); + return false; + } + + XERO_ITER_EL(root, child) + { + + int child_name = child.GetNodeName(); + + if(child_name == el_gain) + { + SetGain(child.GetText().ToFloat()); + } + else if(child_name == el_looping) + { + if(child.GetText().ToInt() == 1) + SetFlag(eLoop); + } + else if(child_name == el_omnipresent) + { + if(child.GetText().ToInt() == 1) + SetFlag(eOmnipresent); + } + else if(child_name == el_pitch) + { + this->m_Pitch = child.GetText().ToFloat(); + } + else if(child_name == el_priority) + { + this->m_Priority = child.GetText().ToFloat(); + } + else if(child_name == el_randorder) + { + if(child.GetText().ToInt() == 1) + SetFlag(eRandOrder); + } + else if(child_name == el_randgain) + { + if(child.GetText().ToInt() == 1) + SetFlag(eRandGain); + } + else if(child_name == el_gainupper) + { + this->m_GainUpper = child.GetText().ToFloat(); + } + else if(child_name == el_gainlower) + { + this->m_GainLower = child.GetText().ToFloat(); + } + else if(child_name == el_randpitch) + { + if(child.GetText().ToInt() == 1) + SetFlag(eRandPitch); + } + else if(child_name == el_pitchupper) + { + this->m_PitchUpper = child.GetText().ToFloat(); + } + else if(child_name == el_pitchlower) + { + this->m_PitchLower = child.GetText().ToFloat(); + } + else if(child_name == el_conegain) + { + this->m_ConeOuterGain = child.GetText().ToFloat(); + } + else if(child_name == el_coneinner) + { + this->m_ConeInnerAngle = child.GetText().ToFloat(); + } + else if(child_name == el_coneouter) + { + this->m_ConeOuterAngle = child.GetText().ToFloat(); + } + else if(child_name == el_sound) + { + this->filenames.push_back(child.GetText().FromUTF8()); + } + else if(child_name == el_path) + { + m_filepath = child.GetText().FromUTF8(); + } + else if(child_name == el_threshold) + { + m_IntensityThreshold = child.GetText().ToFloat(); + } + else if(child_name == el_decay) + { + m_Decay = child.GetText().ToFloat(); + } + } + + Reload(); + return true; +} Index: source/soundmanager/js/JAmbientSound.h =================================================================== --- source/soundmanager/js/JAmbientSound.h (revision 0) +++ source/soundmanager/js/JAmbientSound.h (revision 0) @@ -0,0 +1,51 @@ +/* Copyright (C) 2009 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_JAMBIENTSOUND +#define INCLUDED_JAMBIENTSOUND + +#include "scripting/ScriptableObject.h" +#include "soundmanager/items/ISoundItem.h" + +class JAmbientSound : public CJSObject +{ +public: + JAmbientSound (const VfsPath& pathname); + virtual ~JAmbientSound (); + + + CStr ToString(JSContext* cx, uintN argc, jsval* argv); + + bool Play(JSContext* cx, uintN argc, jsval* argv); + + bool Loop(JSContext* cx, uintN argc, jsval* argv); + bool Free(JSContext* cx, uintN argc, jsval* argv); + + bool SetGain(JSContext* cx, uintN argc, jsval* argv); + bool SetPitch(JSContext* cx, uintN argc, jsval* argv); + bool Fade(JSContext* cx, uintN argc, jsval* argv); + + static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); + void clearSoundItem(); + static void ScriptingInit(); +protected: + + OsPath* mFileName; + +}; + +#endif // #ifndef INCLUDED_JAMBIENTSOUND Index: source/soundmanager/js/JSound.h =================================================================== --- source/soundmanager/js/JSound.h (revision 0) +++ source/soundmanager/js/JSound.h (revision 0) @@ -0,0 +1,67 @@ +/* Copyright (C) 2009 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 . + */ + +// JS sound binding + +// interface rationale: +// - can't just expose fire and forget playSound to script code: +// we sometimes need to loop until a certain condition is met +// (e.g. building is complete) => need means of access (Handle) to sound. +// +// - the current 64-bit Handle can't be stored as-is by JS code; +// we could make it 32 bit, but that limits its usefulness +// (barely enough tag bits). +// +// - instead, we provide a thin class wrapper (using scriptableobject.h) +// on top of the snd API that encapsulates the Handle. + +#ifndef INCLUDED_JSOUND +#define INCLUDED_JSOUND + +#include "scripting/ScriptableObject.h" +#include "soundmanager/items/ISoundItem.h" + +class JSound : public CJSObject +{ +public: + + // note: filename is stored by handle manager; no need to keep a copy here. + + JSound(const VfsPath& pathname); + virtual ~JSound(); + + CStr ToString(JSContext* cx, uintN argc, jsval* argv); + + bool Play(JSContext* cx, uintN argc, jsval* argv); + bool Loop(JSContext* cx, uintN argc, jsval* argv); + + bool Free(JSContext* cx, uintN argc, jsval* argv); + bool SetGain(JSContext* cx, uintN argc, jsval* argv); + bool SetPitch(JSContext* cx, uintN argc, jsval* argv); + bool SetPosition(JSContext* cx, uintN argc, jsval* argv); + bool clearSoundItem(); + + bool Fade(JSContext* cx, uintN argc, jsval* argv); + + static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); + static void ScriptingInit(); + +protected: + ISoundItem* mSndItem; +}; + +#endif // #ifndef INCLUDED_JSOUND Index: source/soundmanager/js/JMusicSound.h =================================================================== --- source/soundmanager/js/JMusicSound.h (revision 0) +++ source/soundmanager/js/JMusicSound.h (revision 0) @@ -0,0 +1,59 @@ +/* Copyright (C) 2009 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 . + */ + +// JS sound binding + +// interface rationale: +// - can't just expose fire and forget playSound to script code: +// we sometimes need to loop until a certain condition is met +// (e.g. building is complete) => need means of access (Handle) to sound. +// +// - the current 64-bit Handle can't be stored as-is by JS code; +// we could make it 32 bit, but that limits its usefulness +// (barely enough tag bits). +// +// - instead, we provide a thin class wrapper (using scriptableobject.h) +// on top of the snd API that encapsulates the Handle. + +#ifndef INCLUDED_JMUSICSOUND +#define INCLUDED_JMUSICSOUND + +#include "scripting/ScriptableObject.h" +#include "soundmanager/items/ISoundItem.h" + +class JMusicSound : public CJSObject +{ +public: + JMusicSound(const VfsPath& pathname); + virtual ~JMusicSound(); + + // Script-bound functions + + CStr ToString(JSContext* cx, uintN argc, jsval* argv); + + bool Play(JSContext* cx, uintN argc, jsval* argv); + bool Loop(JSContext* cx, uintN argc, jsval* argv); + + static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); + + static void ScriptingInit(); + +protected: + OsPath* mFileName; +}; + +#endif // #ifndef INCLUDED_JMUSICSOUND Index: source/soundmanager/js/SMSoundGroup.h =================================================================== --- source/soundmanager/js/SMSoundGroup.h (revision 0) +++ source/soundmanager/js/SMSoundGroup.h (revision 0) @@ -0,0 +1,137 @@ +/* Copyright (C) 2009 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 . + */ + +/** +* ========================================================================= +* File : SoundGroup.h +* Project : 0 A.D. +* Description : Loads up a group of sound files with shared properties, +* and provides a simple interface for playing them. +* ========================================================================= +*/ + +/* +Example usage: + + +Example SoundGroup.xml + + + 1.0 + 0 + 1.0 + 100 + 0 + 0 + 0 + 1.0 + 360 + 360 + audio/voice/hellenes/soldier/Attack_Attackx.ogg + audio/voice/hellenes/soldier/Attack_Chargex.ogg + audio/voice/hellenes/soldier/Attack_Engagex.ogg + audio/voice/hellenes/soldier/Attack_ForMyFamily.ogg + + +*/ + +#ifndef INCLUDED_SMSOUNDGROUP +#define INCLUDED_SMSOUNDGROUP + +#include "lib/res/handle.h" +#include "lib/file/vfs/vfs_path.h" +#include "ps/CStr.h" +#include "maths/Vector3D.h" +#include "lib/res/sound/snd_mgr.h" +#include "soundmanager/items/ISoundItem.h" + +#include + +enum eSndGrpFlags +{ + eRandOrder = 0x01, + eRandGain = 0x02, + eRandPitch = 0x04, + eLoop = 0x08, + eOmnipresent = 0x10 +}; + + +class CSMSoundGroup +{ + NONCOPYABLE(CSMSoundGroup); +public: + CSMSoundGroup(const VfsPath& pathnameXML); + CSMSoundGroup(void); + ~CSMSoundGroup(void); + + // Play next sound in group + // @param position world position of the entity generating the sound + // (ignored if the eOmnipresent flag is set) + void PlayNext(const CVector3D& position); + + // Load a group + bool LoadSoundGroup(const VfsPath& pathnameXML); + + void Reload(); + + // Release all remaining loaded handles + void ReleaseGroup(); + + // Update SoundGroup, remove dead sounds from intensity count + void Update(float TimeSinceLastFrame); + + // Set a flag using a value from eSndGrpFlags + inline void SetFlag(int flag) { m_Flags = (unsigned char)(m_Flags | flag); } + + // Test flag, returns true if flag is set. + inline bool TestFlag(int flag) { return (m_Flags & flag) != 0; } + +private: + void SetGain(float gain); + void UploadPropertiesAndPlay(ISoundItem* hSound, const CVector3D& position); + void SetDefaultValues(); + + size_t m_index; // index of the next sound to play + + Handle m_hReplacement; + + std::vector snd_group; // we store the handles so we can load now and play later + std::vector filenames; // we need the filenames so we can reload when necessary. + + VfsPath m_filepath; // the file path for the list of sound file resources + + float m_CurTime; // Time elapsed since soundgroup was created + float m_TimeWindow; // The Intensity Threshold Window + size_t m_IntensityThreshold; // the allowable intensity before a sound switch + size_t m_Intensity; // our current intensity(number of sounds played since m_CurTime - m_TimeWindow) + float m_Decay; // + unsigned char m_Flags; // up to eight individual parameters, use with eSndGrpFlags. + + float m_Gain; + float m_Pitch; + float m_Priority; + float m_ConeOuterGain; + float m_PitchUpper; + float m_PitchLower; + float m_GainUpper; + float m_GainLower; + float m_ConeInnerAngle; + float m_ConeOuterAngle; +}; + +#endif //#ifndef INCLUDED_SOUNDGROUP Index: source/main.cpp =================================================================== --- source/main.cpp (revision 11718) +++ source/main.cpp (working copy) @@ -383,17 +383,17 @@ // coincide in position and orientation. float down[3] = { -up[0], -up[1], -up[2] }; - { - PROFILE3("sound update"); - if (snd_update(pos, dir, down) < 0) - debug_printf(L"snd_update failed\n"); - } +// { +// PROFILE3("sound update"); +// if (snd_update(pos, dir, down) < 0) +// debug_printf(L"snd_update failed\n"); +// } } else { - PROFILE3("sound update (0)"); - if (snd_update(0, 0, 0) < 0) - debug_printf(L"snd_update (pos=0 version) failed\n"); +// PROFILE3("sound update (0)"); +// if (snd_update(0, 0, 0) < 0) +// debug_printf(L"snd_update (pos=0 version) failed\n"); } // Immediately flush any messages produced by simulation code @@ -477,8 +477,6 @@ // run non-visual simulation replay if requested if (args.Has("replay")) { - snd_disable(true); - Paths paths(args); g_VFS = CreateVfs(20 * MiB); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); Index: source/simulation2/components/CCmpSoundManager.cpp =================================================================== --- source/simulation2/components/CCmpSoundManager.cpp (revision 11718) +++ source/simulation2/components/CCmpSoundManager.cpp (working copy) @@ -24,7 +24,7 @@ #include "simulation2/MessageTypes.h" #include "simulation2/components/ICmpPosition.h" #include "simulation2/components/ICmpRangeManager.h" -#include "sound/SoundGroup.h" +#include "soundmanager/js/SMSoundGroup.h" class CCmpSoundManager : public ICmpSoundManager { @@ -36,7 +36,7 @@ DEFAULT_COMPONENT_ALLOCATOR(SoundManager) - std::map m_SoundGroups; + std::map m_SoundGroups; static std::string GetSchema() { @@ -49,7 +49,7 @@ virtual void Deinit() { - for (std::map::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) + for (std::map::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) delete it->second; m_SoundGroups.clear(); } @@ -76,7 +76,7 @@ // or on some other timer? const CMessageUpdate& msgData = static_cast (msg); float t = msgData.turnLength.ToFloat(); - for (std::map::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) + for (std::map::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) if (it->second) it->second->Update(t); break; @@ -87,10 +87,10 @@ virtual void PlaySoundGroup(std::wstring name, entity_id_t source) { // Make sure the sound group is loaded - CSoundGroup* group; + CSMSoundGroup* group; if (m_SoundGroups.find(name) == m_SoundGroups.end()) { - group = new CSoundGroup(); + group = new CSMSoundGroup(); if (!group->LoadSoundGroup(L"audio/" + name)) { LOGERROR(L"Failed to load sound group '%ls'", name.c_str()); Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js (revision 11718) +++ binaries/data/mods/public/gui/session/session.js (working copy) @@ -408,12 +408,11 @@ // currentAmbient = newRandomSound("ambient", "temperate_", "dayscape"); const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg"; - currentAmbient = new Sound(AMBIENT); + currentAmbient = new AmbientSound(AMBIENT); if (currentAmbient) { currentAmbient.loop(); - currentAmbient.setGain(0.8); } break; @@ -428,7 +427,7 @@ { if (currentAmbient) { - currentAmbient.fade(-1, 0.0, 5.0); + currentAmbient.free(); currentAmbient = null; } } Index: binaries/data/mods/public/gui/common/music.js =================================================================== --- binaries/data/mods/public/gui/common/music.js (revision 11718) +++ binaries/data/mods/public/gui/common/music.js (working copy) @@ -72,11 +72,6 @@ switch (this.currentState) { case this.states.OFF: - if (this.isPlaying()) - { - this.currentMusic.fade(-1, 0.0, 3.0); - this.currentMusic = null; - } break; case this.states.MENU: @@ -146,23 +141,14 @@ Music.prototype.switchMusic = function(track, fadeInPeriod, isLooping) { - if (this.currentMusic) - { - this.currentMusic.fade(-1, 0.0, 5.0); - this.currentMusic = null; - } + this.currentMusic = new MusicSound(this.RELATIVE_MUSIC_PATH + track); - this.currentMusic = new Sound(this.RELATIVE_MUSIC_PATH + track); - if (this.currentMusic) { if (isLooping) this.currentMusic.loop(); else this.currentMusic.play(); - - if (fadeInPeriod) - this.currentMusic.fade(0.0, this.musicGain, fadeInPeriod); } }; Index: binaries/data/mods/public/gui/common/functions_utility_music.js =================================================================== --- binaries/data/mods/public/gui/common/functions_utility_music.js (revision 11718) +++ binaries/data/mods/public/gui/common/functions_utility_music.js (working copy) @@ -66,6 +66,20 @@ //console.write("Playing " + randomSoundPath + " ..."); + switch (soundType) + { + case "music": + return new MusicSound(randomSoundPath); + break; + case "ambient": + return new AmbientSound(randomSoundPath); + break; + case "effect": + console.write ("am loading effect '*"+randomSoundPath+"*'"); + break; + default: + break; + } return new Sound(randomSoundPath); } Index: binaries/data/mods/public/hwdetect/hwdetect.js =================================================================== --- binaries/data/mods/public/hwdetect/hwdetect.js (revision 11718) +++ binaries/data/mods/public/hwdetect/hwdetect.js (working copy) @@ -213,11 +213,11 @@ } // http://trac.wildfiregames.com/ticket/685 - if (os_macosx) - { - warnings.push("Audio has been disabled, due to problems with OpenAL on OS X."); - disable_audio = true; - } +// if (os_macosx) +// { +// warnings.push("Audio has been disabled, due to problems with OpenAL on OS X."); +// disable_audio = true; +// } // http://trac.wildfiregames.com/ticket/684 // https://bugs.freedesktop.org/show_bug.cgi?id=24047 Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg (revision 11718) +++ binaries/data/config/default.cfg (working copy) @@ -73,7 +73,12 @@ ; GENERAL PREFERENCES: -sound.mastergain = 0.5 +sound.mastergain = 0.9 +sound.musicgain = 0.1 +sound.ambientgain = 0.6 +sound.actiongain = 0.7 +sound.bufferCount = 50 +sound.bufferSize = 65536 ; Camera control settings view.scroll.speed = 120.0