From fab5ddfd74b0a2c0709f623b11b73a8f80db6a8f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 5 Apr 2012 17:45:53 +0200 Subject: [PATCH 01/77] Fixed: Compile errors under Linux. --- .../mission_compiler_main_window.cpp | 22 +++++++++---------- .../common/src/game_share/mirror_prop_value.h | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 416a418f1..df909f31a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -81,7 +81,7 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : connect(Core::ICore::instance(), SIGNAL(changeSettings()), this, SLOT(handleChangedSettings())); // Set the default data dir to the primitives path. - QSettings *settings = Core::ICore::instance()->settings(); + QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup(Core::Constants::DATA_PATH_SECTION); m_lastDir = settings->value(Core::Constants::PRIMITIVES_PATH).toString(); ui->dataDirEdit->setText(m_lastDir); @@ -381,7 +381,7 @@ bool MissionCompilerMainWindow::parsePrimForMissions(NLLIGO::IPrimitive const *p { std::string value; // if the node is a mission parse it - if (prim->getPropertyByName("class",value) && !stricmp(value.c_str(),"mission") ) + if (prim->getPropertyByName("class",value) && !NLMISC::stricmp(value.c_str(),"mission") ) { std::string name; prim->getPropertyByName("name",name); @@ -460,20 +460,20 @@ void MissionCompilerMainWindow::loadConfig() { } void MissionCompilerMainWindow::saveConfig() { - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(MISSION_COMPILER_SECTION); - + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(MISSION_COMPILER_SECTION); + QStringList servers; for(int row = 0; row < ui->publishServersList->count(); row++) { QListWidgetItem *item = ui->publishServersList->item(row); if(item->checkState() == Qt::Checked) servers << item->text(); - } - - settings->setValue(SETTING_PUBLISH_SERVER_CHECKS, servers); - - settings->endGroup(); + } + + settings->setValue(SETTING_PUBLISH_SERVER_CHECKS, servers); + + settings->endGroup(); settings->sync(); } diff --git a/code/ryzom/common/src/game_share/mirror_prop_value.h b/code/ryzom/common/src/game_share/mirror_prop_value.h index 08e96761d..1bb4c7571 100644 --- a/code/ryzom/common/src/game_share/mirror_prop_value.h +++ b/code/ryzom/common/src/game_share/mirror_prop_value.h @@ -22,7 +22,7 @@ #include "nel/misc/types_nl.h" #include "nel/misc/stream.h" #include "mirrored_data_set.h" - +#include /** * Read-only handler of a value of a property. @@ -1318,4 +1318,4 @@ protected: #endif // NL_MIRROR_PROP_VALUE_H -/* End of mirror_prop_value.h */ \ No newline at end of file +/* End of mirror_prop_value.h */ From b1bfd3da010fd0df8d2082899c40ab872b4a62fa Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 5 Apr 2012 19:30:41 +0200 Subject: [PATCH 02/77] Fixed: #1455 Bad changes in revision 48a37af6954c --- .../rfr_ryzom_file_retriever.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/code/ryzom/server/src/general_utilities_service/rfr_ryzom_file_retriever.cpp b/code/ryzom/server/src/general_utilities_service/rfr_ryzom_file_retriever.cpp index 6e0f4940f..014f679e4 100644 --- a/code/ryzom/server/src/general_utilities_service/rfr_ryzom_file_retriever.cpp +++ b/code/ryzom/server/src/general_utilities_service/rfr_ryzom_file_retriever.cpp @@ -472,9 +472,11 @@ bool CRyzomFileRetrieverImplementation::downloadBackupFiles(const CSString& shar continue; // generate a local filename for the downloaded file - CSString localFileName= NLMISC::CPath::standardizePath(localDirectory)+(fileName+"_"+fdc[i].FileName); - localFileName = localFileName.replace("/","_"); - localFileName = localFileName.replace(".","_"); + CSString fdcFileNameUnderscore = fdc[i].FileName; + fdcFileNameUnderscore = fdcFileNameUnderscore.replace("/","_"); + CSString localFileNameWithoutPath = fileName + "_" + fdcFileNameUnderscore; + localFileNameWithoutPath = localFileNameWithoutPath.replace(".","_"); + CSString localFileName = NLMISC::CPath::standardizePath(localDirectory) + localFileNameWithoutPath; nlinfo("Requesting file download: REMOTE:%s => LOCAL:%s",fdc[i].FileName.c_str(),localFileName.c_str()); // put in a request for the file to be retrieved From 425927cd506af7e0315aedf0815758ba7ec4d226 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 11:14:21 +0200 Subject: [PATCH 03/77] Changed: #1448 Compilation with CLang (patch provided by GelluleX, thanks !) --- code/CMakeModules/nel.cmake | 2 +- code/nel/include/nel/3d/computed_string.h | 6 +++++- code/nel/include/nel/3d/cube_grid.h | 2 +- code/nel/include/nel/3d/ps_attrib.h | 2 +- code/nel/include/nel/3d/static_quad_grid.h | 2 +- code/nel/include/nel/3d/track_tcb.h | 8 ++++---- code/nel/include/nel/misc/diff_tool.h | 4 ++-- code/ryzom/client/src/cdb.h | 10 +++++----- code/ryzom/client/src/r2/config_var.h | 16 ++++++++++++++++ .../src/game_share/mirror_prop_value_inline.h | 2 +- 10 files changed, 37 insertions(+), 17 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index bd92e743e..a02785a24 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -463,7 +463,7 @@ MACRO(NL_SETUP_BUILD) ENDIF(NOT APPLE) SET(NL_DEBUG_CFLAGS "-DNL_DEBUG -D_DEBUG") - SET(NL_RELEASE_CFLAGS "-DNL_RELEASE -DNDEBUG -O6") + SET(NL_RELEASE_CFLAGS "-DNL_RELEASE -DNDEBUG -O3") ENDIF(MSVC) ENDMACRO(NL_SETUP_BUILD) diff --git a/code/nel/include/nel/3d/computed_string.h b/code/nel/include/nel/3d/computed_string.h index 16fe70fcb..914fa324e 100644 --- a/code/nel/include/nel/3d/computed_string.h +++ b/code/nel/include/nel/3d/computed_string.h @@ -27,12 +27,16 @@ #include #include +namespace NLMISC { + +class CMatrix; + +} namespace NL3D { class CTextureFont; -class CMatrix; struct CComputedString; // *************************************************************************** diff --git a/code/nel/include/nel/3d/cube_grid.h b/code/nel/include/nel/3d/cube_grid.h index 427aed624..483ea8000 100644 --- a/code/nel/include/nel/3d/cube_grid.h +++ b/code/nel/include/nel/3d/cube_grid.h @@ -227,7 +227,7 @@ void CCubeGrid::compile() // build the _StaticGrid _StaticGrids[i].build(_Grids[i]); // And reset the grid. contReset is necessary to clean the CBlockMemory. - contReset(_Grids[i]); + NLMISC::contReset(_Grids[i]); } // done diff --git a/code/nel/include/nel/3d/ps_attrib.h b/code/nel/include/nel/3d/ps_attrib.h index fc17dc787..83549a7b0 100644 --- a/code/nel/include/nel/3d/ps_attrib.h +++ b/code/nel/include/nel/3d/ps_attrib.h @@ -96,7 +96,7 @@ public: try { newStart = new uint8[sizeof(T) * capacity + (1 << snapPower)]; - T *newTab = (T *) ( (uint) (newStart + (1 << snapPower)) & ~((1 << snapPower) - 1)); // snap to a page + T *newTab = (T *) ( (size_t) (newStart + (1 << snapPower)) & ~((1 << snapPower) - 1)); // snap to a page diff --git a/code/nel/include/nel/3d/static_quad_grid.h b/code/nel/include/nel/3d/static_quad_grid.h index 7890e12f4..5a0383c9e 100644 --- a/code/nel/include/nel/3d/static_quad_grid.h +++ b/code/nel/include/nel/3d/static_quad_grid.h @@ -172,7 +172,7 @@ template void CStaticQuadGrid::build(CQuadGrid &quadGrid) { clear(); - contReset(_Grid); + NLMISC::contReset(_Grid); // Copy from quadGrid, and init quads _Size= quadGrid.getSize(); diff --git a/code/nel/include/nel/3d/track_tcb.h b/code/nel/include/nel/3d/track_tcb.h index 8daf5cc2a..fc9c227e4 100644 --- a/code/nel/include/nel/3d/track_tcb.h +++ b/code/nel/include/nel/3d/track_tcb.h @@ -218,7 +218,7 @@ protected: date*= previous->OODeltaTime; NLMISC::clamp(date, 0,1); - date = ease(previous, date); + date = this->ease(previous, date); float hb[4]; this->computeHermiteBasis(date, hb); @@ -242,7 +242,7 @@ protected: ITrackKeyFramer::compile(); // Ease Precompute. - compileTCBEase(this->_MapKey, this->getLoopMode()); + this->compileTCBEase(this->_MapKey, this->getLoopMode()); // Tangents Precompute. @@ -314,7 +314,7 @@ private: float ksm,ksp,kdm,kdp; // compute tangents factors. - computeTCBFactors(key, timeBefore, time, timeAfter, rangeDelta, firstKey, endKey, isLoop, ksm,ksp,kdm,kdp); + this->computeTCBFactors(key, timeBefore, time, timeAfter, rangeDelta, firstKey, endKey, isLoop, ksm,ksp,kdm,kdp); // Delta. TKeyValueType delm, delp; @@ -413,7 +413,7 @@ public: ITrackKeyFramer::compile(); // Ease Precompute. - compileTCBEase(_MapKey, getLoopMode()); + this->compileTCBEase(_MapKey, getLoopMode()); TMapTimeCKey::iterator it; TMapTimeCKey::iterator itNext; diff --git a/code/nel/include/nel/misc/diff_tool.h b/code/nel/include/nel/misc/diff_tool.h index 64989fcaf..2f291c6a0 100644 --- a/code/nel/include/nel/misc/diff_tool.h +++ b/code/nel/include/nel/misc/diff_tool.h @@ -501,8 +501,8 @@ namespace STRING_MANAGER // callback->onSwap(it - context.Reference.begin(), refCount, context); callback->onSwap(index, refCount, context); -// swap(*it, context.Reference[refCount]); - swap(context.Reference[index], context.Reference[refCount]); +// std::swap(*it, context.Reference[refCount]); + std::swap(context.Reference[index], context.Reference[refCount]); } } else if (getHashValue(context.Addition, addCount) != getHashValue(context.Reference, refCount)) diff --git a/code/ryzom/client/src/cdb.h b/code/ryzom/client/src/cdb.h index 24c2dd5eb..f8ad3148a 100644 --- a/code/ryzom/client/src/cdb.h +++ b/code/ryzom/client/src/cdb.h @@ -302,10 +302,10 @@ public : virtual CCDBNodeLeaf *findLeafAtCount( uint& count ) = 0; /// Set the atomic branch flag (when all the modified nodes of a branch should be tranmitted at the same time) - void setAtomic( bool atomicBranch ) { _Atomic = atomicBranch; } + void setAtomic( bool atomicBranch ) { _AtomicFlag = atomicBranch; } /// Return true if the branch has the atomic flag - bool isAtomic() const { return _Atomic; } + bool isAtomic() const { return _AtomicFlag; } // test if the node is a leaf virtual bool isLeaf() const = 0; @@ -333,14 +333,14 @@ public : protected: /// Constructor - ICDBNode() : _Atomic(false) + ICDBNode() : _AtomicFlag(false) { if (_DBSM == NULL) _DBSM = NLMISC::CStringMapper::createLocalMapper(); _Name = NLMISC::CStringMapper::emptyId(); } /// Constructor - ICDBNode (const std::string &name) : _Atomic(false) + ICDBNode (const std::string &name) : _AtomicFlag(false) { if (_DBSM == NULL) _DBSM = NLMISC::CStringMapper::createLocalMapper(); _Name = _DBSM->localMap(name); @@ -351,7 +351,7 @@ protected: void _buildFullName(NLMISC::CSString &fullName); /// Atomic flag: is the branch an atomic group, or is the leaf a member of an atomic group - bool _Atomic : 1; + bool _AtomicFlag : 1; /// Name of the node NLMISC::TStringId _Name; diff --git a/code/ryzom/client/src/r2/config_var.h b/code/ryzom/client/src/r2/config_var.h index 84c9f3891..674c38816 100644 --- a/code/ryzom/client/src/r2/config_var.h +++ b/code/ryzom/client/src/r2/config_var.h @@ -66,6 +66,22 @@ private: const T _Default; }; +// forward declarations for specialisations +std::string getConfigVarTypename(const float &dummy); +bool getConfigVarValue(CLuaObject &luaValue, float &dest); + +std::string getConfigVarTypename(const double &dummy); +bool getConfigVarValue(CLuaObject &luaValue, double &dest); + +std::string getConfigVarTypename(const sint32 &dummy); +bool getConfigVarValue(CLuaObject &luaValue, sint32 &dest); + +std::string getConfigVarTypename(const std::string &dummy); +bool getConfigVarValue(CLuaObject &luaValue, std::string &dest); + +std::string getConfigVarTypename(const NLMISC::CRGBA &dummy); +bool getConfigVarValue(CLuaObject &luaValue, NLMISC::CRGBA &dest); + template const T &CConfigVar::get() const { // Relies on R2 'getConfigVarValue' and 'getConfigVarTypename' functions specialization to retrieve the value (see below) diff --git a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h index cd4c71f4a..83bab3585 100644 --- a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h +++ b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h @@ -1005,7 +1005,7 @@ NLNET::TServiceId8 CMirrorPropValueAlice::getWriterSer if ( _InMirror ) return CMirrorPropValue::getWriterServiceId(); else - return ~0; + return NLNET::TServiceId8(std::numeric_limits::max()); } #endif From d274c3d42e1d0c2f5f1d7790e7cedde3bc16dd23 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 11:29:45 +0200 Subject: [PATCH 04/77] Changed: #1448 Compilation with CLang --- code/nel/include/nel/misc/bit_mem_stream.h | 14 ++++++++++++++ code/nel/include/nel/misc/mutex.h | 4 ++-- code/nel/include/nel/misc/stl_block_allocator.h | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/misc/bit_mem_stream.h b/code/nel/include/nel/misc/bit_mem_stream.h index 8a1af34cc..f9572084d 100644 --- a/code/nel/include/nel/misc/bit_mem_stream.h +++ b/code/nel/include/nel/misc/bit_mem_stream.h @@ -209,6 +209,8 @@ public: { #ifdef NL_DEBUG std::swap(_DbgData, other._DbgData); +#else + nlunreferenced(other); #endif } @@ -225,6 +227,10 @@ public: TBMSSerialInfo serialItem( bitpos, size, type, _DbgData->NextSymbol ); _DbgData->List.push_back( serialItem ); _DbgData->NextSymbol = NULL; +#else + nlunreferenced(bitpos); + nlunreferenced(size); + nlunreferenced(type); #endif } @@ -258,6 +264,10 @@ public: nlwarning( "Missing reserve() corresponding to poke()" ); } _DbgData->NextSymbol = NULL; +#else + nlunreferenced(bitpos); + nlunreferenced(size); + nlunreferenced(type); #endif } @@ -266,6 +276,8 @@ public: { #ifdef NL_DEBUG _DbgData->NextSymbol = symbol; +#else + nlunreferenced(symbol); #endif } @@ -308,6 +320,8 @@ public: } //nlassert( bitpos < (*_List)[_CurrentBrowsedItem].BitPos ); // occurs if stream overflow } +#else + nlunreferenced(bitpos); #endif *eventId = -1; return std::string(); diff --git a/code/nel/include/nel/misc/mutex.h b/code/nel/include/nel/misc/mutex.h index e2205d666..1c01a6134 100644 --- a/code/nel/include/nel/misc/mutex.h +++ b/code/nel/include/nel/misc/mutex.h @@ -717,11 +717,11 @@ class CAutoMutex TMutex &_Mutex; // forbeden copy or assignent - CAutoMutex(const CAutoMutex &other) + CAutoMutex(const CAutoMutex &/* other */) { } - CAutoMutex &operator = (const CAutoMutex &other) + CAutoMutex &operator = (const CAutoMutex &/* other */) { return *this; } diff --git a/code/nel/include/nel/misc/stl_block_allocator.h b/code/nel/include/nel/misc/stl_block_allocator.h index 97486c20c..d85044e58 100644 --- a/code/nel/include/nel/misc/stl_block_allocator.h +++ b/code/nel/include/nel/misc/stl_block_allocator.h @@ -61,7 +61,7 @@ namespace NLMISC { { public: /// Constructor. Must gives a blockMemory to ctor. NB: must gives a CBlockMemory !!! - CSTLBlockAllocator(CBlockMemory *bm) + CSTLBlockAllocator(CBlockMemory */* bm */) { } /// copy ctor From 6c768a144a4666cabdc074cead4d6c1cb6d24d15 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 11:51:21 +0200 Subject: [PATCH 05/77] Changed: #1448 Compilation with CLang --- code/ryzom/common/src/game_share/mirror_prop_value_inline.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h index 83bab3585..dac2f55a6 100644 --- a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h +++ b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h @@ -937,7 +937,7 @@ CMirrorPropValueAlice& CMirrorPropValueAlicetempReassign( srcValue ); } return *this; } From 18ea4f8004fa071c08149706fa35e9308bcfae04 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 14:03:50 +0200 Subject: [PATCH 06/77] Changed: Check for Lua, Luabind, CURL and LibWWW only if compiling the client --- code/ryzom/CMakeLists.txt | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/code/ryzom/CMakeLists.txt b/code/ryzom/CMakeLists.txt index 4202738c1..eb3d1c642 100644 --- a/code/ryzom/CMakeLists.txt +++ b/code/ryzom/CMakeLists.txt @@ -1,18 +1,7 @@ #----------------------------------------------------------------------------- #Platform specifics -IF(WITH_LUA51) - FIND_PACKAGE(Lua51 REQUIRED) -ELSE(WITH_LUA51) - FIND_PACKAGE(Lua50 REQUIRED) -ENDIF(WITH_LUA51) -FIND_PACKAGE(Luabind REQUIRED) -FIND_PACKAGE(CURL REQUIRED) -FIND_PACKAGE(Libwww REQUIRED) FIND_PACKAGE(ZLIB) -IF(NOT WIN32 AND NOT APPLE) - FIND_PACKAGE(X11) -ENDIF(NOT WIN32 AND NOT APPLE) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/common/src ) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/common ) @@ -30,6 +19,15 @@ ENDIF(WITH_STATIC) ADD_SUBDIRECTORY(common) IF(WITH_RYZOM_CLIENT) + IF(WITH_LUA51) + FIND_PACKAGE(Lua51 REQUIRED) + ELSE(WITH_LUA51) + FIND_PACKAGE(Lua50 REQUIRED) + ENDIF(WITH_LUA51) + FIND_PACKAGE(Luabind REQUIRED) + FIND_PACKAGE(CURL REQUIRED) + FIND_PACKAGE(Libwww REQUIRED) + ADD_SUBDIRECTORY(client) ENDIF(WITH_RYZOM_CLIENT) From cdcdc05e882da0a2baa5ab009c78eab694bffd31 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 14:57:15 +0200 Subject: [PATCH 07/77] Fixed: #1448 Compilation with CLang --- code/ryzom/common/src/game_share/txt_command.h | 14 +++++++------- code/ryzom/server/src/ai_service/child_container.h | 4 ++-- code/ryzom/server/src/ai_service/commands.cpp | 2 +- .../ryzom/server/src/ai_service/family_profile.cpp | 5 +---- .../server/src/ai_service/script_compiler.cpp | 8 ++++---- .../src/entities_game_service/harvest_source.cpp | 13 ++++++++++++- .../src/entities_game_service/harvest_source.h | 4 ++-- .../phrase_manager/environmental_effect.cpp | 12 ++++++++++++ .../phrase_manager/environmental_effect.h | 4 ++-- .../src/entities_game_service/player_manager/cdb.h | 8 ++++---- .../player_manager/cdb_branch.cpp | 6 +++--- code/ryzom/server/src/pd_lib/pds_common.h | 2 +- 12 files changed, 51 insertions(+), 31 deletions(-) diff --git a/code/ryzom/common/src/game_share/txt_command.h b/code/ryzom/common/src/game_share/txt_command.h index f8d2ec39e..054f1bc93 100644 --- a/code/ryzom/common/src/game_share/txt_command.h +++ b/code/ryzom/common/src/game_share/txt_command.h @@ -50,21 +50,21 @@ static CTxtCommandSetPtr<__CTxtCommandSet_##setName> setName; //------------------------------------------------------------------------------------------------- #define TXT_COMMAND(cmdName,setName,CONTEXT_CLASS)\ -struct __CTxtCommand_##cmdName: public ITxtCommand\ +struct __CTxtCommand_##cmdName##CONTEXT_CLASS: public ITxtCommand\ {\ - static __CTxtCommand_##cmdName* getInstance()\ + static __CTxtCommand_##cmdName##CONTEXT_CLASS* getInstance()\ {\ - static __CTxtCommand_##cmdName *p=NULL;\ - if (p==NULL) p= new __CTxtCommand_##cmdName;\ + static __CTxtCommand_##cmdName##CONTEXT_CLASS *p=NULL;\ + if (p==NULL) p= new __CTxtCommand_##cmdName##CONTEXT_CLASS;\ return p;\ }\ virtual const char* getName() const {return #cmdName;}\ virtual CTxtCommandResult execute(CONTEXT_CLASS& context,const NLMISC::CVectorSString& args,const NLMISC::CSString& rawArgs,const NLMISC::CSString& fullCmdLine);\ private:\ - __CTxtCommand_##cmdName() {}\ + __CTxtCommand_##cmdName##CONTEXT_CLASS() {}\ };\ -static ITxtCommandRegisterer<__CTxtCommand_##cmdName,__CTxtCommandSet_##setName> __CTxtCommand_##cmdName##_Registerer;\ -CTxtCommandResult __CTxtCommand_##cmdName::execute(CONTEXT_CLASS& context,const NLMISC::CVectorSString& args,const NLMISC::CSString& rawArgs,const NLMISC::CSString& fullCmdLine) +static ITxtCommandRegisterer<__CTxtCommand_##cmdName##CONTEXT_CLASS,__CTxtCommandSet_##setName> __CTxtCommand_##cmdName##CONTEXT_CLASS##_Registerer;\ +CTxtCommandResult __CTxtCommand_##cmdName##CONTEXT_CLASS::execute(CONTEXT_CLASS& context,const NLMISC::CVectorSString& args,const NLMISC::CSString& rawArgs,const NLMISC::CSString& fullCmdLine) //------------------------------------------------------------------------------------------------- diff --git a/code/ryzom/server/src/ai_service/child_container.h b/code/ryzom/server/src/ai_service/child_container.h index 79ffa832c..ce2eb1d27 100644 --- a/code/ryzom/server/src/ai_service/child_container.h +++ b/code/ryzom/server/src/ai_service/child_container.h @@ -546,13 +546,13 @@ CAliasTreeOwner* CAliasCont::getAliasChildByAlias(uint32 alias) const template CAliasTreeOwner* CAliasCont::addAliasChild(CAliasTreeOwner* child) { - return NLMISC::type_cast(addChild(static_cast(child))); + return NLMISC::type_cast(this->addChild(static_cast(child))); } template CAliasTreeOwner* CAliasCont::addAliasChild(CAliasTreeOwner* child, uint32 index) { - return NLMISC::type_cast(addChild(static_cast(child), index)); + return NLMISC::type_cast(this->addChild(static_cast(child), index)); } template diff --git a/code/ryzom/server/src/ai_service/commands.cpp b/code/ryzom/server/src/ai_service/commands.cpp index a85e8fe1c..98063d7e3 100644 --- a/code/ryzom/server/src/ai_service/commands.cpp +++ b/code/ryzom/server/src/ai_service/commands.cpp @@ -844,7 +844,7 @@ public: } protected: private: - size_t _index; + uint32 _index; float _value; bool _detailled; mutable CLogStringWriter _stringWriter; diff --git a/code/ryzom/server/src/ai_service/family_profile.cpp b/code/ryzom/server/src/ai_service/family_profile.cpp index c01faf37f..f95f142c9 100644 --- a/code/ryzom/server/src/ai_service/family_profile.cpp +++ b/code/ryzom/server/src/ai_service/family_profile.cpp @@ -172,6 +172,7 @@ IAiFactory *_ProfileNpc=&_singleProfileNpc; extern IAiFactory *_ProfileTribe; // in another cpp. +NL_ISO_TEMPLATE_SPEC CAiFactoryContainer *CAiFactoryContainer::_Instance = NULL; CFamilyProfileFactory::CFamilyProfileFactory() { @@ -217,10 +218,6 @@ IFamilyProfile* CFamilyProfileFactory::createFamilyProfile(const TStringId &keyW return NULL; } - -NL_ISO_TEMPLATE_SPEC CAiFactoryContainer *CAiFactoryContainer::_Instance = NULL; - - IFamilyProfile* IFamilyProfile::createFamilyProfile(const TStringId &profileName, const IFamilyProfile::CtorParam& ctorParam) { return CFamilyProfileFactory::createFamilyProfile(profileName, ctorParam); diff --git a/code/ryzom/server/src/ai_service/script_compiler.cpp b/code/ryzom/server/src/ai_service/script_compiler.cpp index 03d0fe336..a9982ef48 100644 --- a/code/ryzom/server/src/ai_service/script_compiler.cpp +++ b/code/ryzom/server/src/ai_service/script_compiler.cpp @@ -1933,7 +1933,7 @@ void CSubRuleTracer::generateCode(CSmartPtr &cByteCode) const case CScriptVM::JUMP: byteCode.push_back(op); // + Jump offset. - size_t index; + uint32 index; NLMISC::fromString(param, index); jumpTable.add(CJumpRememberer(index)); byteCode.push_back(0); // Invalid @@ -1950,7 +1950,7 @@ void CSubRuleTracer::generateCode(CSmartPtr &cByteCode) const { if (str.find("Atof")!=string::npos) { - size_t index; + uint32 index; NLMISC::fromString(param, index); --index; string &strRef=_childTracers[index]->_TextValue; @@ -1962,7 +1962,7 @@ void CSubRuleTracer::generateCode(CSmartPtr &cByteCode) const if (str.find("String")!=string::npos) { - size_t index; + uint32 index; NLMISC::fromString(param, index); --index; string &strRef=_childTracers[index]->_TextValue; @@ -2021,7 +2021,7 @@ void CSubRuleTracer::generateCode(CSmartPtr &cByteCode) const if (str.find("Code")!=string::npos) { - size_t index; + uint32 index; NLMISC::fromString(param, index); --index; if (byteCode.size()==0) diff --git a/code/ryzom/server/src/entities_game_service/harvest_source.cpp b/code/ryzom/server/src/entities_game_service/harvest_source.cpp index c655e2ebc..186615e45 100644 --- a/code/ryzom/server/src/entities_game_service/harvest_source.cpp +++ b/code/ryzom/server/src/entities_game_service/harvest_source.cpp @@ -90,6 +90,14 @@ sint8 ExplosionResetPeriod = 50; // 5 s CHarvestSource AutoSpawnSourceIniProperties; +/* + * Access to singleton + */ +CHarvestSourceManager *CHarvestSourceManager::getInstance() +{ + return (CHarvestSourceManager*)_Instance; +} + /* * Initialization of source manager */ @@ -103,7 +111,10 @@ void CHarvestSourceManager::init( TDataSetIndex baseRowIndex, TDataSetIndex size //AutoSpawnSourceIniProperties.setDistVis( 100 ); } - +void CHarvestSourceManager::release() +{ + delete (CHarvestSourceManager*)_Instance; +} /* * HarvestSource constructor diff --git a/code/ryzom/server/src/entities_game_service/harvest_source.h b/code/ryzom/server/src/entities_game_service/harvest_source.h index 59556115e..edd9c546a 100644 --- a/code/ryzom/server/src/entities_game_service/harvest_source.h +++ b/code/ryzom/server/src/entities_game_service/harvest_source.h @@ -391,13 +391,13 @@ class CHarvestSourceManager : public CSimpleEntityManager public: /// Singleton access - static CHarvestSourceManager *getInstance() { return (CHarvestSourceManager*)_Instance; } + static CHarvestSourceManager *getInstance(); /// Initialization void init( TDataSetIndex baseRowIndex, TDataSetIndex size ); /// Release - static void release() { delete (CHarvestSourceManager*)_Instance; } + static void release(); }; diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.cpp b/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.cpp index 984003b20..fabb37350 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.cpp +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.cpp @@ -67,3 +67,15 @@ NLMISC_DYNVARIABLE( uint, NbEnvironmentalEffects, "Number of environmental effec if ( get ) *pointer = CEnvironmentalEffectManager::getInstance()->nbEntities(); } + +/// Singleton access +CEnvironmentalEffectManager *CEnvironmentalEffectManager::getInstance() +{ + return (CEnvironmentalEffectManager*)_Instance; +} + +/// Release +void CEnvironmentalEffectManager::release() +{ + delete (CEnvironmentalEffectManager*)_Instance; +} diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.h b/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.h index 0c81370a9..e53049cca 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/environmental_effect.h @@ -89,10 +89,10 @@ class CEnvironmentalEffectManager : public CSimpleEntityManager Date: Sat, 7 Apr 2012 15:04:26 +0200 Subject: [PATCH 08/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/misc/sstring.h | 40 +++++++++---------- code/nel/src/misc/common.cpp | 6 +-- code/nel/src/misc/sheet_id.cpp | 2 +- code/nel/src/misc/sstring.cpp | 26 ++++++------ .../tools/georges/georges2csv/georges2csv.cpp | 8 ++-- .../game_share/deployment_configuration.cpp | 6 +-- .../cpr_client_patch_repository.cpp | 2 +- .../src/patchman_service/file_manager.cpp | 12 +++--- .../src/patchman_service/file_receiver.cpp | 6 +-- .../src/patchman_service/file_repository.cpp | 2 +- .../pam_patchman_admin_module.cpp | 2 +- .../src/patchman_service/repository.cpp | 14 +++---- .../src/patchman_service/service_main.cpp | 4 +- 13 files changed, 65 insertions(+), 65 deletions(-) diff --git a/code/nel/include/nel/misc/sstring.h b/code/nel/include/nel/misc/sstring.h index 513c681c9..fc17d67f5 100644 --- a/code/nel/include/nel/misc/sstring.h +++ b/code/nel/include/nel/misc/sstring.h @@ -55,7 +55,7 @@ public: /// ctor CSString(int i,const char *fmt="%d"); /// ctor - CSString(unsigned u,const char *fmt="%u"); + CSString(uint32 u,const char *fmt="%u"); /// ctor CSString(double d,const char *fmt="%f"); /// ctor @@ -76,14 +76,14 @@ public: char back() const; /// Return the n left hand most characters of a string - CSString left(unsigned count) const; + CSString left(uint32 count) const; /// Return the n right hand most characters of a string - CSString right(unsigned count) const; + CSString right(uint32 count) const; /// Return the string minus the n left hand most characters of a string - CSString leftCrop(unsigned count) const; + CSString leftCrop(uint32 count) const; /// Return the string minus the n right hand most characters of a string - CSString rightCrop(unsigned count) const; + CSString rightCrop(uint32 count) const; /// Return sub string up to but not including first instance of given character, starting at 'iterator' /// on exit 'iterator' indexes first character after extracted string segment @@ -116,9 +116,9 @@ public: /// Return sub string remaining after the first word CSString tailFromFirstWord() const; /// Count the number of words in a string - unsigned countWords() const; + uint32 countWords() const; /// Extract the given word - CSString word(unsigned idx) const; + CSString word(uint32 idx) const; /// Return first word or quote-encompassed sub-string - can remove extracted sub-string from source string CSString firstWordOrWords(bool truncateThis=false,bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true); @@ -127,9 +127,9 @@ public: /// Return sub string following first word (or quote-encompassed sub-string) CSString tailFromFirstWordOrWords(bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true) const; /// Count the number of words (or quote delimited sub-strings) in a string - unsigned countWordOrWords(bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true) const; + uint32 countWordOrWords(bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true) const; /// Extract the given words (or quote delimited sub-strings) - CSString wordOrWords(unsigned idx,bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true) const; + CSString wordOrWords(uint32 idx,bool useSlashStringEscape=true,bool useRepeatQuoteStringEscape=true) const; /// Return first line - can remove extracted line from source string CSString firstLine(bool truncateThis=false); @@ -138,9 +138,9 @@ public: /// Return sub string remaining after the first line CSString tailFromFirstLine() const; /// Count the number of lines in a string - unsigned countLines() const; + uint32 countLines() const; /// Extract the given line - CSString line(unsigned idx) const; + CSString line(uint32 idx) const; /// A handy utility routine for knowing if a character is a white space character or not (' ','\t','\n','\r',26) static bool isWhiteSpace(char c); @@ -377,7 +377,7 @@ public: /// assignment operator CSString& operator=(int i); /// assignment operator - CSString& operator=(unsigned u); + CSString& operator=(uint32 u); /// assignment operator CSString& operator=(double d); @@ -561,7 +561,7 @@ inline CSString::CSString(int i,const char *fmt) *this=buf; } -inline CSString::CSString(unsigned u,const char *fmt) +inline CSString::CSString(uint32 u,const char *fmt) { char buf[1024]; sprintf(buf,fmt,u); @@ -611,26 +611,26 @@ inline char CSString::back() const return (*this)[size()-1]; } -inline CSString CSString::right(unsigned count) const +inline CSString CSString::right(uint32 count) const { if (count>=size()) return *this; return substr(size()-count); } -inline CSString CSString::rightCrop(unsigned count) const +inline CSString CSString::rightCrop(uint32 count) const { if (count>=size()) return CSString(); return substr(0,size()-count); } -inline CSString CSString::left(unsigned count) const +inline CSString CSString::left(uint32 count) const { return substr(0,count); } -inline CSString CSString::leftCrop(unsigned count) const +inline CSString CSString::leftCrop(uint32 count) const { if (count>=size()) return CSString(); @@ -639,7 +639,7 @@ inline CSString CSString::leftCrop(unsigned count) const inline CSString CSString::splitToWithIterator(char c,uint32& iterator) const { - unsigned i; + uint32 i; CSString result; for (i=iterator;i= 0 && k < 3; --i, ++k ) + for (i = remaining, k = 0; i >= 0 && k < 3; --i, ++k ) { ns = s[i] + ns; // New char is added to front of ns if ( i > 0 && k == 2) ns = separator + ns; // j > 0 means still more digits diff --git a/code/nel/src/misc/sheet_id.cpp b/code/nel/src/misc/sheet_id.cpp index 0eaad3a7a..617c994b7 100644 --- a/code/nel/src/misc/sheet_id.cpp +++ b/code/nel/src/misc/sheet_id.cpp @@ -592,7 +592,7 @@ uint32 CSheetId::typeFromFileExtension(const std::string &fileExtension) { if (!_Initialised) init(false); - unsigned i; + uint i; for (i=0;i<_FileExtensions.size();i++) if (toLower(fileExtension)==_FileExtensions[i]) return i; diff --git a/code/nel/src/misc/sstring.cpp b/code/nel/src/misc/sstring.cpp index 4c37b1f1a..ef1584361 100644 --- a/code/nel/src/misc/sstring.cpp +++ b/code/nel/src/misc/sstring.cpp @@ -37,14 +37,14 @@ namespace NLMISC return token; } - unsigned int i; + uint i; CSString result; // skip leading junk for (i=0;iDataSoFar; - uint32 oldSize= theBuffer.size(); + uint32 oldSize= (uint32)theBuffer.size(); theBuffer.resize(oldSize+data.getBufferSize()); memcpy(&(theBuffer[oldSize]), data.getBuffer(), data.getBufferSize()); @@ -355,7 +355,7 @@ namespace PATCHMAN if (theRequest->DataSoFar.size()>=theRequest->ExpectedFileSize) { // we've reached the end of file - _dealWithReceivedFile(sender,theRequest,NLNET::TBinBuffer((const uint8 *)&theRequest->DataSoFar[0],theRequest->DataSoFar.size())); + _dealWithReceivedFile(sender,theRequest,NLNET::TBinBuffer((const uint8 *)&theRequest->DataSoFar[0],(uint32)theRequest->DataSoFar.size())); return; } @@ -374,7 +374,7 @@ namespace PATCHMAN } // log our progress - _downloadLog(fileName,theRequest->DataSoFar.size(),theRequest->ExpectedFileSize); + _downloadLog(fileName,(uint32)theRequest->DataSoFar.size(),theRequest->ExpectedFileSize); } void CFileReceiver::cbFileDataFailure(NLNET::IModuleProxy *sender, const std::string &fileName) diff --git a/code/ryzom/server/src/patchman_service/file_repository.cpp b/code/ryzom/server/src/patchman_service/file_repository.cpp index d1d2d4b54..72881f587 100644 --- a/code/ryzom/server/src/patchman_service/file_repository.cpp +++ b/code/ryzom/server/src/patchman_service/file_repository.cpp @@ -332,7 +332,7 @@ namespace PATCHMAN CFileReceiverProxy rr(sender); if (ok && !result.empty()) { - rr.cbFileData(_Parent,fileName,startOffset,NLNET::TBinBuffer((const uint8 *)&result[0],result.size())); + rr.cbFileData(_Parent,fileName,startOffset,NLNET::TBinBuffer((const uint8 *)&result[0],(uint32)result.size())); } else { diff --git a/code/ryzom/server/src/patchman_service/pam_patchman_admin_module.cpp b/code/ryzom/server/src/patchman_service/pam_patchman_admin_module.cpp index 0eaf4f946..60efeee33 100644 --- a/code/ryzom/server/src/patchman_service/pam_patchman_admin_module.cpp +++ b/code/ryzom/server/src/patchman_service/pam_patchman_admin_module.cpp @@ -436,7 +436,7 @@ NLMISC_CLASS_COMMAND_IMPL(CPatchmanAdminModule, download) } // iterate over matching files, adding them to the download list - for (uint32 i=fileInfo.size();i--;) + for (uint i=(uint)fileInfo.size();i--;) { _DownloadRequests[fileInfo[i].FileName]= NLMISC::CPath::standardizePath(destination); requestFile(fileInfo[i].FileName); diff --git a/code/ryzom/server/src/patchman_service/repository.cpp b/code/ryzom/server/src/patchman_service/repository.cpp index b71c11ef2..a63a96bcd 100644 --- a/code/ryzom/server/src/patchman_service/repository.cpp +++ b/code/ryzom/server/src/patchman_service/repository.cpp @@ -52,7 +52,7 @@ NLMISC::CSString getRepositoryIndexFileName(const NLMISC::CSString& repositoryNa uint32 getFileVersion(const NLMISC::CSString& fileName) { // start at the back of the file name and scan forwards until we find a '/' or '\\' or ':' or a digit - uint32 i= fileName.size(); + uint32 i= (uint32)fileName.size(); while (i--) { char c= fileName[i]; @@ -184,9 +184,9 @@ void CRepository::updateFile(NLMISC::CSString fileName) nldebug(("GUSREP_Updating repository entry for file: '"+fileName+"'").c_str()); // if the name of the file that has changed contains the target directory name then crop it - if (fileName.left(_TargetDirectory.size())==_TargetDirectory) + if (fileName.left((uint32)_TargetDirectory.size())==_TargetDirectory) { - fileName=fileName.leftCrop(_TargetDirectory.size()); + fileName=fileName.leftCrop((uint32)_TargetDirectory.size()); } // lookup the file in the map @@ -219,9 +219,9 @@ void CRepository::addFileStub(NLMISC::CSString fileName) nldebug(("GUSREP_Adding repository stub for file: '"+fileName+"'").c_str()); // if the name of the file that has changed contains the target directory name then crop it - if (fileName.left(_TargetDirectory.size())==_TargetDirectory) + if (fileName.left((uint32)_TargetDirectory.size())==_TargetDirectory) { - fileName=fileName.leftCrop(_TargetDirectory.size()); + fileName=fileName.leftCrop((uint32)_TargetDirectory.size()); } // make sure the file didn't already exist in the map @@ -258,7 +258,7 @@ uint32 CRepository::update() // get hold of the file name for the next file // CSString fileName= NLMISC::CFile::getFilename(theFile.FileName); - CSString fileName= theFile.FileName.leftCrop(_TargetDirectory.size()); + CSString fileName= theFile.FileName.leftCrop((uint32)_TargetDirectory.size()); // extract the version number from the file name and skip the file if it's too recent or the version number was invalid uint32 fileVersion= getFileVersion(fileName); @@ -345,7 +345,7 @@ void CRepository::setVersion(uint32 version) uint32 CRepository::size() const { - return _Files.size(); + return (uint32)_Files.size(); } const CRepository::CFilesMapEntry& CRepository::operator[](const NLMISC::CSString& key) const diff --git a/code/ryzom/server/src/patchman_service/service_main.cpp b/code/ryzom/server/src/patchman_service/service_main.cpp index aa0cfbffb..7e61baba8 100644 --- a/code/ryzom/server/src/patchman_service/service_main.cpp +++ b/code/ryzom/server/src/patchman_service/service_main.cpp @@ -115,12 +115,12 @@ void CTaskScheduler::update() // check to see if we've broken our max sheduled tasks record... if (_MaxTasks < _Tasks.size()) { - _MaxTasks = _Tasks.size(); + _MaxTasks = (uint32)_Tasks.size(); nldebug("New scheduled task record: %u",_MaxTasks); } // iterate over all scheduled tasks (we go backwards to simplify deletion of executed tasks as we go) - for (uint32 i=_Tasks.size();i--;) + for (uint32 i=(uint32)_Tasks.size();i--;) { // get a refference to the next task STask& theTask= _Tasks[i]; From 18e11b2867a367dfbb2675a1f79d7bfaaf9c0040 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 15:11:18 +0200 Subject: [PATCH 09/77] Changed: Some checks for Clang and MinGW --- code/CMakeModules/nel.cmake | 63 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index a02785a24..d4b12829a 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -185,7 +185,7 @@ Remove the CMakeCache.txt file and try again from another folder, e.g.: rm CMakeCache.txt mkdir cmake cd cmake - cmake -G \"Unix Makefiles\" .. + cmake .. ") ENDIF(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) @@ -333,6 +333,12 @@ MACRO(NL_SETUP_BUILD) SET(HOST_CPU ${CMAKE_SYSTEM_PROCESSOR}) + IF(HOST_CPU MATCHES "amd64") + SET(HOST_CPU "x86_64") + ELSEIF(HOST_CPU MATCHES "i.86") + SET(HOST_CPU "x86") + ENDIF(HOST_CPU MATCHES "amd64") + # Determine target CPU IF(NOT TARGET_CPU) SET(TARGET_CPU $ENV{DEB_HOST_GNU_CPU}) @@ -375,10 +381,10 @@ MACRO(NL_SETUP_BUILD) IF(TARGET_CPU STREQUAL "x86_64") SET(TARGET_X64 1) - SET(PLATFORM_CFLAGS "-DHAVE_X86_64") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -DHAVE_X86_64") ELSEIF(TARGET_CPU STREQUAL "x86") SET(TARGET_X86 1) - SET(PLATFORM_CFLAGS "-DHAVE_X86") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -DHAVE_X86") ENDIF(TARGET_CPU STREQUAL "x86_64") # Fix library paths suffixes for Debian MultiArch @@ -614,30 +620,41 @@ MACRO(SETUP_EXTERNAL) ENDIF(WITH_EXTERNAL) IF(WIN32) - INCLUDE(${CMAKE_ROOT}/Modules/Platform/Windows-cl.cmake) + FIND_PACKAGE(External REQUIRED) + IF(MSVC10) IF(NOT MSVC10_REDIST_DIR) # If you have VC++ 2010 Express, put x64/Microsoft.VC100.CRT/*.dll in ${EXTERNAL_PATH}/redist SET(MSVC10_REDIST_DIR "${EXTERNAL_PATH}/redist") ENDIF(NOT MSVC10_REDIST_DIR) - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\10.0_Config;InstallDir]" ABSOLUTE) - # VC_ROOT_DIR is set to "registry" when a key is not found - IF(VC_ROOT_DIR MATCHES "registry") - GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VCExpress\\10.0_Config;InstallDir]" ABSOLUTE) - IF(VC_ROOT_DIR MATCHES "registry") - MESSAGE(FATAL_ERROR "Unable to find VC++ 2010 directory!") - ENDIF(VC_ROOT_DIR MATCHES "registry") - ENDIF(VC_ROOT_DIR MATCHES "registry") - # convert IDE fullpath to VC++ path - STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${VC_ROOT_DIR}) - ELSE(MSVC10) - IF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") + + IF(NOT VC_DIR) + IF(NOT VC_ROOT_DIR) + GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\10.0_Config;InstallDir]" ABSOLUTE) + # VC_ROOT_DIR is set to "registry" when a key is not found + IF(VC_ROOT_DIR MATCHES "registry") + GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VCExpress\\10.0_Config;InstallDir]" ABSOLUTE) + IF(VC_ROOT_DIR MATCHES "registry") + FILE(TO_CMAKE_PATH $ENV{VS100COMNTOOLS} VC_ROOT_DIR) + IF(NOT VC_ROOT_DIR) + MESSAGE(FATAL_ERROR "Unable to find VC++ 2010 directory!") + ENDIF(NOT VC_ROOT_DIR) + ENDIF(VC_ROOT_DIR MATCHES "registry") + ENDIF(VC_ROOT_DIR MATCHES "registry") + ENDIF(NOT VC_ROOT_DIR) # convert IDE fullpath to VC++ path - STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${CMAKE_MAKE_PROGRAM}) - ELSE(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") - # convert compiler fullpath to VC++ path - STRING(REGEX REPLACE "VC/bin/.+" "VC" VC_DIR ${CMAKE_CXX_COMPILER}) - ENDIF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") + STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${VC_ROOT_DIR}) + ENDIF(NOT VC_DIR) + ELSE(MSVC10) + IF(NOT VC_DIR) + IF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") + # convert IDE fullpath to VC++ path + STRING(REGEX REPLACE "Common7/.*" "VC" VC_DIR ${CMAKE_MAKE_PROGRAM}) + ELSE(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") + # convert compiler fullpath to VC++ path + STRING(REGEX REPLACE "VC/bin/.+" "VC" VC_DIR ${CMAKE_CXX_COMPILER}) + ENDIF(${CMAKE_MAKE_PROGRAM} MATCHES "Common7") + ENDIF(NOT VC_DIR) ENDIF(MSVC10) ELSE(WIN32) IF(APPLE) @@ -658,12 +675,12 @@ MACRO(SETUP_EXTERNAL) IF(WITH_STLPORT) FIND_PACKAGE(STLport REQUIRED) INCLUDE_DIRECTORIES(${STLPORT_INCLUDE_DIR}) - IF(WIN32) + IF(MSVC) SET(VC_INCLUDE_DIR "${VC_DIR}/include") FIND_PACKAGE(WindowsSDK REQUIRED) # use VC++ and Windows SDK include paths INCLUDE_DIRECTORIES(${VC_INCLUDE_DIR} ${WINSDK_INCLUDE_DIR}) - ENDIF(WIN32) + ENDIF(MSVC) ENDIF(WITH_STLPORT) ENDMACRO(SETUP_EXTERNAL) From 50276329253fc560c2ec46dcd3e1717984f36b38 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 15:56:41 +0200 Subject: [PATCH 10/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/misc/bit_mem_stream.h | 11 +++++------ code/nel/include/nel/misc/static_map.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/code/nel/include/nel/misc/bit_mem_stream.h b/code/nel/include/nel/misc/bit_mem_stream.h index f9572084d..3bddc20f7 100644 --- a/code/nel/include/nel/misc/bit_mem_stream.h +++ b/code/nel/include/nel/misc/bit_mem_stream.h @@ -801,11 +801,7 @@ void displayBitStream( const CBitMemStream& msg, sint beginbitpos, sint endbitpo inline std::string CBMSDbgInfo::getEventLegendAtBitPos( CBitMemStream& bms, sint32 eventId ) { #ifdef NL_DEBUG - if ( eventId == -1 ) - { - return std::string(); - } - else + if ( eventId != -1 ) { nlassert( eventId < (sint32)_DbgData->List.size() ); TBMSSerialInfo& serialItem = _DbgData->List[eventId]; // works only with a vector! @@ -814,8 +810,11 @@ inline std::string CBMSDbgInfo::getEventLegendAtBitPos( CBitMemStream& bms, sint bms.getSerialItem( serialItem ).c_str(), (serialItem.Symbol!=NULL)?serialItem.Symbol:"" ); } #else - return std::string(); + nlunreferenced(bms); + nlunreferenced(eventId); #endif + + return std::string(); } diff --git a/code/nel/include/nel/misc/static_map.h b/code/nel/include/nel/misc/static_map.h index 0fd1f8857..b5ec3e228 100644 --- a/code/nel/include/nel/misc/static_map.h +++ b/code/nel/include/nel/misc/static_map.h @@ -93,7 +93,7 @@ public: { } - explicit CStaticMap (const Comp& __comp) : _DataSorted(true) + explicit CStaticMap (const Comp& /* __comp */) : _DataSorted(true) { } From 48e05d005632c50d0fe73293a0933e8e52991f1a Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 20:04:08 +0200 Subject: [PATCH 11/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/3d/index_buffer.h | 2 +- code/nel/include/nel/3d/instance_lighter.h | 2 +- .../include/nel/3d/ps_attrib_maker_bin_op.h | 2 +- .../nel/3d/ps_attrib_maker_bin_op_inline.h | 4 +- .../nel/3d/ps_attrib_maker_iterators.h | 4 +- code/nel/include/nel/3d/ps_direction.h | 2 +- code/nel/include/nel/3d/ps_edit.h | 10 +-- code/nel/include/nel/3d/ps_force.h | 44 +++++------ code/nel/include/nel/3d/vertex_buffer.h | 2 +- .../include/nel/net/module_builder_parts.h | 4 +- code/nel/include/nel/net/transport_class.h | 2 +- .../common/src/game_share/data_set_base.h | 2 +- .../ryzom/common/src/game_share/inventories.h | 4 +- .../src/game_share/persistent_data_inline.h | 5 +- .../mission_manager/mission_event.h | 22 +++--- .../server/src/server_share/msg_ai_service.h | 74 +++++++++---------- .../src/server_share/pet_interface_msg.h | 10 +-- 17 files changed, 98 insertions(+), 97 deletions(-) diff --git a/code/nel/include/nel/3d/index_buffer.h b/code/nel/include/nel/3d/index_buffer.h index 320a06020..7beaafe0b 100644 --- a/code/nel/include/nel/3d/index_buffer.h +++ b/code/nel/include/nel/3d/index_buffer.h @@ -782,7 +782,7 @@ inline void CIndexBuffer::lock (CIndexBufferRead &accessor, uint first, uint las // *************************************************************************** -inline void CIndexBuffer::unlock (uint first, uint end) +inline void CIndexBuffer::unlock (uint /* first */, uint /* end */) { nlassertex (_LockCounter!=0, ("Index buffer not locked")); nlassert (_LockedBuffer || (!isResident() && _NonResidentIndexes.empty())); diff --git a/code/nel/include/nel/3d/instance_lighter.h b/code/nel/include/nel/3d/instance_lighter.h index 7b1dbb6e7..8b1ccf80a 100644 --- a/code/nel/include/nel/3d/instance_lighter.h +++ b/code/nel/include/nel/3d/instance_lighter.h @@ -147,7 +147,7 @@ public: static void addTriangles (const IShape &shape, const NLMISC::CMatrix& modelMT, std::vector& triangleArray, sint instanceId); // Progress callback - virtual void progress (const char *message, float progress) {} + virtual void progress (const char * /* message */, float /* progress */) {} /// \name Static PointLights mgt. diff --git a/code/nel/include/nel/3d/ps_attrib_maker_bin_op.h b/code/nel/include/nel/3d/ps_attrib_maker_bin_op.h index 4370fdeaa..285fa9b26 100644 --- a/code/nel/include/nel/3d/ps_attrib_maker_bin_op.h +++ b/code/nel/include/nel/3d/ps_attrib_maker_bin_op.h @@ -157,7 +157,7 @@ public: } /// return true if an operation is supported. The default support all ops - bool supportOp(CPSBinOp::BinOp op) { return true; } + bool supportOp(CPSBinOp::BinOp /* op */) { return true; } /// get the current operator CPSBinOp::BinOp getOp(void) const { return _Op; } diff --git a/code/nel/include/nel/3d/ps_attrib_maker_bin_op_inline.h b/code/nel/include/nel/3d/ps_attrib_maker_bin_op_inline.h index c25c051a5..8d98a1544 100644 --- a/code/nel/include/nel/3d/ps_attrib_maker_bin_op_inline.h +++ b/code/nel/include/nel/3d/ps_attrib_maker_bin_op_inline.h @@ -57,13 +57,13 @@ inline CPlaneBasis PSBinOpModulate(CPlaneBasis p1, CPlaneBasis p2) } template <> -inline CPlaneBasis PSBinOpAdd(CPlaneBasis p1, CPlaneBasis p2) +inline CPlaneBasis PSBinOpAdd(CPlaneBasis /* p1 */, CPlaneBasis /* p2 */) { nlassert(0); // not allowed for now return CPlaneBasis(NLMISC::CVector::Null); } template <> -inline CPlaneBasis PSBinOpSubtract(CPlaneBasis p1, CPlaneBasis p2) +inline CPlaneBasis PSBinOpSubtract(CPlaneBasis /* p1 */, CPlaneBasis /* p2 */) { nlassert(0); // not allowed for now return CPlaneBasis(NLMISC::CVector::Null); diff --git a/code/nel/include/nel/3d/ps_attrib_maker_iterators.h b/code/nel/include/nel/3d/ps_attrib_maker_iterators.h index 8729ffb10..d9cfbcdd3 100644 --- a/code/nel/include/nel/3d/ps_attrib_maker_iterators.h +++ b/code/nel/include/nel/3d/ps_attrib_maker_iterators.h @@ -60,7 +60,7 @@ namespace NL3D { GET_INLINE float get() const { return float(rand() * (1 / double(RAND_MAX))); } // this may be optimized with a table... void advance() {} - void advance(uint quantity) {} + void advance(uint /* quantity */) {} }; /// this iterator just return the same value @@ -69,7 +69,7 @@ namespace NL3D float Value; GET_INLINE float get() const { return Value; } void advance() {} - void advance(uint quantity) {} + void advance(uint /* quantity */) {} }; /// iterator that use dist to compute the value diff --git a/code/nel/include/nel/3d/ps_direction.h b/code/nel/include/nel/3d/ps_direction.h index 609278a80..eb8bbd827 100644 --- a/code/nel/include/nel/3d/ps_direction.h +++ b/code/nel/include/nel/3d/ps_direction.h @@ -38,7 +38,7 @@ public : /** The direction is taken from a global vector defined in the particle system * NULL or an empty string as a name disable the use of a global value */ - virtual void enableGlobalVectorValue(const std::string &name) {} + virtual void enableGlobalVectorValue(const std::string &/* name */) {} virtual std::string getGlobalVectorValueName() const { return ""; } }; diff --git a/code/nel/include/nel/3d/ps_edit.h b/code/nel/include/nel/3d/ps_edit.h index d3e1a53de..8342868f8 100644 --- a/code/nel/include/nel/3d/ps_edit.h +++ b/code/nel/include/nel/3d/ps_edit.h @@ -65,13 +65,13 @@ struct IPSMover virtual bool supportNonUniformScaling(void) const { NL_PS_FUNC(supportNonUniformScaling); return false ; } // set the scale of the object (uniform scale). The default does nothing - virtual void setScale(uint32 index, float scale) {} ; + virtual void setScale(uint32 /* index */, float /* scale */) {} // set a non uniform scale (if supported) - virtual void setScale(uint32 index, const NLMISC::CVector &s) { NL_PS_FUNC(setScale); } + virtual void setScale(uint32 /* index */, const NLMISC::CVector &/* s */) { NL_PS_FUNC(setScale); } // get the scale of the object - virtual NLMISC::CVector getScale(uint32 index) const { NL_PS_FUNC(getScale); return NLMISC::CVector(1.f, 1.f, 1.f) ; } + virtual NLMISC::CVector getScale(uint32 /* index */) const { NL_PS_FUNC(getScale); return NLMISC::CVector(1.f, 1.f, 1.f) ; } /** some object may not store a whole matrix (e.g planes) * this return true if only a normal is needed to set the orientation of the object @@ -79,10 +79,10 @@ struct IPSMover virtual bool onlyStoreNormal(void) const { NL_PS_FUNC(onlyStoreNormal); return false ; } /// if the object only needs a normal, this return the normal. If not, is return (0, 0, 0) - virtual NLMISC::CVector getNormal(uint32 index) { NL_PS_FUNC(getNormal); return NLMISC::CVector::Null ; } + virtual NLMISC::CVector getNormal(uint32 /* index */) { NL_PS_FUNC(getNormal); return NLMISC::CVector::Null ; } /// if the object only stores a normal, this set the normal of the object. Otherwise it has no effect - virtual void setNormal(uint32 index, NLMISC::CVector n) { NL_PS_FUNC(setNormal); } + virtual void setNormal(uint32 /* index */, NLMISC::CVector /* n */) { NL_PS_FUNC(setNormal); } // set a new orthogonal matrix for the object virtual void setMatrix(uint32 index, const NLMISC::CMatrix &m) = 0 ; diff --git a/code/nel/include/nel/3d/ps_force.h b/code/nel/include/nel/3d/ps_force.h index 5e961dc43..15e28948a 100644 --- a/code/nel/include/nel/3d/ps_force.h +++ b/code/nel/include/nel/3d/ps_force.h @@ -87,9 +87,9 @@ public: * 'accumulate' set to false. * NB : works only with integrable forces */ - virtual void integrate(float date, CPSLocated *src, uint32 startIndex, uint32 numObjects, NLMISC::CVector *destPos = NULL, NLMISC::CVector *destSpeed = NULL, - bool accumulate = false, - uint posStride = sizeof(NLMISC::CVector), uint speedStride = sizeof(NLMISC::CVector) + virtual void integrate(float /* date */, CPSLocated * /* src */, uint32 /* startIndex */, uint32 /* numObjects */, NLMISC::CVector * /* destPos */ = NULL, NLMISC::CVector * /* destSpeed */ = NULL, + bool /* accumulate */ = false, + uint /* posStride */ = sizeof(NLMISC::CVector), uint /* speedStride */ = sizeof(NLMISC::CVector) ) const { nlassert(0); // not an integrable force @@ -100,11 +100,11 @@ public: * If the start date is lower than the creation date, the initial position is used * NB : works only with integrable forces */ - virtual void integrateSingle(float startDate, float deltaT, uint numStep, - const CPSLocated *src, uint32 indexInLocated, - NLMISC::CVector *destPos, - bool accumulate = false, - uint posStride = sizeof(NLMISC::CVector)) const + virtual void integrateSingle(float /* startDate */, float /* deltaT */, uint /* numStep */, + const CPSLocated * /* src */, uint32 /* indexInLocated */, + NLMISC::CVector * /* destPos */, + bool /* accumulate */ = false, + uint /* posStride */ = sizeof(NLMISC::CVector)) const { nlassert(0); // not an integrable force } @@ -170,7 +170,7 @@ public: virtual void setIntensityScheme(CPSAttribMaker *scheme); // deriver have here the opportunity to setup the functor object. The default does nothing - virtual void setupFunctor(uint32 indexInLocated) { } + virtual void setupFunctor(uint32 /* indexInLocated */) { } /// get the attribute maker for a non constant intensity CPSAttribMaker *getIntensityScheme(void) { return _IntensityScheme; } @@ -493,22 +493,22 @@ public: #ifdef NL_OS_WINDOWS __forceinline #endif - void operator() (const NLMISC::CVector &pos, NLMISC::CVector &speed, float invMass) - { + void operator() (const NLMISC::CVector &/* pos */, NLMISC::CVector &speed, float invMass) + { speed -= (CParticleSystem::EllapsedTime * _K * invMass * speed); - } + } - virtual void serial(NLMISC::IStream &f) throw(NLMISC::EStream) - { - f.serialVersion(1); - // we don't save intensity info : it is saved by the owning object (and set before each use of this functor) - } + virtual void serial(NLMISC::IStream &f) throw(NLMISC::EStream) + { + f.serialVersion(1); + // we don't save intensity info : it is saved by the owning object (and set before each use of this functor) + } - // get the friction coefficient - float getK(void) const { return _K; } + // get the friction coefficient + float getK(void) const { return _K; } - // set the friction coefficient - void setK(float coeff) { _K = coeff; } + // set the friction coefficient + void setK(float coeff) { _K = coeff; } protected: // the friction coeff float _K; @@ -630,7 +630,7 @@ struct CPSTurbulForceFunc #ifdef NL_OS_WINDOWS __forceinline #endif - void operator() (const NLMISC::CVector &pos, NLMISC::CVector &speed, float invMass) + void operator() (const NLMISC::CVector &/* pos */, NLMISC::CVector &/* speed */, float /* invMass */) { nlassert(0); diff --git a/code/nel/include/nel/3d/vertex_buffer.h b/code/nel/include/nel/3d/vertex_buffer.h index 83b2b90ea..a64ec0c2c 100644 --- a/code/nel/include/nel/3d/vertex_buffer.h +++ b/code/nel/include/nel/3d/vertex_buffer.h @@ -1223,7 +1223,7 @@ inline void CVertexBuffer::lock (CVertexBufferRead &accessor, uint first, uint l // -------------------------------------------------- -inline void CVertexBuffer::unlock (uint first, uint end) +inline void CVertexBuffer::unlock (uint /* first */, uint /* end */) { nlassertex (_LockCounter!=0, ("Vertex buffer not locked")); nlassert (_LockedBuffer || (!isResident() && _NonResidentVertices.empty())); diff --git a/code/nel/include/nel/net/module_builder_parts.h b/code/nel/include/nel/net/module_builder_parts.h index b080e521f..d2e653315 100644 --- a/code/nel/include/nel/net/module_builder_parts.h +++ b/code/nel/include/nel/net/module_builder_parts.h @@ -188,8 +188,8 @@ namespace NLNET // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} - bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message) {return false;} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy * /* moduleProxy */) {} + bool fwdOnProcessModuleMessage(NLNET::IModuleProxy * /* sender */, const NLNET::CMessage &/* message */) {return false;} // check module up void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) diff --git a/code/nel/include/nel/net/transport_class.h b/code/nel/include/nel/net/transport_class.h index 2517da64b..f1110e492 100644 --- a/code/nel/include/nel/net/transport_class.h +++ b/code/nel/include/nel/net/transport_class.h @@ -335,7 +335,7 @@ protected: T *Value; - virtual void serialDefaultValue (NLMISC::IStream &f) + virtual void serialDefaultValue (NLMISC::IStream &/* f */) { // nothing } diff --git a/code/ryzom/common/src/game_share/data_set_base.h b/code/ryzom/common/src/game_share/data_set_base.h index 0db688aa5..54bd790ab 100644 --- a/code/ryzom/common/src/game_share/data_set_base.h +++ b/code/ryzom/common/src/game_share/data_set_base.h @@ -437,7 +437,7 @@ public: // PUBLIC because template friend classes are not supported /// Return the pointer to the property value template - void getPropPointerForList( TSharedListRow **ppt, TPropertyIndex propIndex, const TDataSetRow& entityIndex, T* typeHint ) const + void getPropPointerForList( TSharedListRow **ppt, TPropertyIndex propIndex, const TDataSetRow& entityIndex, T* /* typeHint */ ) const { #ifndef FAST_MIRROR nlassert( (propIndex != INVALID_PROPERTY_INDEX) && (propIndex < (TPropertyIndex)_PropertyContainer.PropertyValueArrays.size()) ); // Wrong property diff --git a/code/ryzom/common/src/game_share/inventories.h b/code/ryzom/common/src/game_share/inventories.h index 1fd1cab48..2dfbb85bf 100644 --- a/code/ryzom/common/src/game_share/inventories.h +++ b/code/ryzom/common/src/game_share/inventories.h @@ -349,7 +349,7 @@ namespace INVENTORIES /// Serial from/to bit stream template - void serialAll( NLMISC::CBitMemStream& bms, const CInventoryCategoryTemplate *templ=0 ) + void serialAll( NLMISC::CBitMemStream& bms, const CInventoryCategoryTemplate * /* templ */ =0 ) { bms.serial( _SlotIndex, CInventoryCategoryTemplate::SlotBitSize ); @@ -385,7 +385,7 @@ namespace INVENTORIES /// Serial from/to bit stream template - void serialOneProp( NLMISC::CBitMemStream& bms, const CInventoryCategoryTemplate *templ=0 ) + void serialOneProp( NLMISC::CBitMemStream& bms, const CInventoryCategoryTemplate * /* templ */ =0 ) { bms.serial( _SlotIndex, CInventoryCategoryTemplate::SlotBitSize ); bms.serial( _OneProp ); diff --git a/code/ryzom/common/src/game_share/persistent_data_inline.h b/code/ryzom/common/src/game_share/persistent_data_inline.h index 2500c524d..9a682afdb 100644 --- a/code/ryzom/common/src/game_share/persistent_data_inline.h +++ b/code/ryzom/common/src/game_share/persistent_data_inline.h @@ -420,7 +420,9 @@ inline const CPersistentDataRecord::CArg& CPersistentDataRecord::popNextArg(TTok inline void CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord::CArg& result) { #ifdef NL_DEBUG - BOMB_IF(peekNextToken()!=token,"Error on read code - token requested doesn't match token found",return); + BOMB_IF(peekNextToken()!=token,"Error on read code - token requested doesn't match token found",return); + #else + nlunreferenced(token); #endif peekNextArg(result); @@ -445,7 +447,6 @@ inline void CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord ++_ArgOffset; ++_TokenOffset; } - return; } //inline CPersistentDataRecord::CArg CPersistentDataRecord::popNextArg(TToken token) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h index 34ff36600..a0f9e77d7 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h @@ -390,7 +390,7 @@ public: :CMissionEvent(EndDynChat,TDataSetRow()){} protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;}; + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; class CMissionEventDebug : public CMissionEvent @@ -400,7 +400,7 @@ public: :CMissionEvent(Debug,TDataSetRow()){} protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;}; + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; @@ -412,7 +412,7 @@ public: uint32 Points; protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;}; + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; class CMissionEventOutpostGain : public CMissionEvent @@ -422,7 +422,7 @@ public: :CMissionEvent(OutpostGain,TDataSetRow()){} protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;}; + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; @@ -437,7 +437,7 @@ public: bool Guild; protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;} + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; class CMissionEventQueueEntryOk: public CMissionEvent @@ -447,7 +447,7 @@ public: :CMissionEvent(QueueEntryOk, TDataSetRow()) {} protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log) { return false; } + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; @@ -458,7 +458,7 @@ public: :CMissionEvent(QueueExit, TDataSetRow()) {} protected: friend class CMissionEvent; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log) { return false; } + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; class CMissionEventGroupSpawned : public CMissionEvent @@ -470,8 +470,8 @@ public: TAIAlias Alias; protected: friend class CMissionEvent; - CMissionEventGroupSpawned(){}; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log) { return false; } + CMissionEventGroupSpawned(){} + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; class CMissionEventGroupDespawned : public CMissionEvent @@ -483,8 +483,8 @@ public: TAIAlias Alias; protected: friend class CMissionEvent; - CMissionEventGroupDespawned(){}; - bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log) { return false; } + CMissionEventGroupDespawned(){} + bool buildFromScript( const std::vector< std::string > & /* script */, NLMISC::CLog& /* log */){return false;} }; #endif // RY_MISSION_EVENT_H diff --git a/code/ryzom/server/src/server_share/msg_ai_service.h b/code/ryzom/server/src/server_share/msg_ai_service.h index c62ca0283..a442b5234 100644 --- a/code/ryzom/server/src/server_share/msg_ai_service.h +++ b/code/ryzom/server/src/server_share/msg_ai_service.h @@ -49,7 +49,7 @@ public: propertyCont ("Character", PropDataSetRow, Character); propertyCont ("Creature", PropDataSetRow, Creature); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {}; }; //---------------------------------------------------------------------------- @@ -68,7 +68,7 @@ public: propertyCont ("Content", PropString, Content); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -94,7 +94,7 @@ public: propertyCont ("Params", PropString, Params); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -115,7 +115,7 @@ public: propertyCont ("GrpAlias", PropUInt32, GrpAlias); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -138,7 +138,7 @@ public: property ("TeamId", PropUInt16, CTEAM::InvalidTeamId, TeamId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -160,7 +160,7 @@ public: // propertyCont ("OutpostNames", PropString, OutpostNames); // } // -// virtual void callback (const std::string &name, NLNET::TServiceId id) {}; +// virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} //}; //---------------------------------------------------------------------------- @@ -198,7 +198,7 @@ public: //// propertyCont ("Fames", PropString, DutyNames); // } // -// virtual void callback (const std::string &name, NLNET::TServiceId id) {}; +// virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} //}; //---------------------------------------------------------------------------- @@ -227,7 +227,7 @@ public: propertyCont ("Parameters", PropString, Parameters); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -253,7 +253,7 @@ public: // property ("GuildIndex", PropDataSetRow, TDataSetRow() ,GuildIndex); // } // -// virtual void callback (const std::string &name, NLNET::TServiceId id) {}; +// virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} //}; @@ -280,7 +280,7 @@ public: // property ("GuildId", PropDataSetRow, TDataSetRow() ,GuildId); // } // -// virtual void callback (const std::string &name, NLNET::TServiceId id) {}; +// virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} //}; //---------------------------------------------------------------------------- @@ -299,7 +299,7 @@ public: property ("BotRowId", PropDataSetRow, TDataSetRow() , BotRowId); property ("Heading", PropFloat,0.0f,Heading); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -317,7 +317,7 @@ public: property ("PlayerRowId", PropDataSetRow, TDataSetRow(), PlayerRowId); property ("TargetRowId", PropDataSetRow, TDataSetRow(), TargetRowId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -336,7 +336,7 @@ public: property ("PlayerRowId", PropDataSetRow, TDataSetRow(), PlayerRowId); property ("CreatureRowId", PropDataSetRow, TDataSetRow(), CreatureRowId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -365,7 +365,7 @@ public: property ("PlayerRowId", PropDataSetRow, TDataSetRow(), PlayerRowId); property ("TargetRowId", PropDataSetRow, TDataSetRow(), TargetRowId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -394,7 +394,7 @@ public: property ("PlayerRowId", PropDataSetRow, TDataSetRow(), PlayerRowId); property ("TargetRowId", PropDataSetRow, TDataSetRow(), TargetRowId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -410,7 +410,7 @@ public: className ("CAIPlayerRespawnMsg"); property ("PlayerRowId", PropDataSetRow, TDataSetRow(), PlayerRowId); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -428,7 +428,7 @@ public: property ("EntityRowId", PropDataSetRow, TDataSetRow(), EntityRowId); property ("AskerRowID", PropDataSetRow, TDataSetRow(), AskerRowID); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -448,7 +448,7 @@ public: property ("AskerRowID", PropDataSetRow, TDataSetRow(), AskerRowID); propertyCont ("Infos", PropString, Infos); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -466,7 +466,7 @@ public: property ("EntityRowId", PropDataSetRow, TDataSetRow(), EntityRowId); property ("EnableAggro", PropBool, true, EnableAggro); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -482,7 +482,7 @@ public: className ("CReportAICollisionAvailableMsg"); propertyCont ("ContinentsCollision", PropString, ContinentsCollision); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -500,7 +500,7 @@ public: property ("InstanceNumber", PropUInt32, uint32(0), InstanceNumber); property ("InstanceContinent", PropString, std::string(), InstanceContinent); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -520,7 +520,7 @@ public: property ("ActionName", PropString, std::string(), ActionName); property ("Url", PropString, std::string(), Url); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -536,7 +536,7 @@ public: className ("CReportAIInstanceDespawnMsg"); propertyCont ("InstanceNumbers", PropUInt32, InstanceNumbers); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; @@ -555,7 +555,7 @@ public: className ("CWarnBadInstanceMsg"); property ("InstanceNumber", PropUInt32, uint32(0), InstanceNumber); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -575,7 +575,7 @@ public: propertyCont ("ActionFlags", PropUInt8, ActionFlags); propertyVector ("Values", PropBool, Values); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} inline void push(TDataSetRow entity, uint8 flag, bool value) { @@ -599,7 +599,7 @@ public: propertyCont ("Entities", PropDataSetRow, Entities); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -620,7 +620,7 @@ public: propertyCont ("SetFull", PropUInt8, SetFull); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -639,7 +639,7 @@ public: propertyCont ("DeltaHp", PropSInt32, DeltaHp); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -657,7 +657,7 @@ public: property ("CreatureId", PropDataSetRow, TDataSetRow(), CreatureId); property ("NewMode", PropUInt8, (uint8)0, NewMode); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -673,7 +673,7 @@ public: className ("CCreatureDespawnMsg"); propertyCont ("Entities", PropDataSetRow, Entities); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; ///////////////////////////////////////////////////////////////////////////// @@ -934,7 +934,7 @@ public: property ("MissionAlias", PropUInt32, uint32(0xffffffff), MissionAlias); property ("DespawnTimeInTick", PropUInt32, uint32(0), DespawnTimeInTick); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -954,7 +954,7 @@ public: property ("GroupAlias", PropUInt32, uint32(0xffffffff), GroupAlias); property ("MissionAlias", PropUInt32, uint32(0xffffffff), MissionAlias); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -974,7 +974,7 @@ public: property ("GroupAlias", PropUInt32, uint32(0xffffffff), GroupAlias); property ("MissionAlias", PropUInt32, uint32(0xffffffff), MissionAlias); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -994,7 +994,7 @@ public: property ("GroupAlias", PropUInt32, uint32(0xffffffff), GroupAlias); property ("MissionAlias", PropUInt32, uint32(0xffffffff), MissionAlias); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; @@ -1046,7 +1046,7 @@ public: propertyCont ("Quantities", PropUInt32, Quantities); property ("MissionText", PropString, std::string(), MissionText); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- @@ -1082,7 +1082,7 @@ public: propertyCont ("Quantities", PropUInt32, Quantities); property ("MissionText", PropString, std::string(), MissionText); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {} + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; @@ -1130,7 +1130,7 @@ public: propertyCont ("Params", PropString, Params); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} TFunEnum getFunEnum(const std::string& funName) const; diff --git a/code/ryzom/server/src/server_share/pet_interface_msg.h b/code/ryzom/server/src/server_share/pet_interface_msg.h index 09bbab13d..6571b271a 100644 --- a/code/ryzom/server/src/server_share/pet_interface_msg.h +++ b/code/ryzom/server/src/server_share/pet_interface_msg.h @@ -59,7 +59,7 @@ public: property ("CustomName", PropUCString, ucstring(""), CustomName); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; @@ -93,7 +93,7 @@ public: property ("PetMirrorRow", PropDataSetRow, TDataSetRow(), PetMirrorRow); property ("PetIndex", PropUInt16, (uint16)0, PetIdx); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; @@ -123,7 +123,7 @@ public: property ("Heading", PropFloat, 0.0f, Heading); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------- @@ -151,7 +151,7 @@ public: className ("CPetCommandConfirmationMsg"); property ("CommandError", PropUInt16, (uint16)NO_ERROR_COMMAND, CommandError); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------- @@ -171,7 +171,7 @@ public: property ("PetMirrorRow", PropDataSetRow, TDataSetRow(), PetMirrorRow); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; #endif //RY_PET_INTERFACE_MESSAGES_H From de9b4be9c60dbde8d50225e5da57dbbc1497cf6d Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 22:30:39 +0200 Subject: [PATCH 12/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/misc/speaker_listener.h | 2 +- code/nel/include/nel/sound/source_common.h | 10 +++--- code/nel/include/nel/sound/stream_sound.h | 2 +- .../src/plugins/bnp_manager/bnp_file.h | 2 +- .../3d/tile_edit_qt/tile_listwidgetitem.h | 12 +++---- code/nelns/login_service/mysql_helper.h | 2 +- .../nel_launcher_windows_ext/BarTabsWnd.h | 4 +-- .../client/src/interface_v3/chat_filter.h | 2 +- .../src/interfaces_manager/scroll_bar.h | 2 +- .../include/scintilla/Platform.h | 4 +-- .../src/admin_modules/admin_modules_itf.h | 18 +++++----- .../server/src/ai_service/ai_logic_action.h | 2 +- .../server/src/ai_service/ai_profile_npc.h | 10 +++--- code/ryzom/server/src/ai_service/continent.h | 2 ++ .../server/src/ai_service/family_profile.h | 10 +++--- code/ryzom/server/src/ai_service/fx_entity.h | 2 +- code/ryzom/server/src/ai_service/sheets.h | 1 + .../building_manager/building_physical.h | 4 +-- .../creature_manager/creature_manager.h | 4 +-- .../egs_progress_callback.h | 2 +- .../egs_sheets/egs_sheets.h | 2 +- .../egs_sheets/egs_static_game_item.h | 2 +- .../entity_manager/entity_base.h | 4 +-- .../entity_manager/entity_manager.h | 2 +- .../game_item_manager/game_item_manager.h | 2 +- .../guild_manager/guild_unifier_itf.h | 6 ++-- .../guild_manager/guild_version_adapter.h | 2 +- .../mission_manager/mission_event.h | 34 +++++++++--------- .../mission_manager/mission_step_template.h | 8 ++--- .../modules/r2_give_item.h | 2 +- .../phrase_manager/faber_phrase.h | 8 ++--- .../phrase_manager/fg_extraction_phrase.h | 2 +- .../phrase_manager/fg_prospection_phrase.h | 2 +- .../phrase_manager/harvest_phrase.h | 8 ++--- .../phrase_manager/s_effect.h | 2 +- .../phrase_manager/s_phrase.h | 6 ++-- .../phrase_manager/toxic_cloud.h | 2 +- .../player_manager/cdb.h | 4 +-- .../player_manager/cdb_synchronised.h | 4 +-- .../known_and_memorized_phrases.h | 4 +-- .../player_manager/player_manager.h | 4 +-- .../progression/progression_pve.h | 4 +-- .../pvp_manager/pvp_base.h | 2 ++ .../shop_type/item_for_sale.h | 2 +- .../entities_game_service/world_instances.h | 2 +- .../src/entities_game_service/zone_manager.h | 2 +- .../frontend_service/selection_generator.h | 4 +-- .../src/general_utilities_service/gus_chat.h | 1 + .../general_utilities_service/gus_mirror.h | 14 ++++---- .../gus_module_factory.h | 2 +- .../stat_char_commands.cpp | 20 +++++++---- .../stat_char_filter_factory.cpp | 4 +-- .../stat_char_filter_factory.h | 1 + .../stat_char_info_extractor_factory.cpp | 4 +-- .../stat_char_info_extractor_factory.h | 1 + .../stat_char_scan_script.cpp | 2 +- .../stat_character_scan_job.cpp | 2 +- .../stat_file_list_builder_factory.cpp | 4 +-- .../stat_file_list_builder_factory.h | 1 + .../stat_globals.cpp | 6 ++-- .../stat_job_manager.cpp | 2 +- .../stat_user_file_list_builders.cpp | 6 ++-- .../stats_guild_scan_job.h | 2 +- code/ryzom/server/src/gpm_service/cell.h | 2 +- .../src/input_output_service/chat_manager.h | 4 +-- .../src/patchman_service/module_admin_itf.h | 36 +++++++++---------- .../stat_char_filter_factory.h | 1 + .../stat_char_info_extractor_factory.h | 1 + .../stat_file_list_builder_factory.h | 1 + code/ryzom/server/src/sabrina/faber_phrase.h | 14 ++++---- .../ryzom/server/src/sabrina/harvest_phrase.h | 12 +++---- code/ryzom/server/src/sabrina/magic_phrase.h | 12 +++---- code/ryzom/server/src/sabrina/s_effect.h | 2 +- .../src/server_share/backup_service_itf.h | 12 +++---- .../src/server_share/char_name_mapper_itf.h | 12 +++---- .../src/server_share/chat_unifier_itf.h | 12 +++---- .../src/server_share/command_executor_itf.h | 6 ++-- .../src/server_share/entity_locator_itf.h | 12 +++---- .../src/server_share/logger_service_itf.h | 6 ++-- .../server/src/server_share/mail_forum_itf.h | 6 ++-- .../src/server_share/mission_messages.h | 4 +-- .../server/src/server_share/msg_ai_service.h | 2 +- .../server/src/server_share/msg_gpm_service.h | 2 +- .../server_share/npc_description_messages.h | 11 +++--- .../server/src/server_share/stat_db_tree.h | 12 +++---- .../src/server_share/stat_db_tree_visitor.h | 6 ++-- .../shard_unifier_service/database_mapping.h | 16 ++++----- .../tools/client/client_config/base_dialog.h | 2 +- .../leveldesign/georges_convert/string_ex.h | 6 ++-- .../leveldesign/georges_dll/edit_list_ctrl.h | 10 +++--- .../leveldesign/georges_dll/form_dialog.h | 6 ++-- .../georges_dll/georges_edit_doc.h | 6 ++-- .../georges_dll/georges_interface.h | 2 +- .../georges_dll/output_console_dlg.h | 4 +-- .../georges_dll/plugin_interface.h | 4 +-- .../georges_plugin_sound/PageBase.h | 2 +- .../georges_plugin_sound/PageBgFades.h | 2 +- .../georges_plugin_sound/PageBgFlags.h | 2 +- .../georges_plugin_sound/PageComplex.h | 2 +- .../georges_plugin_sound/PageComtext.h | 2 +- .../georges_plugin_sound/PagePosition.h | 2 +- .../georges_plugin_sound/PageSimple.h | 2 +- .../leveldesign/mission_compiler_lib/step.h | 2 +- .../world_editor/world_editor/action.h | 2 +- .../world_editor/plugin_interface.h | 2 +- .../primitive_plugin.h | 10 +++--- .../tools/stats_scan/char_filter_factory.h | 1 + .../stats_scan/char_info_extractor_factory.h | 1 + code/ryzom/tools/stats_scan/job_manager.h | 3 ++ 109 files changed, 300 insertions(+), 274 deletions(-) diff --git a/code/nel/include/nel/misc/speaker_listener.h b/code/nel/include/nel/misc/speaker_listener.h index 7481e12c8..5f9f86352 100644 --- a/code/nel/include/nel/misc/speaker_listener.h +++ b/code/nel/include/nel/misc/speaker_listener.h @@ -118,7 +118,7 @@ namespace NLMISC _Speaker->registerListener(this); } - void unregisterListener(ISpeaker *speaker) + void unregisterListener(ISpeaker * /* speaker */) { nlassert(_Speaker != NULL); _Speaker->unregisterListener(this); diff --git a/code/nel/include/nel/sound/source_common.h b/code/nel/include/nel/sound/source_common.h index 7781cfb02..34e3f084a 100644 --- a/code/nel/include/nel/sound/source_common.h +++ b/code/nel/include/nel/sound/source_common.h @@ -99,15 +99,15 @@ public: /// \name Streaming source controls //@{ /// Set the sample format. (channels = 1, 2, ...; bitsPerSample = 8, 16; frequency = samples per second, 44100, ...) - virtual void setFormat(uint8 channels, uint8 bitsPerSample, uint32 frequency) { nlassert(false); } + virtual void setFormat(uint8 /* channels */, uint8 /* bitsPerSample */, uint32 /* frequency */) { nlassert(false); } /// Return the sample format information. - virtual void getFormat(uint8 &channels, uint8 &bitsPerSample, uint32 &frequency) const { nlassert(false); } + virtual void getFormat(uint8 &/* channels */, uint8 &/* bitsPerSample */, uint32 &/* frequency */) const { nlassert(false); } /// Get a writable pointer to the buffer of specified size. Use capacity to specify the required bytes. Returns NULL when all the buffer space is already filled. Call setFormat() first. - virtual uint8 *lock(uint capacity) { nlassert(false); return NULL; } + virtual uint8 *lock(uint /* capacity */) { nlassert(false); return NULL; } /// Notify that you are done writing to the locked buffer, so it can be copied over to hardware if needed. Set size to the number of bytes actually written to the buffer. Returns true if ok. - virtual bool unlock(uint size) { nlassert(false); return false; } + virtual bool unlock(uint /* size */) { nlassert(false); return false; } /// Get the recommended buffer size to use with lock()/unlock() - virtual void getRecommendedBufferSize(uint &samples, uint &bytes) const { nlassert(false); } + virtual void getRecommendedBufferSize(uint &/* samples */, uint &/* bytes */) const { nlassert(false); } /// Get the recommended sleep time based on the size of the last submitted buffer and the available buffer space virtual uint32 getRecommendedSleepTime() const { nlassert(false); return 0; } /// Return if there are still buffers available for playback. diff --git a/code/nel/include/nel/sound/stream_sound.h b/code/nel/include/nel/sound/stream_sound.h index 39e6c733c..4b0debe0c 100644 --- a/code/nel/include/nel/sound/stream_sound.h +++ b/code/nel/include/nel/sound/stream_sound.h @@ -46,7 +46,7 @@ public: virtual void importForm(const std::string& filename, NLGEORGES::UFormElm& formRoot); /// Used by the george sound plugin to check sound recursion (ie sound 'toto' use sound 'titi' witch also use sound 'toto' ...). - virtual void getSubSoundList(std::vector > &subsounds) const { } + virtual void getSubSoundList(std::vector > &/* subsounds */) const { } /// Serialize the sound data. virtual void serial(NLMISC::IStream &s); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h index 7527b5dbd..7695ebc2f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -74,7 +74,7 @@ public: * Append the header to a created bnp file * \param filename (consisting the whole path) */ - void appendHeader (const std::string &filename) {}; + void appendHeader (const std::string &filename) {} /** * Create a vector of all packed files inside the bnp file diff --git a/code/nel/tools/3d/tile_edit_qt/tile_listwidgetitem.h b/code/nel/tools/3d/tile_edit_qt/tile_listwidgetitem.h index b12be6437..4cf6ee1ae 100644 --- a/code/nel/tools/3d/tile_edit_qt/tile_listwidgetitem.h +++ b/code/nel/tools/3d/tile_edit_qt/tile_listwidgetitem.h @@ -21,17 +21,17 @@ #include "ui_tile_widget_qt.h" class CTile_ListWidgetItem : public QListWidgetItem - { - public: - CTile_ListWidgetItem ( QListWidget * parent, int type = Type ):QListWidgetItem(parent,type){}; +{ +public: + CTile_ListWidgetItem ( QListWidget * parent, int type = Type ):QListWidgetItem(parent,type){} CTile_ListWidgetItem(QWidget *parent = 0); void initWidget(const QPixmap&, const QString&); - private: - Ui::TileWidget ui; +private: + Ui::TileWidget ui; // Qpixmap tilePixmap; // QString tileLabel; - }; +}; #endif \ No newline at end of file diff --git a/code/nelns/login_service/mysql_helper.h b/code/nelns/login_service/mysql_helper.h index cc81f4647..7f6893b4a 100644 --- a/code/nelns/login_service/mysql_helper.h +++ b/code/nelns/login_service/mysql_helper.h @@ -70,7 +70,7 @@ public: private: //we don't want user to do a copy - CMysqlResult(const CMysqlResult &mysqlResult){}; + CMysqlResult(const CMysqlResult &mysqlResult){} MYSQL_RES *Result; }; diff --git a/code/nelns/login_system/nel_launcher_windows_ext/BarTabsWnd.h b/code/nelns/login_system/nel_launcher_windows_ext/BarTabsWnd.h index eefb93653..2437ae854 100644 --- a/code/nelns/login_system/nel_launcher_windows_ext/BarTabsWnd.h +++ b/code/nelns/login_system/nel_launcher_windows_ext/BarTabsWnd.h @@ -14,9 +14,9 @@ class CTabsObserver { public: - CTabsObserver() {}; + CTabsObserver() {} - virtual void OnTab(int iTab) {}; + virtual void OnTab(int iTab) {} }; class CBarTabsWnd : public CWnd diff --git a/code/ryzom/client/src/interface_v3/chat_filter.h b/code/ryzom/client/src/interface_v3/chat_filter.h index 7524d6c7f..d717d9fb5 100644 --- a/code/ryzom/client/src/interface_v3/chat_filter.h +++ b/code/ryzom/client/src/interface_v3/chat_filter.h @@ -37,7 +37,7 @@ class CChatInputFilter : public NLMISC::CRefCount, public CChatWindow::IObserver { public: CChatInputFilter() {FilterType= CChatGroup::say; DynamicChatDbIndex= 0;} - ~CChatInputFilter(); + virtual ~CChatInputFilter(); /** Display a msg in the chat. The msg will be forwarded to all the listening windows * Listening windows will blick only if there isnt a single visible listening window, so that the player can know if there's a message * \param windowVisible is not NULL, points a bool that will be filled with true if one of the window on the which the msg was displayed is visible. diff --git a/code/ryzom/client/src/interfaces_manager/scroll_bar.h b/code/ryzom/client/src/interfaces_manager/scroll_bar.h index f9e42473a..5b2e248df 100644 --- a/code/ryzom/client/src/interfaces_manager/scroll_bar.h +++ b/code/ryzom/client/src/interfaces_manager/scroll_bar.h @@ -44,7 +44,7 @@ public: CScrollBar(uint id, float x, float y, float x_pixel, float y_pixel, float w, float h, float w_pixel, float h_pixel, bool vertical = true,CScrollableControl *ctrl = NULL); /// destructor - virtual ~CScrollBar() {}; + virtual ~CScrollBar() {} /// Display the control virtual void display(); diff --git a/code/ryzom/client/src/lua_ide_dll_nevrax/include/scintilla/Platform.h b/code/ryzom/client/src/lua_ide_dll_nevrax/include/scintilla/Platform.h index 5a07d109a..8a8d25ff0 100644 --- a/code/ryzom/client/src/lua_ide_dll_nevrax/include/scintilla/Platform.h +++ b/code/ryzom/client/src/lua_ide_dll_nevrax/include/scintilla/Platform.h @@ -277,8 +277,8 @@ private: Surface(const Surface &) {} Surface &operator=(const Surface &) { return *this; } public: - Surface() {}; - virtual ~Surface() {}; + Surface() {} + virtual ~Surface() {} static Surface *Allocate(); virtual void Init()=0; diff --git a/code/ryzom/server/src/admin_modules/admin_modules_itf.h b/code/ryzom/server/src/admin_modules/admin_modules_itf.h index 3f430ed43..248ba6834 100644 --- a/code/ryzom/server/src/admin_modules/admin_modules_itf.h +++ b/code/ryzom/server/src/admin_modules/admin_modules_itf.h @@ -1829,9 +1829,9 @@ namespace ADMIN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -1969,9 +1969,9 @@ namespace ADMIN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -2202,9 +2202,9 @@ namespace ADMIN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/ai_service/ai_logic_action.h b/code/ryzom/server/src/ai_service/ai_logic_action.h index 631f9f620..452087b18 100644 --- a/code/ryzom/server/src/ai_service/ai_logic_action.h +++ b/code/ryzom/server/src/ai_service/ai_logic_action.h @@ -46,7 +46,7 @@ public: // note that actions are NOT responsible for deleting child actions virtual ~IAILogicAction() - {}; + {} // this is the execute 'callback' for the action type. // NOTE: This code should be fast and compact as it may be called very large numbers of times diff --git a/code/ryzom/server/src/ai_service/ai_profile_npc.h b/code/ryzom/server/src/ai_service/ai_profile_npc.h index 9fed6e662..f8ec911f4 100644 --- a/code/ryzom/server/src/ai_service/ai_profile_npc.h +++ b/code/ryzom/server/src/ai_service/ai_profile_npc.h @@ -633,7 +633,7 @@ public CMoveProfile { public: CGrpProfileFollowPlayer(CProfileOwner* owner, TDataSetRow const& playerRow, uint32 dispersionRadius); - virtual ~CGrpProfileFollowPlayer() {}; + virtual ~CGrpProfileFollowPlayer() {} void setBotStandProfile(AITYPES::TProfiles botStandProfileType, IAIProfileFactory* botStandProfileFactory); @@ -641,16 +641,16 @@ public: //@{ virtual void beginProfile(); virtual void updateProfile(uint ticksSinceLastUpdate); - virtual void endProfile() {}; + virtual void endProfile() {} virtual AITYPES::TProfiles getAIProfileType() const { return AITYPES::BOT_FOLLOW_POS; } virtual std::string getOneLineInfoString() const { return std::string("follow_player group profile"); } //@} - void stateChangeProfile() {}; + void stateChangeProfile() {} bool destinationReach() const; - void addBot (CBot* bot) {}; - void removeBot (CBot* bot) {}; + void addBot (CBot* bot) {} + void removeBot (CBot* bot) {} CPathCont* getPathCont (CBot const* bot) { return NULL; }; diff --git a/code/ryzom/server/src/ai_service/continent.h b/code/ryzom/server/src/ai_service/continent.h index a9a265e7d..8b92cf6b9 100644 --- a/code/ryzom/server/src/ai_service/continent.h +++ b/code/ryzom/server/src/ai_service/continent.h @@ -529,6 +529,7 @@ class CLazyProcess : public NLMISC::CRefCount { public: + virtual ~CLazyProcess() {} /// Used to update some dependencies for instance road connections when the first next update of CContinent is called. virtual void update() const = 0; /// Used to check if there's no need to add another lazyprocess. @@ -1052,6 +1053,7 @@ class IGroupDesc : public NLMISC::CRefCount { public: + virtual ~IGroupDesc() {} virtual uint32 groupEnergyValue() const = 0; virtual float groupEnergyCoef() const = 0; virtual bool getCountMultiplierFlag() const = 0; diff --git a/code/ryzom/server/src/ai_service/family_profile.h b/code/ryzom/server/src/ai_service/family_profile.h index a07acab8a..b4df92d92 100644 --- a/code/ryzom/server/src/ai_service/family_profile.h +++ b/code/ryzom/server/src/ai_service/family_profile.h @@ -87,15 +87,15 @@ public: virtual void update() =0; /// Fill a vector of outpost id name assigned to tribe - virtual void fillOutpostNames(std::vector outpostNames) {}; + virtual void fillOutpostNames(std::vector outpostNames) {} /// Add an outpost for the tribe (nb : the family must be a tribe) - virtual void outpostAdd(NLMISC::TStringId outpostName) {}; + virtual void outpostAdd(NLMISC::TStringId outpostName) {} /// Remove an from the tribe - virtual void outpostRemove(NLMISC::TStringId outpostName) {}; + virtual void outpostRemove(NLMISC::TStringId outpostName) {} - virtual void outpostEvent(NLMISC::TStringId outpostName, ZCSTATE::TZcState state) {}; + virtual void outpostEvent(NLMISC::TStringId outpostName, ZCSTATE::TZcState state) {} - virtual void spawnBoss(NLMISC::TStringId outpostName) {}; + virtual void spawnBoss(NLMISC::TStringId outpostName) {} CGroupNpc *createNpcGroup(const CNpcZone *const zone, const CGroupDesc *const groupDesc); diff --git a/code/ryzom/server/src/ai_service/fx_entity.h b/code/ryzom/server/src/ai_service/fx_entity.h index 3fbf66b92..78f4bb37b 100644 --- a/code/ryzom/server/src/ai_service/fx_entity.h +++ b/code/ryzom/server/src/ai_service/fx_entity.h @@ -28,7 +28,7 @@ class CFxEntity { public: CFxEntity(CAIPos const& pos, NLMISC::CSheetId const& sheet); - ~CFxEntity(); + virtual ~CFxEntity(); NLMISC::CEntityId const& id() const; bool spawn(); void despawn(); diff --git a/code/ryzom/server/src/ai_service/sheets.h b/code/ryzom/server/src/ai_service/sheets.h index 7526218c0..0473af4a9 100644 --- a/code/ryzom/server/src/ai_service/sheets.h +++ b/code/ryzom/server/src/ai_service/sheets.h @@ -644,6 +644,7 @@ class IRaceStats : public NLMISC::CRefCount { public: + virtual ~IRaceStats() {} virtual NLMISC::CSheetId SheetId() const = 0; virtual std::string Race() const = 0; }; diff --git a/code/ryzom/server/src/entities_game_service/building_manager/building_physical.h b/code/ryzom/server/src/entities_game_service/building_manager/building_physical.h index d37966aeb..d635ecd71 100644 --- a/code/ryzom/server/src/entities_game_service/building_manager/building_physical.h +++ b/code/ryzom/server/src/entities_game_service/building_manager/building_physical.h @@ -73,9 +73,9 @@ public: /// fill the icon param, send the description text to the user and store the resulting text id in the textId param. virtual void getClientDescription(uint16 roomIdx, uint16 ownerIndex, CCharacter * user, uint64 & icon, uint32 & textId )const = 0; /// callback called when all the appartments of a user must be removed - virtual void onPlayerDeletion( const NLMISC::CEntityId & userId ){}; + virtual void onPlayerDeletion( const NLMISC::CEntityId & userId ){} /// callback called when all the appartments of a guild must be removed - virtual void onGuildDeletion( uint32 guild ){}; + virtual void onGuildDeletion( uint32 guild ){} /// return true if the user is allowed to go in the building virtual bool isUserAllowed(CCharacter * user, uint16 ownerIdx, uint16 roomIdx) = 0; ///return the number of owner in this building ( return 1 for common buildings ) diff --git a/code/ryzom/server/src/entities_game_service/creature_manager/creature_manager.h b/code/ryzom/server/src/entities_game_service/creature_manager/creature_manager.h index 75b40c5c3..388c9f3b4 100644 --- a/code/ryzom/server/src/entities_game_service/creature_manager/creature_manager.h +++ b/code/ryzom/server/src/entities_game_service/creature_manager/creature_manager.h @@ -121,14 +121,14 @@ public: /// exception thrown when creature is unknown struct ECreature : public NLMISC::Exception { - ECreature( const NLMISC::CEntityId& id ) : Exception ("The creature "+id.toString()+" doesn't exist") {}; + ECreature( const NLMISC::CEntityId& id ) : Exception ("The creature "+id.toString()+" doesn't exist") {} }; /// structure describing an unaffected fauna group ( because the description is received before mirror update ) struct SUnaffectedFaunaGroup { SUnaffectedFaunaGroup(const TDataSetRow& entityIndex,TAIAlias groupAlias) - :EntityIndex( entityIndex ),GroupAlias( groupAlias ){}; + :EntityIndex( entityIndex ),GroupAlias( groupAlias ){} TDataSetRow EntityIndex; TAIAlias GroupAlias; }; diff --git a/code/ryzom/server/src/entities_game_service/egs_progress_callback.h b/code/ryzom/server/src/entities_game_service/egs_progress_callback.h index bb1fc9de6..78cbc356e 100644 --- a/code/ryzom/server/src/entities_game_service/egs_progress_callback.h +++ b/code/ryzom/server/src/entities_game_service/egs_progress_callback.h @@ -31,7 +31,7 @@ class CEGSProgressCallback : public NLMISC::IProgressCallback { public: CEGSProgressCallback():IProgressCallback() {} - virtual void progress (float progressValue) {}; + virtual void progress (float progressValue) {} virtual ~CEGSProgressCallback() {} }; diff --git a/code/ryzom/server/src/entities_game_service/egs_sheets/egs_sheets.h b/code/ryzom/server/src/entities_game_service/egs_sheets/egs_sheets.h index 41c1bcec4..a1619f5f4 100644 --- a/code/ryzom/server/src/entities_game_service/egs_sheets/egs_sheets.h +++ b/code/ryzom/server/src/entities_game_service/egs_sheets/egs_sheets.h @@ -74,7 +74,7 @@ public : /// exception thrown when a sheet is unknown struct ESheet : public NLMISC::Exception { - ESheet( const NLMISC::CSheetId& sheetId ) : Exception ("The sheet "+sheetId.toString()+" is unknown") {}; + ESheet( const NLMISC::CSheetId& sheetId ) : Exception ("The sheet "+sheetId.toString()+" is unknown") {} }; public: diff --git a/code/ryzom/server/src/entities_game_service/egs_sheets/egs_static_game_item.h b/code/ryzom/server/src/entities_game_service/egs_sheets/egs_static_game_item.h index 0ef0e0335..0840d01d1 100644 --- a/code/ryzom/server/src/entities_game_service/egs_sheets/egs_static_game_item.h +++ b/code/ryzom/server/src/entities_game_service/egs_sheets/egs_static_game_item.h @@ -429,7 +429,7 @@ struct SShield : public SArmor NL_INSTANCE_COUNTER_DECL(SShield); public: - inline SShield() : SArmor(),ShieldType(SHIELDTYPE::NONE),Unbreakable(false){}; + inline SShield() : SArmor(),ShieldType(SHIELDTYPE::NONE),Unbreakable(false){} virtual void serial(class NLMISC::IStream &f) { diff --git a/code/ryzom/server/src/entities_game_service/entity_manager/entity_base.h b/code/ryzom/server/src/entities_game_service/entity_manager/entity_base.h index 81e5b6cd8..fc3cd5903 100644 --- a/code/ryzom/server/src/entities_game_service/entity_manager/entity_base.h +++ b/code/ryzom/server/src/entities_game_service/entity_manager/entity_base.h @@ -140,7 +140,7 @@ public: _ExceptionString = std::string(" : Invalid stat name ") + var; return _ExceptionString.c_str(); } - virtual ~EInvalidStat() throw() {}; + virtual ~EInvalidStat() throw() {} private: mutable std::string _ExceptionString; }; @@ -820,7 +820,7 @@ public: /// Return the damage using current armor, done by an explosion (e.g. forage source explosion) virtual float getActualDamageFromExplosionWithArmor( float dmg ) const =0; - inline virtual void clearCurrentAction() {}; + inline virtual void clearCurrentAction() {} inline virtual void setCurrentAction(CLIENT_ACTION_TYPE::TClientActionType,NLMISC::TGameCycle) {} diff --git a/code/ryzom/server/src/entities_game_service/entity_manager/entity_manager.h b/code/ryzom/server/src/entities_game_service/entity_manager/entity_manager.h index 88f893c91..cf5422f1a 100644 --- a/code/ryzom/server/src/entities_game_service/entity_manager/entity_manager.h +++ b/code/ryzom/server/src/entities_game_service/entity_manager/entity_manager.h @@ -59,7 +59,7 @@ public: /// exception thrown when entity is unknown struct EEntity : public NLMISC::Exception { - EEntity( const NLMISC::CEntityId& id ) : Exception ("The entity "+id.toString()+" doesn't exist") {}; + EEntity( const NLMISC::CEntityId& id ) : Exception ("The entity "+id.toString()+" doesn't exist") {} }; /* // Success table for calculation of success probability and associate xp-gains diff --git a/code/ryzom/server/src/entities_game_service/game_item_manager/game_item_manager.h b/code/ryzom/server/src/entities_game_service/game_item_manager/game_item_manager.h index a0aee63fc..8bd4e6b90 100644 --- a/code/ryzom/server/src/entities_game_service/game_item_manager/game_item_manager.h +++ b/code/ryzom/server/src/entities_game_service/game_item_manager/game_item_manager.h @@ -61,7 +61,7 @@ public : /// exception thrown when a sheet is unknown struct ESheet : public NLMISC::Exception { - ESheet( const NLMISC::CSheetId& sheetId ) : Exception ("The sheet "+sheetId.toString()+" is unknown") {}; + ESheet( const NLMISC::CSheetId& sheetId ) : Exception ("The sheet "+sheetId.toString()+" is unknown") {} }; public: diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild_unifier_itf.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild_unifier_itf.h index 1280fe6c2..98a4f9229 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild_unifier_itf.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild_unifier_itf.h @@ -490,9 +490,9 @@ namespace GU // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild_version_adapter.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild_version_adapter.h index 2bb3464de..8b59b2a97 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild_version_adapter.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild_version_adapter.h @@ -50,7 +50,7 @@ private: void adaptToVersion4(CGuild &guild) const; private: - CGuildVersionAdapter(){}; + CGuildVersionAdapter(){} /// unique instance static CGuildVersionAdapter* _Instance; }; diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h index a0f9e77d7..b5bb0fccd 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h @@ -94,7 +94,7 @@ public: static bool simMissionEvent(const std::vector< std::string > & script, NLMISC::CLog & log ); protected: - CMissionEvent(){}; + CMissionEvent(){} /// build an event from a script virtual bool buildFromScript( const std::vector< std::string > & script,NLMISC::CLog& log ) = 0; @@ -114,7 +114,7 @@ public: uint32 Mission; protected: friend class CMissionEvent; - CMissionEventMissionDone(){}; + CMissionEventMissionDone(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -130,7 +130,7 @@ public: std::string ScenarioTag; protected: friend class CMissionEvent; - CMissionEventTaggedRingScenarioDone(){}; + CMissionEventTaggedRingScenarioDone(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -158,7 +158,7 @@ public: uint32 Amount; protected: friend class CMissionEvent; - CMissionEventGiveMoney(){}; + CMissionEventGiveMoney(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -171,7 +171,7 @@ public: uint32 PlaceId; protected: friend class CMissionEvent; - CMissionEventVisitPlace(){}; + CMissionEventVisitPlace(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -184,7 +184,7 @@ public: std::vector Bricks; protected: friend class CMissionEvent; - CMissionEventCast(){}; + CMissionEventCast(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -196,7 +196,7 @@ public: :CMissionEvent(Kill, targetEntity, restriction){} protected: friend class CMissionEvent; - CMissionEventKill(){}; + CMissionEventKill(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -209,7 +209,7 @@ public: :CMissionEvent(KillPlayer, victimId){} protected: friend class CMissionEvent; - CMissionEventKillPlayer(){}; + CMissionEventKillPlayer(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -225,7 +225,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventBuyItem(){}; + CMissionEventBuyItem(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -241,7 +241,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventSellItem(){}; + CMissionEventSellItem(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -257,7 +257,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventForage(){}; + CMissionEventForage(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -283,7 +283,7 @@ public: uint Level; protected: friend class CMissionEvent; - CMissionEventSkillProgress(){}; + CMissionEventSkillProgress(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -295,7 +295,7 @@ public: :CMissionEvent(Target,targetEntity ){} protected: friend class CMissionEvent; - CMissionEventTarget(){}; + CMissionEventTarget(){} bool buildFromScript( const std::vector< std::string > & script,NLMISC::CLog& log ); }; @@ -311,7 +311,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventCraft(){}; + CMissionEventCraft(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -351,7 +351,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventLootItem(){}; + CMissionEventLootItem(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -366,7 +366,7 @@ public: uint16 Quality; protected: friend class CMissionEvent; - CMissionEventLootRm(){}; + CMissionEventLootRm(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; @@ -379,7 +379,7 @@ public: TAIAlias Alias; protected: friend class CMissionEvent; - CMissionEventKillGroup(){}; + CMissionEventKillGroup(){} bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log); }; diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_template.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_template.h index 132463bec..6e92fb23f 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_template.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_template.h @@ -102,7 +102,7 @@ public: ///\name accessors //@{ /// return the step actions - inline const std::vector< IMissionAction* > & getActions(){ return _Actions; }; + inline const std::vector< IMissionAction* > & getActions(){ return _Actions; } /// set the out of order index of the step ( it is the index of the OOO/ANY bloc embedding the step ) 0xFF is invalid inline void setOOOStepIndex(uint32 idx){ _OOOStepIndex = idx; } ///\return the OOO index ( see previous line ) @@ -120,9 +120,9 @@ public: ///\set displayed value void setIconDisplayedOnStepNPC(bool displayed){ _IconDisplayedOnStepNPC = displayed;} /// return true if the step is in an OOO block which text wad overriden - bool isInOverridenOOO() { return _IsInOverridenOOO; }; + bool isInOverridenOOO() { return _IsInOverridenOOO; } /// set the isInOverridenOOO flag ( see previous method ) - void setAsInOverridenOOO() { _IsInOverridenOOO = true; }; + void setAsInOverridenOOO() { _IsInOverridenOOO = true; } /// return true if there is a roleplay text in this step bool isThereRoleplayText() { return !_RoleplayText.empty(); } /// check if the current player gift is ok @@ -130,7 +130,7 @@ public: /// retrieve the escort groups - virtual void getEscortGroups( std::vector< TAIAlias > & groups ){}; + virtual void getEscortGroups( std::vector< TAIAlias > & groups ){} virtual bool checkEscortFailure( bool groupWiped ){return false;} /// check the consistency of a text step diff --git a/code/ryzom/server/src/entities_game_service/modules/r2_give_item.h b/code/ryzom/server/src/entities_game_service/modules/r2_give_item.h index 9e56b8aea..a5d46c766 100644 --- a/code/ryzom/server/src/entities_game_service/modules/r2_give_item.h +++ b/code/ryzom/server/src/entities_game_service/modules/r2_give_item.h @@ -81,7 +81,7 @@ public: typedef std::map< TCreatureRowId, TCreatureItemRequest > TPendingRequest; // constructor - CR2GiveItem() {}; + CR2GiveItem() {} // destructor ~CR2GiveItem() {} diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/faber_phrase.h b/code/ryzom/server/src/entities_game_service/phrase_manager/faber_phrase.h index 80a2cf5fb..9458a540d 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/faber_phrase.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/faber_phrase.h @@ -75,10 +75,10 @@ public: ///\unused basic methods from CSPhrase //@{ - virtual void setPrimaryItem( CGameItemPtr itemPtr ){}; - virtual void setSecondaryItem( CGameItemPtr itemPtr ){}; - virtual void addConsumableItem( CGameItemPtr itemPtr ){}; - virtual void setPrimaryTarget( const TDataSetRow& ) {}; + virtual void setPrimaryItem( CGameItemPtr itemPtr ){} + virtual void setSecondaryItem( CGameItemPtr itemPtr ){} + virtual void addConsumableItem( CGameItemPtr itemPtr ){} + virtual void setPrimaryTarget( const TDataSetRow& ) {} //@} // Craft methodes diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/fg_extraction_phrase.h b/code/ryzom/server/src/entities_game_service/phrase_manager/fg_extraction_phrase.h index 250481562..e8941a6ac 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/fg_extraction_phrase.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/fg_extraction_phrase.h @@ -60,7 +60,7 @@ public: /** * set the actor */ - virtual void setActor( const TDataSetRow &entityRowId ){}; + virtual void setActor( const TDataSetRow &entityRowId ){} /** * called at the end of the latency time diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/fg_prospection_phrase.h b/code/ryzom/server/src/entities_game_service/phrase_manager/fg_prospection_phrase.h index 48e27234f..1f202910c 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/fg_prospection_phrase.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/fg_prospection_phrase.h @@ -86,7 +86,7 @@ public: /** * set the actor */ - virtual void setActor( const TDataSetRow &entityRowId ){}; + virtual void setActor( const TDataSetRow &entityRowId ){} //@} diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/harvest_phrase.h b/code/ryzom/server/src/entities_game_service/phrase_manager/harvest_phrase.h index c5cc5afaf..fc011a623 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/harvest_phrase.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/harvest_phrase.h @@ -62,10 +62,10 @@ public: ///\unused basic methods from CSPhrase //@{ - virtual void setPrimaryItem( CGameItemPtr itemPtr ){}; - virtual void setSecondaryItem( CGameItemPtr itemPtr ){}; - virtual void addConsumableItem( CGameItemPtr itemPtr ){}; - virtual void setPrimaryTarget( const TDataSetRow& ) {}; + virtual void setPrimaryItem( CGameItemPtr itemPtr ){} + virtual void setSecondaryItem( CGameItemPtr itemPtr ){} + virtual void addConsumableItem( CGameItemPtr itemPtr ){} + virtual void setPrimaryTarget( const TDataSetRow& ) {} //@} //@} diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/s_effect.h b/code/ryzom/server/src/entities_game_service/phrase_manager/s_effect.h index 7062a0dfd..9a73a3a07 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/s_effect.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/s_effect.h @@ -149,7 +149,7 @@ public: } // callback called when the effect is actually removed. Does nothing by default - virtual void removed(){}; + virtual void removed(){} ///\name read accessors //@{ diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/s_phrase.h b/code/ryzom/server/src/entities_game_service/phrase_manager/s_phrase.h index 0b5ca1862..9d6ecd5f1 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/s_phrase.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/s_phrase.h @@ -113,7 +113,7 @@ public: * set the primary target * \param entityId id of the primary target */ - virtual void setPrimaryTarget( const TDataSetRow &entityRowId ) {}; + virtual void setPrimaryTarget( const TDataSetRow &/* entityRowId */ ) {} /** * evaluate phrase @@ -188,9 +188,9 @@ public: /** Return true if the phrase if grammatically valid (otherwise, send a msg to the player actorRowId) * TODO: anti-hacking */ - static bool validateSabrinaGrammar( const std::vector< const CStaticBrick* >& bricks, TDataSetRow actorRowId ) { return true; } + static bool validateSabrinaGrammar( const std::vector< const CStaticBrick* >& /* bricks */, TDataSetRow /* actorRowId */ ) { return true; } - virtual void setBrickSheets( const std::vector & bricks){} + virtual void setBrickSheets( const std::vector & /* bricks */){} virtual void setEnchantMode(bool){} inline void cyclic(bool b) { _CyclicPhrase = b; } diff --git a/code/ryzom/server/src/entities_game_service/phrase_manager/toxic_cloud.h b/code/ryzom/server/src/entities_game_service/phrase_manager/toxic_cloud.h index f0bcb46c5..d1cd98246 100644 --- a/code/ryzom/server/src/entities_game_service/phrase_manager/toxic_cloud.h +++ b/code/ryzom/server/src/entities_game_service/phrase_manager/toxic_cloud.h @@ -36,7 +36,7 @@ class CToxicCloud : public CEnvironmentalEffect NL_INSTANCE_COUNTER_DECL(CToxicCloud); public: - CToxicCloud() {}; + CToxicCloud() {} /// Init inline void init( const NLMISC::CVector& pos, float radius, sint32 dmgPerHit, NLMISC::TGameCycle updateFrequency, NLMISC::TGameCycle lifetime=ToxicCloudDefaultLifetime) diff --git a/code/ryzom/server/src/entities_game_service/player_manager/cdb.h b/code/ryzom/server/src/entities_game_service/player_manager/cdb.h index d1f7fd8f1..a2af677f7 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/cdb.h +++ b/code/ryzom/server/src/entities_game_service/player_manager/cdb.h @@ -216,7 +216,7 @@ public : /** * Inform a node of its parenthood */ - virtual void setParent(CCDBStructNodeBranch *parent) { nlassertex(0,("setParent() not overloaded for given node type!")); } + virtual void setParent(CCDBStructNodeBranch * /* parent */) { nlassertex(0,("setParent() not overloaded for given node type!")); } /** * get the parent of a node @@ -248,7 +248,7 @@ public : * Browse the tree, and for each atom branch encountered, call the callback passing the argument * provided and the index of the atom branch. */ - virtual void foreachAtomBranchCall( void (*callback)(void*,TCDBDataIndex), void *arg ) const {}; + virtual void foreachAtomBranchCall( void (*callback)(void*,TCDBDataIndex), void *arg ) const {} /** * Browse the tree, building the text id, and for each leaf encountered, call the callback diff --git a/code/ryzom/server/src/entities_game_service/player_manager/cdb_synchronised.h b/code/ryzom/server/src/entities_game_service/player_manager/cdb_synchronised.h index 3968515d2..8e09617ad 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/cdb_synchronised.h +++ b/code/ryzom/server/src/entities_game_service/player_manager/cdb_synchronised.h @@ -50,12 +50,12 @@ public: /// exception thrown when database is not initialized struct EDBNotInit : public NLMISC::Exception { - EDBNotInit() : Exception("CDB: Property Database not initialized") {}; + EDBNotInit() : Exception("CDB: Property Database not initialized") {} }; struct ECDBNotFound : public NLMISC::Exception { - ECDBNotFound() : Exception("CDB: Property not found") {}; + ECDBNotFound() : Exception("CDB: Property not found") {} }; /*struct CPropForClientOnly diff --git a/code/ryzom/server/src/entities_game_service/player_manager/known_and_memorized_phrases.h b/code/ryzom/server/src/entities_game_service/player_manager/known_and_memorized_phrases.h index 646913107..f7fd1d851 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/known_and_memorized_phrases.h +++ b/code/ryzom/server/src/entities_game_service/player_manager/known_and_memorized_phrases.h @@ -126,7 +126,7 @@ private: private: // prevent copy constructor - CMemorizationSet(const CMemorizationSet &other) {}; + CMemorizationSet(const CMemorizationSet &other) {} // prevent copy operator CMemorizationSet &operator =(const CMemorizationSet&other) { return *this;} }; @@ -183,7 +183,7 @@ private: private: // prevent copy constructor - CPlayerPhraseMemory(const CMemorizationSet &other) {}; + CPlayerPhraseMemory(const CMemorizationSet &other) {} // prevent copy operator CPlayerPhraseMemory &operator =(const CMemorizationSet&other) { return *this;} }; diff --git a/code/ryzom/server/src/entities_game_service/player_manager/player_manager.h b/code/ryzom/server/src/entities_game_service/player_manager/player_manager.h index ecacee896..33e4a8ac9 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/player_manager.h +++ b/code/ryzom/server/src/entities_game_service/player_manager/player_manager.h @@ -166,13 +166,13 @@ public : /// exception thrown when player is unknown struct EPlayer : public NLMISC::Exception { - EPlayer( uint32 userId ) : Exception ("Player "+NLMISC::toString(userId)+" not found") {}; + EPlayer( uint32 userId ) : Exception ("Player "+NLMISC::toString(userId)+" not found") {} }; /// exception thrown when player's char is unknown struct EChar : public NLMISC::Exception { - EChar( const NLMISC::CEntityId& id ) : Exception ("The char "+id.toString()+" doesn't exist") {}; + EChar( const NLMISC::CEntityId& id ) : Exception ("The char "+id.toString()+" doesn't exist") {} }; ///init the manager diff --git a/code/ryzom/server/src/entities_game_service/progression/progression_pve.h b/code/ryzom/server/src/entities_game_service/progression/progression_pve.h index 4526c67f6..54cd5f0f2 100644 --- a/code/ryzom/server/src/entities_game_service/progression/progression_pve.h +++ b/code/ryzom/server/src/entities_game_service/progression/progression_pve.h @@ -64,7 +64,7 @@ public: }; /// ctor - CSkillProgress() {}; + CSkillProgress() {} /// dtor virtual ~CSkillProgress() { _SkillsProgress.clear(); } @@ -97,7 +97,7 @@ public: typedef std::map< TDataSetRow, CSkillProgress * > TSkillProgressPerOpponentContainer; /// ctor - CCharacterActions() {}; + CCharacterActions() {} /// dtor+ virtual ~CCharacterActions(); diff --git a/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_base.h b/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_base.h index c26b9f8bc..d48dcdb96 100644 --- a/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_base.h +++ b/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_base.h @@ -56,6 +56,8 @@ public: QuitTeam, }; + virtual ~IPVP() {} + /// get PVP mode to send to the client virtual PVP_MODE::TPVPMode getPVPMode() const = 0; diff --git a/code/ryzom/server/src/entities_game_service/shop_type/item_for_sale.h b/code/ryzom/server/src/entities_game_service/shop_type/item_for_sale.h index fac7fa505..b01bfaa9b 100644 --- a/code/ryzom/server/src/entities_game_service/shop_type/item_for_sale.h +++ b/code/ryzom/server/src/entities_game_service/shop_type/item_for_sale.h @@ -33,7 +33,7 @@ class IItemTrade : public NLMISC::CRefCount public: // virtual destructor - virtual ~IItemTrade() {}; + virtual ~IItemTrade() {} // sheet id virtual NLMISC::CSheetId getSheetId() const = 0; diff --git a/code/ryzom/server/src/entities_game_service/world_instances.h b/code/ryzom/server/src/entities_game_service/world_instances.h index eb8c2728c..63a70204d 100644 --- a/code/ryzom/server/src/entities_game_service/world_instances.h +++ b/code/ryzom/server/src/entities_game_service/world_instances.h @@ -56,7 +56,7 @@ public: struct IAIInstanceReady { virtual void onAiInstanceReady(const CReportStaticAIInstanceMsg &msg) = 0; - virtual void onAiInstanceDown(const CReportStaticAIInstanceMsg &msg) {}; // not mandatory + virtual void onAiInstanceDown(const CReportStaticAIInstanceMsg &msg) {} // not mandatory }; /// Singleton access diff --git a/code/ryzom/server/src/entities_game_service/zone_manager.h b/code/ryzom/server/src/entities_game_service/zone_manager.h index 5aeb2a4a0..065e4548f 100644 --- a/code/ryzom/server/src/entities_game_service/zone_manager.h +++ b/code/ryzom/server/src/entities_game_service/zone_manager.h @@ -318,7 +318,7 @@ public: }; // default constructor - CZoneManager() : DepositSearchTime(50) {}; + CZoneManager() : DepositSearchTime(50) {} ~CZoneManager(); diff --git a/code/ryzom/server/src/frontend_service/selection_generator.h b/code/ryzom/server/src/frontend_service/selection_generator.h index 6f94e2e68..048e391a4 100644 --- a/code/ryzom/server/src/frontend_service/selection_generator.h +++ b/code/ryzom/server/src/frontend_service/selection_generator.h @@ -42,10 +42,10 @@ public: typedef TSelectionLevel result_type; /// Initialization of a selection cycle - virtual void init( TSelectionLevel nblevels ) {}; + virtual void init( TSelectionLevel nblevels ) {} /// Change the number of levels without restarting the cycle - virtual void changeNbLevels( TSelectionLevel nblevels ) {}; + virtual void changeNbLevels( TSelectionLevel nblevels ) {} /// Return the next level to select virtual TSelectionLevel getNext() = 0; diff --git a/code/ryzom/server/src/general_utilities_service/gus_chat.h b/code/ryzom/server/src/general_utilities_service/gus_chat.h index 077f24506..2c49c14f8 100644 --- a/code/ryzom/server/src/general_utilities_service/gus_chat.h +++ b/code/ryzom/server/src/general_utilities_service/gus_chat.h @@ -71,6 +71,7 @@ namespace GUS //----------------------------------------------------------------------------- // Public interface //----------------------------------------------------------------------------- + virtual ~CChatChannel() {} // open the chat channel on all clients and set its name // If historySize is set other than 0, the the chat channel is diff --git a/code/ryzom/server/src/general_utilities_service/gus_mirror.h b/code/ryzom/server/src/general_utilities_service/gus_mirror.h index 096685d50..97e8d97c6 100644 --- a/code/ryzom/server/src/general_utilities_service/gus_mirror.h +++ b/code/ryzom/server/src/general_utilities_service/gus_mirror.h @@ -40,15 +40,15 @@ namespace GUS { public: // General callback - virtual void mirrorIsReady(CGusMirror *gusMirror) {}; - virtual void serviceMirrorUp(CGusMirror *gusMirror, const std::string &serviceName, NLNET::TServiceId serviceId) {}; - virtual void serviceMirrorDown(CGusMirror *gusMirror, const std::string &serviceName, NLNET::TServiceId serviceId) {}; - virtual void mirrorTickUpdate(CGusMirror *gusMirror) {}; + virtual void mirrorIsReady(CGusMirror *gusMirror) {} + virtual void serviceMirrorUp(CGusMirror *gusMirror, const std::string &serviceName, NLNET::TServiceId serviceId) {} + virtual void serviceMirrorDown(CGusMirror *gusMirror, const std::string &serviceName, NLNET::TServiceId serviceId) {} + virtual void mirrorTickUpdate(CGusMirror *gusMirror) {} // mirror updates - virtual void entityAdded(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex) {}; - virtual void entityRemoved(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex, const NLMISC::CEntityId *entityId) {}; - virtual void propertyChanged(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex, TPropertyIndex propIndex) {}; + virtual void entityAdded(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex) {} + virtual void entityRemoved(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex, const NLMISC::CEntityId *entityId) {} + virtual void propertyChanged(CGusMirror *gusMirror, CMirroredDataSet *dataSet, const TDataSetRow &entityIndex, TPropertyIndex propIndex) {} IMirrorModuleCallback(); virtual ~IMirrorModuleCallback(); diff --git a/code/ryzom/server/src/general_utilities_service/gus_module_factory.h b/code/ryzom/server/src/general_utilities_service/gus_module_factory.h index 57c39bf06..fc9a62d38 100644 --- a/code/ryzom/server/src/general_utilities_service/gus_module_factory.h +++ b/code/ryzom/server/src/general_utilities_service/gus_module_factory.h @@ -41,7 +41,7 @@ namespace GUS class IModuleBuilder: public NLMISC::CRefCount { public: - virtual ~IModuleBuilder() {}; + virtual ~IModuleBuilder() {} virtual const NLMISC::CSString& getName() const=0; virtual const NLMISC::CSString& getArgs() const=0; virtual const NLMISC::CSString& getDescription() const=0; diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_commands.cpp b/code/ryzom/server/src/general_utilities_service/stat_char_commands.cpp index f72db4bbd..4a3b98bcd 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_commands.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_char_commands.cpp @@ -128,7 +128,7 @@ NLMISC_CATEGORISED_COMMAND(Stats,listCharNames,"display the names of the charact std::vector files; NLMISC::CPath::getPathContent(STAT_GLOBALS::getInputFilePath().c_str(),false,false,true,files); - for (uint32 i=files.size();i--;) + for (uint32 i=(uint32)files.size();i--;) { if (!NLMISC::testWildCard(NLMISC::CFile::getFilename(files[i]),wildcard)) { @@ -227,7 +227,8 @@ NLMISC_CATEGORISED_COMMAND(Stats,jobsPromote,"pause execution of jobs","" if (args.size()!=1) return false; - uint32 idx= atoi(args[0].c_str()); + uint32 idx; + NLMISC::fromString(args[0], idx); if ( (idx==0 && args[0]!="0") ) { nlwarning("Argument is not a valid job id - should be a number"); @@ -249,7 +250,8 @@ NLMISC_CATEGORISED_COMMAND(Stats,JobUpdatesPerUpdate,"set or display the number if (args.size()==1) { - uint32 count= atoi(args[0].c_str()); + uint32 count; + NLMISC::fromString(args[0], count); if ( (count==0 && args[0]!="0") ) { nlwarning("Argument is not a valid number"); @@ -311,7 +313,8 @@ NLMISC_CATEGORISED_COMMAND(Stats,jobsDisplayDetails,"display detailed info for t case 1: { - uint32 idx= atoi(args[0].c_str()); + uint32 idx; + NLMISC::fromString(args[0], idx); if ( (idx==0 && args[0]!="0") ) { nlwarning("Argument is not a valid job id - should be a number"); @@ -476,8 +479,11 @@ NLMISC_CATEGORISED_COMMAND(Stats,charScanScriptDeleteLine,"","") return false; } + uint32 line; + NLMISC::fromString(args[0], line); + // line numbering starts at 1 so an invalid number will be inored anyway - return TheCharScanScriptFile->deleteLine(atoi(args[0].c_str())); + return TheCharScanScriptFile->deleteLine(line); } NLMISC_CATEGORISED_COMMAND(Stats,charScanScriptAddInfoExtractor,""," []") @@ -761,7 +767,7 @@ NLMISC_CATEGORISED_COMMAND(Stats,charScanScriptTestFilteredFileList,"list the se return false; // generating a new file list - if (args[0]==CSString("generate").left(args[0].size())) + if (args[0]==CSString("generate").left((uint)args[0].size())) { if (TheCharScanScriptFile==NULL) { @@ -796,7 +802,7 @@ NLMISC_CATEGORISED_COMMAND(Stats,charScanScriptTestFilteredFileList,"list the se } // displaying the last generated file list - if (args[0]==CSString("display").left(args[0].size())) + if (args[0]==CSString("display").left((uint)args[0].size())) { nlinfo("Filtered file list for the current job"); fdc.display(&log); diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.cpp b/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.cpp index 6902fe6ad..7c5f850fe 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.cpp @@ -63,7 +63,7 @@ void CCharFilterFactory::displayFilterList(NLMISC::CLog* log) { std::string s= _Filters[i]->getName(); if (s.size()>longestName) - longestName=s.size(); + longestName=(uint32)s.size(); } // iterate over the filters displaying names and description @@ -75,7 +75,7 @@ void CCharFilterFactory::displayFilterList(NLMISC::CLog* log) uint32 CCharFilterFactory::getFilterBuilderCount() { - return _Filters.size(); + return (uint32)_Filters.size(); } ICharFilterBuilder* CCharFilterFactory::getFilterBuilder(uint32 idx) diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.h b/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.h index 656cc0cca..a96b7d6a5 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.h +++ b/code/ryzom/server/src/general_utilities_service/stat_char_filter_factory.h @@ -52,6 +52,7 @@ public: class ICharFilterBuilder: public NLMISC::CRefCount { public: + virtual ~ICharFilterBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual ICharFilter* build(const std::string& rawArgs)=0; diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.cpp b/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.cpp index 0ff961def..da481cf03 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.cpp @@ -63,7 +63,7 @@ void CCharInfoExtractorFactory::displayInfoExtractorList(NLMISC::CLog* log) { std::string s= _InfoExtractors[i]->getName(); if (s.size()>longestName) - longestName=s.size(); + longestName=(uint32)s.size(); } // iterate over the infoExtractors displaying names and description @@ -75,7 +75,7 @@ void CCharInfoExtractorFactory::displayInfoExtractorList(NLMISC::CLog* log) uint32 CCharInfoExtractorFactory::getInfoExtractorBuilderCount() { - return _InfoExtractors.size(); + return (uint32)_InfoExtractors.size(); } ICharInfoExtractorBuilder* CCharInfoExtractorFactory::getInfoExtractorBuilder(uint32 idx) diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.h b/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.h index 868004c39..cd182c169 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.h +++ b/code/ryzom/server/src/general_utilities_service/stat_char_info_extractor_factory.h @@ -56,6 +56,7 @@ public: class ICharInfoExtractorBuilder: public NLMISC::CRefCount { public: + virtual ~ICharInfoExtractorBuilder() {} virtual const char* getName() const=0; virtual const char* getDescription() const=0; virtual const char* getFields() const=0; diff --git a/code/ryzom/server/src/general_utilities_service/stat_char_scan_script.cpp b/code/ryzom/server/src/general_utilities_service/stat_char_scan_script.cpp index 48db0b447..030239d1e 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_char_scan_script.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_char_scan_script.cpp @@ -298,7 +298,7 @@ void CCharScanScriptCommandRegistry::displayScriptCommands(NLMISC::CLog* log) { std::string s= _ScriptCommands[i]->getName(); if (s.size()>longestName) - longestName=s.size(); + longestName=(uint32)s.size(); } // iterate over the script commands displaying names and description diff --git a/code/ryzom/server/src/general_utilities_service/stat_character_scan_job.cpp b/code/ryzom/server/src/general_utilities_service/stat_character_scan_job.cpp index 4b01b8574..9b85fb3b3 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_character_scan_job.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_character_scan_job.cpp @@ -293,7 +293,7 @@ bool CCharacterScanJob::runForFile(const std::string& fileName) c.apply(pdr); // iterate over the filters executing their core code - for (uint32 i=_Filters.size();i--;) + for (uint32 i=(uint32)_Filters.size();i--;) { if (!_Filters[i]->evaluate(&c)) return true; diff --git a/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.cpp b/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.cpp index e6e860966..c05576c22 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.cpp @@ -63,7 +63,7 @@ void CFileListBuilderFactory::displayFileListBuilderList(NLMISC::CLog* log) { std::string s= _FileLists[i]->getName(); if (s.size()>longestName) - longestName=s.size(); + longestName=(uint32)s.size(); } // iterate over the filters displaying names and description @@ -75,7 +75,7 @@ void CFileListBuilderFactory::displayFileListBuilderList(NLMISC::CLog* log) uint32 CFileListBuilderFactory::getFileListBuilderCount() { - return _FileLists.size(); + return (uint32)_FileLists.size(); } IFileListBuilderBuilder* CFileListBuilderFactory::getFileListBuilder(uint32 idx) diff --git a/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.h b/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.h index fbfb5dc61..367c0caa2 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.h +++ b/code/ryzom/server/src/general_utilities_service/stat_file_list_builder_factory.h @@ -54,6 +54,7 @@ public: class IFileListBuilderBuilder: public NLMISC::CRefCount { public: + virtual ~IFileListBuilderBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual IFileListBuilder* build(const std::string& rawArgs)=0; diff --git a/code/ryzom/server/src/general_utilities_service/stat_globals.cpp b/code/ryzom/server/src/general_utilities_service/stat_globals.cpp index af1150b0f..a959fb751 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_globals.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_globals.cpp @@ -61,19 +61,19 @@ namespace STAT_GLOBALS NLMISC::CSString getInputFilePath(const NLMISC::CSString& path) { NLMISC::CSString pathRoot= NLMISC::CPath::standardizePath(InputFileDirectory.get()); - return (path.left(pathRoot.size())==pathRoot)? path: pathRoot+path; + return (path.left((uint)pathRoot.size())==pathRoot)? path: pathRoot+path; } NLMISC::CSString getScriptFilePath(const NLMISC::CSString& path) { NLMISC::CSString pathRoot= NLMISC::CPath::standardizePath(ScriptDirectory.get()); - return (path.left(pathRoot.size())==pathRoot)? path: pathRoot+path; + return (path.left((uint)pathRoot.size())==pathRoot)? path: pathRoot+path; } NLMISC::CSString getOutputFilePath(const NLMISC::CSString& path) { NLMISC::CSString pathRoot= NLMISC::CPath::standardizePath(OutputDirectory.get()); - return (path.left(pathRoot.size())==pathRoot)? path: pathRoot+path; + return (path.left((uint)pathRoot.size())==pathRoot)? path: pathRoot+path; } diff --git a/code/ryzom/server/src/general_utilities_service/stat_job_manager.cpp b/code/ryzom/server/src/general_utilities_service/stat_job_manager.cpp index 818c509d7..6c5a8c008 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_job_manager.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_job_manager.cpp @@ -131,7 +131,7 @@ void CJobManager::serviceUpdate() uint32 CJobManager::addJob(NLMISC::CSmartPtr job) { nlassert(job!=NULL); - uint32 id= _Jobs.size(); + uint32 id= (uint32)_Jobs.size(); _UnfinishedJobs.push_back(id); _Jobs.push_back(job); return id; diff --git a/code/ryzom/server/src/general_utilities_service/stat_user_file_list_builders.cpp b/code/ryzom/server/src/general_utilities_service/stat_user_file_list_builders.cpp index 3f3bbb8c7..c2a5658d5 100644 --- a/code/ryzom/server/src/general_utilities_service/stat_user_file_list_builders.cpp +++ b/code/ryzom/server/src/general_utilities_service/stat_user_file_list_builders.cpp @@ -73,7 +73,8 @@ FILE_LIST_BUILDER(MinFileSize,"") return false; } - for (uint32 i=fdc.size();i--;) + uint32 ttlNumFiles= fdc.size(); + for (uint32 i=ttlNumFiles;i--;) { if (fdc[i].FileSize") return false; } - for (uint32 i=fdc.size();i--;) + uint32 ttlNumFiles= fdc.size(); + for (uint32 i=ttlNumFiles;i--;) { if (fdc[i].FileSize>maxSize) fdc.removeFile(i); diff --git a/code/ryzom/server/src/general_utilities_service/stats_guild_scan_job.h b/code/ryzom/server/src/general_utilities_service/stats_guild_scan_job.h index e75564e49..ebcc43e7e 100644 --- a/code/ryzom/server/src/general_utilities_service/stats_guild_scan_job.h +++ b/code/ryzom/server/src/general_utilities_service/stats_guild_scan_job.h @@ -53,7 +53,7 @@ public: private: // private data - internal to job - typedef enum { INIT, WORK, CLOSED, ERROR } TState; + enum TState { INIT, WORK, CLOSED, ERROR }; TState _State; uint32 _NextFile; CFileDescriptionContainer _Files; diff --git a/code/ryzom/server/src/gpm_service/cell.h b/code/ryzom/server/src/gpm_service/cell.h index 054156dbc..a4846a7a6 100644 --- a/code/ryzom/server/src/gpm_service/cell.h +++ b/code/ryzom/server/src/gpm_service/cell.h @@ -34,7 +34,7 @@ class CCell { public: /// default constructor - CCell() : _LastVisionUpdate(0) {}; + CCell() : _LastVisionUpdate(0) {} /// initialisation void init( sint32 cellId ) diff --git a/code/ryzom/server/src/input_output_service/chat_manager.h b/code/ryzom/server/src/input_output_service/chat_manager.h index 8fbc34deb..811bdc710 100644 --- a/code/ryzom/server/src/input_output_service/chat_manager.h +++ b/code/ryzom/server/src/input_output_service/chat_manager.h @@ -58,13 +58,13 @@ public : /// exception thrown when client is unknown struct EChatClient : public NLMISC::Exception { - EChatClient( const NLMISC::CEntityId& id ) : Exception ("Don't have chat infos for the char "+id.toString()) {}; + EChatClient( const NLMISC::CEntityId& id ) : Exception ("Don't have chat infos for the char "+id.toString()) {} }; /// exception thrown when group is unknown struct EChatGroup : public NLMISC::Exception { - EChatGroup( const TGroupId& gId ) : Exception ("Can't find the group "+gId.toString()) {}; + EChatGroup( const TGroupId& gId ) : Exception ("Can't find the group "+gId.toString()) {} }; /** diff --git a/code/ryzom/server/src/patchman_service/module_admin_itf.h b/code/ryzom/server/src/patchman_service/module_admin_itf.h index d9649f603..a0d4aa34e 100644 --- a/code/ryzom/server/src/patchman_service/module_admin_itf.h +++ b/code/ryzom/server/src/patchman_service/module_admin_itf.h @@ -71,9 +71,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -211,9 +211,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -371,9 +371,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -504,9 +504,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -711,9 +711,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -911,9 +911,9 @@ namespace PATCHMAN // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/pd_support_service/stat_char_filter_factory.h b/code/ryzom/server/src/pd_support_service/stat_char_filter_factory.h index 656cc0cca..a96b7d6a5 100644 --- a/code/ryzom/server/src/pd_support_service/stat_char_filter_factory.h +++ b/code/ryzom/server/src/pd_support_service/stat_char_filter_factory.h @@ -52,6 +52,7 @@ public: class ICharFilterBuilder: public NLMISC::CRefCount { public: + virtual ~ICharFilterBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual ICharFilter* build(const std::string& rawArgs)=0; diff --git a/code/ryzom/server/src/pd_support_service/stat_char_info_extractor_factory.h b/code/ryzom/server/src/pd_support_service/stat_char_info_extractor_factory.h index 868004c39..cd182c169 100644 --- a/code/ryzom/server/src/pd_support_service/stat_char_info_extractor_factory.h +++ b/code/ryzom/server/src/pd_support_service/stat_char_info_extractor_factory.h @@ -56,6 +56,7 @@ public: class ICharInfoExtractorBuilder: public NLMISC::CRefCount { public: + virtual ~ICharInfoExtractorBuilder() {} virtual const char* getName() const=0; virtual const char* getDescription() const=0; virtual const char* getFields() const=0; diff --git a/code/ryzom/server/src/pd_support_service/stat_file_list_builder_factory.h b/code/ryzom/server/src/pd_support_service/stat_file_list_builder_factory.h index fbfb5dc61..367c0caa2 100644 --- a/code/ryzom/server/src/pd_support_service/stat_file_list_builder_factory.h +++ b/code/ryzom/server/src/pd_support_service/stat_file_list_builder_factory.h @@ -54,6 +54,7 @@ public: class IFileListBuilderBuilder: public NLMISC::CRefCount { public: + virtual ~IFileListBuilderBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual IFileListBuilder* build(const std::string& rawArgs)=0; diff --git a/code/ryzom/server/src/sabrina/faber_phrase.h b/code/ryzom/server/src/sabrina/faber_phrase.h index 8069dc4f6..4d85e8b53 100644 --- a/code/ryzom/server/src/sabrina/faber_phrase.h +++ b/code/ryzom/server/src/sabrina/faber_phrase.h @@ -64,21 +64,21 @@ public: /** * set the actor */ - virtual void setActor( const TDataSetRow &entityRowId ){}; + virtual void setActor( const TDataSetRow &entityRowId ){} /** * called at the end of the latency time */ - virtual void end(){}; + virtual void end(){} //@} ///\unused basic methods from CSPhrase //@{ - virtual void setPrimaryItem( CGameItemPtr itemPtr ){}; - virtual void setSecondaryItem( CGameItemPtr itemPtr ){}; - virtual void addConsumableItem( CGameItemPtr itemPtr ){}; - virtual void setPrimaryTarget( const TDataSetRow& ) {}; - virtual void addTargetEntity( const TDataSetRow& ) {}; + virtual void setPrimaryItem( CGameItemPtr itemPtr ){} + virtual void setSecondaryItem( CGameItemPtr itemPtr ){} + virtual void addConsumableItem( CGameItemPtr itemPtr ){} + virtual void setPrimaryTarget( const TDataSetRow& ) {} + virtual void addTargetEntity( const TDataSetRow& ) {} //@} inline const TDataSetRow & getActor() { return _ActorRowId;} diff --git a/code/ryzom/server/src/sabrina/harvest_phrase.h b/code/ryzom/server/src/sabrina/harvest_phrase.h index b5f290019..c300b1abd 100644 --- a/code/ryzom/server/src/sabrina/harvest_phrase.h +++ b/code/ryzom/server/src/sabrina/harvest_phrase.h @@ -62,11 +62,11 @@ public: ///\unused basic methods from CSPhrase //@{ - virtual void setPrimaryItem( CGameItemPtr itemPtr ){}; - virtual void setSecondaryItem( CGameItemPtr itemPtr ){}; - virtual void addConsumableItem( CGameItemPtr itemPtr ){}; - virtual void setPrimaryTarget( const TDataSetRow& ) {}; - virtual void addTargetEntity( const TDataSetRow& ) {}; + virtual void setPrimaryItem( CGameItemPtr itemPtr ){} + virtual void setSecondaryItem( CGameItemPtr itemPtr ){} + virtual void addConsumableItem( CGameItemPtr itemPtr ){} + virtual void setPrimaryTarget( const TDataSetRow& ) {} + virtual void addTargetEntity( const TDataSetRow& ) {} //@} //@} @@ -74,7 +74,7 @@ public: /** * set the actor */ - virtual void setActor( const TDataSetRow &entityRowId ){}; + virtual void setActor( const TDataSetRow &entityRowId ){} /** * called at the end of the latency time diff --git a/code/ryzom/server/src/sabrina/magic_phrase.h b/code/ryzom/server/src/sabrina/magic_phrase.h index fbfe5dee2..45ee645cd 100644 --- a/code/ryzom/server/src/sabrina/magic_phrase.h +++ b/code/ryzom/server/src/sabrina/magic_phrase.h @@ -78,17 +78,17 @@ public: virtual void execute(); virtual void apply(); virtual void stop(); - virtual void setPrimaryTarget( const TDataSetRow &entityRowId ){ _Targets[0] = entityRowId; }; - virtual void addTargetEntity( const TDataSetRow &entityRowId ){ _Targets.push_back(entityRowId);}; + virtual void setPrimaryTarget( const TDataSetRow &entityRowId ){ _Targets[0] = entityRowId; } + virtual void addTargetEntity( const TDataSetRow &entityRowId ){ _Targets.push_back(entityRowId);} //@} /// \name Unused virtual methods from CSPhrase //@{ virtual void setActor( const TDataSetRow & actorRowId ){ } - virtual void end(){}; - virtual void setPrimaryItem( CGameItemPtr itemPtr ){ }; - virtual void setSecondaryItem( CGameItemPtr itemPtr ){ }; - virtual void addConsumableItem( CGameItemPtr itemPtr ){ }; + virtual void end(){} + virtual void setPrimaryItem( CGameItemPtr itemPtr ){ } + virtual void setSecondaryItem( CGameItemPtr itemPtr ){ } + virtual void addConsumableItem( CGameItemPtr itemPtr ){ } //@} private: diff --git a/code/ryzom/server/src/sabrina/s_effect.h b/code/ryzom/server/src/sabrina/s_effect.h index ca34245ee..c706756eb 100644 --- a/code/ryzom/server/src/sabrina/s_effect.h +++ b/code/ryzom/server/src/sabrina/s_effect.h @@ -55,7 +55,7 @@ public: virtual bool update( uint32 & updateFlag ) = 0; /// callback called when the effect is actually removed. Does nothing by default - virtual void removed(){}; + virtual void removed(){} ///\name accessors //@{ diff --git a/code/ryzom/server/src/server_share/backup_service_itf.h b/code/ryzom/server/src/server_share/backup_service_itf.h index ed6aadc0d..5b1ffe352 100644 --- a/code/ryzom/server/src/server_share/backup_service_itf.h +++ b/code/ryzom/server/src/server_share/backup_service_itf.h @@ -63,9 +63,9 @@ namespace BS // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -185,9 +185,9 @@ namespace BS // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/char_name_mapper_itf.h b/code/ryzom/server/src/server_share/char_name_mapper_itf.h index b7133fe9a..456bcc0c7 100644 --- a/code/ryzom/server/src/server_share/char_name_mapper_itf.h +++ b/code/ryzom/server/src/server_share/char_name_mapper_itf.h @@ -217,9 +217,9 @@ namespace CNM // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -330,9 +330,9 @@ namespace CNM // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/chat_unifier_itf.h b/code/ryzom/server/src/server_share/chat_unifier_itf.h index 8b736a082..f284c8fbb 100644 --- a/code/ryzom/server/src/server_share/chat_unifier_itf.h +++ b/code/ryzom/server/src/server_share/chat_unifier_itf.h @@ -214,9 +214,9 @@ namespace CHATUNI // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -331,9 +331,9 @@ namespace CHATUNI // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/command_executor_itf.h b/code/ryzom/server/src/server_share/command_executor_itf.h index dce7dce44..58a7f1c51 100644 --- a/code/ryzom/server/src/server_share/command_executor_itf.h +++ b/code/ryzom/server/src/server_share/command_executor_itf.h @@ -63,9 +63,9 @@ namespace CMDEXE // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/entity_locator_itf.h b/code/ryzom/server/src/server_share/entity_locator_itf.h index 59459a82d..317ad5199 100644 --- a/code/ryzom/server/src/server_share/entity_locator_itf.h +++ b/code/ryzom/server/src/server_share/entity_locator_itf.h @@ -139,9 +139,9 @@ namespace ENTITYLOC // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); @@ -384,9 +384,9 @@ namespace ENTITYLOC // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/logger_service_itf.h b/code/ryzom/server/src/server_share/logger_service_itf.h index 8996c1369..051cc85cb 100644 --- a/code/ryzom/server/src/server_share/logger_service_itf.h +++ b/code/ryzom/server/src/server_share/logger_service_itf.h @@ -983,9 +983,9 @@ namespace LGS // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/mail_forum_itf.h b/code/ryzom/server/src/server_share/mail_forum_itf.h index aeaf0709b..b16cd7d24 100644 --- a/code/ryzom/server/src/server_share/mail_forum_itf.h +++ b/code/ryzom/server/src/server_share/mail_forum_itf.h @@ -63,9 +63,9 @@ namespace MFS // unused interceptors std::string fwdBuildModuleManifest() const { return std::string(); } - void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {}; - void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {}; + void fwdOnModuleUp(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleDown(NLNET::IModuleProxy *moduleProxy) {} + void fwdOnModuleSecurityChange(NLNET::IModuleProxy *moduleProxy) {} // process module message interceptor bool fwdOnProcessModuleMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message); diff --git a/code/ryzom/server/src/server_share/mission_messages.h b/code/ryzom/server/src/server_share/mission_messages.h index d779b709d..9c03f1f91 100644 --- a/code/ryzom/server/src/server_share/mission_messages.h +++ b/code/ryzom/server/src/server_share/mission_messages.h @@ -56,7 +56,7 @@ public: property ("Length", PropUInt8, (uint8)0,Length); } - virtual void callback (const std::string &name, uint8 id) {}; + virtual void callback (const std::string &name, uint8 id) {} }; */ @@ -115,7 +115,7 @@ public: property ("RewardType", PropUInt8, (uint8)MISSION_DESC::NbReward, RewardType); } - virtual void callback (const std::string &name, uint8 id) {}; + virtual void callback (const std::string &name, uint8 id) {} }; */ diff --git a/code/ryzom/server/src/server_share/msg_ai_service.h b/code/ryzom/server/src/server_share/msg_ai_service.h index a442b5234..132e56e1c 100644 --- a/code/ryzom/server/src/server_share/msg_ai_service.h +++ b/code/ryzom/server/src/server_share/msg_ai_service.h @@ -49,7 +49,7 @@ public: propertyCont ("Character", PropDataSetRow, Character); propertyCont ("Creature", PropDataSetRow, Creature); } - virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {}; + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) {} }; //---------------------------------------------------------------------------- diff --git a/code/ryzom/server/src/server_share/msg_gpm_service.h b/code/ryzom/server/src/server_share/msg_gpm_service.h index c4039be97..d985c8781 100644 --- a/code/ryzom/server/src/server_share/msg_gpm_service.h +++ b/code/ryzom/server/src/server_share/msg_gpm_service.h @@ -73,7 +73,7 @@ public: property("Type", PropUInt32, (uint32)Player, Type); } - virtual void callback (const std::string &name, NLNET::TServiceId id) {}; + virtual void callback (const std::string &name, NLNET::TServiceId id) {} }; diff --git a/code/ryzom/server/src/server_share/npc_description_messages.h b/code/ryzom/server/src/server_share/npc_description_messages.h index 051cbeb44..1c838ad80 100644 --- a/code/ryzom/server/src/server_share/npc_description_messages.h +++ b/code/ryzom/server/src/server_share/npc_description_messages.h @@ -47,8 +47,9 @@ std::string buildChatDebugString(const std::vector &ShopCategories, class CNpcChatProfile { public: - CNpcChatProfile() : /*_guildCreator(false),*/ /*_dynamicMissionGiver(false),*/ _Organization(0), _FilterExplicitActionTradeByPlayerRace(false), - _ExplicitActionSPType(EGSPD::CSPType::Unknown), _FilterExplicitActionTradeByBotRace(true){} + CNpcChatProfile() : /*_guildCreator(false),*/ /*_dynamicMissionGiver(false),*/ _FilterExplicitActionTradeByPlayerRace(false), + _ExplicitActionSPType(EGSPD::CSPType::Unknown), _FilterExplicitActionTradeByBotRace(true), + _DynamicMissionGiver(false), _Organization(0) {} CNpcChatProfile(const CNpcChatProfile &other0,const CNpcChatProfile &other1); virtual ~CNpcChatProfile() {} @@ -84,7 +85,7 @@ public: const std::string &getWebPageName() const { return _WebPageName; } const NLMISC::CSheetId &getOutpost() const { return _Outpost; } - const uint32 getOrganization() const { return _Organization; } + uint32 getOrganization() const { return _Organization; } protected: std::vector< RYMSG::TExplicitSale > _ExplicitSales; @@ -146,7 +147,7 @@ struct CCharacterBotChatBeginEnd : public CMirrorTransportClass propertyCont ("botChatEnd", PropUInt32, BotChatEnd); } - virtual void callback (const std::string &name, NLNET::TServiceId id) { } + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) { } }; struct CCharacterDynChatBeginEnd : public CMirrorTransportClass @@ -162,7 +163,7 @@ struct CCharacterDynChatBeginEnd : public CMirrorTransportClass propertyCont ("dnChatEnd", PropUInt32, DynChatEnd); } - virtual void callback (const std::string &name, NLNET::TServiceId id) { } + virtual void callback (const std::string &/* name */, NLNET::TServiceId /* id */) { } }; struct CCustomElementId diff --git a/code/ryzom/server/src/server_share/stat_db_tree.h b/code/ryzom/server/src/server_share/stat_db_tree.h index 8faaa85ee..7472a2092 100644 --- a/code/ryzom/server/src/server_share/stat_db_tree.h +++ b/code/ryzom/server/src/server_share/stat_db_tree.h @@ -95,13 +95,13 @@ class CStatDBLeaf : public IStatDBNode public: virtual ~CStatDBLeaf() {} - bool setNode(const std::string & path, IStatDBNodePtr node) { return false; } - IStatDBNodePtr getNode(const std::string & path) { return NULL; } - void getNodes(const std::string & pathPattern, std::vector & matchingNodes, - const std::string & currentPath) {} - IStatDBNodePtr removeNode(const std::string & path) { return NULL; } + bool setNode(const std::string & /* path */, IStatDBNodePtr /* node */) { return false; } + IStatDBNodePtr getNode(const std::string & /* path */) { return NULL; } + void getNodes(const std::string & /* pathPattern */, std::vector & /* matchingNodes */, + const std::string & /* currentPath */) {} + IStatDBNodePtr removeNode(const std::string & /* path */) { return NULL; } - virtual void acceptVisitor(CStatDBNodeVisitor & visitor, const std::string & currentPath) {} + virtual void acceptVisitor(CStatDBNodeVisitor & /* visitor */, const std::string & /* currentPath */) {} }; /** diff --git a/code/ryzom/server/src/server_share/stat_db_tree_visitor.h b/code/ryzom/server/src/server_share/stat_db_tree_visitor.h index 1d57a6491..98e7455d3 100644 --- a/code/ryzom/server/src/server_share/stat_db_tree_visitor.h +++ b/code/ryzom/server/src/server_share/stat_db_tree_visitor.h @@ -34,9 +34,9 @@ class CStatDBTableLeaf; class CStatDBNodeVisitor { public: - virtual void visitBranch(CStatDBBranch * branch, const std::string & path) {} - virtual void visitValueLeaf(CStatDBValueLeaf * valueLeaf, const std::string & path) {} - virtual void visitTableLeaf(CStatDBTableLeaf * tableLeaf, const std::string & path) {} + virtual void visitBranch(CStatDBBranch * /* branch */, const std::string & /* path */) {} + virtual void visitValueLeaf(CStatDBValueLeaf * /* valueLeaf */, const std::string & /* path */) {} + virtual void visitTableLeaf(CStatDBTableLeaf * /* tableLeaf */, const std::string & /* path */) {} protected: CStatDBNodeVisitor() {} diff --git a/code/ryzom/server/src/shard_unifier_service/database_mapping.h b/code/ryzom/server/src/shard_unifier_service/database_mapping.h index d6f9b8689..61df29e9a 100644 --- a/code/ryzom/server/src/shard_unifier_service/database_mapping.h +++ b/code/ryzom/server/src/shard_unifier_service/database_mapping.h @@ -111,9 +111,9 @@ namespace RSMGR CKnownUserPtr() : _FileName(NULL), _LineNum(0), - _Ptr(NULL), _NextPtr(NULL), - _PrevPtr(NULL) + _PrevPtr(NULL), + _Ptr(NULL) { } @@ -1091,9 +1091,9 @@ namespace RSMGR CGuildInvitePtr() : _FileName(NULL), _LineNum(0), - _Ptr(NULL), _NextPtr(NULL), - _PrevPtr(NULL) + _PrevPtr(NULL), + _Ptr(NULL) { } @@ -1231,9 +1231,9 @@ namespace RSMGR CPlayerRatingPtr() : _FileName(NULL), _LineNum(0), - _Ptr(NULL), _NextPtr(NULL), - _PrevPtr(NULL) + _PrevPtr(NULL), + _Ptr(NULL) { } @@ -1371,9 +1371,9 @@ namespace RSMGR CJournalEntryPtr() : _FileName(NULL), _LineNum(0), - _Ptr(NULL), _NextPtr(NULL), - _PrevPtr(NULL) + _PrevPtr(NULL), + _Ptr(NULL) { } diff --git a/code/ryzom/tools/client/client_config/base_dialog.h b/code/ryzom/tools/client/client_config/base_dialog.h index c81c8a207..4082968b4 100644 --- a/code/ryzom/tools/client/client_config/base_dialog.h +++ b/code/ryzom/tools/client/client_config/base_dialog.h @@ -34,7 +34,7 @@ class CBaseDialog : public CDialog public: /// Constructor - CBaseDialog (uint id, CWnd* pParent = NULL) : CDialog(id, pParent) {}; + CBaseDialog (uint id, CWnd* pParent = NULL) : CDialog(id, pParent) {} /// On ok virtual void OnOK (); diff --git a/code/ryzom/tools/leveldesign/georges_convert/string_ex.h b/code/ryzom/tools/leveldesign/georges_convert/string_ex.h index 69489c4ac..a395e8f42 100644 --- a/code/ryzom/tools/leveldesign/georges_convert/string_ex.h +++ b/code/ryzom/tools/leveldesign/georges_convert/string_ex.h @@ -26,9 +26,9 @@ class CStringEx : public std::basic_string, std::al { public: - CStringEx() : std::basic_string, std::allocator >() {;} - CStringEx( char* _pc ) : std::basic_string, std::allocator >( (char *)_pc ) {}; - CStringEx( std::string _s ) : std::basic_string, std::allocator >( (std::string)_s) {}; + CStringEx() : std::basic_string, std::allocator >() {} + CStringEx( char* _pc ) : std::basic_string, std::allocator >( (char *)_pc ) {} + CStringEx( std::string _s ) : std::basic_string, std::allocator >( (std::string)_s) {} CStringEx( const char _c, int _i ) { append( _i, _c ); } virtual ~CStringEx(); diff --git a/code/ryzom/tools/leveldesign/georges_dll/edit_list_ctrl.h b/code/ryzom/tools/leveldesign/georges_dll/edit_list_ctrl.h index 8b61618a1..08b477e38 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/edit_list_ctrl.h +++ b/code/ryzom/tools/leveldesign/georges_dll/edit_list_ctrl.h @@ -84,12 +84,12 @@ public: uint Item, SubItem, ColumnCount; uint DlgIndex; - virtual TItemEdit getItemEditMode (uint item, uint subItem) {return EditFixedCombo;}; + virtual TItemEdit getItemEditMode (uint item, uint subItem) {return EditFixedCombo;} virtual void getComboBoxStrings (uint item, uint subItem, std::vector &retStrings); - virtual void getMemComboBoxProp (uint item, uint subItem, std::string ®Adr, bool &browse) { regAdr = ""; browse = false; }; - virtual void getNewItemText (uint item, uint subItem, std::string &ret) { ret = "new"; }; - virtual void getBrowseInfo (uint item, uint subItem, std::string &defExt, std::string &defFilename, std::string &defDir, std::string &filter) { defExt=""; defFilename=""; filter=""; defDir="";}; - virtual void onItemChanged (uint item, uint subItem) {}; + virtual void getMemComboBoxProp (uint item, uint subItem, std::string ®Adr, bool &browse) { regAdr = ""; browse = false; } + virtual void getNewItemText (uint item, uint subItem, std::string &ret) { ret = "new"; } + virtual void getBrowseInfo (uint item, uint subItem, std::string &defExt, std::string &defFilename, std::string &defDir, std::string &filter) { defExt=""; defFilename=""; filter=""; defDir="";} + virtual void onItemChanged (uint item, uint subItem) {} void memComboBoxAsChange (bool selChange); void closeMemComboBox (bool update); diff --git a/code/ryzom/tools/leveldesign/georges_dll/form_dialog.h b/code/ryzom/tools/leveldesign/georges_dll/form_dialog.h index 0fe05ab8c..29e10aa00 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/form_dialog.h +++ b/code/ryzom/tools/leveldesign/georges_dll/form_dialog.h @@ -66,7 +66,7 @@ public: IFormWidget (CFormDialog *dialog, uint structId, const char *formName, TTypeSrc typeSrc, uint slot); // Destructor - virtual ~IFormWidget () {}; + virtual ~IFormWidget () {} // Update the text of the base label void updateLabel (); @@ -90,8 +90,8 @@ public: virtual bool extendableHeight () const; // Ok / cancel hit - virtual void onOk () {}; - virtual void onCancel () {}; + virtual void onOk () {} + virtual void onCancel () {} // Update widget date virtual void updateData (bool update = true) = 0; diff --git a/code/ryzom/tools/leveldesign/georges_dll/georges_edit_doc.h b/code/ryzom/tools/leveldesign/georges_dll/georges_edit_doc.h index 649a2927d..b6f9ef7f6 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/georges_edit_doc.h +++ b/code/ryzom/tools/leveldesign/georges_dll/georges_edit_doc.h @@ -311,7 +311,7 @@ public: class CGeorgesEditDocType : public CGeorgesEditDoc { protected: // create from serialization only - CGeorgesEditDocType() {}; + CGeorgesEditDocType() {} DECLARE_DYNCREATE(CGeorgesEditDocType) // Document is a type ? @@ -341,7 +341,7 @@ protected: class CGeorgesEditDocDfn : public CGeorgesEditDoc { protected: // create from serialization only - CGeorgesEditDocDfn() {}; + CGeorgesEditDocDfn() {} DECLARE_DYNCREATE(CGeorgesEditDocDfn) // Document is a dfn ? @@ -371,7 +371,7 @@ protected: class CGeorgesEditDocForm : public CGeorgesEditDoc { protected: // create from serialization only - CGeorgesEditDocForm() {}; + CGeorgesEditDocForm() {} DECLARE_DYNCREATE(CGeorgesEditDocForm) // Docuemnt is a form ? diff --git a/code/ryzom/tools/leveldesign/georges_dll/georges_interface.h b/code/ryzom/tools/leveldesign/georges_dll/georges_interface.h index 28ab22042..fbac91024 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/georges_interface.h +++ b/code/ryzom/tools/leveldesign/georges_dll/georges_interface.h @@ -61,7 +61,7 @@ enum TUI class IGeorges { public: - virtual ~IGeorges() {}; + virtual ~IGeorges() {} // Init the UI virtual void initUI (int m_nCmdShow, bool exeStandalone, HWND parent=NULL)=0; diff --git a/code/ryzom/tools/leveldesign/georges_dll/output_console_dlg.h b/code/ryzom/tools/leveldesign/georges_dll/output_console_dlg.h index a796e7fe6..47d447c59 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/output_console_dlg.h +++ b/code/ryzom/tools/leveldesign/georges_dll/output_console_dlg.h @@ -36,8 +36,8 @@ public: void outputString (const char *message); // From CDialog - void OnOK () {}; - void OnCancel (); + void OnOK () {} + void OnCancel () // Dialog Data //{{AFX_DATA(COutputConsoleDlg) diff --git a/code/ryzom/tools/leveldesign/georges_dll/plugin_interface.h b/code/ryzom/tools/leveldesign/georges_dll/plugin_interface.h index 4908d9474..36ad36062 100644 --- a/code/ryzom/tools/leveldesign/georges_dll/plugin_interface.h +++ b/code/ryzom/tools/leveldesign/georges_dll/plugin_interface.h @@ -65,7 +65,7 @@ public: /** * Destructor must uninitialise the plugin interface */ - virtual ~IEditPlugin () {}; + virtual ~IEditPlugin () {} /// Window related @@ -157,7 +157,7 @@ public: /** * Destructor must uninitialise the plugin interface */ - virtual ~IEditDocumentPlugin () {}; + virtual ~IEditDocumentPlugin () {} /// Event message diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBase.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBase.h index 4cee69902..6af4bc18c 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBase.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBase.h @@ -62,7 +62,7 @@ public: /// The master dialog call this method when the document is changed/updated static void docChanged(); /// The docChanged static method call this method on each page. - virtual void onDocChanged() {}; + virtual void onDocChanged() {} }; diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFades.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFades.h index 306f39293..43a339225 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFades.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFades.h @@ -33,7 +33,7 @@ class CPageBgFades : public CPageBase // Construction public: - CPageBgFades() {}; + CPageBgFades() {} CPageBgFades(NLGEORGES::CSoundDialog *soundDialog); ~CPageBgFades(); diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFlags.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFlags.h index 6fab075c3..6e5c2488c 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFlags.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageBgFlags.h @@ -46,7 +46,7 @@ class CPageBgFlags : public CPageBase // Construction public: - CPageBgFlags() {}; + CPageBgFlags() {} CPageBgFlags(NLGEORGES::CSoundDialog *soundDialog); ~CPageBgFlags(); diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComplex.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComplex.h index 7f9ed68ad..26ee864de 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComplex.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComplex.h @@ -35,7 +35,7 @@ class CPageComplex : public CPageBase // Construction public: - CPageComplex(){}; + CPageComplex(){} CPageComplex(NLGEORGES::CSoundDialog *soundDialog); ~CPageComplex(); diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComtext.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComtext.h index c34d6ef64..3c99f9089 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComtext.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageComtext.h @@ -34,7 +34,7 @@ class CPageComtext : public CPageBase // Construction public: - CPageComtext(){}; + CPageComtext(){} CPageComtext(NLGEORGES::CSoundDialog *soundDialog); ~CPageComtext(); diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PagePosition.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PagePosition.h index 88f21c614..1510c2ea8 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PagePosition.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PagePosition.h @@ -41,7 +41,7 @@ class CPagePosition : public CPageBase // Construction public: - CPagePosition(){}; + CPagePosition(){} CPagePosition(NLGEORGES::CSoundDialog *soundDialog); ~CPagePosition(); diff --git a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageSimple.h b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageSimple.h index 8da0328dc..935f33acb 100644 --- a/code/ryzom/tools/leveldesign/georges_plugin_sound/PageSimple.h +++ b/code/ryzom/tools/leveldesign/georges_plugin_sound/PageSimple.h @@ -36,7 +36,7 @@ class CPageSimple : public CPageBase // Construction public: - CPageSimple() {}; + CPageSimple() {} CPageSimple(NLGEORGES::CSoundDialog *soundDialog); ~CPageSimple(); diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h index fdb1db4be..3a38f8689 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h @@ -89,7 +89,7 @@ public: IStep(CMissionData &md, NLLIGO::IPrimitive *prim); virtual ~IStep() {} - virtual void init(CMissionData &md, NLLIGO::IPrimitive *prim) {}; + virtual void init(CMissionData &md, NLLIGO::IPrimitive *prim) {} /// Get the step name diff --git a/code/ryzom/tools/leveldesign/world_editor/world_editor/action.h b/code/ryzom/tools/leveldesign/world_editor/world_editor/action.h index 511f2a16e..9145f4af4 100644 --- a/code/ryzom/tools/leveldesign/world_editor/world_editor/action.h +++ b/code/ryzom/tools/leveldesign/world_editor/world_editor/action.h @@ -32,7 +32,7 @@ class IAction public: // Virtual destructor - virtual ~IAction () {}; + virtual ~IAction () {} // Do / undo virtual void undo () = 0; diff --git a/code/ryzom/tools/leveldesign/world_editor/world_editor/plugin_interface.h b/code/ryzom/tools/leveldesign/world_editor/world_editor/plugin_interface.h index d2c38ca58..2bbe143c1 100644 --- a/code/ryzom/tools/leveldesign/world_editor/world_editor/plugin_interface.h +++ b/code/ryzom/tools/leveldesign/world_editor/world_editor/plugin_interface.h @@ -163,7 +163,7 @@ public: /// Init the plugin. The plugin receive the world editor interface. virtual void init(IPluginAccess *pluginAccess) =0; /// Delete the plugin. - virtual ~IPluginCallback() {}; + virtual ~IPluginCallback() {} /// The current region has changed. // virtual void primRegionChanged(const std::vector ®ions) = 0; diff --git a/code/ryzom/tools/leveldesign/world_editor/world_editor_primitive_plugin/primitive_plugin.h b/code/ryzom/tools/leveldesign/world_editor/world_editor_primitive_plugin/primitive_plugin.h index 1ea94a512..7fcb78fc1 100644 --- a/code/ryzom/tools/leveldesign/world_editor/world_editor_primitive_plugin/primitive_plugin.h +++ b/code/ryzom/tools/leveldesign/world_editor/world_editor_primitive_plugin/primitive_plugin.h @@ -51,9 +51,9 @@ public: CPrimitivePlugin(); - virtual void positionMoved(const NLMISC::CVector &position) {}; - virtual void lostPositionControl() {}; - virtual void onIdle() {}; + virtual void positionMoved(const NLMISC::CVector &position) {} + virtual void lostPositionControl() {} + virtual void onIdle() {} virtual void drawPrimitive(const NLLIGO::IPrimitive *primitive, const TRenderContext &renderContext); @@ -72,9 +72,9 @@ private: void serial (NLMISC::IStream &s); - static uint getVersion (); + static uint getVersion () - void removed() {}; + void removed() {} }; std::map _CreatureInfos; diff --git a/code/ryzom/tools/stats_scan/char_filter_factory.h b/code/ryzom/tools/stats_scan/char_filter_factory.h index ebfd2a964..642c0dfde 100644 --- a/code/ryzom/tools/stats_scan/char_filter_factory.h +++ b/code/ryzom/tools/stats_scan/char_filter_factory.h @@ -51,6 +51,7 @@ public: class ICharFilterBuilder: public NLMISC::CRefCount { public: + virtual ~ICharFilterBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual ICharFilter* build(const std::string& rawArgs)=0; diff --git a/code/ryzom/tools/stats_scan/char_info_extractor_factory.h b/code/ryzom/tools/stats_scan/char_info_extractor_factory.h index 781a3d2d3..920c4344b 100644 --- a/code/ryzom/tools/stats_scan/char_info_extractor_factory.h +++ b/code/ryzom/tools/stats_scan/char_info_extractor_factory.h @@ -53,6 +53,7 @@ public: class ICharInfoExtractorBuilder: public NLMISC::CRefCount { public: + virtual ~ICharInfoExtractorBuilder() {} virtual const char* getName()=0; virtual const char* getDescription()=0; virtual const char* getFields()=0; diff --git a/code/ryzom/tools/stats_scan/job_manager.h b/code/ryzom/tools/stats_scan/job_manager.h index 136ac77be..a318cffdc 100644 --- a/code/ryzom/tools/stats_scan/job_manager.h +++ b/code/ryzom/tools/stats_scan/job_manager.h @@ -31,6 +31,9 @@ public: class IJob: public NLMISC::CRefCount { public: + // virtual dtor + virtual ~IJob() {} + // return true if the job is finished -> the job object can be deleted virtual bool finished()=0; From 05d423ecddfca64512afe5720f4e20da4f954414 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 22:47:03 +0200 Subject: [PATCH 13/77] Changed: Remove cariage return in build date --- code/CMakeModules/GetRevision.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/CMakeModules/GetRevision.cmake b/code/CMakeModules/GetRevision.cmake index ee3fa2e90..bc48235af 100644 --- a/code/CMakeModules/GetRevision.cmake +++ b/code/CMakeModules/GetRevision.cmake @@ -20,12 +20,13 @@ ENDIF(NOT SOURCE_DIR AND ROOT_DIR) MACRO(NOW RESULT) IF (WIN32) - EXECUTE_PROCESS(COMMAND "wmic" "os" "get" "localdatetime" OUTPUT_VARIABLE DATETIME) + EXECUTE_PROCESS(COMMAND "wmic os get localdatetime" OUTPUT_VARIABLE DATETIME) IF(NOT DATETIME MATCHES "ERROR") STRING(REGEX REPLACE ".*\n([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9]).*" "\\1-\\2-\\3 \\4:\\5:\\6" ${RESULT} "${DATETIME}") ENDIF(NOT DATETIME MATCHES "ERROR") ELSEIF(UNIX) - EXECUTE_PROCESS(COMMAND "date" "+'%Y-%m-%d %H:%M:%S'" OUTPUT_VARIABLE ${RESULT}) + EXECUTE_PROCESS(COMMAND "date +'%Y-%m-%d %H:%M:%S'" OUTPUT_VARIABLE DATETIME) + STRING(REGEX REPLACE "([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+).*" "\\1-\\2-\\3 \\4:\\5:\\6" ${RESULT} "${DATETIME}") ELSE (WIN32) MESSAGE(SEND_ERROR "date not implemented") SET(${RESULT} "0000-00-00 00:00:00") From 41747da931a4bd734bf2ea16550bb956a3cbcfc4 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 23:16:04 +0200 Subject: [PATCH 14/77] Changed: Remove cariage return in build date --- code/CMakeModules/GetRevision.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/CMakeModules/GetRevision.cmake b/code/CMakeModules/GetRevision.cmake index bc48235af..d38215aba 100644 --- a/code/CMakeModules/GetRevision.cmake +++ b/code/CMakeModules/GetRevision.cmake @@ -20,13 +20,13 @@ ENDIF(NOT SOURCE_DIR AND ROOT_DIR) MACRO(NOW RESULT) IF (WIN32) - EXECUTE_PROCESS(COMMAND "wmic os get localdatetime" OUTPUT_VARIABLE DATETIME) + EXECUTE_PROCESS(COMMAND "wmic" "os" "get" "localdatetime" OUTPUT_VARIABLE DATETIME) IF(NOT DATETIME MATCHES "ERROR") STRING(REGEX REPLACE ".*\n([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9]).*" "\\1-\\2-\\3 \\4:\\5:\\6" ${RESULT} "${DATETIME}") ENDIF(NOT DATETIME MATCHES "ERROR") ELSEIF(UNIX) - EXECUTE_PROCESS(COMMAND "date +'%Y-%m-%d %H:%M:%S'" OUTPUT_VARIABLE DATETIME) - STRING(REGEX REPLACE "([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+).*" "\\1-\\2-\\3 \\4:\\5:\\6" ${RESULT} "${DATETIME}") + EXECUTE_PROCESS(COMMAND "date" "+%Y-%m-%d %H:%M:%S" OUTPUT_VARIABLE DATETIME) + STRING(REGEX REPLACE "([0-9: -]+).*" "\\1" ${RESULT} "${DATETIME}") ELSE (WIN32) MESSAGE(SEND_ERROR "date not implemented") SET(${RESULT} "0000-00-00 00:00:00") From 36fcd706fadb5eeab3576e7c50d398012809bcd1 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Apr 2012 23:47:55 +0200 Subject: [PATCH 15/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/ryzom/common/src/game_share/singleton_registry.h | 1 + .../src/entities_game_service/pvp_manager/pvp_interface.h | 3 +-- .../server/src/general_utilities_service/gus_client_manager.h | 1 + .../server/src/general_utilities_service/rs_remote_saves.h | 2 ++ code/ryzom/server/src/general_utilities_service/saves_unit.h | 3 +++ code/ryzom/server/src/patchman_service/patchman_tester.h | 2 ++ code/ryzom/tools/stats_scan/char_filter_factory.h | 1 + code/ryzom/tools/stats_scan/char_info_extractor_factory.h | 1 + code/ryzom/tools/stats_scan/char_scan_script.h | 1 + 9 files changed, 13 insertions(+), 2 deletions(-) diff --git a/code/ryzom/common/src/game_share/singleton_registry.h b/code/ryzom/common/src/game_share/singleton_registry.h index 516417c32..7a9cc0253 100644 --- a/code/ryzom/common/src/game_share/singleton_registry.h +++ b/code/ryzom/common/src/game_share/singleton_registry.h @@ -58,6 +58,7 @@ protected: // protect from untrolled instantiation // this method registers the singleton with the singleton registry IServiceSingleton(); + virtual ~IServiceSingleton() {} private: // prohibit copy diff --git a/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_interface.h b/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_interface.h index 8524320df..dd5c92b68 100644 --- a/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_interface.h +++ b/code/ryzom/server/src/entities_game_service/pvp_manager/pvp_interface.h @@ -38,6 +38,7 @@ class CGameItemPtr; class IPVPInterface : public NLMISC::CRefCount { public: + virtual ~IPVPInterface() {} ///\name PVP MODE FEATURES DEPENDANTS //@{ /// return pvp relation between the two players @@ -61,8 +62,6 @@ public: /// killed character in PvP faction virtual void characterKilledInPvPFaction( CCharacter * character, PVP_CLAN::TPVPClan looserFaction, sint32 factionPoint ) const = 0; //@} - -private: }; #endif // RY_PVP_INTERFACE_H diff --git a/code/ryzom/server/src/general_utilities_service/gus_client_manager.h b/code/ryzom/server/src/general_utilities_service/gus_client_manager.h index c6f8b3f07..6714e4185 100644 --- a/code/ryzom/server/src/general_utilities_service/gus_client_manager.h +++ b/code/ryzom/server/src/general_utilities_service/gus_client_manager.h @@ -93,6 +93,7 @@ namespace GUS class IConnectionHandler: public NLMISC::CRefCount { public: + virtual ~IConnectionHandler() {} virtual void connect(TClientId)=0; virtual void disconnect(TClientId)=0; }; diff --git a/code/ryzom/server/src/general_utilities_service/rs_remote_saves.h b/code/ryzom/server/src/general_utilities_service/rs_remote_saves.h index 5ab25b8ac..1d91279bf 100644 --- a/code/ryzom/server/src/general_utilities_service/rs_remote_saves.h +++ b/code/ryzom/server/src/general_utilities_service/rs_remote_saves.h @@ -68,6 +68,8 @@ namespace SAVES class CRemoteSavesManager: public NLMISC::CRefCount { public: + virtual ~CRemoteSavesManager() {} + static CRemoteSavesManager* getInstance(); // interface used by CRemoteSavesInterface objects in their ctor to declare themselves diff --git a/code/ryzom/server/src/general_utilities_service/saves_unit.h b/code/ryzom/server/src/general_utilities_service/saves_unit.h index 3ea79287a..9685aa7bb 100644 --- a/code/ryzom/server/src/general_utilities_service/saves_unit.h +++ b/code/ryzom/server/src/general_utilities_service/saves_unit.h @@ -141,6 +141,9 @@ namespace SAVES // ctor ISavesUnitElement(); + // dtor + virtual ~ISavesUnitElement() {} + // perform the 'rescan' operation for this element - verify whether anything has changed // the parent parameter is used to supply addNew(), addChange() and addDeleted() methods // to signal any detected changes diff --git a/code/ryzom/server/src/patchman_service/patchman_tester.h b/code/ryzom/server/src/patchman_service/patchman_tester.h index ccab3d800..16a620c1c 100644 --- a/code/ryzom/server/src/patchman_service/patchman_tester.h +++ b/code/ryzom/server/src/patchman_service/patchman_tester.h @@ -38,6 +38,8 @@ namespace PATCHMAN class CPatchmanTester { public: + virtual ~CPatchmanTester() {} + // this is a singleton so it has a getInstance() method to get to the singleton instance static CPatchmanTester& getInstance(); diff --git a/code/ryzom/tools/stats_scan/char_filter_factory.h b/code/ryzom/tools/stats_scan/char_filter_factory.h index 642c0dfde..35ee52a07 100644 --- a/code/ryzom/tools/stats_scan/char_filter_factory.h +++ b/code/ryzom/tools/stats_scan/char_filter_factory.h @@ -39,6 +39,7 @@ class CStatsScanCharacter; class ICharFilter: public NLMISC::CRefCount { public: + virtual ~ICharFilter() {} virtual std::string toString() const=0; virtual bool evaluate(const CStatsScanCharacter* c)=0; }; diff --git a/code/ryzom/tools/stats_scan/char_info_extractor_factory.h b/code/ryzom/tools/stats_scan/char_info_extractor_factory.h index 920c4344b..35e3e7b68 100644 --- a/code/ryzom/tools/stats_scan/char_info_extractor_factory.h +++ b/code/ryzom/tools/stats_scan/char_info_extractor_factory.h @@ -41,6 +41,7 @@ class CStatsScanCharacter; class ICharInfoExtractor: public NLMISC::CRefCount { public: + virtual ~ICharInfoExtractor() {} virtual std::string toString() const=0; virtual void execute(CCharacterScanJob* job,const CStatsScanCharacter* c)=0; }; diff --git a/code/ryzom/tools/stats_scan/char_scan_script.h b/code/ryzom/tools/stats_scan/char_scan_script.h index 5f4a4d107..ac3a6780b 100644 --- a/code/ryzom/tools/stats_scan/char_scan_script.h +++ b/code/ryzom/tools/stats_scan/char_scan_script.h @@ -97,6 +97,7 @@ private: class ICharScanScriptCommand: public NLMISC::CRefCount { public: + virtual ~ICharScanScriptCommand() {} virtual const char* getName()=0; virtual const char* getSyntax()=0; virtual const char* getDescription()=0; From 55f715020252cc63424c4b653e6a0ca53426823b Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 8 Apr 2012 14:23:36 +0200 Subject: [PATCH 16/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/ryzom/common/src/game_share/mirror_prop_value_inline.h | 1 + code/ryzom/server/src/input_output_service/stdpch.h | 1 + 2 files changed, 2 insertions(+) diff --git a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h index dac2f55a6..2824977f3 100644 --- a/code/ryzom/common/src/game_share/mirror_prop_value_inline.h +++ b/code/ryzom/common/src/game_share/mirror_prop_value_inline.h @@ -25,6 +25,7 @@ #include "nel/misc/common.h" //#endif +#include /* * Activate this define if you want to set a value into the mirror only if it is different from the previous value diff --git a/code/ryzom/server/src/input_output_service/stdpch.h b/code/ryzom/server/src/input_output_service/stdpch.h index c1a8fea5c..743601942 100644 --- a/code/ryzom/server/src/input_output_service/stdpch.h +++ b/code/ryzom/server/src/input_output_service/stdpch.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "nel/georges/load_form.h" From 0bc94787cb565a7a8b5232aa8ea216a9809e1f50 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 18:21:41 +0200 Subject: [PATCH 18/77] Fixed: Do not mess with the order of the old TSampleFormat enum --HG-- branch : sound_dev --- code/nel/include/nel/sound/driver/sound_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/include/nel/sound/driver/sound_driver.h b/code/nel/include/nel/sound/driver/sound_driver.h index 193026a42..2fa450249 100644 --- a/code/nel/include/nel/sound/driver/sound_driver.h +++ b/code/nel/include/nel/sound/driver/sound_driver.h @@ -41,7 +41,7 @@ namespace NLSOUND /* * Sound sample format */ -enum TSampleFormat { SampleFormatUnknown, Mono8, Mono16ADPCM, Mono16, Stereo8, Stereo16 }; +enum TSampleFormat { Mono8, Mono16ADPCM, Mono16, Stereo8, Stereo16, SampleFormatUnknown = (~0) }; /** * Abstract sound driver (implemented in sound driver dynamic library) From b1c9745c8d6962eb6fc2ce0ec1c6352e79483b5c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 18:24:09 +0200 Subject: [PATCH 19/77] Added: Useful comment --HG-- branch : sound_dev --- code/nel/include/nel/sound/driver/sound_driver.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/nel/include/nel/sound/driver/sound_driver.h b/code/nel/include/nel/sound/driver/sound_driver.h index 2fa450249..c9551f16b 100644 --- a/code/nel/include/nel/sound/driver/sound_driver.h +++ b/code/nel/include/nel/sound/driver/sound_driver.h @@ -39,7 +39,9 @@ namespace NLSOUND #endif /* - * Sound sample format + * Deprecated sound sample format. + * For compatibility with old code. + * Do not modify. */ enum TSampleFormat { Mono8, Mono16ADPCM, Mono16, Stereo8, Stereo16, SampleFormatUnknown = (~0) }; From afdbc97039f2d7b09ff8c07e7ae49f70181bba6c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 18:42:31 +0200 Subject: [PATCH 20/77] Reverted: 2424536ec6f9 The setAsyncLoading function is used for a different purpose. In our case, async means that the file is read on the fly, and otherwise it is fully loaded into memory before being encoded. Synchronous mode is used for example during loading when hard disk access is not guaranteed. --HG-- branch : sound_dev --- code/nel/src/sound/driver/music_buffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/nel/src/sound/driver/music_buffer.cpp b/code/nel/src/sound/driver/music_buffer.cpp index 40088d49e..6da9d91ec 100644 --- a/code/nel/src/sound/driver/music_buffer.cpp +++ b/code/nel/src/sound/driver/music_buffer.cpp @@ -46,7 +46,8 @@ IMusicBuffer *IMusicBuffer::createMusicBuffer(const std::string &filepath, bool string type = CFile::getExtension(filepath); CIFile *ifile = new CIFile(); - ifile->setAsyncLoading(async); + ifile->setCacheFileOnOpen(!async); + ifile->allowBNPCacheFileOnOpen(!async); ifile->open(lookup); IMusicBuffer *mb = createMusicBuffer(type, ifile, loop); From 87ba382ef525911ba44b166161b34313181a223c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 19:03:48 +0200 Subject: [PATCH 21/77] Reverted: dc87ef1a34b6 Parameter distMax depends on fixed values in sheets and other code. Using this to switch between rolloff and minimum sound is likely causing the glitchy sound volumes of background sounds. Must be fixed in whatever code is using too large value of distMax instead, and not here. --HG-- branch : sound_dev --- code/nel/src/sound/driver/source.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/code/nel/src/sound/driver/source.cpp b/code/nel/src/sound/driver/source.cpp index ace98ff81..2f96d74d8 100644 --- a/code/nel/src/sound/driver/source.cpp +++ b/code/nel/src/sound/driver/source.cpp @@ -26,15 +26,12 @@ namespace NLSOUND // common method used only with OptionManualRolloff. return the volume in 1/100th DB ( = mB) modified sint32 ISource::computeManualRollOff(sint32 volumeMB, sint32 mbMin, sint32 mbMax, double alpha, float sqrdist, float distMin, float distMax) { - // root square of max float value - static float maxSqrt = sqrt(std::numeric_limits::max()); - if (sqrdist < distMin * distMin) { // no attenuation return volumeMB; } - else if ((distMax < maxSqrt) && (sqrdist > distMax * distMax)) + else if (sqrdist > distMax * distMax) { // full attenuation return mbMin; From f858bbfbe96b53abce67f119a92152400ab3d5bb Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 20:00:53 +0200 Subject: [PATCH 22/77] Added: Log warnings when setting excessively high max distances on sound sources --HG-- branch : sound_dev --- code/nel/src/sound/driver/fmod/source_fmod.cpp | 7 +++++++ code/nel/src/sound/driver/openal/source_al.cpp | 8 ++++++++ code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/code/nel/src/sound/driver/fmod/source_fmod.cpp b/code/nel/src/sound/driver/fmod/source_fmod.cpp index 2ae9a0ea1..4ef2f89d5 100644 --- a/code/nel/src/sound/driver/fmod/source_fmod.cpp +++ b/code/nel/src/sound/driver/fmod/source_fmod.cpp @@ -413,6 +413,13 @@ bool CSourceFMod::getSourceRelativeMode() const // ****************************************************************** void CSourceFMod::setMinMaxDistances( float mindist, float maxdist, bool /* deferred */ ) { + static float maxSqrt = sqrt(std::numeric_limits::max()); + if (maxdist >= maxSqrt) + { + nlwarning("SOUND_DEV (FMod): Ridiculously high max distance set on source"); + maxdist = maxSqrt; + } + _MinDist= mindist; _MaxDist= maxdist; if(_FModChannel!=-1) diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index fe2dcb086..c207f0d31 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -500,6 +500,14 @@ bool CSourceAL::getSourceRelativeMode() const void CSourceAL::setMinMaxDistances( float mindist, float maxdist, bool /* deferred */) { nlassert( (mindist >= 0.0f) && (maxdist >= 0.0f) ); + + static float maxSqrt = sqrt(std::numeric_limits::max()); + if (maxdist >= maxSqrt) + { + nlwarning("SOUND_DEV (OpenAL): Ridiculously high max distance set on source"); + maxdist = maxSqrt; + } + _MinDistance = mindist; _MaxDistance = maxdist; diff --git a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp index 9c34beaf8..df8101b40 100644 --- a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp +++ b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp @@ -793,7 +793,14 @@ bool CSourceXAudio2::getSourceRelativeMode() const void CSourceXAudio2::setMinMaxDistances(float mindist, float maxdist, bool /* deferred */) { // nldebug(NLSOUND_XAUDIO2_PREFIX "setMinMaxDistances %f, %f", mindist, maxdist); - + + static float maxSqrt = sqrt(std::numeric_limits::max()); + if (maxdist >= maxSqrt) + { + nlwarning("SOUND_DEV (XAudio2): Ridiculously high max distance set on source"); + maxdist = maxSqrt; + } + _Emitter.InnerRadius = mindist; _MinDistance = mindist; _MaxDistance = maxdist; From 19546acadec23f9981703701cde99fca3fb38fdd Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 20:18:13 +0200 Subject: [PATCH 23/77] Reverted: a1603fc4efbf Only release internal resources in driver destructor, not the driver classes, otherwise there will be crashes on exit. The driver classes are deleted by the application. --HG-- branch : sound_dev --- code/nel/src/sound/driver/openal/sound_driver_al.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/nel/src/sound/driver/openal/sound_driver_al.cpp b/code/nel/src/sound/driver/openal/sound_driver_al.cpp index e94755c63..6844514c3 100644 --- a/code/nel/src/sound/driver/openal/sound_driver_al.cpp +++ b/code/nel/src/sound/driver/openal/sound_driver_al.cpp @@ -198,7 +198,7 @@ CSoundDriverAL::~CSoundDriverAL() { nlwarning("AL: _Sources.size(): '%u'", (uint32)_Sources.size()); set::iterator it(_Sources.begin()), end(_Sources.end()); - for (; it != end; ++it) delete *it; + for (; it != end; ++it) it->release(); _Sources.clear(); } if (!_Buffers.empty()) alDeleteBuffers(compactAliveNames(_Buffers, alIsBuffer), &*_Buffers.begin()); @@ -207,7 +207,7 @@ CSoundDriverAL::~CSoundDriverAL() { nlwarning("AL: _Effects.size(): '%u'", (uint32)_Effects.size()); set::iterator it(_Effects.begin()), end(_Effects.end()); - for (; it != end; ++it) delete *it; + for (; it != end; ++it) it->release(); _Effects.clear(); } From dd02d76df86a80991e2bedc7ca13f0946ecbc090 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 20:28:14 +0200 Subject: [PATCH 24/77] Reverted: 3fdff8debe52 No comment --HG-- branch : sound_dev --- code/nel/src/sound/driver/openal/sound_driver_al.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/code/nel/src/sound/driver/openal/sound_driver_al.cpp b/code/nel/src/sound/driver/openal/sound_driver_al.cpp index 6844514c3..ec4f70839 100644 --- a/code/nel/src/sound/driver/openal/sound_driver_al.cpp +++ b/code/nel/src/sound/driver/openal/sound_driver_al.cpp @@ -619,12 +619,8 @@ void CSoundDriverAL::commit3DChanges() // Sync up sources & listener 3d position. if (getOption(OptionManualRolloff)) { - set::iterator it = _Sources.begin(), iend = _Sources.end(); - while(it != iend) - { + for (std::set::iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it) (*it)->updateManualRolloff(); - ++it; - } } // update the music (XFade etc...) From 8491bd4829c211ff92e829b447ebb17871fff9e1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 21:12:48 +0200 Subject: [PATCH 25/77] Removed: OpenAL music implementation --HG-- branch : sound_dev --- .../src/sound/driver/openal/listener_al.cpp | 22 +- .../sound/driver/openal/music_channel_al.cpp | 328 ------------------ .../sound/driver/openal/music_channel_al.h | 106 ------ .../sound/driver/openal/sound_driver_al.cpp | 64 +--- .../src/sound/driver/openal/sound_driver_al.h | 68 +--- .../nel/src/sound/driver/openal/source_al.cpp | 278 +++------------ code/nel/src/sound/driver/openal/source_al.h | 59 +--- 7 files changed, 99 insertions(+), 826 deletions(-) delete mode 100644 code/nel/src/sound/driver/openal/music_channel_al.cpp delete mode 100644 code/nel/src/sound/driver/openal/music_channel_al.h diff --git a/code/nel/src/sound/driver/openal/listener_al.cpp b/code/nel/src/sound/driver/openal/listener_al.cpp index 67eaf2478..da7e6bc31 100644 --- a/code/nel/src/sound/driver/openal/listener_al.cpp +++ b/code/nel/src/sound/driver/openal/listener_al.cpp @@ -145,9 +145,8 @@ void CListenerAL::getOrientation( NLMISC::CVector& front, NLMISC::CVector& u */ void CListenerAL::setGain( float gain ) { - CSoundDriverAL::getInstance()->setGain(gain); -// alListenerf( AL_GAIN, gain ); -// alTestError(); + alListenerf( AL_GAIN, gain ); + alTestError(); } @@ -156,15 +155,14 @@ void CListenerAL::setGain( float gain ) */ float CListenerAL::getGain() const { - return CSoundDriverAL::getInstance()->getGain(); -// ALfloat gain; -//#ifdef NL_OS_WINDOWS -// alGetListenerf( AL_GAIN, &gain ); -//#else -// alGetListenerfv( AL_GAIN, &gain ); -//#endif -// alTestError(); -// return gain; + ALfloat gain; +#ifdef NL_OS_WINDOWS + alGetListenerf( AL_GAIN, &gain ); +#else + alGetListenerfv( AL_GAIN, &gain ); +#endif + alTestError(); + return gain; } diff --git a/code/nel/src/sound/driver/openal/music_channel_al.cpp b/code/nel/src/sound/driver/openal/music_channel_al.cpp deleted file mode 100644 index b1849b33e..000000000 --- a/code/nel/src/sound/driver/openal/music_channel_al.cpp +++ /dev/null @@ -1,328 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdopenal.h" - -// Project includes -#include "sound_driver_al.h" -#include "source_al.h" -#include "buffer_al.h" -#include "music_channel_al.h" - -using namespace std; -using namespace NLMISC; - -namespace NLSOUND -{ - -CMusicChannelAL::CMusicChannelAL(CSoundDriverAL *soundDriver) -: _SoundDriver(soundDriver), _MusicBuffer(NULL), _Thread(NULL), _Buffer(NULL), _Source(NULL), _Playing(false), _Async(false), _Gain(1.0) -{ - // create a default source for music streaming - _Source = static_cast(_SoundDriver->createSource()); - _Source->setType(SourceMusic); - _Source->setStreamingBufferSize(32768); -} - -CMusicChannelAL::~CMusicChannelAL() -{ - release(); - if (_SoundDriver) { _SoundDriver->removeMusicChannel(this); _SoundDriver = NULL; } -} - -void CMusicChannelAL::release() -{ - // stop thread before deleting it - stop(); - - // delete thread - if (_Thread) - { - delete _Thread; - _Thread = NULL; - } - - // delete source - if (_Source) - { - delete _Source; - _Source = NULL; - } -} - -/// Fill IBuffer with data from IMusicBuffer -bool CMusicChannelAL::fillBuffer(IBuffer *buffer, uint length) -{ - if (!buffer || !length) - { - nlwarning("AL: No data to stream"); - return false; - } - - // fill buffer with music data - uint8 *tmp = buffer->lock(length); - if (tmp == NULL) - { - nlwarning("AL: Can't allocate %u bytes for buffer", length); - return false; - } - - uint32 size = _MusicBuffer->getNextBytes(tmp, length, length); - buffer->unlock(size); - - return size > 0; -} - -/// Use buffer format from IMusicBuffer -void CMusicChannelAL::setBufferFormat(IBuffer *buffer) -{ - if (!buffer) - { - nlwarning("AL: No buffer specified"); - return; - } - - // use the same format as music for buffers - buffer->setFormat(IBuffer::FormatPcm, _MusicBuffer->getChannels(), - _MusicBuffer->getBitsPerSample(), _MusicBuffer->getSamplesPerSec()); -} - -void CMusicChannelAL::run() -{ - bool first = true; - - // use queued buffers - do - { - // buffers to update - std::vector buffers; - - if (first) - { - // get all buffers to queue - _Source->getStreamingBuffers(buffers); - - // set format for each buffer - for(uint i = 0; i < buffers.size(); ++i) - setBufferFormat(buffers[i]); - } - else - { - // get unqueued buffers - _Source->getProcessedStreamingBuffers(buffers); - } - - // fill buffers - for(uint i = 0; i < buffers.size(); ++i) - { - if (!fillBuffer(buffers[i], _Source->getStreamingBufferSize())) - break; - - // add buffer to streaming buffers queue - _Source->submitStreamingBuffer(buffers[i]); - } - - // play the source - if (first) - { - _Source->play(); - first = false; - } - - // wait 100ms before rechecking buffers - nlSleep(100); - } - while(!_MusicBuffer->isMusicEnded() && _Playing); - - - // music finished without interruption - if (_Playing) - { - // wait until source is not playing - while(_Source->isPlaying() && _Playing) nlSleep(1000); - - _Source->stop(); - - _Playing = false; - } -} - -/// Play sync music -bool CMusicChannelAL::playSync() -{ - // use an unique buffer managed by CMusicChannelAL - _Buffer = _SoundDriver->createBuffer(); - - // set format - setBufferFormat(_Buffer); - - // fill data - fillBuffer(_Buffer, _MusicBuffer->getUncompressedSize()); - - // we don't need _MusicBuffer anymore because all is loaded into memory - if (_MusicBuffer) - { - delete _MusicBuffer; - _MusicBuffer = NULL; - } - - // delete previous queued buffers - _Source->setStreamingBuffersMax(0); - - // use this buffer as source - _Source->setStaticBuffer(_Buffer); - - // play the source - return _Source->play(); -} - -/** Play some music (.ogg etc...) - * NB: if an old music was played, it is first stop with stopMusic() - * \param filepath file path, CPath::lookup is done here - * \param async stream music from hard disk, preload in memory if false - * \param loop must be true to play the music in loop. - */ -bool CMusicChannelAL::play(const std::string &filepath, bool async, bool loop) -{ - // stop a previous music - stop(); - - // when not using async, we must load the whole file once - _MusicBuffer = IMusicBuffer::createMusicBuffer(filepath, async, async ? loop:false); - - if (_MusicBuffer) - { - _Async = async; - _Playing = true; - - _Source->setSourceRelativeMode(true); - - if (_Async) - { - // create the thread if it's not yet created - if (!_Thread) _Thread = IThread::create(this); - - if (!_Thread) - { - nlwarning("AL: Can't create a new thread"); - return false; - } - - // use 4 queued buffers - _Source->setStreamingBuffersMax(4); - - // we need to loop the source only if not async - _Source->setLooping(false); - - // start the thread - _Thread->start(); - } - else - { - // we need to loop the source only if not async - _Source->setLooping(loop); - - return playSync(); - } - } - else - { - nlwarning("AL: Can't stream file %s", filepath.c_str()); - return false; - } - - return true; -} - -/// Stop the music previously loaded and played (the Memory is also freed) -void CMusicChannelAL::stop() -{ - _Playing = false; - - _Source->stop(); - - // if not using async streaming, we manage static buffer ourself - if (!_Async && _Buffer) - { - _Source->setStaticBuffer(NULL); - delete _Buffer; - _Buffer = NULL; - } - - // wait until thread is finished - if (_Thread) - _Thread->wait(); - - if (_MusicBuffer) - { - delete _MusicBuffer; - _MusicBuffer = NULL; - } -} - -/// Pause the music previously loaded and played (the Memory is not freed) -void CMusicChannelAL::pause() -{ - _Source->pause(); -} - -/// Resume the music previously paused -void CMusicChannelAL::resume() -{ - _Source->play(); -} - -/// Return true if a song is finished. -bool CMusicChannelAL::isEnded() -{ - return !_Playing; -} - -/// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading -bool CMusicChannelAL::isLoadingAsync() -{ - return _Async && _Playing && !_Source->isPlaying(); -} - -/// Return the total length (in second) of the music currently played -float CMusicChannelAL::getLength() -{ - if (_MusicBuffer) return _MusicBuffer->getLength(); - else return .0f; -} - -/** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1) - * NB: in OpenAL driver, the volume of music IS affected by IListener::setGain() - */ -void CMusicChannelAL::setVolume(float gain) -{ - _Gain = gain; - _Source->setGain(gain); -} - -/// Update music -void CMusicChannelAL::update() -{ - // stop sync music once finished playing - if (_Playing && !_Async && !_Source->isPlaying()) - { - stop(); - } -} - -} /* namespace NLSOUND */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/openal/music_channel_al.h b/code/nel/src/sound/driver/openal/music_channel_al.h deleted file mode 100644 index 157c18810..000000000 --- a/code/nel/src/sound/driver/openal/music_channel_al.h +++ /dev/null @@ -1,106 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef NLSOUND_MUSIC_CHANNEL_AL_H -#define NLSOUND_MUSIC_CHANNEL_AL_H - -#include "nel/sound/driver/music_channel.h" - -namespace NLSOUND -{ - class CSoundDriverAL; - class IMusicBuffer; - -/** - * \brief CMusicChannelAL - * \date 2010-07-27 16:56GMT - * \author Kervala - * CMusicChannelAL is an implementation of the IMusicChannel interface to run on OpenAL. - */ -class CMusicChannelAL : public IMusicChannel, public NLMISC::IRunnable -{ -protected: - // outside pointers - CSoundDriverAL* _SoundDriver; - - // pointers - IMusicBuffer* _MusicBuffer; - NLMISC::IThread* _Thread; - - IBuffer* _Buffer; - CSourceAL* _Source; - bool _Playing; - bool _Async; - - float _Gain; - - /// Fill IBuffer with data from IMusicBuffer - bool fillBuffer(IBuffer *buffer, uint length); - - /// Use buffer format from IMusicBuffer - void setBufferFormat(IBuffer *buffer); - - /// Declared in NLMISC::IRunnable interface - virtual void run(); - -public: - CMusicChannelAL(CSoundDriverAL *soundDriver); - virtual ~CMusicChannelAL(); - void release(); - - /** Play some music (.ogg etc...) - * NB: if an old music was played, it is first stop with stopMusic() - * \param filepath file path, CPath::lookup is done here - * \param async stream music from hard disk, preload in memory if false - * \param loop must be true to play the music in loop. - */ - virtual bool play(const std::string &filepath, bool async, bool loop); - - /// Stop the music previously loaded and played (the Memory is also freed) - virtual void stop(); - - /// Pause the music previously loaded and played (the Memory is not freed) - virtual void pause(); - - /// Resume the music previously paused - virtual void resume(); - - /// Return true if a song is finished. - virtual bool isEnded(); - - /// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading - virtual bool isLoadingAsync(); - - /// Return the total length (in second) of the music currently played - virtual float getLength(); - - /** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1) - * NB: in OpenAL driver, the volume of music IS affected by IListener::setGain() - */ - virtual void setVolume(float gain); - - /// Play sync music - bool playSync(); - - /// Update music - void update(); -}; /* class CMusicChannelAL */ - -} /* namespace NLSOUND */ - -#endif /* #ifndef NLSOUND_MUSIC_CHANNEL_AL_H */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/openal/sound_driver_al.cpp b/code/nel/src/sound/driver/openal/sound_driver_al.cpp index ec4f70839..22b1eaf3f 100644 --- a/code/nel/src/sound/driver/openal/sound_driver_al.cpp +++ b/code/nel/src/sound/driver/openal/sound_driver_al.cpp @@ -16,7 +16,6 @@ #include "stdopenal.h" #include "sound_driver_al.h" -#include "music_channel_al.h" #include "buffer_al.h" #include "listener_al.h" #include "source_al.h" @@ -174,7 +173,7 @@ uint32 NLSOUND_interfaceVersion () */ CSoundDriverAL::CSoundDriverAL(ISoundDriver::IStringMapperProvider *stringMapper) : _StringMapper(stringMapper), _AlDevice(NULL), _AlContext(NULL), -_NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f), _MasterGain(1.f) +_NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f) { alExtInit(); } @@ -184,21 +183,13 @@ _NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f), _MasterGain(1.f) */ CSoundDriverAL::~CSoundDriverAL() { - // Release internal resources of all remaining IMusicChannel instances - if (_MusicChannels.size()) - { - nlwarning("AL: _MusicChannels.size(): '%u'", (uint32)_MusicChannels.size()); - set::iterator it(_MusicChannels.begin()), end(_MusicChannels.end()); - for (; it != end; ++it) delete *it; - _MusicChannels.clear(); - } // Remove the allocated (but not exported) source and buffer names- // Release internal resources of all remaining ISource instances if (_Sources.size()) { nlwarning("AL: _Sources.size(): '%u'", (uint32)_Sources.size()); set::iterator it(_Sources.begin()), end(_Sources.end()); - for (; it != end; ++it) it->release(); + for (; it != end; ++it) (*it)->release(); // CSourceAL will be deleted by user _Sources.clear(); } if (!_Buffers.empty()) alDeleteBuffers(compactAliveNames(_Buffers, alIsBuffer), &*_Buffers.begin()); @@ -207,7 +198,7 @@ CSoundDriverAL::~CSoundDriverAL() { nlwarning("AL: _Effects.size(): '%u'", (uint32)_Effects.size()); set::iterator it(_Effects.begin()), end(_Effects.end()); - for (; it != end; ++it) it->release(); + for (; it != end; ++it) (*it)->release(); // CEffectAL will be deleted by user _Effects.clear(); } @@ -622,9 +613,6 @@ void CSoundDriverAL::commit3DChanges() for (std::set::iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it) (*it)->updateManualRolloff(); } - - // update the music (XFade etc...) - updateMusic(); } /// Write information about the driver to the output stream. @@ -652,23 +640,6 @@ void CSoundDriverAL::displayBench(NLMISC::CLog *log) NLMISC::CHTimer::display(log, CHTimer::TotalTime); } -/** Get music info. Returns false if the song is not found or the function is not implemented. - * \param filepath path to file, CPath::lookup done by driver - * \param artist returns the song artist (empty if not available) - * \param title returns the title (empty if not available) - */ -bool CSoundDriverAL::getMusicInfo(const std::string &filepath, std::string &artist, std::string &title) -{ - // add support for additional non-standard music file types info here - return IMusicBuffer::getInfo(filepath, artist, title); -} - -void CSoundDriverAL::updateMusic() -{ - set::iterator it(_MusicChannels.begin()), end(_MusicChannels.end()); - for (; it != end; ++it) (*it)->update(); -} - /// Remove a buffer void CSoundDriverAL::removeBuffer(CBufferAL *buffer) { @@ -691,35 +662,6 @@ void CSoundDriverAL::removeEffect(CEffectAL *effect) else nlwarning("AL: removeEffect already called"); } -/// Create a music channel -IMusicChannel *CSoundDriverAL::createMusicChannel() -{ - CMusicChannelAL *music_channel = new CMusicChannelAL(this); - _MusicChannels.insert(music_channel); - return static_cast(music_channel); -} - -/// (Internal) Remove a music channel (should be called by the destructor of the music channel class). -void CSoundDriverAL::removeMusicChannel(CMusicChannelAL *musicChannel) -{ - if (_MusicChannels.find(musicChannel) != _MusicChannels.end()) _MusicChannels.erase(musicChannel); - else nlwarning("AL: removeMusicChannel already called"); -} - -/// Set the gain -void CSoundDriverAL::setGain( float gain ) -{ - clamp(gain, 0.f, 1.f); - _MasterGain= gain; - // TODO: update all sources in not using manual rollof ? -} - -/// Get the gain -float CSoundDriverAL::getGain() -{ - return _MasterGain; -} - /// Delete a buffer or a source bool CSoundDriverAL::deleteItem( ALuint name, TDeleteFunctionAL aldeletefunc, vector& names ) { diff --git a/code/nel/src/sound/driver/openal/sound_driver_al.h b/code/nel/src/sound/driver/openal/sound_driver_al.h index 50bffa379..154bc6d78 100644 --- a/code/nel/src/sound/driver/openal/sound_driver_al.h +++ b/code/nel/src/sound/driver/openal/sound_driver_al.h @@ -19,13 +19,11 @@ #include -namespace NLSOUND -{ +namespace NLSOUND { class CBufferAL; class CListenerAL; class CSourceAL; class CEffectAL; - class CMusicChannelAL; // alGenBuffers, alGenSources //typedef ALAPI ALvoid ALAPIENTRY (*TGenFunctionAL) ( ALsizei, ALuint* ); @@ -82,8 +80,6 @@ private: std::set _Sources; // Allocated effects std::set _Effects; - /// Array with the allocated music channels created by client code. - std::set _MusicChannels; // Number of exported buffers (including any deleted buffers) uint _NbExpBuffers; // Number of exported sources (including any deleted sources) @@ -101,6 +97,10 @@ public: /// Destructor virtual ~CSoundDriverAL(); + inline ALCdevice *getAlDevice() { return _AlDevice; } + inline ALCcontext *getAlContext() { return _AlContext; } + inline float getRolloffFactor() { return _RolloffFactor; } + /// Return a list of available devices for the user. The value at index 0 is empty, and is used for automatic device selection. virtual void getDevices(std::vector &devices); /// Initialize the driver with a user selected device. If device.empty(), the default or most appropriate device is used. @@ -111,73 +111,46 @@ public: /// Return if an option is enabled (including those that cannot be disabled on this driver). virtual bool getOption(TSoundOptions option); - /// Commit all the changes made to 3D settings of listener and sources - virtual void commit3DChanges(); - + /// Create a sound buffer + virtual IBuffer *createBuffer(); /// Create the listener instance virtual IListener *createListener(); - /// Create a source, destroy with delete + /// Create a source virtual ISource *createSource(); - /// Create a sound buffer, destroy with delete - virtual IBuffer *createBuffer(); /// Create a reverb effect virtual IReverbEffect *createReverbEffect(); /// Return the maximum number of sources that can created virtual uint countMaxSources(); /// Return the maximum number of effects that can be created virtual uint countMaxEffects(); - - /// Write information about the driver to the output stream. - virtual void writeProfile(std::string& /* out */); - + virtual void startBench(); virtual void endBench(); virtual void displayBench(NLMISC::CLog * /* log */); - /// Create a music channel, destroy with destroyMusicChannel. - virtual IMusicChannel *createMusicChannel(); - - /** Get music info. Returns false if the song is not found or the function is not implemented. - * \param filepath path to file, CPath::lookup done by driver - * \param artist returns the song artist (empty if not available) - * \param title returns the title (empty if not available) - */ - virtual bool getMusicInfo(const std::string &filepath, std::string &artist, std::string &title); - - /// Get audio/container extensions that are supported natively by the driver implementation. - virtual void getMusicExtensions(std::vector & /* extensions */) const { } - /// Return if a music extension is supported by the driver's music channel. - virtual bool isMusicExtensionSupported(const std::string & /* extension */) const { return false; } - - ALCdevice *getAlDevice() { return _AlDevice; } - ALCcontext *getAlContext() { return _AlContext; } - float getRolloffFactor() { return _RolloffFactor; } /// Change the rolloff factor and apply to all sources void applyRolloffFactor(float f); + /// Commit all the changes made to 3D settings of listener and sources + virtual void commit3DChanges(); + + /// Write information about the driver to the output stream. + virtual void writeProfile(std::string& /* out */); + /// Remove a buffer void removeBuffer(CBufferAL *buffer); /// Remove a source void removeSource(CSourceAL *source); /// Remove an effect void removeEffect(CEffectAL *effect); - /// (Internal) Remove music channel (should be called by the destructor of the music channel class). - void removeMusicChannel(CMusicChannelAL *musicChannel); - - /** Set the gain (volume value inside [0 , 1]). (default: 1) - * 0.0 -> silence - * 0.5 -> -6dB - * 1.0 -> no attenuation - * values > 1 (amplification) not supported by most drivers - */ - void setGain( float gain ); - /// Get the gain - float getGain(); + /// Get audio/container extensions that are supported natively by the driver implementation. + virtual void getMusicExtensions(std::vector & /* extensions */) const { } + /// Return if a music extension is supported by the driver's music channel. + virtual bool isMusicExtensionSupported(const std::string & /* extension */) const { return false; } protected: - void updateMusic(); /// Allocate nb new buffers or sources void allocateNewItems( TGenFunctionAL algenfunc, TTestFunctionAL altestfunc, @@ -195,9 +168,6 @@ protected: /// Delete a buffer or a source bool deleteItem( ALuint name, TDeleteFunctionAL aldeletefunc, std::vector& names ); - - /// Master Volume [0,1] - float _MasterGain; }; diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index c207f0d31..19ef1780f 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -15,55 +15,36 @@ // along with this program. If not, see . #include "stdopenal.h" -#include "source_al.h" #include "sound_driver_al.h" #include "listener_al.h" #include "effect_al.h" #include "buffer_al.h" +#include "source_al.h" #include "ext_al.h" using namespace std; using namespace NLMISC; -namespace NLSOUND -{ +namespace NLSOUND { -CSourceAL::CSourceAL(CSoundDriverAL *soundDriver):ISource(), _SoundDriver(NULL), _Source(AL_NONE), - _DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL) +CSourceAL::CSourceAL(CSoundDriverAL *soundDriver) : +_SoundDriver(NULL), _Buffer(NULL), _Source(AL_NONE), +_DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL), +_IsPlaying(false), _IsPaused(false), _StartTime(0), +_Pos(0.0f, 0.0f, 0.0f), _Gain(NLSOUND_DEFAULT_GAIN), _Alpha(1.0), +_MinDistance(1.0f), _MaxDistance(numeric_limits::max()), +_Effect(NULL), _Direct(true), +_DirectGain(NLSOUND_DEFAULT_DIRECT_GAIN), _EffectGain(NLSOUND_DEFAULT_EFFECT_GAIN), +_DirectFilterType(ISource::FilterLowPass), _EffectFilterType(ISource::FilterLowPass), +_DirectFilterEnabled(false), _EffectFilterEnabled(false), +_DirectFilterPassGain(NLSOUND_DEFAULT_FILTER_PASS_GAIN), _EffectFilterPassGain(NLSOUND_DEFAULT_FILTER_PASS_GAIN) { - _IsPlaying = false; - _IsPaused = false; - _StartTime = 0; - - _Type = SourceSound; - _Buffer = NULL; - _BuffersMax = 0; - _BufferSize = 32768; - - _PosRelative = false; - _Gain = NLSOUND_DEFAULT_GAIN; - _Alpha = 0.0; - _Pos = CVector::Null; - _MinDistance = 1.0f; - _MaxDistance = numeric_limits::max(); - - _Effect = NULL; - _Direct = true; - _DirectGain = NLSOUND_DEFAULT_DIRECT_GAIN; - _EffectGain = NLSOUND_DEFAULT_EFFECT_GAIN; - _DirectFilterType = ISource::FilterLowPass; - _EffectFilterType = ISource::FilterLowPass; - _DirectFilterEnabled = false; - _EffectFilterEnabled = false; - _DirectFilterPassGain = NLSOUND_DEFAULT_FILTER_PASS_GAIN; - _EffectFilterPassGain = NLSOUND_DEFAULT_FILTER_PASS_GAIN; - // create the al source alGenSources(1, &_Source); alTestError(); - + // configure rolloff - if (!soundDriver || soundDriver->getOption(ISoundDriver::OptionManualRolloff)) + if (soundDriver->getOption(ISoundDriver::OptionManualRolloff)) { alSourcef(_Source, AL_ROLLOFF_FACTOR, 0); alTestError(); @@ -73,16 +54,15 @@ CSourceAL::CSourceAL(CSoundDriverAL *soundDriver):ISource(), _SoundDriver(NULL), alSourcef(_Source, AL_ROLLOFF_FACTOR, soundDriver->getRolloffFactor()); alTestError(); } - + // create filters - if (soundDriver && soundDriver->getOption(ISoundDriver::OptionEnvironmentEffects)) + if (soundDriver->getOption(ISoundDriver::OptionEnvironmentEffects)) { alGenFilters(1, &_DirectFilter); alFilteri(_DirectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); alFilterf(_DirectFilter, AL_LOWPASS_GAIN, NLSOUND_DEFAULT_DIRECT_GAIN); alFilterf(_DirectFilter, AL_LOWPASS_GAINHF, NLSOUND_DEFAULT_FILTER_PASS_GAIN); alTestError(); - alGenFilters(1, &_EffectFilter); alFilteri(_EffectFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); alFilterf(_EffectFilter, AL_LOWPASS_GAIN, NLSOUND_DEFAULT_EFFECT_GAIN); @@ -103,8 +83,6 @@ CSourceAL::~CSourceAL() void CSourceAL::release() { - unqueueBuffers(); - removeBuffers(); if (_Source != AL_NONE) { alDeleteSources(1, &_Source); _Source = AL_NONE; } if (_DirectFilter != AL_FILTER_NULL) { alDeleteFilters(1, &_DirectFilter); _DirectFilter = AL_FILTER_NULL; } if (_EffectFilter != AL_FILTER_NULL) { alDeleteFilters(1, &_EffectFilter); _EffectFilter = AL_FILTER_NULL; } @@ -114,37 +92,13 @@ void CSourceAL::release() /// (Internal) Update the 3d changes. void CSourceAL::updateManualRolloff() { - CVector pos = getPos(); - - // make relative to listener (if not already!) - if (!_PosRelative) - pos -= CListenerAL::getInstance()->getPos(); - - float sqrdist = pos.sqrnorm(); - float rolloff = ISource::computeManualRolloff(_Alpha, sqrdist, _MinDistance, _MaxDistance); - float volume = _Gain * rolloff; - - // apply SFX volume - if (_SoundDriver && _Type == SourceSound) - volume *= _SoundDriver->getGain(); - - // set the attenuated volume - alSourcef(_Source, AL_GAIN, volume); + CVector distanceVector = _Pos - CListenerAL::getInstance()->getPos(); + float distanceSquare = distanceVector.sqrnorm(); + float rolloff = ISource::computeManualRolloff(_Alpha, distanceSquare, _MinDistance, _MaxDistance); + alSourcef(_Source, AL_GAIN, _Gain * rolloff); alTestError(); } -/// Set type of the source -void CSourceAL::setType(TSourceType type) -{ - _Type = type; -} - -/// Get type of the source -TSourceType CSourceAL::getType() const -{ - return _Type; -} - /// Enable or disable streaming mode. Source must be stopped to call this. void CSourceAL::setStreaming(bool /* streaming */) { @@ -199,11 +153,10 @@ void CSourceAL::submitStreamingBuffer(IBuffer *buffer) CBufferAL *bufferAL = static_cast(buffer); ALuint bufferName = bufferAL->bufferName(); nlassert(bufferName); - - // queue the buffer alSourceQueueBuffers(_Source, 1, &bufferName); alTestError(); - + _QueuedBuffers.push(bufferAL); + // Resume playback if the internal OpenAL source stopped due to buffer underrun. ALint srcstate; alGetSourcei(_Source, AL_SOURCE_STATE, &srcstate); @@ -218,11 +171,23 @@ void CSourceAL::submitStreamingBuffer(IBuffer *buffer) /// Return the amount of buffers in the queue (playing and waiting). 3 buffers is optimal. uint CSourceAL::countStreamingBuffers() const { + // a bit ugly here, but makes a much easier/simpler implementation on both drivers + ALint buffersProcessed; + alGetSourcei(_Source, AL_BUFFERS_PROCESSED, &buffersProcessed); + while (buffersProcessed) + { + ALuint bufferName = _QueuedBuffers.front()->bufferName(); + alSourceUnqueueBuffers(_Source, 1, &bufferName); + alTestError(); + const_cast &>(_QueuedBuffers).pop(); + --buffersProcessed; + } // return how many are left in the queue - ALint buffersQueued; - alGetSourcei(_Source, AL_BUFFERS_QUEUED, &buffersQueued); - alTestError(); - return (uint)buffersQueued; + //ALint buffersQueued; + //alGetSourcei(_SourceName, AL_BUFFERS_QUEUED, &buffersQueued); + //alTestError(); + //return (uint)buffersQueued; + return (uint)_QueuedBuffers.size(); } /// Set looping on/off for future playbacks (default: off) @@ -260,7 +225,7 @@ bool CSourceAL::play() _IsPaused = false; alSourcePlay(_Source); _IsPlaying = true; - _StartTime = CTime::getLocalTime(); + _StartTime = CTime::getLocalTime(); // TODO: Played time should freeze when buffering fails, and be calculated based on the number of buffers played plus passed time. This is necessary for synchronizing animation with sound. return true; // Streaming mode //nlwarning("AL: Cannot play null buffer; streaming not implemented" ); @@ -288,8 +253,14 @@ void CSourceAL::stop() _IsPaused = false; alSourceStop(_Source); alTestError(); - - unqueueBuffers(); + // unqueue buffers + while (_QueuedBuffers.size()) + { + ALuint bufferName = _QueuedBuffers.front()->bufferName(); + alSourceUnqueueBuffers(_Source, 1, &bufferName); + _QueuedBuffers.pop(); + alTestError(); + } // Streaming mode //nlwarning("AL: Cannot stop null buffer; streaming not implemented" ); //nlstop; @@ -379,8 +350,7 @@ bool CSourceAL::isPaused() const uint32 CSourceAL::getTime() { if (!_StartTime) return 0; - - return (uint32)(CTime::getLocalTime() - _StartTime); + return (uint32)(CTime::getLocalTime() - _StartTime); } /// Set the position vector. @@ -438,16 +408,9 @@ void CSourceAL::getDirection( NLMISC::CVector& dir ) const void CSourceAL::setGain(float gain) { _Gain = std::min(std::max(gain, NLSOUND_MIN_GAIN), NLSOUND_MAX_GAIN); - - if ((_SoundDriver == NULL) || !_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) + if (!_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) { - float gain = _Gain; - - // apply SFX volume - if (_SoundDriver && _Type == SourceSound) - gain *= _SoundDriver->getGain(); - - alSourcef(_Source, AL_GAIN, gain); + alSourcef(_Source, AL_GAIN, _Gain); alTestError(); } } @@ -481,7 +444,6 @@ float CSourceAL::getPitch() const /// Set the source relative mode. If true, positions are interpreted relative to the listener position. void CSourceAL::setSourceRelativeMode( bool mode ) { - _PosRelative = mode; alSourcei(_Source, AL_SOURCE_RELATIVE, mode?AL_TRUE:AL_FALSE ); alTestError(); } @@ -489,29 +451,19 @@ void CSourceAL::setSourceRelativeMode( bool mode ) /// Get the source relative mode (3D mode only) bool CSourceAL::getSourceRelativeMode() const { - return _PosRelative; -// ALint b; -// alGetSourcei(_Source, AL_SOURCE_RELATIVE, &b ); -// alTestError(); -// return (b==AL_TRUE); + ALint b; + alGetSourcei(_Source, AL_SOURCE_RELATIVE, &b ); + alTestError(); + return (b==AL_TRUE); } /// Set the min and max distances (3D mode only) void CSourceAL::setMinMaxDistances( float mindist, float maxdist, bool /* deferred */) { nlassert( (mindist >= 0.0f) && (maxdist >= 0.0f) ); - - static float maxSqrt = sqrt(std::numeric_limits::max()); - if (maxdist >= maxSqrt) - { - nlwarning("SOUND_DEV (OpenAL): Ridiculously high max distance set on source"); - maxdist = maxSqrt; - } - _MinDistance = mindist; _MaxDistance = maxdist; - - if (!_SoundDriver || !_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) + if (!_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) { alSourcef(_Source, AL_REFERENCE_DISTANCE, mindist); alSourcef(_Source, AL_MAX_DISTANCE, maxdist); @@ -813,122 +765,4 @@ float CSourceAL::getEffectFilterPassGain() const return _EffectFilterPassGain; } -/// Get already processed buffers and unqueue them -void CSourceAL::getProcessedStreamingBuffers(std::vector &buffers) -{ - // get the number of processed buffers - ALint buffersProcessed; - alGetSourcei(_Source, AL_BUFFERS_PROCESSED, &buffersProcessed); - alTestError(); - - // exit if more processed buffer than allocated ones - if ((uint)buffersProcessed > _BuffersMax) return; - - // unqueue all previously processed buffers and get their name - alSourceUnqueueBuffers(_Source, buffersProcessed, &(_BuffersName[0])); - alTestError(); - - // add each processed buffer to the array - for(uint i = 0; i < (uint)buffersProcessed; ++i) - { - // if buffer is found, return it - std::map::const_iterator it = _Buffers.find(_BuffersName[i]); - if (it != _Buffers.end()) - buffers.push_back(it->second); - } -} - -/// Get all existing buffers -void CSourceAL::getStreamingBuffers(std::vector &buffers) -{ - std::map::const_iterator it = _Buffers.begin(), iend = _Buffers.end(); - while(it != iend) - { - buffers.push_back(it->second); - ++it; - } -} - -/// Unqueue all buffers -void CSourceAL::unqueueBuffers() -{ - // get count of buffers in queue - uint count = countStreamingBuffers(); - - if (count > 0) - { - // unqueue all of them - alSourceUnqueueBuffers(_Source, count, &(_BuffersName[0])); - alTestError(); - } -} - -/// Delete all allocated buffers -void CSourceAL::removeBuffers() -{ - // delete each buffer - std::map::const_iterator it = _Buffers.begin(), iend = _Buffers.end(); - while(it != iend) - { - delete it->second; - ++it; - } - - _Buffers.clear(); -} - -/// Get available streaming buffers count -uint CSourceAL::getStreamingBuffersMax() const -{ - return _BuffersMax; -} - -/// Set available streaming buffers count and allocate them -void CSourceAL::setStreamingBuffersMax(uint buffers) -{ - // remember previous value - uint oldBuffersMax = _BuffersMax; - - _BuffersMax = buffers; - - // resize the temporary buffer names array - _BuffersName.resize(buffers); - - // remove all buffers - unqueueBuffers(); - removeBuffers(); - - for(uint i = 0; i < _BuffersMax; ++i) - { - try - { - // create a new buffer - CBufferAL *buffer = static_cast(_SoundDriver->createBuffer()); - // use StorageSoftware because buffers will be reused - // deleting and recreating them is a waste of time - buffer->setStorageMode(IBuffer::StorageSoftware); - _Buffers[buffer->bufferName()] = buffer; - } - catch(const ESoundDriverGenBuf &e) - { - nlwarning("Cannot create %d buffers. openal fails after %d buffers", buffers, i); - _BuffersMax = i; - _BuffersName.resize(i); - break; - } - } -} - -/// Set the default size for streaming buffers -void CSourceAL::setStreamingBufferSize(uint size) -{ - _BufferSize = size; -} - -/// Get the default size for streaming buffers -uint CSourceAL::getStreamingBufferSize() const -{ - return _BufferSize; -} - } // NLSOUND diff --git a/code/nel/src/sound/driver/openal/source_al.h b/code/nel/src/sound/driver/openal/source_al.h index b5611b997..cae9ff042 100644 --- a/code/nel/src/sound/driver/openal/source_al.h +++ b/code/nel/src/sound/driver/openal/source_al.h @@ -17,17 +17,14 @@ #ifndef NL_SOURCE_AL_H #define NL_SOURCE_AL_H -#include "nel/sound/driver/source.h" +#include -namespace NLSOUND -{ +namespace NLSOUND { class IBuffer; class CBufferAL; class CSoundDriverAL; class CEffectAL; - enum TSourceType { SourceSound, SourceMusic }; - /** * OpenAL sound source * @@ -50,29 +47,19 @@ private: /// Sound driver CSoundDriverAL *_SoundDriver; + /// Assigned buffer object + CBufferAL *_Buffer; + std::queue _QueuedBuffers; + /// AL Handles ALuint _Source; ALuint _DirectFilter, _EffectFilter; - - /// Assigned buffer object - CBufferAL *_Buffer; - /// Queued buffers map (uint is buffer name) - std::map _Buffers; - - /// Temporary queued buffers array - std::vector _BuffersName; - /// Max count of queued buffers allowed - uint _BuffersMax; - /// Default size of a buffer - uint _BufferSize; - - /// Position is relative to listener - bool _PosRelative; - + /// Playing status bool _IsPlaying; bool _IsPaused; - NLMISC::TTime _StartTime; + NLMISC::TTime _StartTime; + NLMISC::CVector _Pos; float _Gain; double _Alpha; @@ -90,9 +77,6 @@ private: TFilter _DirectFilterType, _EffectFilterType; bool _DirectFilterEnabled, _EffectFilterEnabled; float _DirectFilterPassGain, _EffectFilterPassGain; - - /// Source type can be SourceSound or SourceMusic - TSourceType _Type; public: /// Constructor @@ -104,13 +88,8 @@ public: void release(); /// Return the OpenAL source name - ALuint getSource() const { return _Source; } - - /// Set type of the source - void setType(TSourceType type); - /// Get type of the source - TSourceType getType() const; - + inline ALuint getSource() const { return _Source; } + /// (Internal) Set the effect send for this source, NULL to disable. void setEffect(CEffectAL *effect); /// (Internal) Setup the direct send filter. @@ -275,22 +254,6 @@ public: virtual float getEffectFilterPassGain() const; //@} - /// Get already processed buffers and unqueue them - void getProcessedStreamingBuffers(std::vector &buffers); - /// Get all existing buffers - void getStreamingBuffers(std::vector &buffers); - /// Unqueue all buffers - void unqueueBuffers(); - /// Delete all allocated buffers - void removeBuffers(); - /// Get available streaming buffers count - uint getStreamingBuffersMax() const; - /// Set available streaming buffers count and allocate them - void setStreamingBuffersMax(uint max); - /// Set the default size for streaming buffers - void setStreamingBufferSize(uint size); - /// Get the default size for streaming buffers - uint getStreamingBufferSize() const; }; } // NLSOUND From c4adc53a0fd6193b2aeae572e04369841349a734 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 21:46:07 +0200 Subject: [PATCH 26/77] Removed: #795 XAudio2 music implementation --HG-- branch : sound_dev --- .../sound/driver/openal/sound_driver_al.cpp | 3 + .../driver/xaudio2/music_channel_xaudio2.cpp | 248 ------------------ .../driver/xaudio2/music_channel_xaudio2.h | 117 --------- .../driver/xaudio2/sound_driver_xaudio2.cpp | 39 +-- .../driver/xaudio2/sound_driver_xaudio2.h | 17 -- .../nel/src/sound/driver/xaudio2/stdxaudio2.h | 1 - 6 files changed, 4 insertions(+), 421 deletions(-) delete mode 100644 code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.cpp delete mode 100644 code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.h diff --git a/code/nel/src/sound/driver/openal/sound_driver_al.cpp b/code/nel/src/sound/driver/openal/sound_driver_al.cpp index 22b1eaf3f..e7eaca3f6 100644 --- a/code/nel/src/sound/driver/openal/sound_driver_al.cpp +++ b/code/nel/src/sound/driver/openal/sound_driver_al.cpp @@ -183,6 +183,9 @@ _NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f) */ CSoundDriverAL::~CSoundDriverAL() { + // WARNING: Only internal resources are released here, + // the created instances must still be released by the user! + // Remove the allocated (but not exported) source and buffer names- // Release internal resources of all remaining ISource instances if (_Sources.size()) diff --git a/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.cpp deleted file mode 100644 index 4110ecf41..000000000 --- a/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdxaudio2.h" - -// Project includes -#include "sound_driver_xaudio2.h" -#include "music_channel_xaudio2.h" - -using namespace std; -using namespace NLMISC; - -namespace NLSOUND { - -CMusicChannelXAudio2::CMusicChannelXAudio2(CSoundDriverXAudio2 *soundDriver) -: _MusicBuffer(NULL), _SourceVoice(NULL), _BufferPos(0), _SoundDriver(soundDriver), _Gain(1.0) -{ - nlwarning(NLSOUND_XAUDIO2_PREFIX "Initializing CMusicChannelXAudio2"); - - stop(); -} - -CMusicChannelXAudio2::~CMusicChannelXAudio2() -{ - release(); - if (_SoundDriver) { _SoundDriver->removeMusicChannel(this); _SoundDriver = NULL; } - - nlwarning(NLSOUND_XAUDIO2_PREFIX "Destroying CMusicChannelXAudio2"); -} - -void CMusicChannelXAudio2::release() -{ - nlwarning(NLSOUND_XAUDIO2_PREFIX "Releasing CMusicChannelXAudio2"); - - stop(); -} - -/** Play some music (.ogg etc...) - * NB: if an old music was played, it is first stop with stopMusic() - * \param filepath file path, CPath::lookup is done here - * \param async stream music from hard disk, preload in memory if false - * \param loop must be true to play the music in loop. - */ -bool CMusicChannelXAudio2::play(const std::string &filepath, bool async, bool loop) -{ - // nlinfo(NLSOUND_XAUDIO2_PREFIX "play %s %u", filepath.c_str(), (uint32)loop); - - _SoundDriver->performanceIncreaseMusicPlayCounter(); - - HRESULT hr; - - stop(); - - _MusicBuffer = IMusicBuffer::createMusicBuffer(filepath, async, loop); - - if (_MusicBuffer) - { - WAVEFORMATEX wfe; - wfe.cbSize = 0; - wfe.wFormatTag = WAVE_FORMAT_PCM; // todo: getFormat(); - wfe.nChannels = _MusicBuffer->getChannels(); - wfe.wBitsPerSample = _MusicBuffer->getBitsPerSample(); - wfe.nSamplesPerSec = _MusicBuffer->getSamplesPerSec(); - wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8; - wfe.nAvgBytesPerSec = wfe.nSamplesPerSec * wfe.nBlockAlign; - - XAUDIO2_VOICE_DETAILS voice_details; - _SoundDriver->getMasteringVoice()->GetVoiceDetails(&voice_details); - - // nlinfo(NLSOUND_XAUDIO2_PREFIX "Creating music voice with %u channels, %u bits per sample, %u samples per sec, " - // "on mastering voice with %u channels, %u samples per sec", - // (uint32)wfe.nChannels, (uint32)wfe.wBitsPerSample, (uint32)wfe.nSamplesPerSec, - // (uint32)voice_details.InputChannels, (uint32)voice_details.InputSampleRate); - - if (FAILED(hr = _SoundDriver->getXAudio2()->CreateSourceVoice(&_SourceVoice, &wfe, XAUDIO2_VOICE_NOPITCH, 1.0f, this, NULL, NULL))) - { - nlwarning(NLSOUND_XAUDIO2_PREFIX "FAILED CreateSourceVoice"); - stop(); return false; - } - - _SourceVoice->SetVolume(_Gain); - _SourceVoice->Start(0); - } - else - { - nlwarning(NLSOUND_XAUDIO2_PREFIX "no _MusicBuffer"); - return false; - } - - return true; -} - -/// Stop the music previously loaded and played (the Memory is also freed) -void CMusicChannelXAudio2::stop() -{ - if (_SourceVoice) { _SourceVoice->DestroyVoice(); _SourceVoice = NULL; } - if (_MusicBuffer) { delete _MusicBuffer; _MusicBuffer = NULL; } - // memset(_Buffer, 0, sizeof(_Buffer)); - _BufferPos = 0; -} - -/** Pause the music previously loaded and played (the Memory is not freed) - */ -void CMusicChannelXAudio2::pause() -{ - if (_SourceVoice) _SourceVoice->Stop(0); -} - -/// Resume the music previously paused -void CMusicChannelXAudio2::resume() -{ - if (_SourceVoice) _SourceVoice->Start(0); -} - -/// Return true if a song is finished. -bool CMusicChannelXAudio2::isEnded() -{ - if (_MusicBuffer) - { - if (!_MusicBuffer->isMusicEnded()) - return false; - } - if (_SourceVoice) - { - XAUDIO2_VOICE_STATE voice_state; - _SourceVoice->GetState(&voice_state); - if (voice_state.BuffersQueued) - { - // nldebug(NLSOUND_XAUDIO2_PREFIX "isEnded() -> voice_state.BuffersQueued, wait ..."); - return false; - } - } - // nldebug(NLSOUND_XAUDIO2_PREFIX "isEnded() -> stop()"); - stop(); - return true; -} - -/// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading -bool CMusicChannelXAudio2::isLoadingAsync() -{ - return false; -} - -/// Return the total length (in second) of the music currently played -float CMusicChannelXAudio2::getLength() -{ - if (_MusicBuffer) return _MusicBuffer->getLength(); - else return .0f; -} - -/** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1) - * NB: the volume of music is NOT affected by IListener::setGain() - */ -void CMusicChannelXAudio2::setVolume(float gain) -{ - _Gain = gain; - if (_SourceVoice) _SourceVoice->SetVolume(gain); -} - -void CMusicChannelXAudio2::OnVoiceProcessingPassStart(UINT32 BytesRequired) -{ - if (BytesRequired > 0) - { - // nlwarning(NLSOUND_XAUDIO2_PREFIX "Bytes Required: %u", BytesRequired); // byte req to not have disruption - - if (_MusicBuffer) - { - uint32 minimum = BytesRequired * 2; // give some more than required :p - if (_MusicBuffer->getRequiredBytes() > minimum) minimum = _MusicBuffer->getRequiredBytes(); - if (minimum > sizeof(_Buffer) - _BufferPos) _BufferPos = 0; - uint32 maximum = sizeof(_Buffer) - _BufferPos; - uint8 *buffer = &_Buffer[_BufferPos]; - uint32 length = _MusicBuffer->getNextBytes(buffer, minimum, maximum); - _BufferPos += length; - - if (length) - { - XAUDIO2_BUFFER xbuffer; - xbuffer.AudioBytes = length; - xbuffer.Flags = 0; - xbuffer.LoopBegin = 0; - xbuffer.LoopCount = 0; - xbuffer.LoopLength = 0; - xbuffer.pAudioData = buffer; - xbuffer.pContext = NULL; // nothing here for now - xbuffer.PlayBegin = 0; - xbuffer.PlayLength = 0; - - _SourceVoice->SubmitSourceBuffer(&xbuffer); - } - else - { - // nldebug(NLSOUND_XAUDIO2_PREFIX "!length -> delete _MusicBuffer"); - // set member var to null before deleting it to avoid crashing main thread - IMusicBuffer *music_buffer = _MusicBuffer; - _MusicBuffer = NULL; delete music_buffer; - _SourceVoice->Discontinuity(); - } - } - } -} - -void CMusicChannelXAudio2::OnVoiceProcessingPassEnd() -{ - -} - -void CMusicChannelXAudio2::OnStreamEnd() -{ - -} - -void CMusicChannelXAudio2::OnBufferStart(void * /* pBufferContext */) -{ - -} - -void CMusicChannelXAudio2::OnBufferEnd(void * /* pBufferContext */) -{ - -} - -void CMusicChannelXAudio2::OnLoopEnd(void * /* pBufferContext */) -{ - -} - -void CMusicChannelXAudio2::OnVoiceError(void * /* pBufferContext */, HRESULT /* Error */) -{ - -} - -} /* namespace NLSOUND */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.h b/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.h deleted file mode 100644 index ade13e25f..000000000 --- a/code/nel/src/sound/driver/xaudio2/music_channel_xaudio2.h +++ /dev/null @@ -1,117 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef NLSOUND_MUSIC_CHANNEL_XAUDIO2_H -#define NLSOUND_MUSIC_CHANNEL_XAUDIO2_H - -#include - -namespace NLSOUND { - class CSoundDriverXAudio2; - class IMusicBuffer; - -/** - * \brief CMusicChannelXAudio2 - * \date 2008-08-30 13:31GMT - * \author Jan Boon (Kaetemi) - * CMusicChannelXAudio2 is an implementation of the IMusicChannel interface to run on XAudio2. - * TODO: Properly decode the audio on a seperate thread. - * TODO: Change IMusicChannel to IAudioStream, and fix the interface to make more sense. - */ -class CMusicChannelXAudio2 : public IMusicChannel, IXAudio2VoiceCallback -{ -protected: - // outside pointers - CSoundDriverXAudio2 *_SoundDriver; - - // pointers - IMusicBuffer *_MusicBuffer; - IXAudio2SourceVoice *_SourceVoice; - // todo: thread for async loading of music buffer and source voice - // isasyncloading checks if thread exists - // isended checks if not async loading too - - // instances - uint8 _Buffer[64 * 1024]; // no specific reason, lol - uint32 _BufferPos; // 0 - float _Gain; - -public: - CMusicChannelXAudio2(CSoundDriverXAudio2 *soundDriver); - virtual ~CMusicChannelXAudio2(); - void release(); - -private: - // XAudio2 Callbacks - // Called just before this voice's processing pass begins. - STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired); - // Called just after this voice's processing pass ends. - STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS); - // Called when this voice has just finished playing a buffer stream - // (as marked with the XAUDIO2_END_OF_STREAM flag on the last buffer). - STDMETHOD_(void, OnStreamEnd) (THIS); - // Called when this voice is about to start processing a new buffer. - STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext); - // Called when this voice has just finished processing a buffer. - // The buffer can now be reused or destroyed. - STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext); - // Called when this voice has just reached the end position of a loop. - STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext); - // Called in the event of a critical error during voice processing, - // such as a failing XAPO or an error from the hardware XMA decoder. - // The voice may have to be destroyed and re-created to recover from - // the error. The callback arguments report which buffer was being - // processed when the error occurred, and its HRESULT code. - STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error); - -public: - /** Play some music (.ogg etc...) - * NB: if an old music was played, it is first stop with stopMusic() - * \param filepath file path, CPath::lookup is done here - * \param async stream music from hard disk, preload in memory if false - * \param loop must be true to play the music in loop. - */ - virtual bool play(const std::string &filepath, bool async, bool loop); - - /// Stop the music previously loaded and played (the Memory is also freed) - virtual void stop(); - - /// Pause the music previously loaded and played (the Memory is not freed) - virtual void pause(); - - /// Resume the music previously paused - virtual void resume(); - - /// Return true if a song is finished. - virtual bool isEnded(); - - /// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading - virtual bool isLoadingAsync(); - - /// Return the total length (in second) of the music currently played - virtual float getLength(); - - /** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1) - * NB: the volume of music is NOT affected by IListener::setGain() - */ - virtual void setVolume(float gain); -}; /* class CMusicChannelXAudio2 */ - -} /* namespace NLSOUND */ - -#endif /* #ifndef NLSOUND_MUSIC_CHANNEL_XAUDIO2_H */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.cpp index 826f9973b..7c4ea9bbf 100644 --- a/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.cpp +++ b/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.cpp @@ -19,7 +19,6 @@ // Project includes #include "listener_xaudio2.h" #include "source_xaudio2.h" -#include "music_channel_xaudio2.h" #include "effect_xaudio2.h" #include "sound_driver_xaudio2.h" @@ -132,8 +131,7 @@ CSoundDriverXAudio2::CSoundDriverXAudio2(ISoundDriver::IStringMapperProvider * / : _XAudio2(NULL), _MasteringVoice(NULL), _Listener(NULL), _SoundDriverOk(false), _CoInitOk(false), _OperationSetCounter(65536), _PerformanceCommit3DCounter(0), _PerformanceADPCMBufferSize(0), - _PerformancePCMBufferSize(0), _PerformanceMusicPlayCounter(0), - _PerformanceSourcePlayCounter(0) + _PerformancePCMBufferSize(0), _PerformanceSourcePlayCounter(0) { nlwarning(NLSOUND_XAUDIO2_PREFIX "Creating CSoundDriverXAudio2"); @@ -200,14 +198,6 @@ void CSoundDriverXAudio2::release() // WARNING: Only internal resources are released here, // the created instances must still be released by the user! - // Release internal resources of all remaining IMusicChannel instances - if (_MusicChannels.size()) - { - nlwarning(NLSOUND_XAUDIO2_PREFIX "_MusicChannels.size(): '%u'", (uint32)_MusicChannels.size()); - set::iterator it(_MusicChannels.begin()), end(_MusicChannels.end()); - for (; it != end; ++it) (*it)->release(); - _MusicChannels.clear(); - } // Release internal resources of all remaining ISource instances if (_Sources.size()) { @@ -396,14 +386,6 @@ void CSoundDriverXAudio2::destroySourceVoice(IXAudio2SourceVoice *sourceVoice) if (sourceVoice) sourceVoice->DestroyVoice(); } -/// Create a music channel -IMusicChannel *CSoundDriverXAudio2::createMusicChannel() -{ - CMusicChannelXAudio2 *music_channel = new CMusicChannelXAudio2(this); - _MusicChannels.insert(music_channel); - return static_cast(music_channel); -} - /// Create the listener instance IListener *CSoundDriverXAudio2::createListener() { @@ -481,7 +463,6 @@ void CSoundDriverXAudio2::writeProfile(std::string& out) + "\n\tPCMBufferSize: " + toString(_PerformancePCMBufferSize) + "\n\tADPCMBufferSize: " + toString(_PerformanceADPCMBufferSize) + "\n\tSourcePlayCounter: " + toString(_PerformanceSourcePlayCounter) - + "\n\tMusicPlayCounter: " + toString(_PerformanceMusicPlayCounter) + "\n\tCommit3DCounter: " + toString(_PerformanceCommit3DCounter) + "\nXAUDIO2_PERFORMANCE_DATA" + "\n\tAudioCyclesSinceLastQuery: " + toString(performance.AudioCyclesSinceLastQuery) @@ -519,17 +500,6 @@ void CSoundDriverXAudio2::displayBench(NLMISC::CLog *log) NLMISC::CHTimer::display(log, CHTimer::TotalTime); } -/** Get music info. Returns false if the song is not found or the function is not implemented. - * \param filepath path to file, CPath::lookup done by driver - * \param artist returns the song artist (empty if not available) - * \param title returns the title (empty if not available) - */ -bool CSoundDriverXAudio2::getMusicInfo(const std::string &filepath, std::string &artist, std::string &title) -{ - // add support for additional non-standard music file types info here - return IMusicBuffer::getInfo(filepath, artist, title); -} - /// Remove a buffer (should be called by the friend destructor of the buffer class) void CSoundDriverXAudio2::removeBuffer(CBufferXAudio2 *buffer) { @@ -543,13 +513,6 @@ void CSoundDriverXAudio2::removeSource(CSourceXAudio2 *source) if (_Sources.find(source) != _Sources.end()) _Sources.erase(source); else nlwarning("removeSource already called"); } - -/// (Internal) Remove a source (should be called by the destructor of the source class). -void CSoundDriverXAudio2::removeMusicChannel(CMusicChannelXAudio2 *musicChannel) -{ - if (_MusicChannels.find(musicChannel) != _MusicChannels.end()) _MusicChannels.erase(musicChannel); - else nlwarning("removeMusicChannel already called"); -} /// (Internal) Remove an effect (should be called by the destructor of the effect class) void CSoundDriverXAudio2::removeEffect(CEffectXAudio2 *effect) diff --git a/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.h b/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.h index 681d36254..bcf847a5f 100644 --- a/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.h +++ b/code/nel/src/sound/driver/xaudio2/sound_driver_xaudio2.h @@ -62,8 +62,6 @@ protected: std::set _Sources; /// Array with the allocated effects created by client code. std::set _Effects; - /// Array with the allocated music channels created by client code. - std::set _MusicChannels; /// Initialization Handle of X3DAudio. X3DAUDIO_HANDLE _X3DAudioHandle; //I /// Operation set counter @@ -73,7 +71,6 @@ protected: uint _PerformancePCMBufferSize; uint _PerformanceADPCMBufferSize; uint _PerformanceSourcePlayCounter; - uint _PerformanceMusicPlayCounter; uint _PerformanceCommit3DCounter; // user init vars @@ -108,8 +105,6 @@ public: } /// (Internal) Increase the source play counter by one. inline void performanceIncreaseSourcePlayCounter() { ++_PerformanceSourcePlayCounter; } - /// (Internal) Increase the music play counter by one. - inline void performanceIncreaseMusicPlayCounter() { ++_PerformanceMusicPlayCounter; } /// (Internal) Increase the commit 3d counter by one. inline void performanceIncreaseCommit3DCounter() { ++_PerformanceCommit3DCounter; } @@ -172,16 +167,6 @@ public: virtual void startBench(); virtual void endBench(); virtual void displayBench(NLMISC::CLog *log); - - /// Create a music channel, destroy with destroyMusicChannel. - virtual IMusicChannel *createMusicChannel(); - - /** Get music info. Returns false if the song is not found or the function is not implemented. - * \param filepath path to file, CPath::lookup done by driver - * \param artist returns the song artist (empty if not available) - * \param title returns the title (empty if not available) - */ - virtual bool getMusicInfo(const std::string &filepath, std::string &artist, std::string &title); /// Get audio/container extensions that are supported natively by the driver implementation. virtual void getMusicExtensions(std::vector & /* extensions */) const { } @@ -192,8 +177,6 @@ public: void removeBuffer(CBufferXAudio2 *buffer); /// (Internal) Remove a source (should be called by the destructor of the source class). void removeSource(CSourceXAudio2 *source); - /// (Internal) Remove a source (should be called by the destructor of the music channel class). - void removeMusicChannel(CMusicChannelXAudio2 *musicChannel); /// (Internal) Remove the listener (should be called by the destructor of the listener class) inline void removeListener(CListenerXAudio2 *listener) { nlassert(_Listener == listener); _Listener = NULL; } /// (Internal) Remove an effect (should be called by the destructor of the effect class) diff --git a/code/nel/src/sound/driver/xaudio2/stdxaudio2.h b/code/nel/src/sound/driver/xaudio2/stdxaudio2.h index c043f3949..1a1d766e7 100644 --- a/code/nel/src/sound/driver/xaudio2/stdxaudio2.h +++ b/code/nel/src/sound/driver/xaudio2/stdxaudio2.h @@ -57,7 +57,6 @@ #include "nel/sound/driver/listener.h" #include "nel/sound/driver/sound_driver.h" #include "nel/sound/driver/source.h" -#include "nel/sound/driver/music_buffer.h" // Defines #define NLSOUND_XAUDIO2_NAME "NeLSound XAudio2 Driver" From 5c1d3cc6c4e4f607a6034b6ee7ee6445547d5a22 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 9 Apr 2012 22:36:12 +0200 Subject: [PATCH 27/77] Moved: Sound sources sample to sound sample directory Added: CMake configuration for ogg vorbis stream source sample --HG-- branch : sound_dev --- code/nel/samples/CMakeLists.txt | 2 +- code/nel/samples/sound/CMakeLists.txt | 4 ++++ .../{ => sound}/sound_sources/CMakeLists.txt | 2 +- .../{ => sound}/sound_sources/data/DFN/alpha.typ | 0 .../{ => sound}/sound_sources/data/DFN/angle.typ | 0 .../sound_sources/data/DFN/backgound_sound_item.dfn | 0 .../data/DFN/background_flag_config.dfn | 0 .../sound_sources/data/DFN/background_sound.dfn | 0 .../sound_sources/data/DFN/basics/_typ.dfn | 0 .../sound_sources/data/DFN/basics/_type.typ | 0 .../sound_sources/data/DFN/basics/boolean.typ | 0 .../sound_sources/data/DFN/basics/filename.typ | 0 .../sound_sources/data/DFN/basics/float.typ | 0 .../sound_sources/data/DFN/basics/iboolean.typ | 0 .../sound_sources/data/DFN/basics/int.typ | 0 .../sound_sources/data/DFN/basics/string.typ | 0 .../sound_sources/data/DFN/basics/typ.dfn | 0 .../sound_sources/data/DFN/complex_sound.dfn | 0 .../sound_sources/data/DFN/context_sound.dfn | 0 .../sound_sources/data/DFN/direction.dfn | 0 .../{ => sound}/sound_sources/data/DFN/distance.typ | 0 .../{ => sound}/sound_sources/data/DFN/doppler.typ | 0 .../{ => sound}/sound_sources/data/DFN/gain.typ | 0 .../{ => sound}/sound_sources/data/DFN/listener.dfn | 0 .../sound_sources/data/DFN/mixer_config.dfn | 0 .../sound_sources/data/DFN/music_sound.dfn | 0 .../sound_sources/data/DFN/parameter_id.typ | 0 .../sound_sources/data/DFN/pattern_mode.typ | 0 .../{ => sound}/sound_sources/data/DFN/priority.typ | 0 .../{ => sound}/sound_sources/data/DFN/rolloff.typ | 0 .../sound_sources/data/DFN/simple_sound.dfn | 0 .../{ => sound}/sound_sources/data/DFN/sound.dfn | 0 .../sound_sources/data/DFN/sound_group.dfn | 0 .../sound_sources/data/DFN/sound_group_item.dfn | 0 .../sound_sources/data/DFN/soundbank.dfn | 0 .../sound_sources/data/DFN/transposition.typ | 0 .../sound_sources/data/DFN/user_var_binding.dfn | 0 .../sound_sources/data/animations/readme.txt | 0 .../data/animations/test_anim.sound_anim | 0 .../background_sounds/background_sound.primitive | 0 .../sound_sources/data/background_sounds/readme.txt | 0 .../sound_sources/data/cluster_sound/readme.txt | 0 .../data/cluster_sound/test_clusters.sound_group | 0 .../sound_sources/data/default.mixer_config | 0 .../data/samplebank/base_samples/beep.wav | Bin .../data/samplebank/base_samples/tuut.wav | Bin .../sound_sources/data/soundbank/beep.sound | 0 .../sound_sources/data/soundbank/tuut.sound | 0 .../sound_sources/data/world_editor_classes.xml | 0 code/nel/samples/{ => sound}/sound_sources/main.cpp | 0 .../samples/sound/stream_ogg_vorbis/CMakeLists.txt | 12 ++++++++++++ .../sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp | 4 ++-- 52 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 code/nel/samples/sound/CMakeLists.txt rename code/nel/samples/{ => sound}/sound_sources/CMakeLists.txt (85%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/alpha.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/angle.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/backgound_sound_item.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/background_flag_config.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/background_sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/_typ.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/_type.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/boolean.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/filename.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/float.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/iboolean.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/int.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/string.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/basics/typ.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/complex_sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/context_sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/direction.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/distance.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/doppler.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/gain.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/listener.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/mixer_config.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/music_sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/parameter_id.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/pattern_mode.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/priority.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/rolloff.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/simple_sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/sound.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/sound_group.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/sound_group_item.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/soundbank.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/transposition.typ (100%) rename code/nel/samples/{ => sound}/sound_sources/data/DFN/user_var_binding.dfn (100%) rename code/nel/samples/{ => sound}/sound_sources/data/animations/readme.txt (100%) rename code/nel/samples/{ => sound}/sound_sources/data/animations/test_anim.sound_anim (100%) rename code/nel/samples/{ => sound}/sound_sources/data/background_sounds/background_sound.primitive (100%) rename code/nel/samples/{ => sound}/sound_sources/data/background_sounds/readme.txt (100%) rename code/nel/samples/{ => sound}/sound_sources/data/cluster_sound/readme.txt (100%) rename code/nel/samples/{ => sound}/sound_sources/data/cluster_sound/test_clusters.sound_group (100%) rename code/nel/samples/{ => sound}/sound_sources/data/default.mixer_config (100%) rename code/nel/samples/{ => sound}/sound_sources/data/samplebank/base_samples/beep.wav (100%) rename code/nel/samples/{ => sound}/sound_sources/data/samplebank/base_samples/tuut.wav (100%) rename code/nel/samples/{ => sound}/sound_sources/data/soundbank/beep.sound (100%) rename code/nel/samples/{ => sound}/sound_sources/data/soundbank/tuut.sound (100%) rename code/nel/samples/{ => sound}/sound_sources/data/world_editor_classes.xml (100%) rename code/nel/samples/{ => sound}/sound_sources/main.cpp (100%) create mode 100644 code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt diff --git a/code/nel/samples/CMakeLists.txt b/code/nel/samples/CMakeLists.txt index 4f38264a0..5abcd985a 100644 --- a/code/nel/samples/CMakeLists.txt +++ b/code/nel/samples/CMakeLists.txt @@ -17,5 +17,5 @@ IF(WITH_PACS) ENDIF(WITH_PACS) IF(WITH_SOUND) - ADD_SUBDIRECTORY(sound_sources) + ADD_SUBDIRECTORY(sound) ENDIF(WITH_SOUND) diff --git a/code/nel/samples/sound/CMakeLists.txt b/code/nel/samples/sound/CMakeLists.txt new file mode 100644 index 000000000..2375331f5 --- /dev/null +++ b/code/nel/samples/sound/CMakeLists.txt @@ -0,0 +1,4 @@ + +ADD_SUBDIRECTORY(sound_sources) +ADD_SUBDIRECTORY(stream_ogg_vorbis) + diff --git a/code/nel/samples/sound_sources/CMakeLists.txt b/code/nel/samples/sound/sound_sources/CMakeLists.txt similarity index 85% rename from code/nel/samples/sound_sources/CMakeLists.txt rename to code/nel/samples/sound/sound_sources/CMakeLists.txt index 4cf1ea427..b0f481e7c 100644 --- a/code/nel/samples/sound_sources/CMakeLists.txt +++ b/code/nel/samples/sound/sound_sources/CMakeLists.txt @@ -7,7 +7,7 @@ ADD_DEFINITIONS(-DNL_SOUND_DATA="\\"${NL_SHARE_PREFIX}/nl_sample_sound/\\"" ${LI INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(nl_sample_sound_sources nelmisc nelsound) -NL_DEFAULT_PROPS(nl_sample_sound_sources "NeL, Samples: Sound System") +NL_DEFAULT_PROPS(nl_sample_sound_sources "NeL, Samples: Sound: Sound Sources") NL_ADD_RUNTIME_FLAGS(nl_sample_sound_sources) INSTALL(TARGETS nl_sample_sound_sources RUNTIME DESTINATION bin COMPONENT samplessound) diff --git a/code/nel/samples/sound_sources/data/DFN/alpha.typ b/code/nel/samples/sound/sound_sources/data/DFN/alpha.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/alpha.typ rename to code/nel/samples/sound/sound_sources/data/DFN/alpha.typ diff --git a/code/nel/samples/sound_sources/data/DFN/angle.typ b/code/nel/samples/sound/sound_sources/data/DFN/angle.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/angle.typ rename to code/nel/samples/sound/sound_sources/data/DFN/angle.typ diff --git a/code/nel/samples/sound_sources/data/DFN/backgound_sound_item.dfn b/code/nel/samples/sound/sound_sources/data/DFN/backgound_sound_item.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/backgound_sound_item.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/backgound_sound_item.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/background_flag_config.dfn b/code/nel/samples/sound/sound_sources/data/DFN/background_flag_config.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/background_flag_config.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/background_flag_config.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/background_sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/background_sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/background_sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/background_sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/basics/_typ.dfn b/code/nel/samples/sound/sound_sources/data/DFN/basics/_typ.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/_typ.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/basics/_typ.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/basics/_type.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/_type.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/_type.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/_type.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/boolean.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/boolean.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/boolean.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/boolean.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/filename.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/filename.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/filename.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/filename.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/float.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/float.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/float.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/float.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/iboolean.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/iboolean.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/iboolean.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/iboolean.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/int.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/int.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/int.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/int.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/string.typ b/code/nel/samples/sound/sound_sources/data/DFN/basics/string.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/string.typ rename to code/nel/samples/sound/sound_sources/data/DFN/basics/string.typ diff --git a/code/nel/samples/sound_sources/data/DFN/basics/typ.dfn b/code/nel/samples/sound/sound_sources/data/DFN/basics/typ.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/basics/typ.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/basics/typ.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/complex_sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/complex_sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/complex_sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/complex_sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/context_sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/context_sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/context_sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/context_sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/direction.dfn b/code/nel/samples/sound/sound_sources/data/DFN/direction.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/direction.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/direction.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/distance.typ b/code/nel/samples/sound/sound_sources/data/DFN/distance.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/distance.typ rename to code/nel/samples/sound/sound_sources/data/DFN/distance.typ diff --git a/code/nel/samples/sound_sources/data/DFN/doppler.typ b/code/nel/samples/sound/sound_sources/data/DFN/doppler.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/doppler.typ rename to code/nel/samples/sound/sound_sources/data/DFN/doppler.typ diff --git a/code/nel/samples/sound_sources/data/DFN/gain.typ b/code/nel/samples/sound/sound_sources/data/DFN/gain.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/gain.typ rename to code/nel/samples/sound/sound_sources/data/DFN/gain.typ diff --git a/code/nel/samples/sound_sources/data/DFN/listener.dfn b/code/nel/samples/sound/sound_sources/data/DFN/listener.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/listener.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/listener.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/mixer_config.dfn b/code/nel/samples/sound/sound_sources/data/DFN/mixer_config.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/mixer_config.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/mixer_config.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/music_sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/music_sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/music_sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/music_sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/parameter_id.typ b/code/nel/samples/sound/sound_sources/data/DFN/parameter_id.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/parameter_id.typ rename to code/nel/samples/sound/sound_sources/data/DFN/parameter_id.typ diff --git a/code/nel/samples/sound_sources/data/DFN/pattern_mode.typ b/code/nel/samples/sound/sound_sources/data/DFN/pattern_mode.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/pattern_mode.typ rename to code/nel/samples/sound/sound_sources/data/DFN/pattern_mode.typ diff --git a/code/nel/samples/sound_sources/data/DFN/priority.typ b/code/nel/samples/sound/sound_sources/data/DFN/priority.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/priority.typ rename to code/nel/samples/sound/sound_sources/data/DFN/priority.typ diff --git a/code/nel/samples/sound_sources/data/DFN/rolloff.typ b/code/nel/samples/sound/sound_sources/data/DFN/rolloff.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/rolloff.typ rename to code/nel/samples/sound/sound_sources/data/DFN/rolloff.typ diff --git a/code/nel/samples/sound_sources/data/DFN/simple_sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/simple_sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/simple_sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/simple_sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/sound.dfn b/code/nel/samples/sound/sound_sources/data/DFN/sound.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/sound.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/sound.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/sound_group.dfn b/code/nel/samples/sound/sound_sources/data/DFN/sound_group.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/sound_group.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/sound_group.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/sound_group_item.dfn b/code/nel/samples/sound/sound_sources/data/DFN/sound_group_item.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/sound_group_item.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/sound_group_item.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/soundbank.dfn b/code/nel/samples/sound/sound_sources/data/DFN/soundbank.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/soundbank.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/soundbank.dfn diff --git a/code/nel/samples/sound_sources/data/DFN/transposition.typ b/code/nel/samples/sound/sound_sources/data/DFN/transposition.typ similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/transposition.typ rename to code/nel/samples/sound/sound_sources/data/DFN/transposition.typ diff --git a/code/nel/samples/sound_sources/data/DFN/user_var_binding.dfn b/code/nel/samples/sound/sound_sources/data/DFN/user_var_binding.dfn similarity index 100% rename from code/nel/samples/sound_sources/data/DFN/user_var_binding.dfn rename to code/nel/samples/sound/sound_sources/data/DFN/user_var_binding.dfn diff --git a/code/nel/samples/sound_sources/data/animations/readme.txt b/code/nel/samples/sound/sound_sources/data/animations/readme.txt similarity index 100% rename from code/nel/samples/sound_sources/data/animations/readme.txt rename to code/nel/samples/sound/sound_sources/data/animations/readme.txt diff --git a/code/nel/samples/sound_sources/data/animations/test_anim.sound_anim b/code/nel/samples/sound/sound_sources/data/animations/test_anim.sound_anim similarity index 100% rename from code/nel/samples/sound_sources/data/animations/test_anim.sound_anim rename to code/nel/samples/sound/sound_sources/data/animations/test_anim.sound_anim diff --git a/code/nel/samples/sound_sources/data/background_sounds/background_sound.primitive b/code/nel/samples/sound/sound_sources/data/background_sounds/background_sound.primitive similarity index 100% rename from code/nel/samples/sound_sources/data/background_sounds/background_sound.primitive rename to code/nel/samples/sound/sound_sources/data/background_sounds/background_sound.primitive diff --git a/code/nel/samples/sound_sources/data/background_sounds/readme.txt b/code/nel/samples/sound/sound_sources/data/background_sounds/readme.txt similarity index 100% rename from code/nel/samples/sound_sources/data/background_sounds/readme.txt rename to code/nel/samples/sound/sound_sources/data/background_sounds/readme.txt diff --git a/code/nel/samples/sound_sources/data/cluster_sound/readme.txt b/code/nel/samples/sound/sound_sources/data/cluster_sound/readme.txt similarity index 100% rename from code/nel/samples/sound_sources/data/cluster_sound/readme.txt rename to code/nel/samples/sound/sound_sources/data/cluster_sound/readme.txt diff --git a/code/nel/samples/sound_sources/data/cluster_sound/test_clusters.sound_group b/code/nel/samples/sound/sound_sources/data/cluster_sound/test_clusters.sound_group similarity index 100% rename from code/nel/samples/sound_sources/data/cluster_sound/test_clusters.sound_group rename to code/nel/samples/sound/sound_sources/data/cluster_sound/test_clusters.sound_group diff --git a/code/nel/samples/sound_sources/data/default.mixer_config b/code/nel/samples/sound/sound_sources/data/default.mixer_config similarity index 100% rename from code/nel/samples/sound_sources/data/default.mixer_config rename to code/nel/samples/sound/sound_sources/data/default.mixer_config diff --git a/code/nel/samples/sound_sources/data/samplebank/base_samples/beep.wav b/code/nel/samples/sound/sound_sources/data/samplebank/base_samples/beep.wav similarity index 100% rename from code/nel/samples/sound_sources/data/samplebank/base_samples/beep.wav rename to code/nel/samples/sound/sound_sources/data/samplebank/base_samples/beep.wav diff --git a/code/nel/samples/sound_sources/data/samplebank/base_samples/tuut.wav b/code/nel/samples/sound/sound_sources/data/samplebank/base_samples/tuut.wav similarity index 100% rename from code/nel/samples/sound_sources/data/samplebank/base_samples/tuut.wav rename to code/nel/samples/sound/sound_sources/data/samplebank/base_samples/tuut.wav diff --git a/code/nel/samples/sound_sources/data/soundbank/beep.sound b/code/nel/samples/sound/sound_sources/data/soundbank/beep.sound similarity index 100% rename from code/nel/samples/sound_sources/data/soundbank/beep.sound rename to code/nel/samples/sound/sound_sources/data/soundbank/beep.sound diff --git a/code/nel/samples/sound_sources/data/soundbank/tuut.sound b/code/nel/samples/sound/sound_sources/data/soundbank/tuut.sound similarity index 100% rename from code/nel/samples/sound_sources/data/soundbank/tuut.sound rename to code/nel/samples/sound/sound_sources/data/soundbank/tuut.sound diff --git a/code/nel/samples/sound_sources/data/world_editor_classes.xml b/code/nel/samples/sound/sound_sources/data/world_editor_classes.xml similarity index 100% rename from code/nel/samples/sound_sources/data/world_editor_classes.xml rename to code/nel/samples/sound/sound_sources/data/world_editor_classes.xml diff --git a/code/nel/samples/sound_sources/main.cpp b/code/nel/samples/sound/sound_sources/main.cpp similarity index 100% rename from code/nel/samples/sound_sources/main.cpp rename to code/nel/samples/sound/sound_sources/main.cpp diff --git a/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt b/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt new file mode 100644 index 000000000..51fdccb66 --- /dev/null +++ b/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt @@ -0,0 +1,12 @@ +FILE(GLOB SRC *.cpp *.h) + +ADD_EXECUTABLE(nl_sample_stream_ogg_vorbis ${SRC}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(nl_sample_stream_ogg_vorbis nelmisc nelsound) +NL_DEFAULT_PROPS(nl_sample_stream_ogg_vorbis "NeL, Samples: Sound: Stream OGG Vorbis") +NL_ADD_RUNTIME_FLAGS(nl_sample_sound_sources) + +INSTALL(TARGETS nl_sample_stream_ogg_vorbis RUNTIME DESTINATION bin COMPONENT samplessound) + diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 441eee932..7d200635a 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -48,7 +48,7 @@ #define NL_SOUND_DATA "." #endif // NL_SOUND_DATA -#define SAMPLE_OGG "O:/src/samples/music_stream/data/aeon_1_10_mystic_river.ogg" +#define SAMPLE_OGG "D:/source/kaetemi/toverhex/src/samples/music_stream/data/aeon_1_10_mystic_river.ogg" using namespace std; using namespace NLMISC; @@ -430,7 +430,7 @@ static void runSample() printf("End of song\n"); printf("Press ANY key to exit\n"); - while (!_kbhit()) { s_AudioMixer->update(); Sleep(10); } _getch(); + while (!_kbhit()) { s_AudioMixer->update(); nlSleep(10); } _getch(); return; } From b2971ffc0c587d53fc8d8202b28a1af9da77236f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 00:05:01 +0200 Subject: [PATCH 28/77] Fixed: Update audio mixer during buffer streaming in streaming sample --HG-- branch : sound_dev --- code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 7d200635a..db5b8b214 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -367,6 +367,7 @@ static void initSample() string sample = SAMPLE_OGG; s_AudioDecoder = IAudioDecoder::createAudioDecoder(sample, false, false); s_StreamSource->setFormat(s_AudioDecoder->getChannels(), s_AudioDecoder->getBitsPerSample(), (uint32)s_AudioDecoder->getSamplesPerSec()); + s_StreamSource->setPitch(2.0f); } //CMutex *s_Mutex = NULL; @@ -413,6 +414,7 @@ static void runSample() return; } bufferMore(bytes); + s_AudioMixer->update(); //if (!s_StreamSource->asUSource()->isst) //{ // printf("*!playing!*"); @@ -424,6 +426,7 @@ static void runSample() while (s_StreamSource->hasFilledBuffersAvailable()) { nlSleep(40); + s_AudioMixer->update(); } s_StreamSource->stop(); From 57411c0a7b6fc7dea0c15f20be17752e4adb9da9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 00:06:05 +0200 Subject: [PATCH 29/77] Added: FormatNotSet value for TBufferFormat --HG-- branch : sound_dev --- code/nel/include/nel/sound/driver/buffer.h | 2 ++ code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/sound/driver/buffer.h b/code/nel/include/nel/sound/driver/buffer.h index 5d488f1c7..27a8d9f00 100644 --- a/code/nel/include/nel/sound/driver/buffer.h +++ b/code/nel/include/nel/sound/driver/buffer.h @@ -48,6 +48,8 @@ public: /// Intel/DVI ADPCM format, only available for 1 channel at 16 bits per sample, encoded at 4 bits per sample. /// This is only implemented in the DSound and XAudio2 driver. FormatDviAdpcm = 11, + /// No format set. Used when a TBufferFormat value has not been set to any value yet. + FormatNotSet = (~0), }; /// The storage mode of this buffer. Also controls the X-RAM extension of OpenAL. enum TStorageMode diff --git a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp index df8101b40..7ee723787 100644 --- a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp +++ b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp @@ -121,8 +121,7 @@ void CSourceXAudio2::commit3DChanges() { nlassert(_SourceVoice); - // Only mono buffers get 3d sound, multi-channel buffers go directly to the speakers (calculate rolloff too!!). - // Todo: stereo buffers calculate distance ? + // Only mono buffers get 3d sound, multi-channel buffers go directly to the speakers without any distance rolloff. if (_Channels > 1) { // _SoundDriver->getDSPSettings()->DstChannelCount = 1; @@ -535,7 +534,7 @@ bool CSourceXAudio2::preparePlay(IBuffer::TBufferFormat bufferFormat, uint8 chan // destroy adpcm utility (if it exists) delete _AdpcmUtility; _AdpcmUtility = NULL; // reset current stuff - _Format = (IBuffer::TBufferFormat)~0; + _Format = IBuffer::FormatNotSet; _Channels = 0; _BitsPerSample = 0; } From e796c84824697e7d1320b6bc8f3b08af09b9855b Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 10 Apr 2012 13:50:23 +0200 Subject: [PATCH 30/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/misc/stl_block_allocator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/include/nel/misc/stl_block_allocator.h b/code/nel/include/nel/misc/stl_block_allocator.h index d85044e58..441d49560 100644 --- a/code/nel/include/nel/misc/stl_block_allocator.h +++ b/code/nel/include/nel/misc/stl_block_allocator.h @@ -61,7 +61,7 @@ namespace NLMISC { { public: /// Constructor. Must gives a blockMemory to ctor. NB: must gives a CBlockMemory !!! - CSTLBlockAllocator(CBlockMemory */* bm */) + CSTLBlockAllocator(CBlockMemory * /* bm */) { } /// copy ctor From 0b64102ab82893e9922c38209ff194ec707edd38 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 15:24:35 +0200 Subject: [PATCH 31/77] Added: #1459 Group controllers for sound sources --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_mixer_user.h | 7 +- .../nel/include/nel/sound/background_source.h | 2 +- code/nel/include/nel/sound/complex_source.h | 2 +- code/nel/include/nel/sound/group_controller.h | 100 ++++++++++++++++ .../include/nel/sound/group_controller_root.h | 67 +++++++++++ code/nel/include/nel/sound/music_source.h | 2 +- code/nel/include/nel/sound/simple_source.h | 11 +- code/nel/include/nel/sound/sound.h | 3 + code/nel/include/nel/sound/source_common.h | 14 ++- code/nel/include/nel/sound/stream_source.h | 15 +-- code/nel/include/nel/sound/u_audio_mixer.h | 5 +- .../include/nel/sound/u_group_controller.h | 63 ++++++++++ code/nel/src/sound/audio_mixer_user.cpp | 21 ++-- code/nel/src/sound/background_source.cpp | 6 +- code/nel/src/sound/complex_source.cpp | 14 +-- code/nel/src/sound/group_controller.cpp | 112 ++++++++++++++++++ code/nel/src/sound/group_controller_root.cpp | 99 ++++++++++++++++ code/nel/src/sound/music_source.cpp | 4 +- code/nel/src/sound/simple_source.cpp | 33 +----- code/nel/src/sound/source_common.cpp | 11 +- code/nel/src/sound/stream_source.cpp | 22 +--- 21 files changed, 510 insertions(+), 103 deletions(-) create mode 100644 code/nel/include/nel/sound/group_controller.h create mode 100644 code/nel/include/nel/sound/group_controller_root.h create mode 100644 code/nel/include/nel/sound/u_group_controller.h create mode 100644 code/nel/src/sound/group_controller.cpp create mode 100644 code/nel/src/sound/group_controller_root.cpp diff --git a/code/nel/include/nel/sound/audio_mixer_user.h b/code/nel/include/nel/sound/audio_mixer_user.h index 7cd0de051..eded75fc6 100644 --- a/code/nel/include/nel/sound/audio_mixer_user.h +++ b/code/nel/include/nel/sound/audio_mixer_user.h @@ -204,9 +204,9 @@ public: * pass a callback function that will be called (if not NULL) just before deleting the spawned * source. */ - virtual USource *createSource( const NLMISC::TStringId &name, bool spawn=false, TSpawnEndCallback cb=NULL, void *cbUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0 ); + virtual USource *createSource( const NLMISC::TStringId &name, bool spawn=false, TSpawnEndCallback cb=NULL, void *cbUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0, UGroupController *groupController = NULL); /// Add a logical sound source (by sound id). To remove a source, just delete it. See createSource(const char*) - virtual USource *createSource( TSoundId id, bool spawn=false, TSpawnEndCallback cb=NULL, void *cbUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0 ); + virtual USource *createSource( TSoundId id, bool spawn=false, TSpawnEndCallback cb=NULL, void *cbUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0, UGroupController *groupController = NULL); /// Add a source which was created by an EnvSound void addSource( CSourceCommon *source ); /** Delete a logical sound source. If you don't call it, the source will be auto-deleted @@ -431,8 +431,9 @@ private: // utility function for automatic sample bank loading. bool tryToLoadSampleBank(const std::string &sampleName); - +public: typedef CHashSet > TSourceContainer; +private: typedef CHashSet > TMixerUpdateContainer; typedef CHashMap, THashPtr > TBufferToSourceContainer; // typedef std::multimap TTimedEventContainer; diff --git a/code/nel/include/nel/sound/background_source.h b/code/nel/include/nel/sound/background_source.h index cdf044776..14ea1cf53 100644 --- a/code/nel/include/nel/sound/background_source.h +++ b/code/nel/include/nel/sound/background_source.h @@ -36,7 +36,7 @@ class CBackgroundSource : public CSourceCommon , public CAudioMixerUser::IMixerU { public: /// Constructor - CBackgroundSource (CBackgroundSound *backgroundSound=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0); + CBackgroundSource (CBackgroundSound *backgroundSound=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); /// Destructor ~CBackgroundSource (); diff --git a/code/nel/include/nel/sound/complex_source.h b/code/nel/include/nel/sound/complex_source.h index d27b5af5b..d1135b1ad 100644 --- a/code/nel/include/nel/sound/complex_source.h +++ b/code/nel/include/nel/sound/complex_source.h @@ -34,7 +34,7 @@ class CComplexSource : public CSourceCommon, public CAudioMixerUser::IMixerEvent { public: /// Constructor - CComplexSource (CComplexSound *soundPattern=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0); + CComplexSource (CComplexSound *soundPattern=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); /// Destructor ~CComplexSource (); diff --git a/code/nel/include/nel/sound/group_controller.h b/code/nel/include/nel/sound/group_controller.h new file mode 100644 index 000000000..4c443a52d --- /dev/null +++ b/code/nel/include/nel/sound/group_controller.h @@ -0,0 +1,100 @@ +/** + * \file group_controller.h + * \brief CGroupController + * \date 2012-04-10 09:29GMT + * \author Jan Boon (Kaetemi) + * CGroupController + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_GROUP_CONTROLLER_H +#define NLSOUND_GROUP_CONTROLLER_H +#include + +// STL includes +#include +#include + +// NeL includes +#include +#include +#include + +// Project includes + +namespace NLSOUND { + class CSourceCommon; + class CGroupControllerRoot; + +/** + * \brief CGroupController + * \date 2012-04-10 09:29GMT + * \author Jan Boon (Kaetemi) + * CGroupController + */ +class CGroupController : public UGroupController +{ +public: + friend CGroupControllerRoot; + +private: + CGroupController *m_Parent; + std::map m_Children; + + float m_DevGain; + float m_UserGain; + float m_FinalGain; + + int m_NbSourcesInclChild; + CAudioMixerUser::TSourceContainer m_Sources; + +public: + CGroupController(CGroupController *parent); + + /// \name UGroupController + //@{ + virtual void setDevGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); m_DevGain = gain; updateSourceGain(); } + virtual float getDevGain() { return m_DevGain; } + + virtual void setUserGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); m_UserGain = gain; updateSourceGain(); } + virtual float getUserGain() { return m_UserGain; } + //@} + + inline float getFinalGain() const { return m_FinalGain; } + + void addSource(CSourceCommon *source); + void removeSource(CSourceCommon *source); + +private: + virtual ~CGroupController(); // subnodes can only be deleted by the root + inline float calculateTotalGain() { return m_DevGain * m_UserGain; } + virtual void calculateFinalGain(); + virtual void increaseSources(); + virtual void decreaseSources(); + void updateSourceGain(); + +}; /* class CGroupController */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_GROUP_CONTROLLER_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/group_controller_root.h b/code/nel/include/nel/sound/group_controller_root.h new file mode 100644 index 000000000..82c3af849 --- /dev/null +++ b/code/nel/include/nel/sound/group_controller_root.h @@ -0,0 +1,67 @@ +/** + * \file group_controller_root.h + * \brief CGroupControllerRoot + * \date 2012-04-10 09:44GMT + * \author Jan Boon (Kaetemi) + * CGroupControllerRoot + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_GROUP_CONTROLLER_ROOT_H +#define NLSOUND_GROUP_CONTROLLER_ROOT_H +#include + +// STL includes + +// NeL includes + +// Project includes +#include + +namespace NLSOUND { + +/** + * \brief CGroupControllerRoot + * \date 2012-04-10 09:44GMT + * \author Jan Boon (Kaetemi) + * CGroupControllerRoot + */ +class CGroupControllerRoot : public CGroupController +{ +public: + CGroupControllerRoot(); + virtual ~CGroupControllerRoot(); + + /// Gets the group controller in a certain path with separator '/', if it doesn't exist yet it will be created. + CGroupController *getGroupController(const std::string &path); + +protected: + virtual void calculateFinalGain(); + virtual void increaseSources(); + virtual void decreaseSources(); + +}; /* class CGroupControllerRoot */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_GROUP_CONTROLLER_ROOT_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/music_source.h b/code/nel/include/nel/sound/music_source.h index a23547c6c..be09c1837 100644 --- a/code/nel/include/nel/sound/music_source.h +++ b/code/nel/include/nel/sound/music_source.h @@ -35,7 +35,7 @@ class CMusicSource : public CSourceCommon { public: /// Constructor - CMusicSource (class CMusicSound *sound=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0); + CMusicSource (class CMusicSound *sound=NULL, bool spawn=false, TSpawnEndCallback cb=0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); /// Destructor ~CMusicSource (); diff --git a/code/nel/include/nel/sound/simple_source.h b/code/nel/include/nel/sound/simple_source.h index ac2af0099..9b21200c9 100644 --- a/code/nel/include/nel/sound/simple_source.h +++ b/code/nel/include/nel/sound/simple_source.h @@ -40,7 +40,7 @@ class CSimpleSource : public CSourceCommon, public CAudioMixerUser::IMixerEvent { public: /// Constructor - CSimpleSource(CSimpleSound *simpleSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0); + CSimpleSource(CSimpleSound *simpleSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); /// Destructor virtual ~CSimpleSource(); @@ -97,14 +97,7 @@ public: * 1.0 -> no attenuation * values > 1 (amplification) not supported by most drivers */ - virtual void setGain( float gain ); - /** Set the gain amount (value inside [0, 1]) to map between 0 and the nominal gain - * (which is getSource()->getGain()). Does nothing if getSource() is null. - */ - virtual void setRelativeGain( float gain ); - /** Shift the frequency. 1.0f equals identity, each reduction of 50% equals a pitch shift - * of one octave. 0 is not a legal value. - */ + virtual void updateFinalGain(); virtual void setPitch( float pitch ); /// Set the source relative mode. If true, positions are interpreted relative to the listener position (default: false) virtual void setSourceRelativeMode( bool mode ); diff --git a/code/nel/include/nel/sound/sound.h b/code/nel/include/nel/sound/sound.h index 2112a0e73..ef0e62f72 100644 --- a/code/nel/include/nel/sound/sound.h +++ b/code/nel/include/nel/sound/sound.h @@ -30,6 +30,7 @@ namespace NLSOUND { class ISoundDriver; class IBuffer; class CSound; +class CGroupController; /// Sound names hash map @@ -104,6 +105,8 @@ public: /// Return the max distance (if detailed()) virtual float getMaxDistance() const { return _MaxDist; } + inline CGroupController *getGroupController() const { return NULL; } // TODO, RETURN THE GROUP CONTROLLER + /// Set looping void setLooping( bool looping ) { _Looping = looping; } diff --git a/code/nel/include/nel/sound/source_common.h b/code/nel/include/nel/sound/source_common.h index 34e3f084a..ca0a9a2b8 100644 --- a/code/nel/include/nel/sound/source_common.h +++ b/code/nel/include/nel/sound/source_common.h @@ -22,7 +22,7 @@ #include "nel/sound/u_stream_source.h" #include "nel/3d/cluster.h" #include "nel/sound/sound.h" - +#include "nel/sound/group_controller.h" namespace NLSOUND { @@ -36,11 +36,12 @@ public: SOURCE_SIMPLE, SOURCE_COMPLEX, SOURCE_BACKGROUND, - SOURCE_MUSIC, + SOURCE_MUSIC, // DEPRECATED SOURCE_STREAM }; - CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster); + /// When groupController is NULL it will use the groupcontroller specified in the TSoundId. You should manually specify the groupController if this source is a child of another source, so that the parent source controller of the user-specified .sound file is the one that will be used. + CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController); ~CSourceCommon(); @@ -63,6 +64,8 @@ public: void setGain( float gain ); void setRelativeGain( float gain ); float getRelativeGain() const; + /// Called whenever the gain is changed trough setGain, setRelativeGain or the group controller's gain settings change. + virtual void updateFinalGain() { } void setSourceRelativeMode( bool mode ); /// return the user param for the user callback void *getCallbackUserParam(void) const { return _CbUserParam; } @@ -74,6 +77,8 @@ public: virtual void getDirection( NLMISC::CVector& dir ) const { dir = _Direction; } /// Get the gain virtual float getGain() const { return _Gain; } + /// Get the final gain, including group controller changes. Use this when setting the physical source output gain. + inline float getFinalGain() const { return _Gain * _GroupController->getFinalGain(); } /// Get the pitch virtual float getPitch() const { return _Pitch; } /// Get the source relative mode @@ -145,6 +150,9 @@ protected: /// An optional user var controler. NLMISC::TStringId _UserVarControler; + /// Group controller for gain + CGroupController *_GroupController; + }; } // NLSOUND diff --git a/code/nel/include/nel/sound/stream_source.h b/code/nel/include/nel/sound/stream_source.h index a5ac7c9f8..702a26006 100644 --- a/code/nel/include/nel/sound/stream_source.h +++ b/code/nel/include/nel/sound/stream_source.h @@ -41,7 +41,7 @@ namespace NLSOUND { class CStreamSource : public CSourceCommon { public: - CStreamSource(CStreamSound *streamSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0); + CStreamSource(CStreamSound *streamSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); virtual ~CStreamSource(); /// Return the sound binded to the source (or NULL if there is no sound) @@ -80,18 +80,7 @@ public: virtual void setVelocity(const NLMISC::CVector& vel); /// Set the direction vector (3D mode only, ignored in stereo mode) (default: (0,0,0) as non-directional) virtual void setDirection(const NLMISC::CVector& dir); - /** Set the gain (volume value inside [0 , 1]). (default: 1) - * 0.0 -> silence - * 0.5 -> -6dB - * 1.0 -> no attenuation - * values > 1 (amplification) not supported by most drivers - */ - virtual void setGain(float gain); - - /** Set the gain amount (value inside [0, 1]) to map between 0 and the nominal gain - * (which is getSource()->getGain()). Does nothing if getSource() is null. - */ - virtual void setRelativeGain(float gain); + virtual void updateFinalGain(); /** Shift the frequency. 1.0f equals identity, each reduction of 50% equals a pitch shift * of one octave. 0 is not a legal value. */ diff --git a/code/nel/include/nel/sound/u_audio_mixer.h b/code/nel/include/nel/sound/u_audio_mixer.h index e1c2a274e..b6016d079 100644 --- a/code/nel/include/nel/sound/u_audio_mixer.h +++ b/code/nel/include/nel/sound/u_audio_mixer.h @@ -20,6 +20,7 @@ #include "nel/misc/types_nl.h" #include "nel/misc/string_mapper.h" #include "nel/sound/u_source.h" +#include "nel/sound/u_group_controller.h" #include "nel/ligo/primitive.h" #include @@ -291,9 +292,9 @@ public: * pass a callback function that will be called (if not NULL) just before deleting the spawned * source. */ - virtual USource *createSource( const NLMISC::TStringId &name, bool spawn=false, TSpawnEndCallback cb=NULL, void *callbackUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context=0) = 0; + virtual USource *createSource(const NLMISC::TStringId &name, bool spawn=false, TSpawnEndCallback cb=NULL, void *callbackUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0, UGroupController *groupController = NULL) = 0; /// Add a logical sound source (by sound id). To remove a source, just delete it. See createSource(const char*) - virtual USource *createSource( TSoundId id, bool spawn=false, TSpawnEndCallback cb=NULL, void *callbackUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context=0 ) = 0; + virtual USource *createSource(TSoundId id, bool spawn=false, TSpawnEndCallback cb=NULL, void *callbackUserParam = NULL, NL3D::CCluster *cluster = 0, CSoundContext *context = 0, UGroupController *groupController = NULL) = 0; /** Use this method to set the listener position instead of using getListener->setPos(); * It's because we have to update the background sounds in this case. diff --git a/code/nel/include/nel/sound/u_group_controller.h b/code/nel/include/nel/sound/u_group_controller.h new file mode 100644 index 000000000..0e8eeed47 --- /dev/null +++ b/code/nel/include/nel/sound/u_group_controller.h @@ -0,0 +1,63 @@ +/** + * \file u_group_controller.h + * \brief UGroupController + * \date 2012-04-10 12:49GMT + * \author Jan Boon (Kaetemi) + * UGroupController + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_U_GROUP_CONTROLLER_H +#define NLSOUND_U_GROUP_CONTROLLER_H +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NLSOUND { + +/** + * \brief UGroupController + * \date 2012-04-10 12:49GMT + * \author Jan Boon (Kaetemi) + * UGroupController + */ +class UGroupController +{ + virtual void setDevGain(float gain) = 0; + virtual float getDevGain() = 0; + + virtual void setUserGain(float gain) = 0; + virtual float getUserGain() = 0; + +protected: + virtual ~UGroupController(); + +}; /* class UGroupController */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_U_GROUP_CONTROLLER_H */ + +/* end of file */ diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index 367abd0e1..5ee4907c5 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -51,6 +51,7 @@ #include "nel/sound/sample_bank_manager.h" #include "nel/sound/sample_bank.h" #include "nel/sound/sound_bank.h" +#include "nel/sound/group_controller.h" using namespace std; using namespace NLMISC; @@ -1689,7 +1690,7 @@ void CAudioMixerUser::update() // _Tracks[i]->DrvSource->setPos(source->getPos() * (1-css->PosAlpha) + css->Position*(css->PosAlpha)); _Tracks[i]->getPhysicalSource()->setPos(source->getPos() * (1-css->PosAlpha) + vpos*(css->PosAlpha)); // update the relative gain - _Tracks[i]->getPhysicalSource()->setGain(source->getRelativeGain()*source->getGain()*css->Gain); + _Tracks[i]->getPhysicalSource()->setGain(source->getFinalGain() * css->Gain); #if EAX_AVAILABLE == 1 if (_UseEax) { @@ -1829,7 +1830,7 @@ bool CAudioMixerUser::tryToLoadSampleBank(const std::string &sampleName) // ****************************************************************** -USource *CAudioMixerUser::createSource( TSoundId id, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context ) +USource *CAudioMixerUser::createSource( TSoundId id, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context, UGroupController *groupController ) { #if NL_PROFILE_MIXER TTicks start = CTime::getPerformanceTime(); @@ -1915,7 +1916,7 @@ retrySound: } // Create source - CSimpleSource *source = new CSimpleSource( simpleSound, spawn, cb, userParam, cluster); + CSimpleSource *source = new CSimpleSource( simpleSound, spawn, cb, userParam, cluster, static_cast(groupController)); // nldebug("Mixer : source %p created", source); @@ -1939,28 +1940,28 @@ retrySound: { CStreamSound *streamSound = static_cast(id); // This is a stream thingy. - ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster); + ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster, static_cast(groupController)); } break; case CSound::SOUND_COMPLEX: { CComplexSound *complexSound = static_cast(id); // This is a pattern sound. - ret = new CComplexSource(complexSound, spawn, cb, userParam, cluster); + ret = new CComplexSource(complexSound, spawn, cb, userParam, cluster, static_cast(groupController)); } break; case CSound::SOUND_BACKGROUND: { // This is a background sound. CBackgroundSound *bgSound = static_cast(id); - ret = new CBackgroundSource(bgSound, spawn, cb, userParam, cluster); + ret = new CBackgroundSource(bgSound, spawn, cb, userParam, cluster, static_cast(groupController)); } break; case CSound::SOUND_MUSIC: { // This is a background music sound CMusicSound *music_sound= static_cast(id); - ret = new CMusicSource(music_sound, spawn, cb, userParam, cluster); + ret = new CMusicSource(music_sound, spawn, cb, userParam, cluster, static_cast(groupController)); } break; case CSound::SOUND_CONTEXT: @@ -1974,7 +1975,7 @@ retrySound: CSound *sound = ctxSound->getContextSound(*context); if (sound != 0) { - ret = createSource(sound, spawn, cb, userParam, cluster); + ret = createSource(sound, spawn, cb, userParam, cluster, NULL, static_cast(groupController)); // Set the volume of the source according to the context volume if (ret != 0) { @@ -2007,9 +2008,9 @@ retrySound: // ****************************************************************** -USource *CAudioMixerUser::createSource( const NLMISC::TStringId &name, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context) +USource *CAudioMixerUser::createSource( const NLMISC::TStringId &name, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context, UGroupController *groupController) { - return createSource( getSoundId( name ), spawn, cb, userParam, cluster, context); + return createSource( getSoundId( name ), spawn, cb, userParam, cluster, context, groupController); } diff --git a/code/nel/src/sound/background_source.cpp b/code/nel/src/sound/background_source.cpp index c2cb5206a..dbb14242a 100644 --- a/code/nel/src/sound/background_source.cpp +++ b/code/nel/src/sound/background_source.cpp @@ -26,8 +26,8 @@ namespace NLSOUND { -CBackgroundSource::CBackgroundSource(CBackgroundSound *backgroundSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) -: CSourceCommon(backgroundSound, spawn, cb, cbUserParam, cluster) +CBackgroundSource::CBackgroundSource(CBackgroundSound *backgroundSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) +: CSourceCommon(backgroundSound, spawn, cb, cbUserParam, cluster, groupController) { _BackgroundSound = backgroundSound; } @@ -119,7 +119,7 @@ void CBackgroundSource::play() for (; first != last; ++first) { TSubSource subSource; - subSource.Source = mixer->createSource(first->SoundName, false, 0, 0, _Cluster, 0); + subSource.Source = mixer->createSource(first->SoundName, false, 0, 0, _Cluster, NULL, _GroupController); if (subSource.Source != NULL) subSource.Source->setPriority(_Priority); subSource.Filter = first->Filter; diff --git a/code/nel/src/sound/complex_source.cpp b/code/nel/src/sound/complex_source.cpp index cd3d2925d..8fad61a53 100644 --- a/code/nel/src/sound/complex_source.cpp +++ b/code/nel/src/sound/complex_source.cpp @@ -25,8 +25,8 @@ using namespace NLMISC; namespace NLSOUND { -CComplexSource::CComplexSource (CComplexSound *soundPattern, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) -: CSourceCommon(soundPattern, spawn, cb, cbUserParam, cluster), +CComplexSource::CComplexSource (CComplexSound *soundPattern, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) +: CSourceCommon(soundPattern, spawn, cb, cbUserParam, cluster, groupController), _Source1(NULL), _Source2(NULL) { @@ -117,7 +117,7 @@ void CComplexSource::playStuf() else _FadeLength = 0; - _Source2 = mixer->createSource(sound, false, 0, 0, _Cluster); + _Source2 = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController); if (_Source2 == NULL) return; _Source2->setPriority(_Priority); @@ -155,7 +155,7 @@ void CComplexSource::playStuf() { CSound *sound = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++])); - _Source1 = mixer->createSource(sound, false, 0, 0, _Cluster); + _Source1 = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController); if (_Source1 == NULL) return; _Source1->setPriority(_Priority); @@ -202,7 +202,7 @@ void CComplexSource::playStuf() CSound *sound = mixer->getSoundId(*first); if (sound != NULL) { - USource *source = mixer->createSource(sound, false, 0, 0, _Cluster); + USource *source = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController); if (source != NULL) { source->setPriority(_Priority); @@ -512,7 +512,7 @@ void CComplexSource::onUpdate() // determine the XFade length (if next sound is too short. _FadeLength = minof(uint32(_PatternSound->getFadeLength()/_TickPerSecond), (sound2->getDuration()) / 2, (_Source1->getSound()->getDuration())/2); - _Source2 = mixer->createSource(sound2, false, 0, 0, _Cluster); + _Source2 = mixer->createSource(sound2, false, 0, 0, _Cluster, NULL, _GroupController); if (_Source2) { _Source2->setPriority(_Priority); @@ -641,7 +641,7 @@ void CComplexSource::onEvent() CSound *sound = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++])); - _Source1 = mixer->createSource(sound, false, 0, 0, _Cluster); + _Source1 = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController); if (_Source1 == NULL) { stop(); diff --git a/code/nel/src/sound/group_controller.cpp b/code/nel/src/sound/group_controller.cpp new file mode 100644 index 000000000..405be5d4e --- /dev/null +++ b/code/nel/src/sound/group_controller.cpp @@ -0,0 +1,112 @@ +/** + * \file group_controller.cpp + * \brief CGroupController + * \date 2012-04-10 09:29GMT + * \author Jan Boon (Kaetemi) + * CGroupController + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +// #include +#include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NLSOUND { + +CGroupController::CGroupController(CGroupController *parent) : + m_Parent(parent), m_DevGain(1.0f), m_UserGain(1.0f), m_NbSourcesInclChild(0) +{ + +} + +CGroupController::~CGroupController() +{ + // If m_Sources is not empty, a crash is very likely. + nlassert(m_Sources.empty()); + + for (std::map::iterator it(m_Children.begin()), end(m_Children.end()); it != end; ++it) + { + delete it->second; + it->second = NULL; + } + m_Parent = NULL; +} + +void CGroupController::addSource(CSourceCommon *source) +{ + m_Sources.insert(source); + increaseSources(); +} + +void CGroupController::removeSource(CSourceCommon *source) +{ + decreaseSources(); + m_Sources.erase(source); +} + +void CGroupController::calculateFinalGain() // overridden by root +{ + m_FinalGain = calculateTotalGain() * m_Parent->getFinalGain(); +} + +void CGroupController::updateSourceGain() +{ + // Dont update source gain when this controller is inactive. + if (m_NbSourcesInclChild) + { + calculateFinalGain(); + for (CAudioMixerUser::TSourceContainer::iterator it(m_Sources.begin()), end(m_Sources.end()); it != end; ++it) + (*it)->updateFinalGain(); + for (std::map::iterator it(m_Children.begin()), end(m_Children.end()); it != end; ++it) + (*it).second->updateSourceGain(); + } +} + +void CGroupController::increaseSources() // overridden by root +{ + ++m_NbSourcesInclChild; + m_Parent->increaseSources(); + + // Update source gain when this controller was inactive before but the parent was active before. + // Thus, when this controller was the root of inactive controllers. + if (m_NbSourcesInclChild == 1 && m_Parent->m_NbSourcesInclChild > 1) + updateSourceGain(); +} + +void CGroupController::decreaseSources() // overridden by root +{ + --m_NbSourcesInclChild; + m_Parent->decreaseSources(); +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/group_controller_root.cpp b/code/nel/src/sound/group_controller_root.cpp new file mode 100644 index 000000000..c5305e061 --- /dev/null +++ b/code/nel/src/sound/group_controller_root.cpp @@ -0,0 +1,99 @@ +/** + * \file group_controller_root.cpp + * \brief CGroupControllerRoot + * \date 2012-04-10 09:44GMT + * \author Jan Boon (Kaetemi) + * CGroupControllerRoot + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +// #include +#include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NLSOUND { + +CGroupControllerRoot::CGroupControllerRoot() : CGroupController(NULL) +{ + +} + +CGroupControllerRoot::~CGroupControllerRoot() +{ + +} + +void CGroupControllerRoot::calculateFinalGain() +{ + m_FinalGain = calculateTotalGain(); +} + +void CGroupController::increaseSources() +{ + ++m_NbSourcesInclChild; + + // Update source gain when this controller was inactive before. + if (m_NbSourcesInclChild == 1) + updateSourceGain(); +} + +void CGroupController::decreaseSources() +{ + --m_NbSourcesInclChild; +} + +CGroupController *CGroupControllerRoot::getGroupController(const std::string &path) +{ + std::vector pathNodes; + NLMISC::splitString(NLMISC::toLower(path), "/", pathNodes); + CGroupController *active = this; + for (std::vector::iterator it(pathNodes.begin()), end(pathNodes.end()); it != end; ++it) + { + if (!(*it).empty()) + { + std::map::iterator found = active->m_Children.find(*it); + if (found == active->m_Children.end()) + { + active = new CGroupController(active); + active->m_Parent->m_Children[*it] = active; + } + else + { + active = (*found).second; + } + } + } + return active; +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/music_source.cpp b/code/nel/src/sound/music_source.cpp index a47914dde..e5595d9f4 100644 --- a/code/nel/src/sound/music_source.cpp +++ b/code/nel/src/sound/music_source.cpp @@ -26,8 +26,8 @@ namespace NLSOUND { // *************************************************************************** -CMusicSource::CMusicSource(CMusicSound *musicSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) - : CSourceCommon(musicSound, spawn, cb, cbUserParam, cluster) +CMusicSource::CMusicSource(CMusicSound *musicSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) + : CSourceCommon(musicSound, spawn, cb, cbUserParam, cluster, groupController) { _MusicSound= musicSound; } diff --git a/code/nel/src/sound/simple_source.cpp b/code/nel/src/sound/simple_source.cpp index fb0b7bc85..695619f91 100644 --- a/code/nel/src/sound/simple_source.cpp +++ b/code/nel/src/sound/simple_source.cpp @@ -28,8 +28,8 @@ using namespace NLMISC; namespace NLSOUND { -CSimpleSource::CSimpleSource(CSimpleSound *simpleSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) - : CSourceCommon(simpleSound, spawn, cb, cbUserParam, cluster), +CSimpleSource::CSimpleSource(CSimpleSound *simpleSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) + : CSourceCommon(simpleSound, spawn, cb, cbUserParam, cluster, groupController), _SimpleSound(simpleSound), _Track(NULL), _PlayMuted(false) @@ -166,7 +166,7 @@ void CSimpleSource::play() setDirection(_Direction); // because there is a workaround inside pSource->setVelocity(_Velocity); } - pSource->setGain(_Gain); + pSource->setGain(getFinalGain()); pSource->setSourceRelativeMode(_RelativeMode); pSource->setLooping(_Looping); pSource->setPitch(_Pitch); @@ -312,36 +312,13 @@ void CSimpleSource::setDirection(const NLMISC::CVector& dir) } } - -/* Set the gain (volume value inside [0 , 1]). (default: 1) - * 0.0 -> silence - * 0.5 -> -6dB - * 1.0 -> no attenuation - * values > 1 (amplification) not supported by most drivers - */ -void CSimpleSource::setGain(float gain) -{ - CSourceCommon::setGain(gain); - - // Set the gain - if (hasPhysicalSource()) - { - getPhysicalSource()->setGain(gain); - } -} - -void CSimpleSource::setRelativeGain(float gain) +void CSimpleSource::updateFinalGain() { - CSourceCommon::setRelativeGain(gain); - // Set the gain if (hasPhysicalSource()) - { - getPhysicalSource()->setGain(_Gain); - } + getPhysicalSource()->setGain(getFinalGain()); } - /* Shift the frequency. 1.0f equals identity, each reduction of 50% equals a pitch shift * of one octave. 0 is not a legal value. */ diff --git a/code/nel/src/sound/source_common.cpp b/code/nel/src/sound/source_common.cpp index ce2b7e210..3af1eb6ae 100644 --- a/code/nel/src/sound/source_common.cpp +++ b/code/nel/src/sound/source_common.cpp @@ -25,7 +25,7 @@ using namespace NLMISC; namespace NLSOUND { -CSourceCommon::CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) +CSourceCommon::CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) : _Priority(MidPri), _Playing(false), _Looping(false), @@ -41,9 +41,11 @@ CSourceCommon::CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void _SpawnEndCb(cb), _CbUserParam(cbUserParam), _Cluster(cluster), - _UserVarControler(id->getUserVarControler()) + _UserVarControler(id->getUserVarControler()), + _GroupController(groupController ? groupController : id->getGroupController()) { CAudioMixerUser::instance()->addSource(this); + groupController->addSource(this); // get a local copy of the sound parameter _InitialGain = _Gain = id->getGain(); @@ -51,11 +53,11 @@ CSourceCommon::CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void _Looping = id->getLooping(); _Priority = id->getPriority(); _Direction = id->getDirectionVector(); - } CSourceCommon::~CSourceCommon() { + _GroupController->removeSource(this); CAudioMixerUser::instance()->removeSource(this); } @@ -177,6 +179,7 @@ void CSourceCommon::setGain( float gain ) { clamp(gain, 0.0f, 1.0f); _InitialGain = _Gain = gain; + updateFinalGain(); } /* Set the gain amount (value inside [0, 1]) to map between 0 and the nominal gain @@ -185,8 +188,8 @@ void CSourceCommon::setGain( float gain ) void CSourceCommon::setRelativeGain( float gain ) { clamp(gain, 0.0f, 1.0f); - _Gain = _InitialGain * gain; + updateFinalGain(); } /* diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index a5b167c8f..2c3188454 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -28,8 +28,8 @@ using namespace NLMISC; namespace NLSOUND { -CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster) - : CSourceCommon(streamSound, spawn, cb, cbUserParam, cluster), +CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) + : CSourceCommon(streamSound, spawn, cb, cbUserParam, cluster, groupController), m_StreamSound(streamSound), m_Alpha(0.0f), m_Track(NULL), @@ -177,7 +177,7 @@ void CStreamSource::play() setDirection(_Direction); // because there is a workaround inside pSource->setVelocity(_Velocity); } - pSource->setGain(_Gain); + pSource->setGain(getFinalGain()); pSource->setSourceRelativeMode(_RelativeMode); // pSource->setLooping(_Looping); pSource->setPitch(_Pitch); @@ -294,22 +294,12 @@ void CStreamSource::setDirection(const NLMISC::CVector& dir) } } -void CStreamSource::setGain(float gain) +void CStreamSource::updateFinalGain() { CAutoMutex autoMutex(m_BufferMutex); - - CSourceCommon::setGain(gain); - if (hasPhysicalSource()) - getPhysicalSource()->setGain(gain); -} - -void CStreamSource::setRelativeGain(float gain) -{ - CAutoMutex autoMutex(m_BufferMutex); - - CSourceCommon::setRelativeGain(gain); + if (hasPhysicalSource()) - getPhysicalSource()->setGain(_Gain); + getPhysicalSource()->setGain(getFinalGain()); } void CStreamSource::setPitch(float pitch) From 11f0872a0117ca8b2551c45424f872b201d1c276 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 16:32:04 +0200 Subject: [PATCH 32/77] Added: #1459 Load group controller from sound sheets --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_mixer_user.h | 37 ++++------ code/nel/include/nel/sound/containers.h | 67 +++++++++++++++++++ code/nel/include/nel/sound/group_controller.h | 11 +-- .../include/nel/sound/group_controller_root.h | 1 + code/nel/include/nel/sound/sound.h | 5 +- code/nel/include/nel/sound/u_audio_mixer.h | 4 ++ .../include/nel/sound/u_group_controller.h | 2 +- code/nel/src/sound/audio_mixer_user.cpp | 5 ++ code/nel/src/sound/group_controller.cpp | 18 ++++- code/nel/src/sound/group_controller_root.cpp | 9 ++- code/nel/src/sound/music_sound.cpp | 12 ++++ code/nel/src/sound/sound.cpp | 28 ++++++++ code/nel/src/sound/stream_sound.cpp | 14 ++++ 13 files changed, 181 insertions(+), 32 deletions(-) create mode 100644 code/nel/include/nel/sound/containers.h diff --git a/code/nel/include/nel/sound/audio_mixer_user.h b/code/nel/include/nel/sound/audio_mixer_user.h index eded75fc6..c060edd48 100644 --- a/code/nel/include/nel/sound/audio_mixer_user.h +++ b/code/nel/include/nel/sound/audio_mixer_user.h @@ -34,6 +34,14 @@ #include "nel/sound/mixing_track.h" #include "nel/sound/sound.h" #include "nel/sound/music_channel_fader.h" +#include "nel/sound/group_controller_root.h" + +// Current version is 2, Ryzom Live uses 1 +// Provided to allow compatibility with old binary files +#define NLSOUND_SHEET_VERSION_BUILT 1 +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER "effects" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER "music" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER "dialog" namespace NLLIGO { class CLigoConfig; @@ -51,26 +59,6 @@ namespace NLSOUND { class CMusicSoundManager; class IReverbEffect; -/// Hasher functor for hashed container with pointer key. -template -struct THashPtr : public std::unary_function -{ - static const size_t bucket_size = 4; - static const size_t min_buckets = 8; - size_t operator () (const Pointer &ptr) const - { - //CHashSet::hasher h; - // transtype the pointer into int then hash it - //return h.operator()(uint(uintptr_t(ptr))); - return (size_t)(uintptr_t)ptr; - } - inline bool operator() (const Pointer &ptr1, const Pointer &ptr2) const - { - // delegate the work to someone else as well? - return (uintptr_t)ptr1 < (uintptr_t)ptr2; - } -}; - /** * Implementation of UAudioMixer * @@ -197,6 +185,9 @@ public: /// Get a TSoundId from a name (returns NULL if not found) virtual TSoundId getSoundId( const NLMISC::TStringId &name ); + /// Gets the group controller for the given group tree path with separator '/', if it doesn't exist yet it will be created. + /// Examples: "music", "effects", "dialog", "music/background", "music/loading", "music/player", etcetera + virtual UGroupController *getGroupController(const std::string &path); /** Add a logical sound source (returns NULL if name not found). * If spawn is true, the source will auto-delete after playing. If so, the return USource* pointer @@ -431,9 +422,6 @@ private: // utility function for automatic sample bank loading. bool tryToLoadSampleBank(const std::string &sampleName); -public: - typedef CHashSet > TSourceContainer; -private: typedef CHashSet > TMixerUpdateContainer; typedef CHashMap, THashPtr > TBufferToSourceContainer; // typedef std::multimap TTimedEventContainer; @@ -566,6 +554,9 @@ private: // Instance of the background music manager CMusicSoundManager *_BackgroundMusicManager; + /// Group controller + CGroupControllerRoot _GroupController; + public: struct TSampleBankHeader { diff --git a/code/nel/include/nel/sound/containers.h b/code/nel/include/nel/sound/containers.h new file mode 100644 index 000000000..bd4fe7fb9 --- /dev/null +++ b/code/nel/include/nel/sound/containers.h @@ -0,0 +1,67 @@ +/** + * \file containers.h + * \brief CContainers + * \date 2012-04-10 13:57GMT + * \author Unknown (Unknown) + * CContainers + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_CONTAINERS_H +#define NLSOUND_CONTAINERS_H +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NLSOUND { + class CSourceCommon; + +/// Hasher functor for hashed container with pointer key. +template +struct THashPtr : public std::unary_function +{ + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + size_t operator () (const Pointer &ptr) const + { + //CHashSet::hasher h; + // transtype the pointer into int then hash it + //return h.operator()(uint(uintptr_t(ptr))); + return (size_t)(uintptr_t)ptr; + } + inline bool operator() (const Pointer &ptr1, const Pointer &ptr2) const + { + // delegate the work to someone else as well? + return (uintptr_t)ptr1 < (uintptr_t)ptr2; + } +}; + +typedef CHashSet > TSourceContainer; + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_CONTAINERS_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/group_controller.h b/code/nel/include/nel/sound/group_controller.h index 4c443a52d..d93fb8683 100644 --- a/code/nel/include/nel/sound/group_controller.h +++ b/code/nel/include/nel/sound/group_controller.h @@ -35,13 +35,12 @@ // NeL includes #include -#include #include +#include // Project includes namespace NLSOUND { - class CSourceCommon; class CGroupControllerRoot; /** @@ -64,7 +63,7 @@ private: float m_FinalGain; int m_NbSourcesInclChild; - CAudioMixerUser::TSourceContainer m_Sources; + TSourceContainer m_Sources; public: CGroupController(CGroupController *parent); @@ -83,8 +82,12 @@ public: void addSource(CSourceCommon *source); void removeSource(CSourceCommon *source); -private: + virtual std::string getPath(); + +protected: virtual ~CGroupController(); // subnodes can only be deleted by the root + +private: inline float calculateTotalGain() { return m_DevGain * m_UserGain; } virtual void calculateFinalGain(); virtual void increaseSources(); diff --git a/code/nel/include/nel/sound/group_controller_root.h b/code/nel/include/nel/sound/group_controller_root.h index 82c3af849..5d47ff50a 100644 --- a/code/nel/include/nel/sound/group_controller_root.h +++ b/code/nel/include/nel/sound/group_controller_root.h @@ -54,6 +54,7 @@ public: CGroupController *getGroupController(const std::string &path); protected: + virtual std::string getPath(); virtual void calculateFinalGain(); virtual void increaseSources(); virtual void decreaseSources(); diff --git a/code/nel/include/nel/sound/sound.h b/code/nel/include/nel/sound/sound.h index ef0e62f72..7d7542fc9 100644 --- a/code/nel/include/nel/sound/sound.h +++ b/code/nel/include/nel/sound/sound.h @@ -105,7 +105,7 @@ public: /// Return the max distance (if detailed()) virtual float getMaxDistance() const { return _MaxDist; } - inline CGroupController *getGroupController() const { return NULL; } // TODO, RETURN THE GROUP CONTROLLER + inline CGroupController *getGroupController() const { return _GroupController; } /// Set looping void setLooping( bool looping ) { _Looping = looping; } @@ -145,6 +145,9 @@ protected: /// An optional user var controler. NLMISC::TStringId _UserVarControler; + /// The group controller, always exists, owned by the audio mixer + CGroupController *_GroupController; + }; diff --git a/code/nel/include/nel/sound/u_audio_mixer.h b/code/nel/include/nel/sound/u_audio_mixer.h index b6016d079..c4702c3f8 100644 --- a/code/nel/include/nel/sound/u_audio_mixer.h +++ b/code/nel/include/nel/sound/u_audio_mixer.h @@ -286,6 +286,10 @@ public: /// Get a TSoundId from a name (returns NULL if not found) virtual TSoundId getSoundId( const NLMISC::TStringId &name ) = 0; + /// Gets the group controller for the given group tree path with separator '/', if it doesn't exist yet it will be created. + /// Examples: "music", "effects", "dialog", "music/background", "music/loading", "music/player", etcetera + virtual UGroupController *getGroupController(const std::string &path) = 0; + /** Add a logical sound source (returns NULL if name not found). * If spawn is true, the source will auto-delete after playing. If so, the return USource* pointer * is valid only before the time when calling play() plus the duration of the sound. You can diff --git a/code/nel/include/nel/sound/u_group_controller.h b/code/nel/include/nel/sound/u_group_controller.h index 0e8eeed47..a74e9da73 100644 --- a/code/nel/include/nel/sound/u_group_controller.h +++ b/code/nel/include/nel/sound/u_group_controller.h @@ -52,7 +52,7 @@ class UGroupController virtual float getUserGain() = 0; protected: - virtual ~UGroupController(); + virtual ~UGroupController() { } }; /* class UGroupController */ diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index 5ee4907c5..e03b1ae00 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -52,6 +52,7 @@ #include "nel/sound/sample_bank.h" #include "nel/sound/sound_bank.h" #include "nel/sound/group_controller.h" +#include "nel/sound/containers.h" using namespace std; using namespace NLMISC; @@ -1827,6 +1828,10 @@ bool CAudioMixerUser::tryToLoadSampleBank(const std::string &sampleName) } } +UGroupController *CAudioMixerUser::getGroupController(const std::string &path) +{ + return static_cast(_GroupController.getGroupController(path)); +} // ****************************************************************** diff --git a/code/nel/src/sound/group_controller.cpp b/code/nel/src/sound/group_controller.cpp index 405be5d4e..86f46301e 100644 --- a/code/nel/src/sound/group_controller.cpp +++ b/code/nel/src/sound/group_controller.cpp @@ -72,6 +72,22 @@ void CGroupController::removeSource(CSourceCommon *source) m_Sources.erase(source); } +std::string CGroupController::getPath() // overridden by root +{ + for (std::map::iterator it(m_Parent->m_Children.begin()), end(m_Parent->m_Children.end()); it != end; ++it) + { + if (it->second == this) + { + const std::string &name = it->first; + std::string returnPath = m_Parent->getPath() + "/" + name; + if (returnPath[0] == '/') + returnPath = returnPath.substr(1); + return returnPath; + } + } + +} + void CGroupController::calculateFinalGain() // overridden by root { m_FinalGain = calculateTotalGain() * m_Parent->getFinalGain(); @@ -83,7 +99,7 @@ void CGroupController::updateSourceGain() if (m_NbSourcesInclChild) { calculateFinalGain(); - for (CAudioMixerUser::TSourceContainer::iterator it(m_Sources.begin()), end(m_Sources.end()); it != end; ++it) + for (TSourceContainer::iterator it(m_Sources.begin()), end(m_Sources.end()); it != end; ++it) (*it)->updateFinalGain(); for (std::map::iterator it(m_Children.begin()), end(m_Children.end()); it != end; ++it) (*it).second->updateSourceGain(); diff --git a/code/nel/src/sound/group_controller_root.cpp b/code/nel/src/sound/group_controller_root.cpp index c5305e061..f49a40fff 100644 --- a/code/nel/src/sound/group_controller_root.cpp +++ b/code/nel/src/sound/group_controller_root.cpp @@ -51,12 +51,17 @@ CGroupControllerRoot::~CGroupControllerRoot() } +std::string CGroupControllerRoot::getPath() +{ + return ""; +} + void CGroupControllerRoot::calculateFinalGain() { m_FinalGain = calculateTotalGain(); } -void CGroupController::increaseSources() +void CGroupControllerRoot::increaseSources() { ++m_NbSourcesInclChild; @@ -65,7 +70,7 @@ void CGroupController::increaseSources() updateSourceGain(); } -void CGroupController::decreaseSources() +void CGroupControllerRoot::decreaseSources() { --m_NbSourcesInclChild; } diff --git a/code/nel/src/sound/music_sound.cpp b/code/nel/src/sound/music_sound.cpp index 2b192c3c6..f95edfb26 100644 --- a/code/nel/src/sound/music_sound.cpp +++ b/code/nel/src/sound/music_sound.cpp @@ -20,6 +20,9 @@ #include "nel/misc/path.h" #include "nel/georges/u_form_elm.h" +#if NLSOUND_SHEET_VERSION_BUILT < 2 +# include "nel/sound/audio_mixer_user.h" +#endif using namespace std; using namespace NLMISC; @@ -72,6 +75,10 @@ void CMusicSound::importForm(const std::string& filename, NLGEORGES::UFormElm& root.getValueByName(_MinimumPlayTime, ".SoundType.MinimumPlayTime"); root.getValueByName(_TimeBeforeCanReplay, ".SoundType.TimeBeforeCanReplay"); +#if NLSOUND_SHEET_VERSION_BUILT < 2 + _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER)); +#endif + } // *************************************************************************** @@ -97,6 +104,11 @@ void CMusicSound::serial(NLMISC::IStream &s) CStringMapper::serialString(s, _FileName); s.serial(_FadeInLength, _FadeOutLength); s.serial(_MinimumPlayTime, _TimeBeforeCanReplay); + +#if NLSOUND_SHEET_VERSION_BUILT < 2 + if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER)); +#endif + } // *************************************************************************** diff --git a/code/nel/src/sound/sound.cpp b/code/nel/src/sound/sound.cpp index eed318cca..a943fa543 100644 --- a/code/nel/src/sound/sound.cpp +++ b/code/nel/src/sound/sound.cpp @@ -27,6 +27,8 @@ #include "nel/sound/music_sound.h" #include "nel/sound/stream_sound.h" +#include "nel/sound/group_controller.h" + using namespace std; using namespace NLMISC; @@ -134,6 +136,23 @@ void CSound::serial(NLMISC::IStream &s) std::string name = CStringMapper::unmap(_Name); s.serial(name); } + + nlassert(CAudioMixerUser::instance()); // not sure +#if NLSOUND_SHEET_VERSION_BUILT < 2 + if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER)); +#else + if (s.isReading()) + { + std::string groupControllerPath; + s.serial(groupControllerPath); + _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(groupControllerPath)); + } + else + { + std::string groupControllerPath = _GroupController->getPath(); + s.serial(groupControllerPath); + } +#endif } @@ -225,6 +244,15 @@ void CSound::importForm(const std::string& filename, NLGEORGES::UFormElm& roo default: _Priority = MidPri; } + +#if NLSOUND_SHEET_VERSION_BUILT < 2 + _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER)); +#else + std::string groupControllerPath; + root.getValueByName(groupControllerPath, ".GroupControllerPath"); + _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(groupControllerPath)); +#endif + } diff --git a/code/nel/src/sound/stream_sound.cpp b/code/nel/src/sound/stream_sound.cpp index 347fae987..7addbbe0a 100644 --- a/code/nel/src/sound/stream_sound.cpp +++ b/code/nel/src/sound/stream_sound.cpp @@ -17,6 +17,10 @@ #include "stdsound.h" #include "nel/sound/stream_sound.h" +#if NLSOUND_SHEET_VERSION_BUILT < 2 +# include "nel/sound/audio_mixer_user.h" +#endif + namespace NLSOUND { CStreamSound::CStreamSound() @@ -51,6 +55,11 @@ void CStreamSound::importForm(const std::string &filename, NLGEORGES::UFormElm & // Alpha root.getValueByName(m_Alpha, ".SoundType.Alpha"); + +#if NLSOUND_SHEET_VERSION_BUILT < 2 + _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER)); +#endif + } void CStreamSound::serial(NLMISC::IStream &s) @@ -59,6 +68,11 @@ void CStreamSound::serial(NLMISC::IStream &s) s.serial(_MinDist); s.serial(m_Alpha); + +#if NLSOUND_SHEET_VERSION_BUILT < 2 + if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER)); +#endif + } } /* namespace NLSOUND */ From 84f11c6327a5377e7ed38a5bb193fbfc60805891 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 18:18:58 +0200 Subject: [PATCH 33/77] Fixed: #1459 Done --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_mixer_user.h | 3 --- .../include/nel/sound/group_controller_root.h | 3 ++- .../include/nel/sound/u_group_controller.h | 5 +++++ .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 21 ++++++++++++++++--- code/nel/src/sound/group_controller.cpp | 2 ++ code/nel/src/sound/music_sound.cpp | 6 +++--- code/nel/src/sound/sound.cpp | 8 ++++--- code/nel/src/sound/source_common.cpp | 2 +- code/nel/src/sound/stream_sound.cpp | 6 +++--- 9 files changed, 39 insertions(+), 17 deletions(-) diff --git a/code/nel/include/nel/sound/audio_mixer_user.h b/code/nel/include/nel/sound/audio_mixer_user.h index c060edd48..977fe066b 100644 --- a/code/nel/include/nel/sound/audio_mixer_user.h +++ b/code/nel/include/nel/sound/audio_mixer_user.h @@ -39,9 +39,6 @@ // Current version is 2, Ryzom Live uses 1 // Provided to allow compatibility with old binary files #define NLSOUND_SHEET_VERSION_BUILT 1 -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER "effects" -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER "music" -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER "dialog" namespace NLLIGO { class CLigoConfig; diff --git a/code/nel/include/nel/sound/group_controller_root.h b/code/nel/include/nel/sound/group_controller_root.h index 5d47ff50a..2cc86f5b3 100644 --- a/code/nel/include/nel/sound/group_controller_root.h +++ b/code/nel/include/nel/sound/group_controller_root.h @@ -32,6 +32,7 @@ // STL includes // NeL includes +#include // Project includes #include @@ -44,7 +45,7 @@ namespace NLSOUND { * \author Jan Boon (Kaetemi) * CGroupControllerRoot */ -class CGroupControllerRoot : public CGroupController +class CGroupControllerRoot : public CGroupController, public NLMISC::CManualSingleton { public: CGroupControllerRoot(); diff --git a/code/nel/include/nel/sound/u_group_controller.h b/code/nel/include/nel/sound/u_group_controller.h index a74e9da73..533f29936 100644 --- a/code/nel/include/nel/sound/u_group_controller.h +++ b/code/nel/include/nel/sound/u_group_controller.h @@ -35,6 +35,10 @@ // Project includes +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER "effects" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER "music" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER "dialog" + namespace NLSOUND { /** @@ -45,6 +49,7 @@ namespace NLSOUND { */ class UGroupController { +public: virtual void setDevGain(float gain) = 0; virtual float getDevGain() = 0; diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index db5b8b214..596d07721 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include // Project includes @@ -325,6 +326,7 @@ static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { static UAudioMixer *s_AudioMixer = NULL; static UStreamSource *s_StreamSource = NULL; static IAudioDecoder *s_AudioDecoder = NULL; +static UGroupController *s_GroupController = NULL; static void initSample() { @@ -368,6 +370,8 @@ static void initSample() s_AudioDecoder = IAudioDecoder::createAudioDecoder(sample, false, false); s_StreamSource->setFormat(s_AudioDecoder->getChannels(), s_AudioDecoder->getBitsPerSample(), (uint32)s_AudioDecoder->getSamplesPerSec()); s_StreamSource->setPitch(2.0f); + + s_GroupController = s_AudioMixer->getGroupController("dialog"); } //CMutex *s_Mutex = NULL; @@ -405,13 +409,23 @@ static void runSample() s_StreamSource->play(); - printf("Press ANY key to exit\n"); + printf("Change volume with - and +\n"); + printf("Press ANY other key to exit\n"); while (!s_AudioDecoder->isMusicEnded()) { if (_kbhit()) { - _getch(); - return; + switch (_getch()) + { + case '+': + s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); + break; + case '-': + s_GroupController->setUserGain(s_GroupController->getUserGain() - 0.1f); + break; + default: + return; + } } bufferMore(bytes); s_AudioMixer->update(); @@ -440,6 +454,7 @@ static void runSample() static void releaseSample() { //NLMISC::CHTimer::clear(); + s_GroupController = NULL; delete s_AudioDecoder; s_AudioDecoder = NULL; delete s_StreamSource; s_StreamSource = NULL; delete s_AudioMixer; s_AudioMixer = NULL; diff --git a/code/nel/src/sound/group_controller.cpp b/code/nel/src/sound/group_controller.cpp index 86f46301e..1cce28bf0 100644 --- a/code/nel/src/sound/group_controller.cpp +++ b/code/nel/src/sound/group_controller.cpp @@ -62,6 +62,8 @@ CGroupController::~CGroupController() void CGroupController::addSource(CSourceCommon *source) { + nlassert(this); + m_Sources.insert(source); increaseSources(); } diff --git a/code/nel/src/sound/music_sound.cpp b/code/nel/src/sound/music_sound.cpp index f95edfb26..803103dd9 100644 --- a/code/nel/src/sound/music_sound.cpp +++ b/code/nel/src/sound/music_sound.cpp @@ -21,7 +21,7 @@ #include "nel/georges/u_form_elm.h" #if NLSOUND_SHEET_VERSION_BUILT < 2 -# include "nel/sound/audio_mixer_user.h" +# include "nel/sound/group_controller_root.h" #endif using namespace std; @@ -76,7 +76,7 @@ void CMusicSound::importForm(const std::string& filename, NLGEORGES::UFormElm& root.getValueByName(_TimeBeforeCanReplay, ".SoundType.TimeBeforeCanReplay"); #if NLSOUND_SHEET_VERSION_BUILT < 2 - _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER)); + _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER); #endif } @@ -106,7 +106,7 @@ void CMusicSound::serial(NLMISC::IStream &s) s.serial(_MinimumPlayTime, _TimeBeforeCanReplay); #if NLSOUND_SHEET_VERSION_BUILT < 2 - if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER)); + if (s.isReading()) _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER); #endif } diff --git a/code/nel/src/sound/sound.cpp b/code/nel/src/sound/sound.cpp index a943fa543..07c10f8ef 100644 --- a/code/nel/src/sound/sound.cpp +++ b/code/nel/src/sound/sound.cpp @@ -28,6 +28,7 @@ #include "nel/sound/stream_sound.h" #include "nel/sound/group_controller.h" +#include "nel/sound/group_controller_root.h" using namespace std; using namespace NLMISC; @@ -137,7 +138,7 @@ void CSound::serial(NLMISC::IStream &s) s.serial(name); } - nlassert(CAudioMixerUser::instance()); // not sure + nlassert(CGroupControllerRoot::getInstance()); // not sure #if NLSOUND_SHEET_VERSION_BUILT < 2 if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER)); #else @@ -245,12 +246,13 @@ void CSound::importForm(const std::string& filename, NLGEORGES::UFormElm& roo _Priority = MidPri; } + nlassert(CGroupControllerRoot::getInstance()); // not sure #if NLSOUND_SHEET_VERSION_BUILT < 2 - _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER)); + _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER); #else std::string groupControllerPath; root.getValueByName(groupControllerPath, ".GroupControllerPath"); - _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(groupControllerPath)); + _GroupController = CGroupControllerRoot::getInstance()->getGroupController(groupControllerPath); #endif } diff --git a/code/nel/src/sound/source_common.cpp b/code/nel/src/sound/source_common.cpp index 3af1eb6ae..7b27deb03 100644 --- a/code/nel/src/sound/source_common.cpp +++ b/code/nel/src/sound/source_common.cpp @@ -45,7 +45,7 @@ CSourceCommon::CSourceCommon(TSoundId id, bool spawn, TSpawnEndCallback cb, void _GroupController(groupController ? groupController : id->getGroupController()) { CAudioMixerUser::instance()->addSource(this); - groupController->addSource(this); + _GroupController->addSource(this); // get a local copy of the sound parameter _InitialGain = _Gain = id->getGain(); diff --git a/code/nel/src/sound/stream_sound.cpp b/code/nel/src/sound/stream_sound.cpp index 7addbbe0a..7552e79e5 100644 --- a/code/nel/src/sound/stream_sound.cpp +++ b/code/nel/src/sound/stream_sound.cpp @@ -18,7 +18,7 @@ #include "nel/sound/stream_sound.h" #if NLSOUND_SHEET_VERSION_BUILT < 2 -# include "nel/sound/audio_mixer_user.h" +# include "nel/sound/group_controller_root.h" #endif namespace NLSOUND { @@ -57,7 +57,7 @@ void CStreamSound::importForm(const std::string &filename, NLGEORGES::UFormElm & root.getValueByName(m_Alpha, ".SoundType.Alpha"); #if NLSOUND_SHEET_VERSION_BUILT < 2 - _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER)); + _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER); #endif } @@ -70,7 +70,7 @@ void CStreamSound::serial(NLMISC::IStream &s) s.serial(m_Alpha); #if NLSOUND_SHEET_VERSION_BUILT < 2 - if (s.isReading()) _GroupController = static_cast(CAudioMixerUser::instance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER)); + if (s.isReading()) _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER); #endif } From b15e014a9d988ee93edf1a6f84535cea3d6d78f3 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 10 Apr 2012 20:40:24 +0200 Subject: [PATCH 34/77] Changed: #795 Move vorbis dependency to nlsound and group project files --HG-- branch : sound_dev --- code/nel/src/sound/CMakeLists.txt | 84 ++++++++++++++++++++++++ code/nel/src/sound/driver/CMakeLists.txt | 9 --- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index 21c1de1f8..efeb15fee 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -1,8 +1,92 @@ + FILE(GLOB SRC *.cpp *.h) FILE(GLOB HEADERS ../../include/nel/sound/*.h) + +FILE(GLOB ANIMATION + sound_anim_manager.cpp ../../include/nel/sound/sound_anim_manager.h + sound_anim_marker.cpp ../../include/nel/sound/sound_anim_marker.h + sound_animation.cpp ../../include/nel/sound/sound_animation.h +) + +FILE(GLOB BACKGROUND_SOUND + background_sound.cpp ../../include/nel/sound/background_sound.h + background_sound_manager.cpp ../../include/nel/sound/background_sound_manager.h + background_source.cpp ../../include/nel/sound/background_source.h + clustered_sound.cpp ../../include/nel/sound/clustered_sound.h + context_sound.cpp ../../include/nel/sound/context_sound.h +) + +FILE(GLOB BANKS + async_file_manager_sound.cpp ../../include/nel/sound/async_file_manager_sound.h + sample_bank.cpp ../../include/nel/sound/sample_bank.h + sample_bank_manager.cpp ../../include/nel/sound/sample_bank_manager.h + sound_bank.cpp ../../include/nel/sound/sound_bank.h +) + +FILE(GLOB MIXER + audio_mixer_user.cpp ../../include/nel/sound/audio_mixer_user.h + ../../include/nel/sound/containers.h + group_controller.cpp ../../include/nel/sound/group_controller.h + group_controller_root.cpp ../../include/nel/sound/group_controller_root.h + listener_user.cpp ../../include/nel/sound/listener_user.h + mixing_track.cpp ../../include/nel/sound/mixing_track.h +) + +FILE(GLOB MUSIC + music_channel_fader.cpp ../../include/nel/sound/music_channel_fader.h + music_sound.cpp ../../include/nel/sound/music_sound.h + music_sound_manager.cpp ../../include/nel/sound/music_sound_manager.h + music_source.cpp ../../include/nel/sound/music_source.h +) + +FILE(GLOB SOUND + complex_sound.cpp ../../include/nel/sound/complex_sound.h + complex_source.cpp ../../include/nel/sound/complex_source.h + simple_sound.cpp ../../include/nel/sound/simple_sound.h + simple_source.cpp ../../include/nel/sound/simple_source.h + sound.cpp ../../include/nel/sound/sound.h + ../../include/nel/sound/sound_pattern.h + source_common.cpp ../../include/nel/sound/source_common.h +) + +FILE(GLOB STREAM + stream_sound.cpp ../../include/nel/sound/stream_sound.h + stream_source.cpp ../../include/nel/sound/stream_source.h +) + +FILE(GLOB USER_CLASSES + ../../include/nel/sound/u_audio_mixer.h + ../../include/nel/sound/u_group_controller.h + ../../include/nel/sound/u_listener.h + ../../include/nel/sound/u_source.h + ../../include/nel/sound/u_stream_source.h +) + +SOURCE_GROUP("" FILES ${SRC} ${HEADERS}) +SOURCE_GROUP("animation" FILES ${ANIMATION}) +SOURCE_GROUP("background_sound" FILES ${BACKGROUND_SOUND}) +SOURCE_GROUP("banks" FILES ${BANKS}) +SOURCE_GROUP("mixer" FILES ${MIXER}) +SOURCE_GROUP("music_deprecated" FILES ${MUSIC}) +SOURCE_GROUP("sound" FILES ${SOUND}) +SOURCE_GROUP("stream" FILES ${STREAM}) +SOURCE_GROUP("user_classes" FILES ${USER_CLASSES}) + + NL_TARGET_LIB(nelsound ${HEADERS} ${SRC}) + +INCLUDE_DIRECTORIES(${VORBIS_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(nelsound ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}) + +IF(WITH_STATIC) + # Add libogg dependency only if target is static because to libvorbisfile + TARGET_LINK_LIBRARIES(nelsound ${OGG_LIBRARY}) +ENDIF(WITH_STATIC) + + INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(nelsound ${LIBXML2_LIBRARIES} nelmisc nelligo nelgeorges nel3d nelsnd_lowlevel) diff --git a/code/nel/src/sound/driver/CMakeLists.txt b/code/nel/src/sound/driver/CMakeLists.txt index 1a8391c41..d1d82169c 100644 --- a/code/nel/src/sound/driver/CMakeLists.txt +++ b/code/nel/src/sound/driver/CMakeLists.txt @@ -3,15 +3,6 @@ FILE(GLOB HEADERS ../../../include/nel/sound/driver/*.h) NL_TARGET_LIB(nelsnd_lowlevel ${HEADERS} ${SRC}) -INCLUDE_DIRECTORIES(${VORBIS_INCLUDE_DIR}) - -TARGET_LINK_LIBRARIES(nelsnd_lowlevel nelmisc ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}) - -IF(WITH_STATIC) - # Add libogg dependency only if target is static because to libvorbisfile - TARGET_LINK_LIBRARIES(nelsnd_lowlevel ${OGG_LIBRARY}) -ENDIF(WITH_STATIC) - SET_TARGET_PROPERTIES(nelsnd_lowlevel PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nelsnd_lowlevel "NeL, Library: Sound Lowlevel") NL_ADD_RUNTIME_FLAGS(nelsnd_lowlevel) From 7a95fae1f9e303cc3148ae0d748d6e738481bf43 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 00:48:47 +0200 Subject: [PATCH 35/77] Changed: Improve performance of ryzom client CSoundManager. Strange code, though. --HG-- branch : sound_dev --- code/nel/include/nel/misc/fast_id_map.h | 151 +++++++++++++++++++ code/nel/src/misc/fast_id_map.cpp | 44 ++++++ code/ryzom/client/src/sound_manager.cpp | 189 ++++++++---------------- code/ryzom/client/src/sound_manager.h | 35 +++-- 4 files changed, 276 insertions(+), 143 deletions(-) create mode 100644 code/nel/include/nel/misc/fast_id_map.h create mode 100644 code/nel/src/misc/fast_id_map.cpp diff --git a/code/nel/include/nel/misc/fast_id_map.h b/code/nel/include/nel/misc/fast_id_map.h new file mode 100644 index 000000000..dbd05bc76 --- /dev/null +++ b/code/nel/include/nel/misc/fast_id_map.h @@ -0,0 +1,151 @@ +/** + * \file fast_id_map.h + * \brief CFastIdMap + * \date 2012-04-10 19:28GMT + * \author Jan Boon (Kaetemi) + * CFastIdMap + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLMISC_FAST_ID_MAP_H +#define NLMISC_FAST_ID_MAP_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +namespace NLMISC { + +/** + * \brief CFastIdMap + * \date 2012-04-10 19:28GMT + * \author Jan Boon (Kaetemi) + * This template allows for assigning unique uint32 identifiers to pointers. + * Useful when externally only exposing an identifier, when pointers may have been deleted. + * The identifier is made from two uint16's, one being the direct index in the identifier vector, + * and the other being a verification value that is increased when the identifier index is re-used. + * TId must be a typedef of uint32. + * TValue should be a pointer. + */ +template +class CFastIdMap +{ +protected: + struct CIdInfo + { + CIdInfo() { } + CIdInfo(uint16 verification, uint16 next, TValue value) : + Verification(verification), Next(next), Value(value) { } + uint16 Verification; + uint16 Next; + TValue Value; + }; + /// ID memory + std::vector m_Ids; + /// Nb of assigned IDs + uint m_Size; + /// Assigned IDs + uint16 m_Next; + +public: + CFastIdMap(TValue defaultValue) : m_Size(0), m_Next(0) + { + // Id 0 will contain the last available unused id, and be 0 if no more unused id's are available + // defaultValue will be returned when the ID is not found + m_Ids.push_back(CIdInfo(0, 0, defaultValue)); + } + + virtual ~CFastIdMap() { } + + void clear() + { + m_Ids.resize(1); + m_Ids[0].Next = 0; + } + + TId insert(TValue value) + { + // get next unused index + uint16 idx = m_Ids[0].Next; + if (idx == 0) + { + // size of used elements must be equal to the vector size minus one, when everything is allocated + nlassert((m_Ids.size() - 1) == m_Size); + + idx = m_Ids.size(); + uint16 verification = rand(); + m_Ids.push_back(CIdInfo(verification, m_Next, value)); + m_Next = idx; + return (TId)(((uint32)verification) << 16) & idx; + } + else + { + m_Ids[0].Next = m_Ids[idx].Next; // restore the last unused id + m_Ids[idx].Value = value; + return (TId)(((uint32)m_Ids[idx].Verification) << 16) & idx; + } + } + + void erase(TId id) + { + uint32 idx = ((uint32)id) & 0xFFFF; + uint16 verification = (uint16)(((uint32)id) >> 16); + if (m_Ids[idx].Verification == verification) + { + m_Ids[idx].Value = m_Ids[0].Value; // clean value for safety + m_Ids[idx].Verification = (uint16)(((uint32)m_Ids[idx].Verification + 1) & 0xFFFF); // change verification value, allow overflow :) + m_Ids[idx].Next = m_Ids[0].Next; // store the last unused id + m_Ids[0].Next = (uint16)idx; // set this as last unused id + } + else + { + nlwarning("Invalid ID"); + } + } + + TValue get(TId id) + { + uint32 idx = ((uint32)id) & 0xFFFF; + uint16 verification = (uint16)(((uint32)id) >> 16); + if (m_Ids[idx].Verification == verification) + { + return m_Ids[idx].Value; + } + else + { + nldebug("Invalid ID"); + return m_Ids[0].Value; + } + } + + inline uint size() { return m_Size; } + +}; /* class CFastIdMap */ + +} /* namespace NLMISC */ + +#endif /* #ifndef NLMISC_FAST_ID_MAP_H */ + +/* end of file */ diff --git a/code/nel/src/misc/fast_id_map.cpp b/code/nel/src/misc/fast_id_map.cpp new file mode 100644 index 000000000..f32d6edd8 --- /dev/null +++ b/code/nel/src/misc/fast_id_map.cpp @@ -0,0 +1,44 @@ +/** + * \file fast_id_map.cpp + * \brief CFastIdMap + * \date 2012-04-10 19:28GMT + * \author Jan Boon (Kaetemi) + * CFastIdMap + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +namespace NLMISC { + +void dummytoavoidthecompilerwarningfastidmap() { } + +} /* namespace NLMISC */ + +/* end of file */ diff --git a/code/ryzom/client/src/sound_manager.cpp b/code/ryzom/client/src/sound_manager.cpp index fb4b3708f..b15a5269b 100644 --- a/code/ryzom/client/src/sound_manager.cpp +++ b/code/ryzom/client/src/sound_manager.cpp @@ -106,9 +106,10 @@ enum TFilterMapping // constructor //----------------------------------------------- CSoundManager::CSoundManager(IProgressCallback * /* progressCallBack */) -: _AudioMixer(NULL), - _EnvSoundRoot(NULL), - _UserEntitySoundLevel(1.0f) +: _AudioMixer(NULL), + _EnvSoundRoot(NULL), + _UserEntitySoundLevel(1.0f), + _Sources(NULL) { _EnableBackgroundMusicAtTime= 0; _GameMusicVolume= 1.f; @@ -421,7 +422,6 @@ void CSoundManager::reset () //--------------------------------------------------- void CSoundManager::init(IProgressCallback *progressCallBack) { - _NextId = 1; _EnvSoundRoot = NULL; _PlaySound = true; @@ -612,7 +612,7 @@ void CSoundManager::init(IProgressCallback *progressCallBack) // add a new source to the world, attached to the specified entity // return 0 if creation failed, sound id if creation was successful //----------------------------------------------- -uint32 CSoundManager::addSource( const NLMISC::TStringId &soundName, const NLMISC::CVector &position, bool play, bool loop, const CEntityId &id) +CSoundManager::TSourceId CSoundManager::addSource( const NLMISC::TStringId &soundName, const NLMISC::CVector &position, bool play, bool loop, const CEntityId &id) { uint32 retValue = 0; @@ -642,22 +642,16 @@ uint32 CSoundManager::addSource( const NLMISC::TStringId &soundName, const NLMIS pSource->play(); } + TSourceId sourceId = _Sources.insert(pSource); + // attach the source to the entity, if specified if (id != CEntityId::Unknown ) { - _AttachedSources.insert( TMultiMapEntityToSource::value_type( id, pSource ) ); + _AttachedSources.insert( TMultiMapEntityToSource::value_type( id, sourceId ) ); } - // set source id - retValue = _NextId; - - // add the new source - _Sources.insert( TMapIdToSource::value_type( _NextId, pSource ) ); - - ++_NextId; - // return the id of the source - return retValue; + return sourceId; } // addSource // @@ -726,24 +720,20 @@ bool CSoundManager::spawnSource(const NLMISC::TStringId &soundName, const NLMISC // removeSource: // remove a source //--------------------------------------------------- -void CSoundManager::removeSource( uint32 sourceId ) +void CSoundManager::removeSource(CSoundManager::TSourceId sourceId) { nldebug("remove the source : %d", sourceId); /// \todo Malkav : optimize speed nldebug("nb sources = %d", _Sources.size() ); - TMapIdToSource::iterator itS = _Sources.find( sourceId ); - if (itS != _Sources.end() ) + USource *pSource = _Sources.get(sourceId); + if (pSource) { - USource *pSource = (*itS).second; - if ( pSource == NULL ) - return; - TMultiMapEntityToSource::iterator it = _AttachedSources.begin();//, itOld; for ( ; it != _AttachedSources.end() ; ++it) { - if ( (*it).second == pSource ) + if ( (*it).second == sourceId ) { (*it).second = NULL; // itOld = it; @@ -759,8 +749,9 @@ nldebug("nb sources = %d", _Sources.size() ); } // delete the source -// _AudioMixer->removeSource (pSource); delete pSource; + // i think there was something going on here + _Sources.erase(sourceId); } } // removeSource // @@ -789,7 +780,7 @@ void CSoundManager::updateEntityPos( const CEntityId &id, const NLMISC::CVector for ( it = range.first; it != range.second ; ++it) { - (*it).second->setPos( pos ); + _Sources.get((*it).second)->setPos( pos ); } } // updateEntityPos // @@ -805,7 +796,7 @@ void CSoundManager::updateEntityVelocity( const CEntityId &id, const NLMISC::CVe for ( it = range.first; it != range.second ; ++it) { - (*it).second->setVelocity( velocity ); + _Sources.get((*it).second)->setVelocity( velocity ); } } // updateEntityVelocity // @@ -822,7 +813,7 @@ void CSoundManager::updateEntityDirection( const CEntityId &id, const NLMISC::CV for ( it = range.first; it != range.second ; ++it) { - (*it).second->setDirection( dir ); + _Sources.get((*it).second)->setDirection( dir ); } } // updateEntityOrientation // @@ -837,26 +828,15 @@ void CSoundManager::removeEntity( const CEntityId &id) TMultiMapEntityToSource::iterator it; const std::pair range = _AttachedSources.equal_range( id ); - - USource *pSource; + for ( it = range.first; it != range.second ; ++it) { - pSource = (*it).second; - if ( pSource != NULL) + TSourceId sourceId = (*it).second; + if (sourceId) { - TMapIdToSource::iterator itS = _Sources.begin();//, itOld; - - for ( ; itS != _Sources.end() ; ++itS) - { - if ( (*itS).second == pSource ) - { - (*itS).second = NULL; - _Sources.erase( itS ); - break; - } - } - // delete the source - delete (*it).second; + USource *pSource = _Sources.get(sourceId); + delete pSource; + _Sources.erase(sourceId); } } @@ -869,34 +849,24 @@ void CSoundManager::removeEntity( const CEntityId &id) //--------------------------------------------------- // setSoundPosition : //--------------------------------------------------- -void CSoundManager::setSoundPosition( uint32 soundId, const NLMISC::CVector &position) +void CSoundManager::setSoundPosition(TSourceId sourceId, const NLMISC::CVector &position) { if (!_PlaySound) return; - TMapIdToSource::iterator it = _Sources.find( soundId ); - if (it != _Sources.end() ) - { - nlassert( (*it).second ); - - (*it).second->setPos( position ); - } + USource *pSource = _Sources.get(sourceId); + if (pSource) pSource->setPos(position); } // setSoundPosition // //--------------------------------------------------- // loopSound : //--------------------------------------------------- -void CSoundManager::loopSound( uint32 soundId, bool loop) +void CSoundManager::loopSound(TSourceId sourceId, bool loop) { if (!_PlaySound) return; - TMapIdToSource::iterator it = _Sources.find( soundId ); - if (it != _Sources.end() ) - { - nlassert( (*it).second ); - - (*it).second->setLooping( loop ); - } + USource *pSource = _Sources.get(sourceId); + if (pSource) pSource->setLooping(loop); } // loopSound // @@ -904,19 +874,17 @@ void CSoundManager::loopSound( uint32 soundId, bool loop) // playSound : // start or stop playing sound //--------------------------------------------------- -void CSoundManager::playSound( uint32 soundId, bool play) +void CSoundManager::playSound(TSourceId sourceId, bool play) { if (!_PlaySound) return; - TMapIdToSource::iterator it = _Sources.find( soundId ); - if (it != _Sources.end() ) + USource *pSource = _Sources.get(sourceId); + if (pSource) { - nlassert( (*it).second ); - if (play) - (*it).second->play(); + pSource->play(); else - (*it).second->stop(); + pSource->stop(); } } // loopSound // @@ -926,16 +894,10 @@ void CSoundManager::playSound( uint32 soundId, bool play) // isPlaying : // return true if the source is playing //--------------------------------------------------- -bool CSoundManager::isPlaying( uint32 sourceId ) +bool CSoundManager::isPlaying(TSourceId sourceId) { - TMapIdToSource::iterator it = _Sources.find( sourceId ); - if (it != _Sources.end() ) - { - nlassert( (*it).second ); - - return ( (*it).second->isPlaying() ); - } - + USource *pSource = _Sources.get(sourceId); + if (pSource) return pSource->isPlaying(); return false; } // isPlaying // @@ -999,16 +961,10 @@ bool CSoundManager::setSoundForSource( uint32 sourceId, TSound sound, const CVec // setSourceGain : // set the gain of the specified source //--------------------------------------------------- -void CSoundManager::setSourceGain( uint32 sourceId, float gain) +void CSoundManager::setSourceGain(TSourceId sourceId, float gain) { - TMapIdToSource::const_iterator it = _Sources.find( sourceId ); - if (it != _Sources.end() ) - { - USource *pSource = (*it).second; - nlassert( pSource ); - - pSource->setGain( gain ); - } + USource *pSource = _Sources.get(sourceId); + if (pSource) pSource->setGain( gain ); } // setSourceGain // @@ -1016,17 +972,10 @@ void CSoundManager::setSourceGain( uint32 sourceId, float gain) // getSourceGain : // get the gain of the specified source (-1 if source not found) //--------------------------------------------------- -float CSoundManager::getSourceGain( uint32 sourceId ) +float CSoundManager::getSourceGain(TSourceId sourceId) { - TMapIdToSource::const_iterator it = _Sources.find( sourceId ); - if (it != _Sources.end() ) - { - USource *pSource = (*it).second; - nlassert( pSource ); - - return ( pSource->getGain() ); - } - + USource *pSource = _Sources.get(sourceId); + if (pSource) return pSource->getGain(); return -1; } // getSourceGain // @@ -1035,16 +984,10 @@ float CSoundManager::getSourceGain( uint32 sourceId ) // setSourcePitch : // set the Pitch of the specified source //--------------------------------------------------- -void CSoundManager::setSourcePitch( uint32 sourceId, float Pitch) +void CSoundManager::setSourcePitch(TSourceId sourceId, float Pitch) { - TMapIdToSource::const_iterator it = _Sources.find( sourceId ); - if (it != _Sources.end() ) - { - USource *pSource = (*it).second; - nlassert( pSource ); - - pSource->setPitch( Pitch ); - } + USource *pSource = _Sources.get(sourceId); + if (pSource) pSource->setPitch(Pitch); } // setSourcePitch // @@ -1052,17 +995,10 @@ void CSoundManager::setSourcePitch( uint32 sourceId, float Pitch) // getSourcePitch : // get the Pitch of the specified source (-1 if source not found) //--------------------------------------------------- -float CSoundManager::getSourcePitch( uint32 sourceId ) +float CSoundManager::getSourcePitch(TSourceId sourceId) { - TMapIdToSource::const_iterator it = _Sources.find( sourceId ); - if (it != _Sources.end() ) - { - USource *pSource = (*it).second; - nlassert( pSource ); - - return ( pSource->getPitch() ); - } - + USource *pSource = _Sources.get(sourceId); + if (pSource) return pSource->getPitch(); return -1; } // getSourcePitch // @@ -1204,23 +1140,26 @@ void CSoundManager::playPositionedSounds( const CVector& /* pos */ ) list::iterator itPSnd; for( itPSnd = _PositionedSounds.begin(); itPSnd != _PositionedSounds.end(); ++itPSnd ) { - TMapIdToSource::const_iterator itSrc = _Sources.find( *itPSnd ); - if( itSrc == _Sources.end() ) + USource *pSource = _Sources.get(*itPSnd); + if (!pSource) { nlwarning(" : The source %d is unknown",*itPSnd); } - /* - if( (pos - (*itSrc).second.getPos()).norm() < ...) + else { - if( (*itSrc).second.pSource->isPlaying() == false ) + /* + if( (pos - (*itSrc).second.getPos()).norm() < ...) { - (*itSrc).second.pSource->play(); + if( (*itSrc).second.pSource->isPlaying() == false ) + { + (*itSrc).second.pSource->play(); + } + } + */ + if (!pSource->isPlaying()) + { + pSource->play(); } - } - */ - if( (*itSrc).second->isPlaying() == false ) - { - (*itSrc).second->play(); } } diff --git a/code/ryzom/client/src/sound_manager.h b/code/ryzom/client/src/sound_manager.h index 629d9a957..260ab6e81 100644 --- a/code/ryzom/client/src/sound_manager.h +++ b/code/ryzom/client/src/sound_manager.h @@ -26,6 +26,7 @@ #include "nel/misc/types_nl.h" #include "nel/misc/vector.h" #include "nel/misc/config_file.h" +#include "nel/misc/fast_id_map.h" // game_share #include "nel/misc/entity_id.h" // sound @@ -45,8 +46,6 @@ namespace NLMISC class IProgressCallback; } - - /** * class managing all the sounds for the client * \author David Fleury @@ -55,9 +54,12 @@ namespace NLMISC */ class CSoundManager { +public: + typedef uint32 TSourceId; - typedef CHashMultiMap TMultiMapEntityToSource; - typedef std::map TMapIdToSource; +private: + typedef CHashMultiMap TMultiMapEntityToSource; + typedef NLMISC::CFastIdMap TMapIdToSource; /// Load the properties for this sound and aplly them. void loadProperties(const string &soundName, USource *source); @@ -86,7 +88,7 @@ public: /// Return the audio mixer instance pointer. NLSOUND::UAudioMixer *getMixer(); - uint32 addSource( const NLMISC::TStringId &soundName, const NLMISC::CVector &position, bool play = true , bool loop = false, const NLMISC::CEntityId &id = NLMISC::CEntityId::Unknown ); + TSourceId addSource( const NLMISC::TStringId &soundName, const NLMISC::CVector &position, bool play = true , bool loop = false, const NLMISC::CEntityId &id = NLMISC::CEntityId::Unknown ); /// spawn a new source to the world but sound manager don't keep any link and the sound will be automatically deleted when finnished bool spawnSource (const NLMISC::TStringId &soundName, NLSOUND::CSoundContext &context); @@ -98,7 +100,7 @@ public: * remove a source * \param uint32 source id */ - void removeSource( uint32 sourceId ); + void removeSource( TSourceId sourceId ); /** @@ -190,28 +192,28 @@ public: * \param uint32 source id * \param CVector& new position */ - void setSoundPosition( uint32 sourceId, const NLMISC::CVector &position); + void setSoundPosition( TSourceId sourceId, const NLMISC::CVector &position); /** * loop a sound (or stop looping) * \param uint32 source id * \param bool loop (true = loop) */ - void loopSound( uint32 sourceId, bool loop); + void loopSound( TSourceId sourceId, bool loop); /** * play or stop a sound * \param uint32 source id * \param bool play (true = play, false = stop) */ - void playSound( uint32 sourceId, bool play); + void playSound( TSourceId sourceId, bool play); /** * test whether the sepcified source is playing or not * \param uint32 source id * \return bool true if the source is playing */ - bool isPlaying( uint32 sourceId ); + bool isPlaying( TSourceId sourceId ); /** * select the env effect corresponding to tag @@ -236,14 +238,14 @@ public: * \param uint32 sourceId * \param float new gain (0-1) */ - void setSourceGain( uint32 sourceId, float gain); + void setSourceGain( TSourceId sourceId, float gain); /** * get source Gain * \param uint32 sourceId * \return float new gain (0-1) (-1 if source not found) */ - float getSourceGain( uint32 sourceId ); + float getSourceGain( TSourceId sourceId ); /** * set source Pitch @@ -251,14 +253,14 @@ public: * \param uint32 sourceId * \param float new Pitch (0-1) */ - void setSourcePitch( uint32 sourceId, float gain); + void setSourcePitch( TSourceId sourceId, float gain); /** * get source Pitch * \param uint32 sourceId * \return float new Pitch (0-1) (>0) (-1 if source not found) */ - float getSourcePitch( uint32 sourceId ); + float getSourcePitch( TSourceId sourceId ); /** * Play all the positioned sounds which are near the given position @@ -346,10 +348,7 @@ private: //CStepSounds _StepSounds; /// list of positioned sounds - std::list _PositionedSounds; - - /// the next value that will be used as id for the next sound to be created - uint32 _NextId; + std::list _PositionedSounds; /// Gain value for user entity sound. float _UserEntitySoundLevel; From 18f75a4bd0896b8b592364abdae98b160f0aa629 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:24:48 +0200 Subject: [PATCH 36/77] Changed: #1459 Use effects group controller for ryzom client effects volume control --HG-- branch : sound_dev --- code/nel/include/nel/sound/group_controller.h | 2 ++ code/nel/include/nel/sound/u_group_controller.h | 2 ++ code/ryzom/client/src/sound_manager.cpp | 13 ++++++++++--- code/ryzom/client/src/sound_manager.h | 3 +++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/sound/group_controller.h b/code/nel/include/nel/sound/group_controller.h index d93fb8683..c3ac4838f 100644 --- a/code/nel/include/nel/sound/group_controller.h +++ b/code/nel/include/nel/sound/group_controller.h @@ -75,6 +75,8 @@ public: virtual void setUserGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); m_UserGain = gain; updateSourceGain(); } virtual float getUserGain() { return m_UserGain; } + + virtual void setGain(float devGain, float userGain) { NLMISC::clamp(devGain, 0.0f, 1.0f); NLMISC::clamp(userGain, 0.0f, 1.0f); m_DevGain = devGain; m_UserGain = userGain; updateSourceGain(); } //@} inline float getFinalGain() const { return m_FinalGain; } diff --git a/code/nel/include/nel/sound/u_group_controller.h b/code/nel/include/nel/sound/u_group_controller.h index 533f29936..54a28b835 100644 --- a/code/nel/include/nel/sound/u_group_controller.h +++ b/code/nel/include/nel/sound/u_group_controller.h @@ -56,6 +56,8 @@ public: virtual void setUserGain(float gain) = 0; virtual float getUserGain() = 0; + virtual void setGain(float devGain, float userGain) = 0; + protected: virtual ~UGroupController() { } diff --git a/code/ryzom/client/src/sound_manager.cpp b/code/ryzom/client/src/sound_manager.cpp index b15a5269b..a82e4b422 100644 --- a/code/ryzom/client/src/sound_manager.cpp +++ b/code/ryzom/client/src/sound_manager.cpp @@ -138,9 +138,11 @@ CSoundManager::~CSoundManager() // detach the sound from the particule system NL3D::UParticleSystemSound::setPSSound(NULL); + _GroupControllerEffects = NULL; + // free the audio mixer (and delete all sources) - if (_AudioMixer) - delete _AudioMixer; + delete _AudioMixer; + _AudioMixer = NULL; // release sound anim properly releaseSoundAnim(); @@ -404,6 +406,8 @@ void CSoundManager::reset () NL3D::UParticleSystemSound::setPSSound(NULL); + _GroupControllerEffects = NULL; + delete _AudioMixer; _AudioMixer = NULL; @@ -477,6 +481,9 @@ void CSoundManager::init(IProgressCallback *progressCallBack) */ new CSoundAnimManager(_AudioMixer); + // get the controller group for effects + _GroupControllerEffects = _AudioMixer->getGroupController("effects"); + // restore the volume SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); @@ -1536,7 +1543,7 @@ void CSoundManager::updateVolume() _AudioMixer->setEventMusicVolume(_GameMusicVolume); // update sfx volume - _AudioMixer->getListener()->setGain(_SFXVolume*_FadeSFXVolume); + _GroupControllerEffects->setGain(_FadeSFXVolume, _SFXVolume); } } diff --git a/code/ryzom/client/src/sound_manager.h b/code/ryzom/client/src/sound_manager.h index 260ab6e81..e5db85648 100644 --- a/code/ryzom/client/src/sound_manager.h +++ b/code/ryzom/client/src/sound_manager.h @@ -332,6 +332,9 @@ private: /// Pointer on the audio mixer object NLSOUND::UAudioMixer *_AudioMixer; + /// The root effects group controller for volume settings + NLSOUND::UGroupController *_GroupControllerEffects; + /// Pointer on the root of the environmental sounds tree (if any) NLSOUND::UEnvSound *_EnvSoundRoot; From 1ab53c8bb2f2422cc2f8fdf0b75c857e288d50d0 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:39:21 +0200 Subject: [PATCH 37/77] Changed: Apply changes from CMusicBuffer to CAudioDecoder --HG-- branch : sound_dev --- .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 596d07721..13cf936ef 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -494,40 +494,59 @@ uint32 CAudioDecoderVorbis::getRequiredBytes() uint32 CAudioDecoderVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) { - int current_section = 0; // ??? + sint current_section = 0; // ??? if (_IsMusicEnded) return 0; nlassert(minimum <= maximum); // can't have this.. uint32 bytes_read = 0; +#ifdef NL_BIG_ENDIAN + sint endianness = 1; +#else + sint endianness = 0; +#endif do { // signed 16-bit or unsigned 8-bit little-endian samples - int br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, - 0, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. + sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, + endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. getBitsPerSample() == 8 ? 1 : 2, getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. ¤t_section); // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); - if (br <= 0) - { - if (br == 0) + if (br > 0) + { + bytes_read += (uint32)br; + } + else if (br == 0) // EOF + { + if (_Loop) { - if (_Loop) - { - ov_pcm_seek(&_OggVorbisFile, 0); - //_Stream->seek(0, NLMISC::IStream::begin); - } - else - { - _IsMusicEnded = true; - break; - } + ov_pcm_seek(&_OggVorbisFile, 0); + //_Stream->seek(0, NLMISC::IStream::begin); } - else + else { - nlwarning("ov_read: %i", br); + _IsMusicEnded = true; + break; + } + } + else + { + // error + switch(br) + { + case OV_HOLE: + nlwarning("ov_read returned OV_HOLE"); + break; + case OV_EINVAL: + nlwarning("ov_read returned OV_EINVAL"); + break; + case OV_EBADLINK: + nlwarning("ov_read returned OV_EBADLINK"); + break; + default: + nlwarning("ov_read returned %d", br); } } - else bytes_read += (uint32)br; } while (bytes_read < minimum); return bytes_read; } From 09717924cf46874cb827213733d3cb1dc823ff44 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:40:42 +0200 Subject: [PATCH 38/77] Removed: #795 CMusicBuffer from sound lowlevel --HG-- branch : sound_dev --- .../include/nel/sound/driver/music_buffer.h | 119 ---------- .../nel/sound/driver/music_buffer_vorbis.h | 100 -------- code/nel/src/sound/driver/music_buffer.cpp | 109 --------- .../src/sound/driver/music_buffer_vorbis.cpp | 221 ------------------ 4 files changed, 549 deletions(-) delete mode 100644 code/nel/include/nel/sound/driver/music_buffer.h delete mode 100644 code/nel/include/nel/sound/driver/music_buffer_vorbis.h delete mode 100644 code/nel/src/sound/driver/music_buffer.cpp delete mode 100644 code/nel/src/sound/driver/music_buffer_vorbis.cpp diff --git a/code/nel/include/nel/sound/driver/music_buffer.h b/code/nel/include/nel/sound/driver/music_buffer.h deleted file mode 100644 index 571e27a31..000000000 --- a/code/nel/include/nel/sound/driver/music_buffer.h +++ /dev/null @@ -1,119 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef NLSOUND_MUSIC_BUFFER_H -#define NLSOUND_MUSIC_BUFFER_H - -namespace NLMISC -{ - class IStream; - class CIFile; -} - -namespace NLSOUND -{ - - /* - * TODO: Streaming - * Some kind of decent streaming functionality, to get rid of the current music implementation. Audio decoding should be done on nlsound level. IBuffer needs a writable implementation, it allocates and owns the data memory, which can be written to by nlsound. When buffer is written, a function needs to be called to 'finalize' the buffer (so it can be submitted to OpenAL for example). - * Required interface functions, IBuffer: - * /// Allocate a new writable buffer. If this buffer was already allocated, the previous data is released. - * /// May return NULL if the format or frequency is not supported by the driver. - * uint8 *IBuffer::openWritable(uint size, TBufferFormat bufferFormat, uint8 channels, uint8 bitsPerSample, uint32 frequency); - * /// Tell that you are done writing to this buffer, so it can be copied over to hardware if needed. - * /// If keepLocal is true, a local copy of the buffer will be kept (so allocation can be re-used later). - * /// keepLocal overrides the OptionLocalBufferCopy flag. The buffer can use this function internally. - * void IBuffer::lockWritable(bool keepLocal); - * Required interface functions, ISource: - * /// Enable or disable the streaming facilities. - * void ISource::setStreaming(bool streaming); - * /// Submits a new buffer to the stream. A buffer of 100ms length is optimal for streaming. - * /// Should be called by a thread which checks countStreamingBuffers every 100ms - * void ISource::submitStreamingBuffer(IBuffer *buffer); - * /// Returns the number of buffers that are queued (includes playing buffer). 3 buffers is optimal. - * uint ISource::countStreamingBuffers(); - * Other required interface functions, ISource: - * /// Enable or disable 3d calculations (to send directly to speakers). - * void ISource::set3DMode(bool enable); - * For compatibility with music trough fmod, ISoundDriver: - * /// Returns true if the sound driver has a native implementation of IMusicChannel (bad!). - * /// If this returns false, use the nlsound music channel, which goes trough Ctrack/ISource, - * /// The nlsound music channel requires support for IBuffer/ISource streaming. - * bool ISoundDriver::hasMusicChannel(); - */ - -/** - * \brief IMusicBuffer - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * IMusicBuffer is only used by the driver implementation to stream - * music files into a readable format (it's a simple decoder interface). - * You should not call these functions (getSongTitle) on nlsound or user level, - * as a driver might have additional music types implemented. - * TODO: Change IMusicBuffer to IAudioDecoder, and change the interface to make more sense. - * TODO: Allow user application to register more decoders. - * TODO: Look into libavcodec for decoding audio. - */ -class IMusicBuffer -{ -private: - // pointers - /// Stream from file created by IMusicBuffer - NLMISC::IStream *_InternalStream; - -public: - IMusicBuffer(); - virtual ~IMusicBuffer(); - - /// Create a new music buffer, may return NULL if unknown type, destroy with delete. Filepath lookup done here. If async is true, it will stream from hd, else it will load in memory first. - static IMusicBuffer *createMusicBuffer(const std::string &filepath, bool async, bool loop); - - /// Create a new music buffer from a stream, type is file extension like "ogg" etc. - static IMusicBuffer *createMusicBuffer(const std::string &type, NLMISC::IStream *stream, bool loop); - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(const std::string &filepath, std::string &artist, std::string &title); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes() =0; - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) =0; - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels() =0; - - /// Get the samples per second (often 44100) in output. - virtual uint32 getSamplesPerSec() =0; - - /// Get the bits per sample (often 16) in output. - virtual uint8 getBitsPerSample() =0; - - /// Get if the music has ended playing (never true if loop). - virtual bool isMusicEnded() =0; - - /// Get the total time in seconds. - virtual float getLength() =0; - - /// Get the size of uncompressed data in bytes. - virtual uint getUncompressedSize() =0; -}; /* class IMusicBuffer */ - -} /* namespace NLSOUND */ - -#endif /* #ifndef NLSOUND_MUSIC_BUFFER_H */ - -/* end of file */ diff --git a/code/nel/include/nel/sound/driver/music_buffer_vorbis.h b/code/nel/include/nel/sound/driver/music_buffer_vorbis.h deleted file mode 100644 index 978f393da..000000000 --- a/code/nel/include/nel/sound/driver/music_buffer_vorbis.h +++ /dev/null @@ -1,100 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef NLSOUND_MUSIC_BUFFER_VORBIS_H -#define NLSOUND_MUSIC_BUFFER_VORBIS_H - -// STL includes - -// 3rd Party includes -#ifdef NL_OS_WINDOWS -# pragma warning( push ) -# pragma warning( disable : 4244 ) -#endif -#include -#ifdef NL_OS_WINDOWS -# pragma warning( pop ) -#endif - -// NeL includes - -// Project includes -#include "music_buffer.h" - -namespace NLSOUND -{ - -/** - * \brief CMusicBufferVorbis - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * CMusicBufferVorbis - * Create trough IMusicBuffer, type "ogg" - */ -class CMusicBufferVorbis : public IMusicBuffer -{ -protected: - // outside pointers - NLMISC::IStream *_Stream; - - // pointers - - // instances - OggVorbis_File _OggVorbisFile; - bool _Loop; - bool _IsMusicEnded; - sint32 _StreamOffset; - sint32 _StreamSize; -public: - CMusicBufferVorbis(NLMISC::IStream *stream, bool loop); - virtual ~CMusicBufferVorbis(); - inline NLMISC::IStream *getStream() { return _Stream; } - inline sint32 getStreamSize() { return _StreamSize; } - inline sint32 getStreamOffset() { return _StreamOffset; } - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes(); - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum); - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels(); - - /// Get the samples per second (often 44100) in output. - virtual uint32 getSamplesPerSec(); - - /// Get the bits per sample (often 16) in output. - virtual uint8 getBitsPerSample(); - - /// Get if the music has ended playing (never true if loop). - virtual bool isMusicEnded(); - - /// Get the total time in seconds. - virtual float getLength(); - - /// Get the size of uncompressed data in bytes. - virtual uint getUncompressedSize(); -}; /* class CMusicBufferVorbis */ - -} /* namespace NLSOUND */ - -#endif /* #ifndef NLSOUND_MUSIC_BUFFER_VORBIS_H */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/music_buffer.cpp b/code/nel/src/sound/driver/music_buffer.cpp deleted file mode 100644 index 6da9d91ec..000000000 --- a/code/nel/src/sound/driver/music_buffer.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdsound_lowlevel.h" - -#include "nel/sound/driver/music_buffer_vorbis.h" -#include "nel/sound/driver/music_buffer.h" - -using namespace std; -using namespace NLMISC; - -namespace NLSOUND -{ - -IMusicBuffer::IMusicBuffer() : _InternalStream(NULL) -{ - -} - -IMusicBuffer::~IMusicBuffer() -{ - if (_InternalStream) { delete _InternalStream; _InternalStream = NULL; } -} - -IMusicBuffer *IMusicBuffer::createMusicBuffer(const std::string &filepath, bool async, bool loop) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return NULL; - } - string type = CFile::getExtension(filepath); - - CIFile *ifile = new CIFile(); - ifile->setCacheFileOnOpen(!async); - ifile->allowBNPCacheFileOnOpen(!async); - ifile->open(lookup); - - IMusicBuffer *mb = createMusicBuffer(type, ifile, loop); - - if (mb) mb->_InternalStream = ifile; - else delete ifile; - - return mb; -} - -IMusicBuffer *IMusicBuffer::createMusicBuffer(const std::string &type, NLMISC::IStream *stream, bool loop) -{ - if (!stream) - { - nlwarning("Stream is NULL"); - return NULL; - } - string type_lower = toLower(type); - if (type_lower == "ogg") - { - return new CMusicBufferVorbis(stream, loop); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - return NULL; - } -} - -bool IMusicBuffer::getInfo(const std::string &filepath, std::string &artist, std::string &title) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return false; - } - string type = CFile::getExtension(filepath); - string type_lower = toLower(type); - - if (type_lower == "ogg") - { - CIFile ifile; - ifile.setCacheFileOnOpen(false); - ifile.allowBNPCacheFileOnOpen(false); - ifile.open(lookup); - return CMusicBufferVorbis::getInfo(&ifile, artist, title); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - artist.clear(); title.clear(); - return false; - } -} - -} /* namespace NLSOUND */ - -/* end of file */ diff --git a/code/nel/src/sound/driver/music_buffer_vorbis.cpp b/code/nel/src/sound/driver/music_buffer_vorbis.cpp deleted file mode 100644 index 9d5543c62..000000000 --- a/code/nel/src/sound/driver/music_buffer_vorbis.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdsound_lowlevel.h" - -// Project includes -#include "nel/sound/driver/music_buffer_vorbis.h" - -using namespace std; -using namespace NLMISC; - -namespace NLSOUND -{ - -size_t vorbisReadFunc(void *ptr, size_t size, size_t nmemb, void *datasource) -{ - CMusicBufferVorbis *music_buffer_vorbis = (CMusicBufferVorbis *)datasource; - NLMISC::IStream *stream = music_buffer_vorbis->getStream(); - nlassert(stream->isReading()); - sint32 length = (sint32)(size * nmemb); - if (length > music_buffer_vorbis->getStreamSize() - stream->getPos()) - length = music_buffer_vorbis->getStreamSize() - stream->getPos(); - stream->serialBuffer((uint8 *)ptr, length); - return length; -} - -int vorbisSeekFunc(void *datasource, ogg_int64_t offset, int whence) -{ - if (whence == SEEK_CUR && offset == 0) - { - // nlwarning(NLSOUND_XAUDIO2_PREFIX "This seek call doesn't do a damn thing, wtf."); - return 0; // ooookkaaaaaayyy - } - - CMusicBufferVorbis *music_buffer_vorbis = (CMusicBufferVorbis *)datasource; - - NLMISC::IStream::TSeekOrigin origin; - switch (whence) - { - case SEEK_SET: - origin = NLMISC::IStream::begin; - break; - case SEEK_CUR: - origin = NLMISC::IStream::current; - break; - case SEEK_END: - origin = NLMISC::IStream::end; - break; - default: - // nlwarning(NLSOUND_XAUDIO2_PREFIX "Seeking to fake origin."); - return -1; - } - - if (music_buffer_vorbis->getStream()->seek(SEEK_SET ? music_buffer_vorbis->getStreamOffset() + (sint32)offset : (sint32)offset, origin)) return 0; - else return -1; -} - -//int vorbisCloseFunc(void *datasource) -//{ -// //CMusicBufferVorbis *music_buffer_vorbis = (CMusicBufferVorbis *)datasource; -//} - -long vorbisTellFunc(void *datasource) -{ - CMusicBufferVorbis *music_buffer_vorbis = (CMusicBufferVorbis *)datasource; - return (long)(music_buffer_vorbis->getStream()->getPos() - music_buffer_vorbis->getStreamOffset()); -} - -static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { - (size_t (*)(void *, size_t, size_t, void *)) vorbisReadFunc, - (int (*)(void *, ogg_int64_t, int)) vorbisSeekFunc, - (int (*)(void *)) NULL, //vorbisCloseFunc, - (long (*)(void *)) vorbisTellFunc -}; - -CMusicBufferVorbis::CMusicBufferVorbis(NLMISC::IStream *stream, bool loop) -: _Stream(stream), _Loop(loop), _IsMusicEnded(false) -{ - _StreamOffset = stream->getPos(); - stream->seek(0, NLMISC::IStream::end); - _StreamSize = stream->getPos(); - stream->seek(_StreamOffset, NLMISC::IStream::begin); - ov_open_callbacks(this, &_OggVorbisFile, NULL, 0, OV_CALLBACKS_NLMISC_STREAM); -} - -CMusicBufferVorbis::~CMusicBufferVorbis() -{ - ov_clear(&_OggVorbisFile); -} - -/// Get information on a music file (only artist and title at the moment). -bool CMusicBufferVorbis::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title) -{ - CMusicBufferVorbis mbv(stream, false); // just opens and closes the oggvorbisfile thing :) - vorbis_comment *vc = ov_comment(&mbv._OggVorbisFile, -1); - char *title_c = vorbis_comment_query(vc, "title", 0); - if (title_c) title = title_c; else title.clear(); - char *artist_c = vorbis_comment_query(vc, "artist", 0); - if (artist_c) artist = artist_c; else artist.clear(); - return true; -} - -uint32 CMusicBufferVorbis::getRequiredBytes() -{ - return 0; // no minimum requirement of bytes to buffer out -} - -uint32 CMusicBufferVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) -{ - sint current_section = 0; // ??? - if (_IsMusicEnded) return 0; - nlassert(minimum <= maximum); // can't have this.. - uint32 bytes_read = 0; - -#ifdef NL_BIG_ENDIAN - sint endianness = 1; -#else - sint endianness = 0; -#endif - - do - { - // signed 16-bit or unsigned 8-bit little-endian samples - sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, - endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for big endian. Typical value is 0. - getBitsPerSample() == 8 ? 1 : 2, - getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. - ¤t_section); - // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); - if (br > 0) - { - bytes_read += (uint32)br; - } - else if (br == 0) // EOF - { - if (_Loop) - { - ov_pcm_seek(&_OggVorbisFile, 0); - //_Stream->seek(0, NLMISC::IStream::begin); - } - else - { - _IsMusicEnded = true; - break; - } - } - else - { - // error - switch(br) - { - case OV_HOLE: - nlwarning("ov_read returned OV_HOLE"); - break; - - case OV_EINVAL: - nlwarning("ov_read returned OV_EINVAL"); - break; - - case OV_EBADLINK: - nlwarning("ov_read returned OV_EBADLINK"); - break; - - default: - nlwarning("ov_read returned %d", br); - } - - return 0; - } - } while (bytes_read < minimum); - return bytes_read; -} - -uint8 CMusicBufferVorbis::getChannels() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return (uint8)vi->channels; -} - -uint32 CMusicBufferVorbis::getSamplesPerSec() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return vi->rate; -} - -uint8 CMusicBufferVorbis::getBitsPerSample() -{ - return 16; -} - -bool CMusicBufferVorbis::isMusicEnded() -{ - return _IsMusicEnded; -} - -float CMusicBufferVorbis::getLength() -{ - return (float)ov_time_total(&_OggVorbisFile, -1); -} - -uint CMusicBufferVorbis::getUncompressedSize() -{ - return (uint)ov_pcm_total(&_OggVorbisFile, -1) * (getBitsPerSample() / 2) * getChannels(); -} - -} /* namespace NLSOUND */ - -/* end of file */ From 1b88f2782b69ab8c08742761877b34771840d295 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:52:28 +0200 Subject: [PATCH 39/77] Moved: #795 CAudioDecoder from stream ogg vorbis sample to nlsound --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_decoder.h | 104 +++++ .../include/nel/sound/audio_decoder_vorbis.h | 108 +++++ .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 394 +----------------- code/nel/src/sound/CMakeLists.txt | 6 + code/nel/src/sound/audio_decoder.cpp | 139 ++++++ code/nel/src/sound/audio_decoder_vorbis.cpp | 224 ++++++++++ 6 files changed, 582 insertions(+), 393 deletions(-) create mode 100644 code/nel/include/nel/sound/audio_decoder.h create mode 100644 code/nel/include/nel/sound/audio_decoder_vorbis.h create mode 100644 code/nel/src/sound/audio_decoder.cpp create mode 100644 code/nel/src/sound/audio_decoder_vorbis.cpp diff --git a/code/nel/include/nel/sound/audio_decoder.h b/code/nel/include/nel/sound/audio_decoder.h new file mode 100644 index 000000000..bded79cfe --- /dev/null +++ b/code/nel/include/nel/sound/audio_decoder.h @@ -0,0 +1,104 @@ +/** + * \file audio_decoder.h + * \brief IAudioDecoder + * \date 2012-04-11 09:34GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_AUDIO_DECODER_H +#define NLSOUND_AUDIO_DECODER_H +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NLSOUND { + +/** + * \brief IAudioDecoder + * \date 2008-08-30 11:38GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder is only used by the driver implementation to stream + * music files into a readable format (it's a simple decoder interface). + * You should not call these functions (getSongTitle) on nlsound or user level, + * as a driver might have additional music types implemented. + * TODO: Split IAudioDecoder into IAudioDecoder (actual decoding) and IMediaDemuxer (stream splitter), and change the interface to make more sense. + * TODO: Allow user application to register more decoders. + * TODO: Look into libavcodec for decoding audio? + */ +class IAudioDecoder +{ +private: + // pointers + /// Stream from file created by IAudioDecoder + NLMISC::IStream *_InternalStream; + +public: + IAudioDecoder(); + virtual ~IAudioDecoder(); + + /// Create a new music buffer, may return NULL if unknown type, destroy with delete. Filepath lookup done here. If async is true, it will stream from hd, else it will load in memory first. + static IAudioDecoder *createAudioDecoder(const std::string &filepath, bool async, bool loop); + + /// Create a new music buffer from a stream, type is file extension like "ogg" etc. + static IAudioDecoder *createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop); + + /// Get information on a music file (only artist and title at the moment). + static bool getInfo(const std::string &filepath, std::string &artist, std::string &title); + + /// Get audio/container extensions that are currently supported by the nel sound library. + static void getMusicExtensions(std::vector &extensions); + + /// Return if a music extension is supported by the nel sound library. + static bool isMusicExtensionSupported(const std::string &extension); + + /// Get how many bytes the music buffer requires for output minimum. + virtual uint32 getRequiredBytes() = 0; + + /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). + virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) = 0; + + /// Get the amount of channels (2 is stereo) in output. + virtual uint8 getChannels() = 0; + + /// Get the samples per second (often 44100) in output. + virtual uint getSamplesPerSec() = 0; + + /// Get the bits per sample (often 16) in output. + virtual uint8 getBitsPerSample() = 0; + + /// Get if the music has ended playing (never true if loop). + virtual bool isMusicEnded() = 0; + + /// Get the total time in seconds. + virtual float getLength() = 0; +}; /* class IAudioDecoder */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_AUDIO_DECODER_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/audio_decoder_vorbis.h b/code/nel/include/nel/sound/audio_decoder_vorbis.h new file mode 100644 index 000000000..c65c4de82 --- /dev/null +++ b/code/nel/include/nel/sound/audio_decoder_vorbis.h @@ -0,0 +1,108 @@ +/** + * \file audio_decoder_vorbis.h + * \brief CAudioDecoderVorbis + * \date 2012-04-11 09:35GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_AUDIO_DECODER_VORBIS_H +#define NLSOUND_AUDIO_DECODER_VORBIS_H +#include + +// STL includes + +// 3rd Party includes +#ifdef NL_OS_WINDOWS +# pragma warning( push ) +# pragma warning( disable : 4244 ) +#endif +#include +#ifdef NL_OS_WINDOWS +# pragma warning( pop ) +#endif + +// NeL includes +#include + +// Project includes + +namespace NLSOUND { + +/** + * \brief CAudioDecoderVorbis + * \date 2008-08-30 11:38GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + * Create trough IAudioDecoder, type "ogg" + */ +class CAudioDecoderVorbis : public IAudioDecoder +{ +protected: + // outside pointers + NLMISC::IStream *_Stream; + + // pointers + + // instances + OggVorbis_File _OggVorbisFile; + bool _Loop; + bool _IsMusicEnded; + sint32 _StreamOffset; + sint32 _StreamSize; +public: + CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop); + virtual ~CAudioDecoderVorbis(); + inline NLMISC::IStream *getStream() { return _Stream; } + inline sint32 getStreamSize() { return _StreamSize; } + inline sint32 getStreamOffset() { return _StreamOffset; } + + /// Get information on a music file (only artist and title at the moment). + static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title); + + /// Get how many bytes the music buffer requires for output minimum. + virtual uint32 getRequiredBytes(); + + /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). + virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum); + + /// Get the amount of channels (2 is stereo) in output. + virtual uint8 getChannels(); + + /// Get the samples per second (often 44100) in output. + virtual uint getSamplesPerSec(); + + /// Get the bits per sample (often 16) in output. + virtual uint8 getBitsPerSample(); + + /// Get if the music has ended playing (never true if loop). + virtual bool isMusicEnded(); + + /// Get the total time in seconds. + virtual float getLength(); +}; /* class CAudioDecoderVorbis */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_AUDIO_DECODER_VORBIS_H */ + +/* end of file */ diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 13cf936ef..c82589471 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -20,16 +20,6 @@ #include #include -// 3rd Party includes -#ifdef NL_OS_WINDOWS -# pragma warning( push ) -# pragma warning( disable : 4244 ) -#endif -#include -#ifdef NL_OS_WINDOWS -# pragma warning( pop ) -#endif - // NeL includes #include #include @@ -41,6 +31,7 @@ #include #include #include +#include #include // Project includes @@ -57,272 +48,6 @@ using namespace NLSOUND; namespace NLSAMPLE { -/** - * \brief IAudioDecoder - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * IAudioDecoder is only used by the driver implementation to stream - * music files into a readable format (it's a simple decoder interface). - * You should not call these functions (getSongTitle) on nlsound or user level, - * as a driver might have additional music types implemented. - * TODO: Split IAudioDecoder into IAudioDecoder (actual decoding) and IMediaDemuxer (stream splitter), and change the interface to make more sense. - * TODO: Allow user application to register more decoders. - * TODO: Look into libavcodec for decoding audio? - */ -class IAudioDecoder -{ -private: - // pointers - /// Stream from file created by IAudioDecoder - NLMISC::IStream *_InternalStream; - -public: - IAudioDecoder(); - virtual ~IAudioDecoder(); - - /// Create a new music buffer, may return NULL if unknown type, destroy with delete. Filepath lookup done here. If async is true, it will stream from hd, else it will load in memory first. - static IAudioDecoder *createAudioDecoder(const std::string &filepath, bool async, bool loop); - - /// Create a new music buffer from a stream, type is file extension like "ogg" etc. - static IAudioDecoder *createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop); - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(const std::string &filepath, std::string &artist, std::string &title); - - /// Get audio/container extensions that are currently supported by the nel sound library. - static void getMusicExtensions(std::vector &extensions); - - /// Return if a music extension is supported by the nel sound library. - static bool isMusicExtensionSupported(const std::string &extension); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes() = 0; - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) = 0; - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels() = 0; - - /// Get the samples per second (often 44100) in output. - virtual uint getSamplesPerSec() = 0; - - /// Get the bits per sample (often 16) in output. - virtual uint8 getBitsPerSample() = 0; - - /// Get if the music has ended playing (never true if loop). - virtual bool isMusicEnded() = 0; - - /// Get the total time in seconds. - virtual float getLength() = 0; -}; /* class IAudioDecoder */ - -/** - * \brief CAudioDecoderVorbis - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * CAudioDecoderVorbis - * Create trough IAudioDecoder, type "ogg" - */ -class CAudioDecoderVorbis : public IAudioDecoder -{ -protected: - // outside pointers - NLMISC::IStream *_Stream; - - // pointers - - // instances - OggVorbis_File _OggVorbisFile; - bool _Loop; - bool _IsMusicEnded; - sint32 _StreamOffset; - sint32 _StreamSize; -public: - CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop); - virtual ~CAudioDecoderVorbis(); - inline NLMISC::IStream *getStream() { return _Stream; } - inline sint32 getStreamSize() { return _StreamSize; } - inline sint32 getStreamOffset() { return _StreamOffset; } - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes(); - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum); - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels(); - - /// Get the samples per second (often 44100) in output. - virtual uint getSamplesPerSec(); - - /// Get the bits per sample (often 16) in output. - virtual uint8 getBitsPerSample(); - - /// Get if the music has ended playing (never true if loop). - virtual bool isMusicEnded(); - - /// Get the total time in seconds. - virtual float getLength(); -}; /* class CAudioDecoderVorbis */ - -IAudioDecoder::IAudioDecoder() : _InternalStream(NULL) -{ - -} - -IAudioDecoder::~IAudioDecoder() -{ - if (_InternalStream) { delete _InternalStream; _InternalStream = NULL; } -} - -IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &filepath, bool async, bool loop) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return NULL; - } - string type = CFile::getExtension(filepath); - - CIFile *ifile = new CIFile(); - ifile->setCacheFileOnOpen(!async); - ifile->allowBNPCacheFileOnOpen(!async); - ifile->open(lookup); - - IAudioDecoder *mb = createAudioDecoder(type, ifile, loop); - - if (mb) mb->_InternalStream = ifile; - else delete ifile; - - return mb; -} - -IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop) -{ - if (!stream) - { - nlwarning("Stream is NULL"); - return NULL; - } - string type_lower = toLower(type); - if (type_lower == "ogg") - { - return new CAudioDecoderVorbis(stream, loop); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - return NULL; - } -} - -bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, std::string &title) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return false; - } - string type = CFile::getExtension(filepath); - string type_lower = toLower(type); - - if (type_lower == "ogg") - { - CIFile ifile; - ifile.setCacheFileOnOpen(false); - ifile.allowBNPCacheFileOnOpen(false); - ifile.open(lookup); - return CAudioDecoderVorbis::getInfo(&ifile, artist, title); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - artist.clear(); title.clear(); - return false; - } -} - -/// Get audio/container extensions that are currently supported by the nel sound library. -void IAudioDecoder::getMusicExtensions(std::vector &extensions) -{ - extensions.push_back("ogg"); - // extensions.push_back("wav"); // TODO: Easy. -} - -/// Return if a music extension is supported by the nel sound library. -bool IAudioDecoder::isMusicExtensionSupported(const std::string &extension) -{ - return (extension == "ogg"); -} - -size_t vorbisReadFunc(void *ptr, size_t size, size_t nmemb, void *datasource) -{ - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - NLMISC::IStream *stream = audio_decoder_vorbis->getStream(); - nlassert(stream->isReading()); - sint32 length = (sint32)(size * nmemb); - if (length > audio_decoder_vorbis->getStreamSize() - stream->getPos()) - length = audio_decoder_vorbis->getStreamSize() - stream->getPos(); - stream->serialBuffer((uint8 *)ptr, length); - return length; -} - -int vorbisSeekFunc(void *datasource, ogg_int64_t offset, int whence) -{ - if (whence == SEEK_CUR && offset == 0) - { - // nlwarning(NLSOUND_XAUDIO2_PREFIX "This seek call doesn't do a damn thing, wtf."); - return 0; // ooookkaaaaaayyy - } - - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - - NLMISC::IStream::TSeekOrigin origin; - switch (whence) - { - case SEEK_SET: - origin = NLMISC::IStream::begin; - break; - case SEEK_CUR: - origin = NLMISC::IStream::current; - break; - case SEEK_END: - origin = NLMISC::IStream::end; - break; - default: - // nlwarning(NLSOUND_XAUDIO2_PREFIX "Seeking to fake origin."); - return -1; - } - - if (audio_decoder_vorbis->getStream()->seek(SEEK_SET ? audio_decoder_vorbis->getStreamOffset() + (sint32)offset : (sint32)offset, origin)) return 0; - else return -1; -} - -//int vorbisCloseFunc(void *datasource) -//{ -// //CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; -//} - -long vorbisTellFunc(void *datasource) -{ - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - return (long)(audio_decoder_vorbis->getStream()->getPos() - audio_decoder_vorbis->getStreamOffset()); -} - -static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { - (size_t (*)(void *, size_t, size_t, void *)) vorbisReadFunc, - (int (*)(void *, ogg_int64_t, int)) vorbisSeekFunc, - (int (*)(void *)) NULL, //vorbisCloseFunc, - (long (*)(void *)) vorbisTellFunc -}; - static UAudioMixer *s_AudioMixer = NULL; static UStreamSource *s_StreamSource = NULL; static IAudioDecoder *s_AudioDecoder = NULL; @@ -460,123 +185,6 @@ static void releaseSample() delete s_AudioMixer; s_AudioMixer = NULL; } -CAudioDecoderVorbis::CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop) -: _Stream(stream), _Loop(loop), _StreamSize(0), _IsMusicEnded(false) -{ - _StreamOffset = stream->getPos(); - stream->seek(0, NLMISC::IStream::end); - _StreamSize = stream->getPos(); - stream->seek(_StreamOffset, NLMISC::IStream::begin); - ov_open_callbacks(this, &_OggVorbisFile, NULL, 0, OV_CALLBACKS_NLMISC_STREAM); -} - -CAudioDecoderVorbis::~CAudioDecoderVorbis() -{ - ov_clear(&_OggVorbisFile); -} - -/// Get information on a music file (only artist and title at the moment). -bool CAudioDecoderVorbis::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title) -{ - CAudioDecoderVorbis mbv(stream, false); // just opens and closes the oggvorbisfile thing :) - vorbis_comment *vc = ov_comment(&mbv._OggVorbisFile, -1); - char *title_c = vorbis_comment_query(vc, "title", 0); - if (title_c) title = title_c; else title.clear(); - char *artist_c = vorbis_comment_query(vc, "artist", 0); - if (artist_c) artist = artist_c; else artist.clear(); - return true; -} - -uint32 CAudioDecoderVorbis::getRequiredBytes() -{ - return 0; // no minimum requirement of bytes to buffer out -} - -uint32 CAudioDecoderVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) -{ - sint current_section = 0; // ??? - if (_IsMusicEnded) return 0; - nlassert(minimum <= maximum); // can't have this.. - uint32 bytes_read = 0; -#ifdef NL_BIG_ENDIAN - sint endianness = 1; -#else - sint endianness = 0; -#endif - do - { - // signed 16-bit or unsigned 8-bit little-endian samples - sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, - endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. - getBitsPerSample() == 8 ? 1 : 2, - getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. - ¤t_section); - // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); - if (br > 0) - { - bytes_read += (uint32)br; - } - else if (br == 0) // EOF - { - if (_Loop) - { - ov_pcm_seek(&_OggVorbisFile, 0); - //_Stream->seek(0, NLMISC::IStream::begin); - } - else - { - _IsMusicEnded = true; - break; - } - } - else - { - // error - switch(br) - { - case OV_HOLE: - nlwarning("ov_read returned OV_HOLE"); - break; - case OV_EINVAL: - nlwarning("ov_read returned OV_EINVAL"); - break; - case OV_EBADLINK: - nlwarning("ov_read returned OV_EBADLINK"); - break; - default: - nlwarning("ov_read returned %d", br); - } - } - } while (bytes_read < minimum); - return bytes_read; -} - -uint8 CAudioDecoderVorbis::getChannels() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return (uint8)vi->channels; -} - -uint CAudioDecoderVorbis::getSamplesPerSec() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return (uint)vi->rate; -} - -uint8 CAudioDecoderVorbis::getBitsPerSample() -{ - return 16; -} - -bool CAudioDecoderVorbis::isMusicEnded() -{ - return _IsMusicEnded; -} - -float CAudioDecoderVorbis::getLength() -{ - return (float)ov_time_total(&_OggVorbisFile, -1); -} } /* namespace NLSAMPLE */ diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index efeb15fee..80e545d25 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -55,6 +55,11 @@ FILE(GLOB STREAM stream_source.cpp ../../include/nel/sound/stream_source.h ) +FILE(GLOB STREAM_FILE + audio_decoder.cpp ../../include/nel/sound/audio_decoder.h + audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h +) + FILE(GLOB USER_CLASSES ../../include/nel/sound/u_audio_mixer.h ../../include/nel/sound/u_group_controller.h @@ -71,6 +76,7 @@ SOURCE_GROUP("mixer" FILES ${MIXER}) SOURCE_GROUP("music_deprecated" FILES ${MUSIC}) SOURCE_GROUP("sound" FILES ${SOUND}) SOURCE_GROUP("stream" FILES ${STREAM}) +SOURCE_GROUP("stream_file" FILES ${STREAM_FILE}) SOURCE_GROUP("user_classes" FILES ${USER_CLASSES}) diff --git a/code/nel/src/sound/audio_decoder.cpp b/code/nel/src/sound/audio_decoder.cpp new file mode 100644 index 000000000..eef031417 --- /dev/null +++ b/code/nel/src/sound/audio_decoder.cpp @@ -0,0 +1,139 @@ +/** + * \file audio_decoder.cpp + * \brief IAudioDecoder + * \date 2012-04-11 09:34GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +#include +#include + +// Project includes +#include + +using namespace std; +using namespace NLMISC; + +namespace NLSOUND { + +IAudioDecoder::IAudioDecoder() : _InternalStream(NULL) +{ + +} + +IAudioDecoder::~IAudioDecoder() +{ + if (_InternalStream) { delete _InternalStream; _InternalStream = NULL; } +} + +IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &filepath, bool async, bool loop) +{ + std::string lookup = CPath::lookup(filepath, false); + if (lookup.empty()) + { + nlwarning("Music file %s does not exist!", filepath.c_str()); + return NULL; + } + std::string type = CFile::getExtension(filepath); + + CIFile *ifile = new CIFile(); + ifile->setCacheFileOnOpen(!async); + ifile->allowBNPCacheFileOnOpen(!async); + ifile->open(lookup); + + IAudioDecoder *mb = createAudioDecoder(type, ifile, loop); + + if (mb) mb->_InternalStream = ifile; + else delete ifile; + + return mb; +} + +IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop) +{ + if (!stream) + { + nlwarning("Stream is NULL"); + return NULL; + } + std::string type_lower = toLower(type); + if (type_lower == "ogg") + { + return new CAudioDecoderVorbis(stream, loop); + } + else + { + nlwarning("Music file type unknown: '%s'", type_lower.c_str()); + return NULL; + } +} + +bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, std::string &title) +{ + std::string lookup = CPath::lookup(filepath, false); + if (lookup.empty()) + { + nlwarning("Music file %s does not exist!", filepath.c_str()); + return false; + } + std::string type = CFile::getExtension(filepath); + std::string type_lower = NLMISC::toLower(type); + + if (type_lower == "ogg") + { + CIFile ifile; + ifile.setCacheFileOnOpen(false); + ifile.allowBNPCacheFileOnOpen(false); + ifile.open(lookup); + return CAudioDecoderVorbis::getInfo(&ifile, artist, title); + } + else + { + nlwarning("Music file type unknown: '%s'", type_lower.c_str()); + artist.clear(); title.clear(); + return false; + } +} + +/// Get audio/container extensions that are currently supported by the nel sound library. +void IAudioDecoder::getMusicExtensions(std::vector &extensions) +{ + extensions.push_back("ogg"); + // extensions.push_back("wav"); // TODO: Easy. +} + +/// Return if a music extension is supported by the nel sound library. +bool IAudioDecoder::isMusicExtensionSupported(const std::string &extension) +{ + return (extension == "ogg"); +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/audio_decoder_vorbis.cpp b/code/nel/src/sound/audio_decoder_vorbis.cpp new file mode 100644 index 000000000..1f79b0ddc --- /dev/null +++ b/code/nel/src/sound/audio_decoder_vorbis.cpp @@ -0,0 +1,224 @@ +/** + * \file audio_decoder_vorbis.cpp + * \brief CAudioDecoderVorbis + * \date 2012-04-11 09:35GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +using namespace std; +using namespace NLMISC; + +namespace NLSOUND { + +size_t vorbisReadFunc(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + NLMISC::IStream *stream = audio_decoder_vorbis->getStream(); + nlassert(stream->isReading()); + sint32 length = (sint32)(size * nmemb); + if (length > audio_decoder_vorbis->getStreamSize() - stream->getPos()) + length = audio_decoder_vorbis->getStreamSize() - stream->getPos(); + stream->serialBuffer((uint8 *)ptr, length); + return length; +} + +int vorbisSeekFunc(void *datasource, ogg_int64_t offset, int whence) +{ + if (whence == SEEK_CUR && offset == 0) + { + // nlwarning(NLSOUND_XAUDIO2_PREFIX "This seek call doesn't do a damn thing, wtf."); + return 0; // ooookkaaaaaayyy + } + + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + + NLMISC::IStream::TSeekOrigin origin; + switch (whence) + { + case SEEK_SET: + origin = NLMISC::IStream::begin; + break; + case SEEK_CUR: + origin = NLMISC::IStream::current; + break; + case SEEK_END: + origin = NLMISC::IStream::end; + break; + default: + // nlwarning(NLSOUND_XAUDIO2_PREFIX "Seeking to fake origin."); + return -1; + } + + if (audio_decoder_vorbis->getStream()->seek(SEEK_SET ? audio_decoder_vorbis->getStreamOffset() + (sint32)offset : (sint32)offset, origin)) return 0; + else return -1; +} + +//int vorbisCloseFunc(void *datasource) +//{ +// //CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; +//} + +long vorbisTellFunc(void *datasource) +{ + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + return (long)(audio_decoder_vorbis->getStream()->getPos() - audio_decoder_vorbis->getStreamOffset()); +} + +static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { + (size_t (*)(void *, size_t, size_t, void *)) vorbisReadFunc, + (int (*)(void *, ogg_int64_t, int)) vorbisSeekFunc, + (int (*)(void *)) NULL, //vorbisCloseFunc, + (long (*)(void *)) vorbisTellFunc +}; + +CAudioDecoderVorbis::CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop) +: _Stream(stream), _Loop(loop), _StreamSize(0), _IsMusicEnded(false) +{ + _StreamOffset = stream->getPos(); + stream->seek(0, NLMISC::IStream::end); + _StreamSize = stream->getPos(); + stream->seek(_StreamOffset, NLMISC::IStream::begin); + ov_open_callbacks(this, &_OggVorbisFile, NULL, 0, OV_CALLBACKS_NLMISC_STREAM); +} + +CAudioDecoderVorbis::~CAudioDecoderVorbis() +{ + ov_clear(&_OggVorbisFile); +} + +/// Get information on a music file (only artist and title at the moment). +bool CAudioDecoderVorbis::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title) +{ + CAudioDecoderVorbis mbv(stream, false); // just opens and closes the oggvorbisfile thing :) + vorbis_comment *vc = ov_comment(&mbv._OggVorbisFile, -1); + char *title_c = vorbis_comment_query(vc, "title", 0); + if (title_c) title = title_c; else title.clear(); + char *artist_c = vorbis_comment_query(vc, "artist", 0); + if (artist_c) artist = artist_c; else artist.clear(); + return true; +} + +uint32 CAudioDecoderVorbis::getRequiredBytes() +{ + return 0; // no minimum requirement of bytes to buffer out +} + +uint32 CAudioDecoderVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) +{ + sint current_section = 0; // ??? + if (_IsMusicEnded) return 0; + nlassert(minimum <= maximum); // can't have this.. + uint32 bytes_read = 0; +#ifdef NL_BIG_ENDIAN + sint endianness = 1; +#else + sint endianness = 0; +#endif + do + { + // signed 16-bit or unsigned 8-bit little-endian samples + sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, + endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. + getBitsPerSample() == 8 ? 1 : 2, + getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. + ¤t_section); + // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); + if (br > 0) + { + bytes_read += (uint32)br; + } + else if (br == 0) // EOF + { + if (_Loop) + { + ov_pcm_seek(&_OggVorbisFile, 0); + //_Stream->seek(0, NLMISC::IStream::begin); + } + else + { + _IsMusicEnded = true; + break; + } + } + else + { + // error + switch(br) + { + case OV_HOLE: + nlwarning("ov_read returned OV_HOLE"); + break; + case OV_EINVAL: + nlwarning("ov_read returned OV_EINVAL"); + break; + case OV_EBADLINK: + nlwarning("ov_read returned OV_EBADLINK"); + break; + default: + nlwarning("ov_read returned %d", br); + } + } + } while (bytes_read < minimum); + return bytes_read; +} + +uint8 CAudioDecoderVorbis::getChannels() +{ + vorbis_info *vi = ov_info(&_OggVorbisFile, -1); + return (uint8)vi->channels; +} + +uint CAudioDecoderVorbis::getSamplesPerSec() +{ + vorbis_info *vi = ov_info(&_OggVorbisFile, -1); + return (uint)vi->rate; +} + +uint8 CAudioDecoderVorbis::getBitsPerSample() +{ + return 16; +} + +bool CAudioDecoderVorbis::isMusicEnded() +{ + return _IsMusicEnded; +} + +float CAudioDecoderVorbis::getLength() +{ + return (float)ov_time_total(&_OggVorbisFile, -1); +} + +} /* namespace NLSOUND */ + +/* end of file */ From 1d0052d22868f5170ce01c90cc855105398e47fc Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:55:30 +0200 Subject: [PATCH 40/77] Changed: Turned off pitch change in stream ogg vorbis sample --HG-- branch : sound_dev --- code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index c82589471..2709a0b81 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -94,7 +94,7 @@ static void initSample() string sample = SAMPLE_OGG; s_AudioDecoder = IAudioDecoder::createAudioDecoder(sample, false, false); s_StreamSource->setFormat(s_AudioDecoder->getChannels(), s_AudioDecoder->getBitsPerSample(), (uint32)s_AudioDecoder->getSamplesPerSec()); - s_StreamSource->setPitch(2.0f); + //s_StreamSource->setPitch(2.0f); s_GroupController = s_AudioMixer->getGroupController("dialog"); } From 268f2e247f67fe1104d0b28c51f24b43b2075a76 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 13:24:03 +0200 Subject: [PATCH 41/77] Fixed: Potential bug in CWinThread, someone using a mutex on the stack again --HG-- branch : sound_dev --- code/nel/src/misc/win_thread.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/code/nel/src/misc/win_thread.cpp b/code/nel/src/misc/win_thread.cpp index 4192e927b..bbe6eccf2 100644 --- a/code/nel/src/misc/win_thread.cpp +++ b/code/nel/src/misc/win_thread.cpp @@ -73,6 +73,19 @@ CWinThread::CWinThread (IRunnable *runnable, uint32 stackSize) _MainThread = false; } +namespace { +class CWinCriticalSection +{ +private: + CRITICAL_SECTION cs; +public: + CWinCriticalSection() { InitializeCriticalSection(&cs); } + ~CWinCriticalSection() { DeleteCriticalSection(&cs); } + inline void enter() { EnterCriticalSection(&cs); } + inline void leave() { LeaveCriticalSection(&cs); } +}; +}/* anonymous namespace */ + CWinThread::CWinThread (void* threadHandle, uint32 threadId) { // Main thread @@ -99,14 +112,12 @@ CWinThread::CWinThread (void* threadHandle, uint32 threadId) nlassert(0); // WARNING: following code has not tested! don't know if it work fo real ... // This is just a suggestion of a possible solution, should this situation one day occur ... // Ensure that this thread don't get deleted, or we could suspend the main thread - CRITICAL_SECTION cs; - InitializeCriticalSection(&cs); - EnterCriticalSection(&cs); + static CWinCriticalSection cs; + cs.enter(); // the 2 following statement must be executed atomicaly among the threads of the current process ! SuspendThread(threadHandle); _SuspendCount = ResumeThread(threadHandle); - LeaveCriticalSection(&cs); - DeleteCriticalSection(&cs); + cs.leave(); } } From 0028303c35b160369adfca1e788f9e26e735b28e Mon Sep 17 00:00:00 2001 From: kervala Date: Wed, 11 Apr 2012 15:30:38 +0200 Subject: [PATCH 42/77] Changed: Some Debian multiarch fixes Added: Option WITH_SYMBOLS to keep symbols when compiling in Release --- code/CMakeModules/nel.cmake | 66 +++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index d4b12829a..e6277c2b5 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -5,7 +5,11 @@ MACRO(NL_GEN_PC name) IF(NOT WIN32 AND WITH_INSTALL_LIBRARIES) CONFIGURE_FILE(${name}.in "${CMAKE_CURRENT_BINARY_DIR}/${name}") - INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}" DESTINATION lib/pkgconfig) + IF(CMAKE_LIBRARY_ARCHITECTURE) + INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}" DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig) + ELSE(CMAKE_LIBRARY_ARCHITECTURE) + INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}" DESTINATION lib/pkgconfig) + ENDIF(CMAKE_LIBRARY_ARCHITECTURE) ENDIF(NOT WIN32 AND WITH_INSTALL_LIBRARIES) ENDMACRO(NL_GEN_PC) @@ -230,6 +234,17 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS) ### # Optional support ### + + # Check if CMake is launched from a Debian packaging script + SET(DEB_HOST_GNU_CPU $ENV{DEB_HOST_GNU_CPU}) + + # Don't strip if generating a .deb + IF(DEB_HOST_GNU_CPU) + OPTION(WITH_SYMBOLS "Keep debug symbols in binaries" ON ) + ELSE(DEB_HOST_GNU_CPU) + OPTION(WITH_SYMBOLS "Keep debug symbols in binaries" OFF) + ENDIF(DEB_HOST_GNU_CPU) + IF(WIN32) OPTION(WITH_STLPORT "With STLport support." ON ) ELSE(WIN32) @@ -388,12 +403,20 @@ MACRO(NL_SETUP_BUILD) ENDIF(TARGET_CPU STREQUAL "x86_64") # Fix library paths suffixes for Debian MultiArch - IF(NOT CMAKE_LIBRARY_ARCHITECTURE) - SET(CMAKE_LIBRARY_ARCHITECTURE $ENV{DEB_HOST_MULTIARCH}) - ENDIF(NOT CMAKE_LIBRARY_ARCHITECTURE) + SET(DEBIAN_MULTIARCH $ENV{DEB_HOST_MULTIARCH}) + + IF(DEBIAN_MULTIARCH) + SET(CMAKE_LIBRARY_ARCHITECTURE ${DEBIAN_MULTIARCH}) + ENDIF(DEBIAN_MULTIARCH) IF(CMAKE_LIBRARY_ARCHITECTURE) - SET(CMAKE_LIBRARY_PATH "/lib/${CMAKE_LIBRARY_ARCHITECTURE};/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE};${CMAKE_LIBRARY_PATH}") + SET(CMAKE_LIBRARY_PATH /lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} ${CMAKE_LIBRARY_PATH}) + IF(TARGET_X64) + SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /lib64 /usr/lib64) + ENDIF(TARGET_X64) + IF(TARGET_X86) + SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /lib32 /usr/lib32) + ENDIF(TARGET_X86) ENDIF(CMAKE_LIBRARY_ARCHITECTURE) IF(MSVC) @@ -416,10 +439,10 @@ MACRO(NL_SETUP_BUILD) MESSAGE(FATAL_ERROR "Can't determine compiler version ${MSVC_VERSION}") ENDIF(MSVC10) - SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DWIN32 /D_WINDOWS /W3 /Zi /Zm1000 /MP /Gy-") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DWIN32 /D_WINDOWS /W3 /Zm1000 /MP /Gy-") # Common link flags - SET(PLATFORM_LINKFLAGS "-DEBUG") + SET(PLATFORM_LINKFLAGS "") IF(TARGET_X64) # Fix a bug with Intellisense @@ -434,10 +457,17 @@ MACRO(NL_SETUP_BUILD) # Exceptions are only set for C++ SET(PLATFORM_CXXFLAGS "${PLATFORM_CFLAGS} /EHa") - SET(NL_DEBUG_CFLAGS "/MDd /RTC1 /D_DEBUG ${MIN_OPTIMIZATIONS}") - SET(NL_RELEASE_CFLAGS "/MD /D NDEBUG ${SPEED_OPTIMIZATIONS}") - SET(NL_DEBUG_LINKFLAGS "/NODEFAULTLIB:msvcrt /INCREMENTAL:YES") - SET(NL_RELEASE_LINKFLAGS "/OPT:REF /OPT:ICF /INCREMENTAL:NO") + IF(WITH_SYMBOLS) + SET(NL_RELEASE_CFLAGS "/Zi ${NL_RELEASE_CFLAGS}") + SET(NL_RELEASE_LINKFLAGS "/DEBUG ${NL_RELEASE_LINKFLAGS}") + ELSE(WITH_SYMBOLS) + SET(NL_RELEASE_LINKFLAGS "/RELEASE ${NL_RELEASE_LINKFLAGS}") + ENDIF(WITH_SYMBOLS) + + SET(NL_DEBUG_CFLAGS "/Zi /MDd /RTC1 /D_DEBUG ${MIN_OPTIMIZATIONS} ${NL_DEBUG_CFLAGS}") + SET(NL_RELEASE_CFLAGS "/MD /DNDEBUG ${SPEED_OPTIMIZATIONS} ${NL_RELEASE_CFLAGS}") + SET(NL_DEBUG_LINKFLAGS "/DEBUG /OPT:NOREF /OPT:NOICF /NODEFAULTLIB:msvcrt /INCREMENTAL:YES ${NL_DEBUG_LINKFLAGS}") + SET(NL_RELEASE_LINKFLAGS "/OPT:REF /OPT:ICF /INCREMENTAL:NO ${NL_RELEASE_LINKFLAGS}") ELSE(MSVC) IF(HOST_CPU STREQUAL "x86_64" AND TARGET_CPU STREQUAL "x86") SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m32 -march=i686") @@ -447,7 +477,7 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m64") ENDIF(HOST_CPU STREQUAL "x86" AND TARGET_CPU STREQUAL "x86_64") - SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -g -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") IF(WITH_COVERAGE) SET(PLATFORM_CFLAGS "-fprofile-arcs -ftest-coverage ${PLATFORM_CFLAGS}") @@ -468,8 +498,16 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -Wl,--no-undefined -Wl,--as-needed") ENDIF(NOT APPLE) - SET(NL_DEBUG_CFLAGS "-DNL_DEBUG -D_DEBUG") - SET(NL_RELEASE_CFLAGS "-DNL_RELEASE -DNDEBUG -O3") + IF(WITH_SYMBOLS) + SET(NL_RELEASE_CFLAGS "${NL_RELEASE_CFLAGS} -g") + ELSE(WITH_SYMBOLS) + IF(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + SET(NL_RELEASE_LINKFLAGS "-Wl,-s ${NL_RELEASE_LINKFLAGS}") + ENDIF(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + ENDIF(WITH_SYMBOLS) + + SET(NL_DEBUG_CFLAGS "-DNL_DEBUG -D_DEBUG ${NL_DEBUG_CFLAGS}") + SET(NL_RELEASE_CFLAGS "-DNL_RELEASE -DNDEBUG -O3 ${NL_RELEASE_CFLAGS}") ENDIF(MSVC) ENDMACRO(NL_SETUP_BUILD) From 1758ed6a96e56c6cd786ddb9c72b08dc27338eb2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 15:38:49 +0200 Subject: [PATCH 43/77] Added: Function to set thread priority --HG-- branch : sound_dev --- code/nel/include/nel/misc/p_thread.h | 1 + code/nel/include/nel/misc/thread.h | 13 +++++++++++++ code/nel/include/nel/misc/win_thread.h | 4 ++-- code/nel/src/misc/p_thread.cpp | 26 ++++++++++++++++++++++++++ code/nel/src/misc/win_thread.cpp | 10 +++++----- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/code/nel/include/nel/misc/p_thread.h b/code/nel/include/nel/misc/p_thread.h index cd027aa37..82f6a4ee3 100644 --- a/code/nel/include/nel/misc/p_thread.h +++ b/code/nel/include/nel/misc/p_thread.h @@ -48,6 +48,7 @@ public: virtual void wait(); virtual bool setCPUMask(uint64 cpuMask); virtual uint64 getCPUMask(); + virtual void setPriority(TThreadPriority priority); virtual std::string getUserName(); virtual IRunnable *getRunnable() diff --git a/code/nel/include/nel/misc/thread.h b/code/nel/include/nel/misc/thread.h index 82ef78a13..983e6b12a 100644 --- a/code/nel/include/nel/misc/thread.h +++ b/code/nel/include/nel/misc/thread.h @@ -68,6 +68,16 @@ public: } }; +/// Thread priorities, numbering follows Win32 for now +enum TThreadPriority +{ + ThreadPriorityLowest = -2, + ThreadPriorityLow = -1, + ThreadPriorityNormal = 0, + ThreadPriorityHigh = 1, + ThreadPriorityHighest = 2, +}; + /** * Thread base interface, must be implemented for all OS * \author Vianney Lecroart @@ -119,6 +129,9 @@ public: */ virtual uint64 getCPUMask()=0; + /// Set the thread priority. Thread must have been started before. + virtual void setPriority(TThreadPriority priority) = 0; + /** * Get the thread user name. * Under Linux return thread owner, under windows return the name of the logon user. diff --git a/code/nel/include/nel/misc/win_thread.h b/code/nel/include/nel/misc/win_thread.h index a2e8b5389..628942dde 100644 --- a/code/nel/include/nel/misc/win_thread.h +++ b/code/nel/include/nel/misc/win_thread.h @@ -49,6 +49,7 @@ public: virtual void wait(); virtual bool setCPUMask(uint64 cpuMask); virtual uint64 getCPUMask(); + virtual void setPriority(TThreadPriority priority); virtual std::string getUserName(); virtual IRunnable *getRunnable() @@ -69,8 +70,7 @@ public: void suspend(); // Resume the thread. No-op if already resumed void resume(); - // set priority as defined by "SetThreadpriority" - void setPriority(int priority); + // Priority boost void enablePriorityBoost(bool enabled); /// private use diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index d4f1582b4..e20981c98 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -207,6 +207,32 @@ uint64 CPThread::getCPUMask() return cpuMask; } +void CPThread::setPriority(TThreadPriority priority) +{ + // TODO: Verify and test this + switch (priority) + { + case ThreadPriorityHigh: + { + int minPrio = sched_get_priority_min(SCHED_FIFO); + int maxPrio = sched_get_priority_max(SCHED_FIFO); + int prio = ((maxPrio - minPrio) / 4) + minPrio; + pthread_setschedparam(_ThreadHandle, SCHED_FIFO, prio); + break; + } + case ThreadPriorityHighest: + { + int minPrio = sched_get_priority_min(SCHED_FIFO); + int maxPrio = sched_get_priority_max(SCHED_FIFO); + int prio = ((maxPrio - minPrio) / 2) + minPrio; + pthread_setschedparam(_ThreadHandle, SCHED_FIFO, prio); + break; + } + default: + pthread_setschedparam(_ThreadHandle, SCHED_OTHER, 0); + } +} + /* * getUserName */ diff --git a/code/nel/src/misc/win_thread.cpp b/code/nel/src/misc/win_thread.cpp index bbe6eccf2..f8a961a74 100644 --- a/code/nel/src/misc/win_thread.cpp +++ b/code/nel/src/misc/win_thread.cpp @@ -84,6 +84,7 @@ public: inline void enter() { EnterCriticalSection(&cs); } inline void leave() { LeaveCriticalSection(&cs); } }; +CWinCriticalSection s_CS; }/* anonymous namespace */ CWinThread::CWinThread (void* threadHandle, uint32 threadId) @@ -112,12 +113,11 @@ CWinThread::CWinThread (void* threadHandle, uint32 threadId) nlassert(0); // WARNING: following code has not tested! don't know if it work fo real ... // This is just a suggestion of a possible solution, should this situation one day occur ... // Ensure that this thread don't get deleted, or we could suspend the main thread - static CWinCriticalSection cs; - cs.enter(); + s_CS.enter(); // the 2 following statement must be executed atomicaly among the threads of the current process ! SuspendThread(threadHandle); _SuspendCount = ResumeThread(threadHandle); - cs.leave(); + s_CS.leave(); } } @@ -159,10 +159,10 @@ void CWinThread::resume() } } -void CWinThread::setPriority(int priority) +void CWinThread::setPriority(TThreadPriority priority) { nlassert(ThreadHandle); // 'start' was not called !! - BOOL result = SetThreadPriority(ThreadHandle, priority); + BOOL result = SetThreadPriority(ThreadHandle, (int)priority); nlassert(result); } From 19599cd9f53a99ecd37caa12d8fe93a6bc66351d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 15:44:02 +0200 Subject: [PATCH 44/77] Changed: Allow changing looping parameter of audio decoder after init --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_decoder.h | 3 +++ code/nel/include/nel/sound/audio_decoder_vorbis.h | 3 +++ code/nel/src/sound/audio_decoder_vorbis.cpp | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/code/nel/include/nel/sound/audio_decoder.h b/code/nel/include/nel/sound/audio_decoder.h index bded79cfe..3babc1de1 100644 --- a/code/nel/include/nel/sound/audio_decoder.h +++ b/code/nel/include/nel/sound/audio_decoder.h @@ -95,6 +95,9 @@ public: /// Get the total time in seconds. virtual float getLength() = 0; + + /// Set looping + virtual void setLooping(bool loop) = 0; }; /* class IAudioDecoder */ } /* namespace NLSOUND */ diff --git a/code/nel/include/nel/sound/audio_decoder_vorbis.h b/code/nel/include/nel/sound/audio_decoder_vorbis.h index c65c4de82..bd7e45019 100644 --- a/code/nel/include/nel/sound/audio_decoder_vorbis.h +++ b/code/nel/include/nel/sound/audio_decoder_vorbis.h @@ -99,6 +99,9 @@ public: /// Get the total time in seconds. virtual float getLength(); + + /// Set looping + virtual void setLooping(bool loop); }; /* class CAudioDecoderVorbis */ } /* namespace NLSOUND */ diff --git a/code/nel/src/sound/audio_decoder_vorbis.cpp b/code/nel/src/sound/audio_decoder_vorbis.cpp index 1f79b0ddc..c54ce0ade 100644 --- a/code/nel/src/sound/audio_decoder_vorbis.cpp +++ b/code/nel/src/sound/audio_decoder_vorbis.cpp @@ -219,6 +219,11 @@ float CAudioDecoderVorbis::getLength() return (float)ov_time_total(&_OggVorbisFile, -1); } +void CAudioDecoderVorbis::setLooping(bool loop) +{ + _Loop = loop; +} + } /* namespace NLSOUND */ /* end of file */ From 70ca1197c5c487671b88b0022f895e53d4220c15 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 15:45:04 +0200 Subject: [PATCH 45/77] Fixed: Typo in cmake project for stream sample --HG-- branch : sound_dev --- code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt b/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt index 51fdccb66..db978fc88 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt +++ b/code/nel/samples/sound/stream_ogg_vorbis/CMakeLists.txt @@ -6,7 +6,7 @@ INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(nl_sample_stream_ogg_vorbis nelmisc nelsound) NL_DEFAULT_PROPS(nl_sample_stream_ogg_vorbis "NeL, Samples: Sound: Stream OGG Vorbis") -NL_ADD_RUNTIME_FLAGS(nl_sample_sound_sources) +NL_ADD_RUNTIME_FLAGS(nl_sample_stream_ogg_vorbis) INSTALL(TARGETS nl_sample_stream_ogg_vorbis RUNTIME DESTINATION bin COMPONENT samplessound) From 57aa8b4d740295b084ea3057edb53aba33635b1f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 15:45:59 +0200 Subject: [PATCH 46/77] Fixed: #795 Removed unused include --HG-- branch : sound_dev --- code/nel/src/sound/driver/openal/stdopenal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/code/nel/src/sound/driver/openal/stdopenal.h b/code/nel/src/sound/driver/openal/stdopenal.h index 12960d57f..1661ef6fe 100644 --- a/code/nel/src/sound/driver/openal/stdopenal.h +++ b/code/nel/src/sound/driver/openal/stdopenal.h @@ -58,6 +58,5 @@ #include "nel/sound/driver/source.h" #include "nel/sound/driver/listener.h" #include "nel/sound/driver/effect.h" -#include "nel/sound/driver/music_buffer.h" /* end of file */ From bbd291a0898694cae814966060199368fdfcbc7a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 17:39:46 +0200 Subject: [PATCH 47/77] Added: #795 Streamed audio file sound source --HG-- branch : sound_dev --- code/nel/include/nel/sound/audio_mixer_user.h | 1 + code/nel/include/nel/sound/simple_source.h | 2 + code/nel/include/nel/sound/sound.h | 5 +- code/nel/include/nel/sound/source_common.h | 3 +- .../nel/include/nel/sound/stream_file_sound.h | 94 +++++++ .../include/nel/sound/stream_file_source.h | 105 ++++++++ code/nel/include/nel/sound/stream_source.h | 10 +- code/nel/src/sound/CMakeLists.txt | 2 + code/nel/src/sound/audio_mixer_user.cpp | 18 ++ code/nel/src/sound/simple_source.cpp | 12 +- code/nel/src/sound/sound.cpp | 6 + code/nel/src/sound/sound_bank.cpp | 4 + code/nel/src/sound/stream_file_sound.cpp | 91 +++++++ code/nel/src/sound/stream_file_source.cpp | 244 ++++++++++++++++++ code/nel/src/sound/stream_sound.cpp | 5 +- code/nel/src/sound/stream_source.cpp | 31 ++- 16 files changed, 621 insertions(+), 12 deletions(-) create mode 100644 code/nel/include/nel/sound/stream_file_sound.h create mode 100644 code/nel/include/nel/sound/stream_file_source.h create mode 100644 code/nel/src/sound/stream_file_sound.cpp create mode 100644 code/nel/src/sound/stream_file_source.cpp diff --git a/code/nel/include/nel/sound/audio_mixer_user.h b/code/nel/include/nel/sound/audio_mixer_user.h index 977fe066b..1316fe34a 100644 --- a/code/nel/include/nel/sound/audio_mixer_user.h +++ b/code/nel/include/nel/sound/audio_mixer_user.h @@ -403,6 +403,7 @@ public: /// Add a source for play as possible (for non discadable sound) void addSourceWaitingForPlay(CSourceCommon *source); + void removeSourceWaitingForPlay(CSourceCommon *source); /// Read all user controled var sheets void initUserVar(); diff --git a/code/nel/include/nel/sound/simple_source.h b/code/nel/include/nel/sound/simple_source.h index 9b21200c9..c330fd7fa 100644 --- a/code/nel/include/nel/sound/simple_source.h +++ b/code/nel/include/nel/sound/simple_source.h @@ -142,6 +142,8 @@ private: /// True when the sound is played muted and until the mixer event notifying the end. bool _PlayMuted; + bool _WaitingForPlay; + }; diff --git a/code/nel/include/nel/sound/sound.h b/code/nel/include/nel/sound/sound.h index 7d7542fc9..e9d4f755c 100644 --- a/code/nel/include/nel/sound/sound.h +++ b/code/nel/include/nel/sound/sound.h @@ -61,8 +61,9 @@ public: SOUND_COMPLEX, SOUND_BACKGROUND, SOUND_CONTEXT, - SOUND_MUSIC, - SOUND_STREAM + SOUND_MUSIC, // soon to be deprecated hopefully + SOUND_STREAM, + SOUND_STREAM_FILE }; diff --git a/code/nel/include/nel/sound/source_common.h b/code/nel/include/nel/sound/source_common.h index ca0a9a2b8..1180fd68e 100644 --- a/code/nel/include/nel/sound/source_common.h +++ b/code/nel/include/nel/sound/source_common.h @@ -37,7 +37,8 @@ public: SOURCE_COMPLEX, SOURCE_BACKGROUND, SOURCE_MUSIC, // DEPRECATED - SOURCE_STREAM + SOURCE_STREAM, + SOURCE_STREAM_FILE }; /// When groupController is NULL it will use the groupcontroller specified in the TSoundId. You should manually specify the groupController if this source is a child of another source, so that the parent source controller of the user-specified .sound file is the one that will be used. diff --git a/code/nel/include/nel/sound/stream_file_sound.h b/code/nel/include/nel/sound/stream_file_sound.h new file mode 100644 index 000000000..2429c3382 --- /dev/null +++ b/code/nel/include/nel/sound/stream_file_sound.h @@ -0,0 +1,94 @@ +/** + * \file stream_file_sound.h + * \brief CStreamFileSound + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSound + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_STREAM_FILE_SOUND_H +#define NLSOUND_STREAM_FILE_SOUND_H +#include + +// STL includes + +// NeL includes + +// Project includes +#include + +namespace NLSOUND { + class CSourceMusicChannel; + +/** + * \brief CStreamFileSound + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSound + */ +class CStreamFileSound : public CStreamSound +{ +public: + friend CSourceMusicChannel; + +public: + CStreamFileSound(); + virtual ~CStreamFileSound(); + + /// Get the type of the sound. + virtual TSOUND_TYPE getSoundType() { return SOUND_STREAM_FILE; } + + /// Load the sound parameters from georges' form + virtual void importForm(const std::string& filename, NLGEORGES::UFormElm& formRoot); + + /// Used by the george sound plugin to check sound recursion (ie sound 'toto' use sound 'titi' witch also use sound 'toto' ...). + virtual void getSubSoundList(std::vector > &/* subsounds */) const { } + + /// Serialize the sound data. + virtual void serial(NLMISC::IStream &s); + + /// Return the length of the sound in ms + virtual uint32 getDuration() { return 0; } + + inline bool getAsync() { return m_Async; } + + inline const std::string &getFilePath() { return m_FilePath; } + +private: + /// Used by CSourceMusicChannel to set the filePath and default settings on other parameters. + void setMusicFilePath(const std::string &filePath, bool async = true, bool loop = false); + +private: + CStreamFileSound(const CStreamFileSound &); + CStreamFileSound &operator=(const CStreamFileSound &); + +private: + bool m_Async; + std::string m_FilePath; + +}; /* class CStreamFileSound */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_STREAM_FILE_SOUND_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h new file mode 100644 index 000000000..16153d406 --- /dev/null +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -0,0 +1,105 @@ +/** + * \file stream_file_source.h + * \brief CStreamFileSource + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSource + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_STREAM_FILE_SOURCE_H +#define NLSOUND_STREAM_FILE_SOURCE_H +#include + +// STL includes + +// NeL includes +#include + +// Project includes +#include +#include + +namespace NLSOUND { + class IAudioDecoder; + +/** + * \brief CStreamFileSource + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSource + */ +class CStreamFileSource : public CStreamSource, private NLMISC::IRunnable +{ +public: + CStreamFileSource(CStreamFileSound *streamFileSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL); + virtual ~CStreamFileSource(); + + /// Return the source type + TSOURCE_TYPE getType() const { return SOURCE_STREAM_FILE; } + + /// \name Playback control + //@{ + /// Play + virtual void play(); + /// Stop playing + virtual void stop(); + /// Get playing state. Return false even if the source has stopped on its own. + virtual bool isPlaying(); + /// Pause (following legacy music channel implementation) + void pause(); + /// Resume (following legacy music channel implementation) + void resume(); + /// check if song ended (following legacy music channel implementation) + bool isEnded(); + //@} + + /// \name Decoding thread + //@{ + virtual void getName (std::string &result) const { result = "CStreamFileSource"; } + virtual void run(); + //@} + + // TODO: getTime + +private: + void bufferMore(uint bytes); + +private: + CStreamFileSource(const CStreamFileSource &); + CStreamFileSource &operator=(const CStreamFileSource &); + +private: + inline CStreamFileSound *getStreamFileSound() { return static_cast(m_StreamSound); } + + NLMISC::IThread *m_Thread; + + IAudioDecoder *m_AudioDecoder; + + bool m_Paused; + +}; /* class CStreamFileSource */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_STREAM_FILE_SOURCE_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/stream_source.h b/code/nel/include/nel/sound/stream_source.h index 702a26006..d0a31cf38 100644 --- a/code/nel/include/nel/sound/stream_source.h +++ b/code/nel/include/nel/sound/stream_source.h @@ -114,7 +114,7 @@ private: CStreamSource(const CStreamSource &); CStreamSource &operator=(const CStreamSource &); -private: +protected: /// Return the source type TSOURCE_TYPE getType() const { return SOURCE_STREAM; } @@ -162,7 +162,13 @@ private: /// The bytes per second according to the buffer format uint m_BytesPerSecond; - + + /// Waiting for play for high priority sources + bool m_WaitingForPlay; + + /// Inverse pitch + float m_PitchInv; + }; /* class CStreamSource */ } /* namespace NLSOUND */ diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index 80e545d25..fae6ca7a6 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -58,6 +58,8 @@ FILE(GLOB STREAM FILE(GLOB STREAM_FILE audio_decoder.cpp ../../include/nel/sound/audio_decoder.h audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h + stream_file_sound.cpp ../../include/nel/sound/stream_file_sound.h + stream_file_source.cpp ../../include/nel/sound/stream_file_source.h ) FILE(GLOB USER_CLASSES diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index e03b1ae00..5d938a036 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -45,6 +45,7 @@ #include "nel/sound/context_sound.h" #include "nel/sound/music_source.h" #include "nel/sound/stream_source.h" +#include "nel/sound/stream_file_source.h" #include "nel/sound/simple_sound.h" #include "nel/sound/music_sound.h" #include "nel/sound/stream_sound.h" @@ -250,6 +251,16 @@ void CAudioMixerUser::addSourceWaitingForPlay(CSourceCommon *source) _SourceWaitingForPlay.push_back(source); } +// ****************************************************************** + +void CAudioMixerUser::removeSourceWaitingForPlay(CSourceCommon *source) +{ + std::list::iterator it = find(_SourceWaitingForPlay.begin(), _SourceWaitingForPlay.end(), source); + if (it != _SourceWaitingForPlay.end()) + { + _SourceWaitingForPlay.erase(it); + } +} // ****************************************************************** @@ -1948,6 +1959,13 @@ retrySound: ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster, static_cast(groupController)); } break; + case CSound::SOUND_STREAM_FILE: + { + CStreamFileSound *streamFileSound = static_cast(id); + // This is a stream file thingy. + ret = new CStreamFileSource(streamFileSound, spawn, cb, userParam, cluster, static_cast(groupController)); + } + break; case CSound::SOUND_COMPLEX: { CComplexSound *complexSound = static_cast(id); diff --git a/code/nel/src/sound/simple_source.cpp b/code/nel/src/sound/simple_source.cpp index 695619f91..7c76533e3 100644 --- a/code/nel/src/sound/simple_source.cpp +++ b/code/nel/src/sound/simple_source.cpp @@ -32,7 +32,8 @@ CSimpleSource::CSimpleSource(CSimpleSound *simpleSound, bool spawn, TSpawnEndCal : CSourceCommon(simpleSound, spawn, cb, cbUserParam, cluster, groupController), _SimpleSound(simpleSound), _Track(NULL), - _PlayMuted(false) + _PlayMuted(false), + _WaitingForPlay(false) { nlassert(_SimpleSound != 0); @@ -183,6 +184,7 @@ void CSimpleSource::play() { // This sound is not discardable, add it in waiting playlist mixer->addSourceWaitingForPlay(this); + _WaitingForPlay = true; return; } // there is no available track, just do a 'muted' play @@ -193,6 +195,7 @@ void CSimpleSource::play() } CSourceCommon::play(); + _WaitingForPlay = false; } /// Mixer event call when doing muted play @@ -219,6 +222,13 @@ void CSimpleSource::stop() // nldebug("CSimpleSource %p : stop", (CAudioMixerUser::IMixerEvent*)this); // nlassert(_Playing); + if (_WaitingForPlay) + { + nlassert(!_Playing); // cannot already be playing if waiting for play + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->removeSourceWaitingForPlay(this); + } + if (!_Playing) return; diff --git a/code/nel/src/sound/sound.cpp b/code/nel/src/sound/sound.cpp index 07c10f8ef..4d753bb96 100644 --- a/code/nel/src/sound/sound.cpp +++ b/code/nel/src/sound/sound.cpp @@ -26,6 +26,7 @@ #include "nel/sound/context_sound.h" #include "nel/sound/music_sound.h" #include "nel/sound/stream_sound.h" +#include "nel/sound/stream_file_sound.h" #include "nel/sound/group_controller.h" #include "nel/sound/group_controller_root.h" @@ -84,6 +85,11 @@ CSound *CSound::createSound(const std::string &filename, NLGEORGES::UFormElm& fo ret = new CStreamSound(); ret->importForm(filename, formRoot); } + else if (dfnName == "stream_file_sound.dfn") + { + ret = new CStreamFileSound(); + ret->importForm(filename, formRoot); + } else { nlassertex(false, ("SoundType unsuported : %s", dfnName.c_str())); diff --git a/code/nel/src/sound/sound_bank.cpp b/code/nel/src/sound/sound_bank.cpp index b4031b571..dd2076a72 100644 --- a/code/nel/src/sound/sound_bank.cpp +++ b/code/nel/src/sound/sound_bank.cpp @@ -23,6 +23,7 @@ #include "nel/sound/background_sound.h" #include "nel/sound/music_sound.h" #include "nel/sound/stream_sound.h" +#include "nel/sound/stream_file_sound.h" #include "nel/georges/u_form_loader.h" #include "nel/georges/u_form_elm.h" @@ -194,6 +195,9 @@ public: case CSound::SOUND_STREAM: Sound = new CStreamSound(); break; + case CSound::SOUND_STREAM_FILE: + Sound = new CStreamFileSound(); + break; default: Sound = 0; } diff --git a/code/nel/src/sound/stream_file_sound.cpp b/code/nel/src/sound/stream_file_sound.cpp new file mode 100644 index 000000000..7640d7893 --- /dev/null +++ b/code/nel/src/sound/stream_file_sound.cpp @@ -0,0 +1,91 @@ +/** + * \file stream_file_sound.cpp + * \brief CStreamFileSound + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSound + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +// #include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NLSOUND { + +CStreamFileSound::CStreamFileSound() +{ + +} + +CStreamFileSound::~CStreamFileSound() +{ + +} + +void CStreamFileSound::importForm(const std::string &filename, NLGEORGES::UFormElm &root) +{ + // Call the base class + CStreamSound::importForm(filename, root); + + // Async + root.getValueByName(m_Async, ".SoundType.Async"); + + // FilePath + root.getValueByName(m_FilePath, ".SoundType.FilePath"); +} + +void CStreamFileSound::serial(NLMISC::IStream &s) +{ + CStreamSound::serial(s); + + s.serial(m_Async); + s.serial(m_FilePath); +} + +void CStreamFileSound::setMusicFilePath(const std::string &filePath, bool async, bool loop) +{ + _ConeInnerAngle = NLMISC::Pi * 2; + _ConeOuterAngle = NLMISC::Pi * 2; + _Looping = loop; + _Gain = 1.0f; + _ConeOuterGain = 1.0f; + _Direction = NLMISC::CVector(0.f, 0.f, 0.f); + _Pitch = 1.0f; + _Priority = HighestPri; + _MaxDist = 9000.0f; + _MinDist = 1000.0f; + m_Async = async; + m_FilePath = filePath; +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp new file mode 100644 index 000000000..4514bf4c5 --- /dev/null +++ b/code/nel/src/sound/stream_file_source.cpp @@ -0,0 +1,244 @@ +/** + * \file stream_file_source.cpp + * \brief CStreamFileSource + * \date 2012-04-11 09:57GMT + * \author Jan Boon (Kaetemi) + * CStreamFileSource + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +// #include + +// Project includes +#include +#include + +using namespace std; +// using namespace NLMISC; + +namespace NLSOUND { + +CStreamFileSource::CStreamFileSource(CStreamFileSound *streamFileSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) +: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false) +{ + m_Thread = NLMISC::IThread::create(this); +} + +CStreamFileSource::~CStreamFileSource() +{ + stop(); + m_Thread->wait(); // thread must have stopped for delete! + delete m_Thread; + m_Thread = NULL; + delete m_AudioDecoder; + m_AudioDecoder = NULL; +} + +void CStreamFileSource::play() +{ + // note: CStreamSource will assert crash if already physically playing! + + nldebug("play"); + + if (m_Thread->isRunning() && m_WaitingForPlay) + { + if (m_NextBuffer || !m_FreeBuffers) + { + CStreamSource::play(); + } + else + { + m_WaitingForPlay = true; + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->addSourceWaitingForPlay(this); + } + } + else if (!_Playing) + { + if (!m_WaitingForPlay) + { + // thread may be stopping from stop call + m_Thread->wait(); + } + nlassert(!_Playing); + m_WaitingForPlay = true; + m_Thread->start(); + m_Thread->setPriority(NLMISC::ThreadPriorityHighest); + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->addSourceWaitingForPlay(this); + } + + /*if (!m_WaitingForPlay) + { + m_WaitingForPlay = true; + + m_Thread->wait(); // thread must have stopped to restart it! + + m_Thread->start(); + m_Thread->setPriority(NLMISC::ThreadPriorityHighest); + } + + CStreamSource::play();*/ +} + +void CStreamFileSource::stop() +{ + nldebug("stop"); + + CStreamSource::stop(); + + // thread will check _Playing to stop +} + +bool CStreamFileSource::isPlaying() +{ + nldebug("isPlaying"); + + return m_Thread->isRunning(); +} + +void CStreamFileSource::pause() +{ + nldebug("pause"); + + if (!m_Paused) + { + // thread checks for this to not delete the audio decoder + m_Paused = true; + + // stop the underlying system + CStreamSource::stop(); + + // thread will check _Playing to stop + } + else + { + nlwarning("Already paused"); + } +} + +void CStreamFileSource::resume() +{ + nldebug("resume"); + + if (m_Paused) + { + m_Thread->wait(); // thread must have stopped to restart it! + + play(); + } + else + { + nlwarning("Not paused"); + } +} + +bool CStreamFileSource::isEnded() +{ + return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused); +} + +void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) +{ + uint8 *buffer = this->lock(bytes * 2); + if (buffer) + { + uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2); + this->unlock(result); + } +} + +void CStreamFileSource::run() +{ + nldebug("run"); + + bool looping = _Looping; + if (m_Paused) + { + // handle paused! + m_Paused = false; + } + else if (m_AudioDecoder) // audio decoder should normally not exist when not paused and starting the thread + { + nlwarning("CAudioDecoder already exists, possible thread race bug with pause"); + delete m_AudioDecoder; + m_AudioDecoder = NULL; + } + if (!m_AudioDecoder) + { + // load the file + m_AudioDecoder = IAudioDecoder::createAudioDecoder(getStreamFileSound()->getFilePath(), getStreamFileSound()->getAsync(), getStreamFileSound()->getLooping()); + if (!m_AudioDecoder) + { + nlwarning("Failed to create IAudioDecoder, likely invalid format"); + return; + } + this->setFormat(m_AudioDecoder->getChannels(), m_AudioDecoder->getBitsPerSample(), (uint32)m_AudioDecoder->getSamplesPerSec()); + } + uint samples, bytes; + this->getRecommendedBufferSize(samples, bytes); + bufferMore(bytes); + while (_Playing || m_WaitingForPlay) + { + if (!m_AudioDecoder->isMusicEnded()) + { + bool newLooping = _Looping; + if (looping != newLooping) + { + m_AudioDecoder->setLooping(looping); + looping = newLooping; + } + + bufferMore(bytes); + NLMISC::nlSleep(this->getRecommendedSleepTime()); + } + else + { + // wait until done playing buffers + while (this->hasFilledBuffersAvailable()) + NLMISC::nlSleep(40); + // stop the physical source + // if (hasPhysicalSource()) + // getPhysicalSource()->stop(); + // the audio mixer will call stop on the logical source + break; + } + } + if (m_Paused) + { + // don't delete anything + } + else + { + delete m_AudioDecoder; + m_AudioDecoder = NULL; + } +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/stream_sound.cpp b/code/nel/src/sound/stream_sound.cpp index 7552e79e5..e16158d4d 100644 --- a/code/nel/src/sound/stream_sound.cpp +++ b/code/nel/src/sound/stream_sound.cpp @@ -35,14 +35,15 @@ CStreamSound::~CStreamSound() void CStreamSound::importForm(const std::string &filename, NLGEORGES::UFormElm &root) { - NLGEORGES::UFormElm *psoundType; + // cannot do this debug check because used also by CStreamFileSound + /*NLGEORGES::UFormElm *psoundType; std::string dfnName; // some basic checking. root.getNodeByName(&psoundType, ".SoundType"); nlassert(psoundType != NULL); psoundType->getDfnName(dfnName); - nlassert(dfnName == "stream_sound.dfn"); + nlassert(dfnName == "stream_sound.dfn");*/ // Call the base class CSound::importForm(filename, root); diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 2c3188454..7f6b0c608 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -36,12 +36,15 @@ CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCal m_FreeBuffers(3), m_NextBuffer(0), m_LastSize(0), - m_BytesPerSecond(0) + m_BytesPerSecond(0), + m_WaitingForPlay(false), + m_PitchInv(1.0f) { nlassert(m_StreamSound != 0); // get a local copy of the stream sound parameter m_Alpha = m_StreamSound->getAlpha();//m_Buffers + m_PitchInv = 1.0f / _Pitch; // create the three buffer objects CAudioMixerUser *mixer = CAudioMixerUser::instance(); @@ -107,6 +110,8 @@ bool CStreamSource::isPlaying() /// Set looping on/off for future playbacks (default: off) void CStreamSource::setLooping(bool l) { + CSourceCommon::setLooping(l); + //CAutoMutex autoMutex(m_BufferMutex); // //CSourceCommon::setLooping(l); @@ -166,7 +171,9 @@ void CStreamSource::play() ISource *pSource = getPhysicalSource(); nlassert(pSource != NULL); - for (uint i = 0; i < m_NextBuffer; ++i) + uint nbS = m_NextBuffer; + if (!m_NextBuffer && !m_FreeBuffers) nbS = 3; + for (uint i = 0; i < nbS; ++i) pSource->submitStreamingBuffer(m_Buffers[i]); // pSource->setPos( _Position, false); @@ -184,6 +191,7 @@ void CStreamSource::play() pSource->setAlpha(m_Alpha); // and play the sound + nlassert(nbS); // must have buffered already! play = pSource->play(); // nldebug("CStreamSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this); } @@ -193,6 +201,7 @@ void CStreamSource::play() { // This sound is not discardable, add it in waiting playlist mixer->addSourceWaitingForPlay(this); + m_WaitingForPlay = true; return; } else @@ -209,10 +218,15 @@ void CStreamSource::play() } if (play) + { CSourceCommon::play(); + m_WaitingForPlay = false; + } } +#ifdef NL_DEBUG nlassert(play); +#endif } /// Stop playing @@ -222,6 +236,13 @@ void CStreamSource::stop() // nldebug("CStreamSource %p : stop", (CAudioMixerUser::IMixerEvent*)this); // nlassert(_Playing); + + if (m_WaitingForPlay) + { + nlassert(!_Playing); // cannot already be playing if waiting for play + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->removeSourceWaitingForPlay(this); + } if (!_Playing) return; @@ -305,7 +326,7 @@ void CStreamSource::updateFinalGain() void CStreamSource::setPitch(float pitch) { CAutoMutex autoMutex(m_BufferMutex); - + m_PitchInv = 1.0f / pitch; CSourceCommon::setPitch(pitch); if (hasPhysicalSource()) getPhysicalSource()->setPitch(pitch); @@ -372,7 +393,9 @@ bool CStreamSource::unlock(uint size) ++m_NextBuffer; m_NextBuffer %= 3; --m_FreeBuffers; if (hasPhysicalSource()) + { getPhysicalSource()->submitStreamingBuffer(buffer); + } m_LastSize = size; } @@ -396,7 +419,7 @@ void CStreamSource::getRecommendedBufferSize(uint &samples, uint &bytes) const uint32 CStreamSource::getRecommendedSleepTime() const { if (m_FreeBuffers > 0) return 0; - uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) / _Pitch); + uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) * m_PitchInv); clamp(sleepTime, (uint32)0, (uint32)1000); return sleepTime; } From 3bbf48e699cb5a2354cf551fd0201f9271be5ac9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 17:44:57 +0200 Subject: [PATCH 48/77] Added: #795 Sample for creating a sound source using .sound sheet with .ogg file --HG-- branch : sound_dev --- code/nel/samples/sound/CMakeLists.txt | 1 + .../samples/sound/stream_file/CMakeLists.txt | 12 + .../stream_file/base_samples.sample_bank | Bin 0 -> 330854 bytes .../stream_file/data/DFN/basics/_typ.dfn | 6 + .../stream_file/data/DFN/basics/_type.typ | 9 + .../stream_file/data/DFN/basics/boolean.typ | 6 + .../stream_file/data/DFN/basics/filename.typ | 4 + .../stream_file/data/DFN/basics/float.typ | 4 + .../stream_file/data/DFN/basics/iboolean.typ | 6 + .../sound/stream_file/data/DFN/basics/int.typ | 4 + .../stream_file/data/DFN/basics/sint16.typ | 4 + .../stream_file/data/DFN/basics/sint32.typ | 4 + .../stream_file/data/DFN/basics/sint64.typ | 4 + .../stream_file/data/DFN/basics/sint8.typ | 4 + .../stream_file/data/DFN/basics/string.typ | 4 + .../sound/stream_file/data/DFN/basics/typ.dfn | 11 + .../stream_file/data/DFN/basics/uint16.typ | 4 + .../stream_file/data/DFN/basics/uint32.typ | 4 + .../stream_file/data/DFN/basics/uint64.typ | 4 + .../stream_file/data/DFN/basics/uint8.typ | 4 + .../stream_file/data/DFN/sound/alpha.typ | 2 + .../stream_file/data/DFN/sound/angle.typ | 2 + .../data/DFN/sound/backgound_sound_item.dfn | 42 ++ .../data/DFN/sound/background_flag_config.dfn | 9 + .../data/DFN/sound/background_sound.dfn | 5 + .../data/DFN/sound/complex_sound.dfn | 19 + .../data/DFN/sound/context_sound.dfn | 5 + .../stream_file/data/DFN/sound/direction.dfn | 6 + .../stream_file/data/DFN/sound/distance.typ | 2 + .../stream_file/data/DFN/sound/doppler.typ | 2 + .../sound/stream_file/data/DFN/sound/gain.typ | 36 ++ .../stream_file/data/DFN/sound/listener.dfn | 5 + .../data/DFN/sound/mixer_config.dfn | 14 + .../data/DFN/sound/music_sound.dfn | 13 + .../data/DFN/sound/parameter_id.typ | 8 + .../data/DFN/sound/pattern_mode.typ | 10 + .../stream_file/data/DFN/sound/priority.typ | 7 + .../stream_file/data/DFN/sound/rolloff.typ | 2 + .../data/DFN/sound/simple_sound.dfn | 12 + .../stream_file/data/DFN/sound/sound.dfn | 17 + .../data/DFN/sound/sound_group.dfn | 8 + .../data/DFN/sound/sound_group_item.dfn | 7 + .../stream_file/data/DFN/sound/soundbank.dfn | 4 + .../data/DFN/sound/stream_file_sound.dfn | 9 + .../data/DFN/sound/stream_sound.dfn | 7 + .../data/DFN/sound/transposition.typ | 52 +++ .../data/DFN/sound/user_var_binding.dfn | 9 + .../stream_file/data/animations/readme.txt | 3 + .../data/animations/test_anim.sound_anim | 9 + .../background_sound.primitive | 144 +++++++ .../data/background_sounds/readme.txt | 1 + .../stream_file/data/cluster_sound/readme.txt | 11 + .../cluster_sound/test_clusters.sound_group | 19 + .../stream_file/data/default.mixer_config | 54 +++ .../data/samplebank/base_samples/beep.wav | Bin 0 -> 88244 bytes .../data/samplebank/base_samples/tuut.wav | Bin 0 -> 176444 bytes .../stream_file/data/soundbank/beep.sound | 19 + .../data/soundbank/default_stream.sound | 20 + .../data/soundbank/stream_file.sound | 18 + .../stream_file/data/soundbank/tuut.sound | 20 + .../stream_file/data/world_editor_classes.xml | 382 ++++++++++++++++++ .../samples/sound/stream_file/stream_file.cpp | 165 ++++++++ 62 files changed, 1278 insertions(+) create mode 100644 code/nel/samples/sound/stream_file/CMakeLists.txt create mode 100644 code/nel/samples/sound/stream_file/base_samples.sample_bank create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/_typ.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/_type.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/boolean.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/filename.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/float.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/iboolean.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/int.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/sint16.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/sint32.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/sint64.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/sint8.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/string.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/typ.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/uint16.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/uint32.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/uint64.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/basics/uint8.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/alpha.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/angle.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/backgound_sound_item.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/background_flag_config.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/background_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/complex_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/context_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/direction.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/distance.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/doppler.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/gain.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/listener.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/mixer_config.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/music_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/parameter_id.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/pattern_mode.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/priority.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/rolloff.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/simple_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/sound_group.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/sound_group_item.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/soundbank.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/stream_file_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/stream_sound.dfn create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/transposition.typ create mode 100644 code/nel/samples/sound/stream_file/data/DFN/sound/user_var_binding.dfn create mode 100644 code/nel/samples/sound/stream_file/data/animations/readme.txt create mode 100644 code/nel/samples/sound/stream_file/data/animations/test_anim.sound_anim create mode 100644 code/nel/samples/sound/stream_file/data/background_sounds/background_sound.primitive create mode 100644 code/nel/samples/sound/stream_file/data/background_sounds/readme.txt create mode 100644 code/nel/samples/sound/stream_file/data/cluster_sound/readme.txt create mode 100644 code/nel/samples/sound/stream_file/data/cluster_sound/test_clusters.sound_group create mode 100644 code/nel/samples/sound/stream_file/data/default.mixer_config create mode 100644 code/nel/samples/sound/stream_file/data/samplebank/base_samples/beep.wav create mode 100644 code/nel/samples/sound/stream_file/data/samplebank/base_samples/tuut.wav create mode 100644 code/nel/samples/sound/stream_file/data/soundbank/beep.sound create mode 100644 code/nel/samples/sound/stream_file/data/soundbank/default_stream.sound create mode 100644 code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound create mode 100644 code/nel/samples/sound/stream_file/data/soundbank/tuut.sound create mode 100644 code/nel/samples/sound/stream_file/data/world_editor_classes.xml create mode 100644 code/nel/samples/sound/stream_file/stream_file.cpp diff --git a/code/nel/samples/sound/CMakeLists.txt b/code/nel/samples/sound/CMakeLists.txt index 2375331f5..6a7696fa4 100644 --- a/code/nel/samples/sound/CMakeLists.txt +++ b/code/nel/samples/sound/CMakeLists.txt @@ -1,4 +1,5 @@ ADD_SUBDIRECTORY(sound_sources) +ADD_SUBDIRECTORY(stream_file) ADD_SUBDIRECTORY(stream_ogg_vorbis) diff --git a/code/nel/samples/sound/stream_file/CMakeLists.txt b/code/nel/samples/sound/stream_file/CMakeLists.txt new file mode 100644 index 000000000..0552387cb --- /dev/null +++ b/code/nel/samples/sound/stream_file/CMakeLists.txt @@ -0,0 +1,12 @@ +FILE(GLOB SRC *.cpp *.h) + +ADD_EXECUTABLE(nl_sample_stream_file ${SRC}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(nl_sample_stream_file nelmisc nelsound) +NL_DEFAULT_PROPS(nl_sample_stream_file "NeL, Samples: Sound: Stream File") +NL_ADD_RUNTIME_FLAGS(nl_sample_stream_file) + +INSTALL(TARGETS nl_sample_stream_file RUNTIME DESTINATION bin COMPONENT samplessound) + diff --git a/code/nel/samples/sound/stream_file/base_samples.sample_bank b/code/nel/samples/sound/stream_file/base_samples.sample_bank new file mode 100644 index 0000000000000000000000000000000000000000..8e9b5b8988cb8ef30ebe65b7ed74dc025d02dd1b GIT binary patch literal 330854 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

;Qe4U9Aq;0 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�do!)Ty=bHa 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

GRnJi)6a%6lggDl1npK=#>oHJEKhQ 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 NKS4?1-?zl${{!f*E2sbf literal 0 HcmV?d00001 diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/_typ.dfn b/code/nel/samples/sound/stream_file/data/DFN/basics/_typ.dfn new file mode 100644 index 000000000..2f8971932 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/_typ.dfn @@ -0,0 +1,6 @@ + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/_type.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/_type.typ new file mode 100644 index 000000000..233beca79 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/_type.typ @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/boolean.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/boolean.typ new file mode 100644 index 000000000..fb6821f08 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/boolean.typ @@ -0,0 +1,6 @@ + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/filename.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/filename.typ new file mode 100644 index 000000000..bdad39a48 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/filename.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/float.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/float.typ new file mode 100644 index 000000000..250620f5e --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/float.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/iboolean.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/iboolean.typ new file mode 100644 index 000000000..d29217bed --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/iboolean.typ @@ -0,0 +1,6 @@ + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/int.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/int.typ new file mode 100644 index 000000000..5b88c4fc4 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/int.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/sint16.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint16.typ new file mode 100644 index 000000000..0adba3ad0 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint16.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/sint32.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint32.typ new file mode 100644 index 000000000..ce470d5fe --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint32.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/sint64.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint64.typ new file mode 100644 index 000000000..5b88c4fc4 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint64.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/sint8.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint8.typ new file mode 100644 index 000000000..462eab92c --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/sint8.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/string.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/string.typ new file mode 100644 index 000000000..89191b1eb --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/string.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/typ.dfn b/code/nel/samples/sound/stream_file/data/DFN/basics/typ.dfn new file mode 100644 index 000000000..d3c9e1757 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/typ.dfn @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/uint16.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint16.typ new file mode 100644 index 000000000..d0e98aad1 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint16.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/uint32.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint32.typ new file mode 100644 index 000000000..60dc00b3c --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint32.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/uint64.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint64.typ new file mode 100644 index 000000000..936a619c8 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint64.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/basics/uint8.typ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint8.typ new file mode 100644 index 000000000..3dee1c5c8 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/basics/uint8.typ @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/alpha.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/alpha.typ new file mode 100644 index 000000000..a6f7d3bff --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/alpha.typ @@ -0,0 +1,2 @@ + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/angle.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/angle.typ new file mode 100644 index 000000000..db94698e0 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/angle.typ @@ -0,0 +1,2 @@ + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/backgound_sound_item.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/backgound_sound_item.dfn new file mode 100644 index 000000000..bbdfdf82a --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/backgound_sound_item.dfn @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tue Oct 08 14:39:12 2002 (boucher) Dfn Structure = +Tue Oct 08 15:03:40 2002 (boucher) Dfn Structure = +Mon Oct 14 16:09:48 2002 (boucher) Dfn Structure = +Wed Oct 16 18:22:30 2002 (boucher) Dfn Structure = +Thu Dec 19 16:26:49 2002 (boucher) Dfn Structure = +Wed Apr 09 19:38:51 2003 (AmazingSound) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/background_flag_config.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/background_flag_config.dfn new file mode 100644 index 000000000..bce6d82a8 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/background_flag_config.dfn @@ -0,0 +1,9 @@ + + + + + + + + Thu Jan 02 14:38:32 2003 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/background_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/background_sound.dfn new file mode 100644 index 000000000..41288d7b8 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/background_sound.dfn @@ -0,0 +1,5 @@ + + + + Tue Oct 08 14:55:03 2002 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/complex_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/complex_sound.dfn new file mode 100644 index 000000000..8164b9d7a --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/complex_sound.dfn @@ -0,0 +1,19 @@ + + + + + + + + + + + Tue Oct 08 14:29:42 2002 (boucher) Dfn Structure = +Tue Oct 08 14:35:40 2002 (boucher) Dfn Structure = +Thu Oct 10 16:15:18 2002 (boucher) Dfn Structure = +Fri Oct 11 10:26:17 2002 (boucher) Dfn Structure = +Fri Oct 11 11:14:25 2002 (boucher) Dfn Structure = +Fri Oct 11 11:15:41 2002 (boucher) Dfn Structure = +Fri Oct 11 16:01:43 2002 (boucher) Dfn Structure = +Wed Oct 16 11:36:41 2002 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/context_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/context_sound.dfn new file mode 100644 index 000000000..f8d05e697 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/context_sound.dfn @@ -0,0 +1,5 @@ + + + + Thu Nov 07 14:01:21 2002 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/direction.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/direction.dfn new file mode 100644 index 000000000..190223cc1 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/direction.dfn @@ -0,0 +1,6 @@ + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/distance.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/distance.typ new file mode 100644 index 000000000..0981c09a0 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/distance.typ @@ -0,0 +1,2 @@ + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/doppler.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/doppler.typ new file mode 100644 index 000000000..2419f2d6c --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/doppler.typ @@ -0,0 +1,2 @@ + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/gain.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/gain.typ new file mode 100644 index 000000000..2000b2652 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/gain.typ @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/listener.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/listener.dfn new file mode 100644 index 000000000..ba52b5af0 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/listener.dfn @@ -0,0 +1,5 @@ + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/mixer_config.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/mixer_config.dfn new file mode 100644 index 000000000..61daec64b --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/mixer_config.dfn @@ -0,0 +1,14 @@ + + + + + + + + + + + Thu Jan 02 14:33:42 2003 (boucher) Dfn Structure = +Thu Jan 02 14:40:05 2003 (boucher) Dfn Structure = +Thu Jan 02 14:41:32 2003 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/music_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/music_sound.dfn new file mode 100644 index 000000000..1935511b2 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/music_sound.dfn @@ -0,0 +1,13 @@ + + + + + + + + Tue Nov 02 11:28:10 2004 (berenguier) Dfn Structure = +Tue Nov 02 11:30:14 2004 (berenguier) Dfn Structure = +Tue Nov 02 11:40:09 2004 (berenguier) Dfn Structure = +Tue Nov 02 11:40:29 2004 (berenguier) Dfn Structure = +Wed Nov 03 10:54:07 2004 (berenguier) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/parameter_id.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/parameter_id.typ new file mode 100644 index 000000000..bc284afb2 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/parameter_id.typ @@ -0,0 +1,8 @@ + + + + + Mon Feb 10 17:31:53 2003 (boucher) Type Predef = +Mon Feb 10 17:31:53 2003 (boucher) Type Type = String +Mon Feb 10 17:31:53 2003 (boucher) Type UI = NonEditableCombo + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/pattern_mode.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/pattern_mode.typ new file mode 100644 index 000000000..b0ac96de5 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/pattern_mode.typ @@ -0,0 +1,10 @@ + + + + + + Tue Oct 08 14:27:39 2002 (boucher) Type Default = Chained +Tue Oct 08 14:27:39 2002 (boucher) Type Predef = +Tue Oct 08 14:27:39 2002 (boucher) Type Type = String +Tue Oct 08 14:27:39 2002 (boucher) Type UI = NonEditableCombo + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/priority.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/priority.typ new file mode 100644 index 000000000..d9bdbbd07 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/priority.typ @@ -0,0 +1,7 @@ + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/rolloff.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/rolloff.typ new file mode 100644 index 000000000..a5818a1a6 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/rolloff.typ @@ -0,0 +1,2 @@ + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/simple_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/simple_sound.dfn new file mode 100644 index 000000000..60e97cc35 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/simple_sound.dfn @@ -0,0 +1,12 @@ + + + + + + + Wed Apr 09 21:13:23 2003 (AmazingSound) Dfn Structure = +Thu Apr 10 15:38:18 2003 (AmazingSound) Dfn Structure = +Thu Apr 10 16:24:05 2003 (AmazingSound) Dfn Structure = +Wed Apr 30 12:44:51 2003 (AmazingSound) Dfn Structure = +Wed Apr 30 12:45:39 2003 (AmazingSound) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/sound.dfn new file mode 100644 index 000000000..a8c336b0b --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/sound.dfn @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + Tue Oct 08 12:33:33 2002 (boucher) Dfn Structure = +Tue Oct 08 14:35:19 2002 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group.dfn new file mode 100644 index 000000000..b058b36ff --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group.dfn @@ -0,0 +1,8 @@ + + + + Thu Jan 09 11:48:30 2003 (boucher) Dfn Structure = +Thu Jan 09 11:51:28 2003 (boucher) Dfn Structure = +Thu Jan 09 11:52:03 2003 (boucher) Dfn Structure = +Thu Jan 09 11:52:08 2003 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group_item.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group_item.dfn new file mode 100644 index 000000000..00420dbb9 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/sound_group_item.dfn @@ -0,0 +1,7 @@ + + + + + Thu Jan 09 11:47:55 2003 (boucher) Dfn Structure = +Thu Jan 09 11:53:15 2003 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/soundbank.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/soundbank.dfn new file mode 100644 index 000000000..38a60a89a --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/soundbank.dfn @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/stream_file_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/stream_file_sound.dfn new file mode 100644 index 000000000..7293fc854 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/stream_file_sound.dfn @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/stream_sound.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/stream_sound.dfn new file mode 100644 index 000000000..82eaa9a6d --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/stream_sound.dfn @@ -0,0 +1,7 @@ + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/transposition.typ b/code/nel/samples/sound/stream_file/data/DFN/sound/transposition.typ new file mode 100644 index 000000000..2d05a4471 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/transposition.typ @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/DFN/sound/user_var_binding.dfn b/code/nel/samples/sound/stream_file/data/DFN/sound/user_var_binding.dfn new file mode 100644 index 000000000..d2740f489 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/DFN/sound/user_var_binding.dfn @@ -0,0 +1,9 @@ + + + + + + Mon Feb 10 17:32:55 2003 (boucher) Dfn Structure = +Mon Feb 10 17:34:17 2003 (boucher) Dfn Structure = +Tue Feb 11 09:49:09 2003 (boucher) Dfn Structure = + diff --git a/code/nel/samples/sound/stream_file/data/animations/readme.txt b/code/nel/samples/sound/stream_file/data/animations/readme.txt new file mode 100644 index 000000000..43c7f150a --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/animations/readme.txt @@ -0,0 +1,3 @@ +This folder contains the sound track for animation. + +Put here all the .sound_anim file generated with NeL Object Viewer. \ No newline at end of file diff --git a/code/nel/samples/sound/stream_file/data/animations/test_anim.sound_anim b/code/nel/samples/sound/stream_file/data/animations/test_anim.sound_anim new file mode 100644 index 000000000..4377e81b6 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/animations/test_anim.sound_anim @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/background_sounds/background_sound.primitive b/code/nel/samples/sound/stream_file/data/background_sounds/background_sound.primitive new file mode 100644 index 000000000..26ff875f1 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/background_sounds/background_sound.primitive @@ -0,0 +1,144 @@ + + + + + + + + + class + audio + + + name + test_audio + + + + + class + sounds + + + name + sounds + + + + + + + + + class + sound_zone + + + name + test_zone + + + sound + beep.sound + + + + + + + + + + + class + sound_path + + + name + test_path + + + sound + tuut.sound + + + + + + + class + sound_point + + + name + test_point + + + sound + tuut.sound + + + + + + + class + sample_banks + + + name + sample_banks + + + + + + + + + bank_names + base_samples + + + class + sample_bank_zone + + + name + base_sample_zone + + + + + + + class + env_fx + + + name + env_fx + + + + + + + + + class + env_fx_zone + + + fx_name + BATHROOM + + + name + test_fx + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/background_sounds/readme.txt b/code/nel/samples/sound/stream_file/data/background_sounds/readme.txt new file mode 100644 index 000000000..6103b141c --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/background_sounds/readme.txt @@ -0,0 +1 @@ +This folder contains the background sound primitive files. \ No newline at end of file diff --git a/code/nel/samples/sound/stream_file/data/cluster_sound/readme.txt b/code/nel/samples/sound/stream_file/data/cluster_sound/readme.txt new file mode 100644 index 000000000..2668e737a --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/cluster_sound/readme.txt @@ -0,0 +1,11 @@ +This folder contains Georges sheets that link sound group defined in +the cluster system (edited in 3DS Max) to a sound sheet. + +This allow to put sound inside the cluster systems. + +Each sheet can contains any number of association. +It is a good practice to merge into a single sheet all +the sound groups/sound sheets thet belong the the +same building. + + \ No newline at end of file diff --git a/code/nel/samples/sound/stream_file/data/cluster_sound/test_clusters.sound_group b/code/nel/samples/sound/stream_file/data/cluster_sound/test_clusters.sound_group new file mode 100644 index 000000000..0ee91ba18 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/cluster_sound/test_clusters.sound_group @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/default.mixer_config b/code/nel/samples/sound/stream_file/data/default.mixer_config new file mode 100644 index 000000000..fd9ca5a59 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/default.mixer_config @@ -0,0 +1,54 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fri Oct 15 15:26:53 2004 (boucher) .SampleBanks[0] = base_samples + diff --git a/code/nel/samples/sound/stream_file/data/samplebank/base_samples/beep.wav b/code/nel/samples/sound/stream_file/data/samplebank/base_samples/beep.wav new file mode 100644 index 0000000000000000000000000000000000000000..8b4108a4c48d982b4247000e00fe8e9cf28a6e87 GIT binary patch literal 88244 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%0D${>b;t@T8SNVU1hzm2LgvtW1l9Z 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

Q>qVdV@EGnacW=y`OWF`d_`tS7b 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 ZerG(Ycr5wj$@dVZN1T5)|6loX{|_@=^X~uv literal 0 HcmV?d00001 diff --git a/code/nel/samples/sound/stream_file/data/samplebank/base_samples/tuut.wav b/code/nel/samples/sound/stream_file/data/samplebank/base_samples/tuut.wav new file mode 100644 index 0000000000000000000000000000000000000000..bae70bc0f9211800e459c78d8fab57d7f78ac4ca GIT binary patch literal 176444 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≈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�=5d@qZCoR62Gx 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 N$u)#d_@(y*{~sNO?b84N literal 0 HcmV?d00001 diff --git a/code/nel/samples/sound/stream_file/data/soundbank/beep.sound b/code/nel/samples/sound/stream_file/data/soundbank/beep.sound new file mode 100644 index 000000000..dbda3735c --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/soundbank/beep.sound @@ -0,0 +1,19 @@ + +

+ + + + + + + + + + + + Fri Oct 15 15:23:46 2004 (boucher) .SoundType = simple_sound.dfn +Fri Oct 15 15:23:46 2004 (boucher) .SoundType.Filename = beep.wav +Fri Oct 15 15:23:46 2004 (boucher) .SoundType.MaxDistance = 1000 +Fri Oct 15 15:23:46 2004 (boucher) .SoundType.MinDistance = 1.2 +Fri Oct 15 15:27:24 2004 (boucher) .SoundType.Alpha = + diff --git a/code/nel/samples/sound/stream_file/data/soundbank/default_stream.sound b/code/nel/samples/sound/stream_file/data/soundbank/default_stream.sound new file mode 100644 index 000000000..c757128fc --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/soundbank/default_stream.sound @@ -0,0 +1,20 @@ + +
+ + + + + + + + + + + + + Thu Jan 28 23:30:44 2010 (Kaetemi) .AbsolutePosition = false +Thu Jan 28 23:30:44 2010 (Kaetemi) .Priority = Highest +Thu Jan 28 23:30:44 2010 (Kaetemi) .SoundType = stream_sound.dfn +Thu Jan 28 23:30:44 2010 (Kaetemi) .SoundType.MaxDistance = 100000 +Thu Jan 28 23:30:44 2010 (Kaetemi) .SoundType.MinDistance = 200000 + diff --git a/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound b/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound new file mode 100644 index 000000000..1b196d749 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/data/soundbank/tuut.sound b/code/nel/samples/sound/stream_file/data/soundbank/tuut.sound new file mode 100644 index 000000000..c45cfba63 --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/soundbank/tuut.sound @@ -0,0 +1,20 @@ + +
+ + + + + + + + + + + + + Fri Oct 15 15:24:54 2004 (boucher) .SoundType = simple_sound.dfn +Fri Oct 15 15:24:54 2004 (boucher) .SoundType.Alpha = 0.5 +Fri Oct 15 15:24:54 2004 (boucher) .SoundType.Filename = tuut.wav +Fri Oct 15 15:24:54 2004 (boucher) .SoundType.MaxDistance = 50 +Fri Oct 15 15:24:54 2004 (boucher) .SoundType.MinDistance = 5 + diff --git a/code/nel/samples/sound/stream_file/data/world_editor_classes.xml b/code/nel/samples/sound/stream_file/data/world_editor_classes.xml new file mode 100644 index 000000000..68ba22ace --- /dev/null +++ b/code/nel/samples/sound/stream_file/data/world_editor_classes.xml @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/nel/samples/sound/stream_file/stream_file.cpp b/code/nel/samples/sound/stream_file/stream_file.cpp new file mode 100644 index 000000000..bdc866538 --- /dev/null +++ b/code/nel/samples/sound/stream_file/stream_file.cpp @@ -0,0 +1,165 @@ +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include + +// STL includes +#include +#include + +// NeL includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For direct play/pause control. +// You should never include this! +#include + +// Project includes + +#ifndef NL_SOUND_DATA +#define NL_SOUND_DATA "." +#endif // NL_SOUND_DATA + +using namespace std; +using namespace NLMISC; +using namespace NLSOUND; + +namespace NLSAMPLE { + +static UAudioMixer *s_AudioMixer = NULL; +static USource *s_Source = NULL; +static CStreamFileSource *s_StreamFileSource = NULL; +static UGroupController *s_GroupController = NULL; + +static void initSample() +{ + if (!INelContext::isContextInitialised()) + new CApplicationContext(); + CPath::addSearchPath(NL_SOUND_DATA"/data", true, false); + + printf("Sample demonstrating OGG playback using stream file .sound sheets."); + printf("\n\n"); + + s_AudioMixer = UAudioMixer::createAudioMixer(); + + // Set the sample path before init, this allow the mixer to build the sample banks + s_AudioMixer->setSamplePath(NL_SOUND_DATA"/data/samplebank"); + // Packed sheet option, this mean we want packed sheet generated in 'data' folder + s_AudioMixer->setPackedSheetOption(NL_SOUND_DATA"/data", true); + + printf("Select NLSOUND Driver:\n"); + printf(" [1] FMod\n"); + printf(" [2] OpenAl\n"); + printf(" [3] DSound\n"); + printf(" [4] XAudio2\n"); + printf("> "); + int selection = getchar(); getchar(); + printf("\n"); + + // init with 8 tracks, EAX enabled, no ADPCM, and automatic sample bank loading + s_AudioMixer->init(8, true, false, NULL, true, (UAudioMixer::TDriver)(selection - '0')); + s_AudioMixer->setLowWaterMark(1); + + CVector initpos(0.0f, 0.0f, 0.0f); + CVector frontvec(0.0f, 1.0f, 0.0f); + CVector upvec(0.0f, 0.0f, 1.0f); + s_AudioMixer->getListener()->setPos(initpos); + s_AudioMixer->getListener()->setOrientation(frontvec, upvec); + + //NLMISC::CHTimer::startBench(); + + s_Source = s_AudioMixer->createSource(CStringMapper::map("stream_file")); + nlassert(s_Source); + s_StreamFileSource = dynamic_cast(s_Source); + nlassert(s_StreamFileSource); + // s_Source->setSourceRelativeMode(true); + // s_Source->setPitch(2.0f); + + s_GroupController = s_AudioMixer->getGroupController("dialog"); +} + +static void runSample() +{ + s_Source->play(); + + printf("Change volume with - and +\n"); + printf("Press ANY other key to exit\n"); + for (; ; ) + { + if (_kbhit()) + { + switch (_getch()) + { + case '+': + s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); + break; + case '-': + s_GroupController->setUserGain(s_GroupController->getUserGain() - 0.1f); + break; + case 'x': + s_Source->stop(); + break; + case 's': + s_Source->play(); + break; + case 'p': + s_StreamFileSource->pause(); + break; + case 'r': + s_StreamFileSource->resume(); + break; + default: + return; + } + } + + s_AudioMixer->update(); + + nlSleep(40); + } +} + +static void releaseSample() +{ + //NLMISC::CHTimer::clear(); + s_GroupController = NULL; + s_StreamFileSource = NULL; + delete s_Source; s_Source = NULL; + delete s_AudioMixer; s_AudioMixer = NULL; +} + + + +} /* namespace NLSAMPLE */ + +int main() +{ + NLSAMPLE::initSample(); + NLSAMPLE::runSample(); + NLSAMPLE::releaseSample(); + return 0; +} + +/* end of file */ From 2f504788f0ed816bbb51580c265123ccae42451d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 18:07:16 +0200 Subject: [PATCH 49/77] Added: #795 Synchronous loading of audio file streams --HG-- branch : sound_dev --- .../include/nel/sound/stream_file_source.h | 1 + code/nel/src/sound/stream_file_source.cpp | 60 ++++++++++++++----- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h index 16153d406..69e85fc8b 100644 --- a/code/nel/include/nel/sound/stream_file_source.h +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -81,6 +81,7 @@ public: // TODO: getTime private: + void prepareDecoder(); void bufferMore(uint bytes); private: diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 4514bf4c5..057e20894 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -84,12 +84,32 @@ void CStreamFileSource::play() // thread may be stopping from stop call m_Thread->wait(); } - nlassert(!_Playing); + else + { + nlwarning("Already playing"); + } + if (!getStreamFileSound()->getAsync()) + prepareDecoder(); + // else load audiodecoder in thread m_WaitingForPlay = true; m_Thread->start(); m_Thread->setPriority(NLMISC::ThreadPriorityHighest); - CAudioMixerUser *mixer = CAudioMixerUser::instance(); - mixer->addSourceWaitingForPlay(this); + if (!getStreamFileSound()->getAsync()) + { + // wait until at least one buffer is ready + while (!(m_NextBuffer || !m_FreeBuffers)) + NLMISC::nlSleep(10); + CStreamSource::play(); + if (!_Playing) + { + nlwarning("Failed to synchronously start playing a file stream source. This happens when all physical tracks are in use. Use a Highest Priority sound"); + } + } + else + { + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->addSourceWaitingForPlay(this); + } } /*if (!m_WaitingForPlay) @@ -162,21 +182,10 @@ bool CStreamFileSource::isEnded() return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused); } -void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) +void CStreamFileSource::prepareDecoder() { - uint8 *buffer = this->lock(bytes * 2); - if (buffer) - { - uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2); - this->unlock(result); - } -} + // creates a new decoder or keeps going with the current decoder if the stream was paused -void CStreamFileSource::run() -{ - nldebug("run"); - - bool looping = _Looping; if (m_Paused) { // handle paused! @@ -199,6 +208,25 @@ void CStreamFileSource::run() } this->setFormat(m_AudioDecoder->getChannels(), m_AudioDecoder->getBitsPerSample(), (uint32)m_AudioDecoder->getSamplesPerSec()); } +} + +void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) +{ + uint8 *buffer = this->lock(bytes * 2); + if (buffer) + { + uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2); + this->unlock(result); + } +} + +void CStreamFileSource::run() +{ + nldebug("run"); + + bool looping = _Looping; + if (getStreamFileSound()->getAsync()) + prepareDecoder(); uint samples, bytes; this->getRecommendedBufferSize(samples, bytes); bufferMore(bytes); From 695dac77838382f6998fc6185caeeb299f9054b4 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 19:30:37 +0200 Subject: [PATCH 50/77] Added: #1460 Music channel on top of stream file source when driver does not have built-in music channels --HG-- branch : sound_dev --- .../include/nel/sound/driver/music_channel.h | 3 + .../include/nel/sound/music_channel_fader.h | 2 + .../include/nel/sound/source_music_channel.h | 100 ++++++++++++++ .../include/nel/sound/stream_file_source.h | 4 + code/nel/include/nel/sound/stream_source.h | 3 + .../samples/sound/stream_file/stream_file.cpp | 5 + code/nel/src/sound/CMakeLists.txt | 1 + code/nel/src/sound/audio_mixer_user.cpp | 4 +- .../sound/driver/fmod/music_channel_fmod.cpp | 6 + .../sound/driver/fmod/music_channel_fmod.h | 3 + code/nel/src/sound/music_channel_fader.cpp | 23 +++- code/nel/src/sound/sound.cpp | 3 +- code/nel/src/sound/source_music_channel.cpp | 123 ++++++++++++++++++ code/nel/src/sound/stream_file_sound.cpp | 2 + code/nel/src/sound/stream_file_source.cpp | 13 ++ code/nel/src/sound/stream_source.cpp | 16 +++ 16 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 code/nel/include/nel/sound/source_music_channel.h create mode 100644 code/nel/src/sound/source_music_channel.cpp diff --git a/code/nel/include/nel/sound/driver/music_channel.h b/code/nel/include/nel/sound/driver/music_channel.h index 9878744c5..116865628 100644 --- a/code/nel/include/nel/sound/driver/music_channel.h +++ b/code/nel/include/nel/sound/driver/music_channel.h @@ -45,6 +45,9 @@ public: /// Stop the music previously loaded and played (the Memory is also freed) virtual void stop() =0; + /// Makes sure any resources are freed, but keeps available for next play call + virtual void reset() =0; + /// Pause the music previously loaded and played (the Memory is not freed) virtual void pause() =0; diff --git a/code/nel/include/nel/sound/music_channel_fader.h b/code/nel/include/nel/sound/music_channel_fader.h index 4e6d35de4..8513c21e5 100644 --- a/code/nel/include/nel/sound/music_channel_fader.h +++ b/code/nel/include/nel/sound/music_channel_fader.h @@ -87,6 +87,8 @@ public: void init(ISoundDriver *soundDriver); void release(); + void reset(); + void update(); // time in seconds inline bool isInitOk() { return _SoundDriver != NULL; } diff --git a/code/nel/include/nel/sound/source_music_channel.h b/code/nel/include/nel/sound/source_music_channel.h new file mode 100644 index 000000000..7ec71fb66 --- /dev/null +++ b/code/nel/include/nel/sound/source_music_channel.h @@ -0,0 +1,100 @@ +/** + * \file source_music_channel.h + * \brief CSourceMusicChannel + * \date 2012-04-11 16:08GMT + * \author Jan Boon (Kaetemi) + * CSourceMusicChannel + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_SOURCE_MUSIC_CHANNEL_H +#define NLSOUND_SOURCE_MUSIC_CHANNEL_H +#include + +// STL includes + +// NeL includes +#include +#include + +// Project includes + +namespace NLSOUND { + class CStreamFileSource; + +/** + * \brief CSourceMusicChannel + * \date 2012-04-11 16:08GMT + * \author Jan Boon (Kaetemi) + * CSourceMusicChannel + */ +class CSourceMusicChannel : public IMusicChannel +{ +public: + CSourceMusicChannel(); + virtual ~CSourceMusicChannel(); + + /** Play some music (.ogg etc...) + * NB: if an old music was played, it is first stop with stopMusic() + * \param filepath file path, CPath::lookup is done here + * \param async stream music from hard disk, preload in memory if false + * \param loop must be true to play the music in loop. + */ + virtual bool play(const std::string &filepath, bool async, bool loop); + + /// Stop the music previously loaded and played (the Memory is also freed) + virtual void stop(); + + /// Makes sure any resources are freed, but keeps available for next play call + virtual void reset(); + + /// Pause the music previously loaded and played (the Memory is not freed) + virtual void pause(); + + /// Resume the music previously paused + virtual void resume(); + + /// Return true if a song is finished. + virtual bool isEnded(); + + /// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading + virtual bool isLoadingAsync(); + + /// Return the total length (in second) of the music currently played + virtual float getLength(); + + /** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1) + * NB: the volume of music is NOT affected by IListener::setGain() + */ + virtual void setVolume(float gain); + +private: + CStreamFileSound m_Sound; + CStreamFileSource *m_Source; + float m_Gain; + +}; /* class CSourceMusicChannel */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_SOURCE_MUSIC_CHANNEL_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h index 69e85fc8b..4bce66719 100644 --- a/code/nel/include/nel/sound/stream_file_source.h +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -70,6 +70,10 @@ public: void resume(); /// check if song ended (following legacy music channel implementation) bool isEnded(); + /// (following legacy music channel implementation) + float getLength(); + /// check if still loading (following legacy music channel implementation) + bool isLoadingAsync(); //@} /// \name Decoding thread diff --git a/code/nel/include/nel/sound/stream_source.h b/code/nel/include/nel/sound/stream_source.h index d0a31cf38..1c94a5b7a 100644 --- a/code/nel/include/nel/sound/stream_source.h +++ b/code/nel/include/nel/sound/stream_source.h @@ -107,6 +107,9 @@ public: virtual bool hasFilledBuffersAvailable() const; //@} + /// Prepare the buffers in this stream for the given maximum capacity. (TODO: Move this into UStreamSource) + void preAllocate(uint capacity); + /// Return the track CTrack *getTrack() { return m_Track; } diff --git a/code/nel/samples/sound/stream_file/stream_file.cpp b/code/nel/samples/sound/stream_file/stream_file.cpp index bdc866538..6ded512eb 100644 --- a/code/nel/samples/sound/stream_file/stream_file.cpp +++ b/code/nel/samples/sound/stream_file/stream_file.cpp @@ -42,6 +42,8 @@ #define NL_SOUND_DATA "." #endif // NL_SOUND_DATA +#define SAMPLE_OGG "D:/source/kaetemi/toverhex/src/samples/music_stream/data/aeon_1_10_mystic_river.ogg" + using namespace std; using namespace NLMISC; using namespace NLSOUND; @@ -130,6 +132,9 @@ static void runSample() case 'r': s_StreamFileSource->resume(); break; + case 'e': + s_AudioMixer->playMusic(SAMPLE_OGG, 1000, true, false); + break; default: return; } diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index fae6ca7a6..e73d0828e 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -38,6 +38,7 @@ FILE(GLOB MUSIC music_sound.cpp ../../include/nel/sound/music_sound.h music_sound_manager.cpp ../../include/nel/sound/music_sound_manager.h music_source.cpp ../../include/nel/sound/music_source.h + source_music_channel.cpp ../../include/nel/sound/source_music_channel.h ) FILE(GLOB SOUND diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index 5d938a036..f47f70413 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -269,8 +269,8 @@ void CAudioMixerUser::reset() _Leaving = true; _SourceWaitingForPlay.clear(); - - /* TODO: Stop music channels */ + + _MusicChannelFaders->reset(); // Stop tracks uint i; diff --git a/code/nel/src/sound/driver/fmod/music_channel_fmod.cpp b/code/nel/src/sound/driver/fmod/music_channel_fmod.cpp index 9c502ea1a..a64dddcfe 100644 --- a/code/nel/src/sound/driver/fmod/music_channel_fmod.cpp +++ b/code/nel/src/sound/driver/fmod/music_channel_fmod.cpp @@ -249,6 +249,12 @@ void CMusicChannelFMod::stop() _CallBackEnded = false; } +void CMusicChannelFMod::reset() +{ + // don't care + stop(); +} + /** Pause the music previously loaded and played (the Memory is not freed) */ void CMusicChannelFMod::pause() diff --git a/code/nel/src/sound/driver/fmod/music_channel_fmod.h b/code/nel/src/sound/driver/fmod/music_channel_fmod.h index d9a17fd99..e91e38a8b 100644 --- a/code/nel/src/sound/driver/fmod/music_channel_fmod.h +++ b/code/nel/src/sound/driver/fmod/music_channel_fmod.h @@ -87,6 +87,9 @@ public: /// Stop the music previously loaded and played (the Memory is also freed) virtual void stop(); + /// Makes sure any resources are freed, but keeps available for next play call + virtual void reset(); + /// Pause the music previously loaded and played (the Memory is not freed) virtual void pause(); diff --git a/code/nel/src/sound/music_channel_fader.cpp b/code/nel/src/sound/music_channel_fader.cpp index ced739093..2fe9fb13c 100644 --- a/code/nel/src/sound/music_channel_fader.cpp +++ b/code/nel/src/sound/music_channel_fader.cpp @@ -20,6 +20,7 @@ // Project includes #include "nel/sound/driver/sound_driver.h" #include "nel/sound/driver/music_channel.h" +#include "nel/sound/source_music_channel.h" using namespace std; using namespace NLMISC; @@ -49,9 +50,16 @@ void CMusicChannelFader::init(ISoundDriver *soundDriver) _MusicFader[i].MusicChannel = _SoundDriver->createMusicChannel(); if (!_MusicFader[i].MusicChannel) { - release(); - nlwarning("No music channel available!"); - return; + if (_SoundDriver->getOption(ISoundDriver::OptionHasBufferStreaming)) + { + _MusicFader[i].MusicChannel = new CSourceMusicChannel(); + } + else + { + release(); + nlwarning("No music channel available!"); + return; + } } } } @@ -69,6 +77,15 @@ void CMusicChannelFader::release() } } +void CMusicChannelFader::reset() +{ + for (uint i = 0; i < _MaxMusicFader; ++i) if (_MusicFader[i].MusicChannel) + { + if (_MusicFader[i].MusicChannel) + _MusicFader[i].MusicChannel->reset(); + } +} + void CMusicChannelFader::update() { TTime current_time = CTime::getLocalTime(); diff --git a/code/nel/src/sound/sound.cpp b/code/nel/src/sound/sound.cpp index 4d753bb96..b1b644ed2 100644 --- a/code/nel/src/sound/sound.cpp +++ b/code/nel/src/sound/sound.cpp @@ -115,7 +115,8 @@ CSound::CSound() : _Looping(false), _MinDist(1.0f), _MaxDist(1000000.0f), - _UserVarControler(CStringMapper::emptyId()) + _UserVarControler(CStringMapper::emptyId()), + _GroupController(NULL) { } diff --git a/code/nel/src/sound/source_music_channel.cpp b/code/nel/src/sound/source_music_channel.cpp new file mode 100644 index 000000000..4395f3b5e --- /dev/null +++ b/code/nel/src/sound/source_music_channel.cpp @@ -0,0 +1,123 @@ +/** + * \file source_music_channel.cpp + * \brief CSourceMusicChannel + * \date 2012-04-11 16:08GMT + * \author Jan Boon (Kaetemi) + * CSourceMusicChannel + */ + +/* + * Copyright (C) 2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * RYZOM CORE 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +// #include +#include + +// Project includes + +using namespace std; +// using namespace NLMISC; + +namespace NLSOUND { + +CSourceMusicChannel::CSourceMusicChannel() : m_Source(NULL), m_Gain(1.0f) +{ + +} + +CSourceMusicChannel::~CSourceMusicChannel() +{ + delete m_Source; + m_Source = NULL; +} + +bool CSourceMusicChannel::play(const std::string &filepath, bool async, bool loop) +{ + if (m_Source) + delete m_Source; + + m_Sound.setMusicFilePath(filepath, async, loop); + + m_Source = new CStreamFileSource(&m_Sound, false, NULL, NULL, NULL, NULL); + + m_Source->play(); + + return m_Source->isPlaying(); +} + +void CSourceMusicChannel::stop() +{ + if (m_Source) + m_Source->stop(); +} + +void CSourceMusicChannel::reset() +{ + delete m_Source; + m_Source = NULL; +} + +void CSourceMusicChannel::pause() +{ + if (m_Source) + m_Source->pause(); +} + +void CSourceMusicChannel::resume() +{ + if (m_Source) + m_Source->resume(); +} + +bool CSourceMusicChannel::isEnded() +{ + if (m_Source) + return m_Source->isEnded(); + return true; +} + +bool CSourceMusicChannel::isLoadingAsync() +{ + if (m_Source) + return m_Source->isLoadingAsync(); + return false; +} + +float CSourceMusicChannel::getLength() +{ + if (m_Source) + return m_Source->getLength(); + return 0.0f; +} + +void CSourceMusicChannel::setVolume(float gain) +{ + m_Gain = gain; + if (m_Source) + m_Source->setRelativeGain(gain); +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/stream_file_sound.cpp b/code/nel/src/sound/stream_file_sound.cpp index 7640d7893..4fc17b51c 100644 --- a/code/nel/src/sound/stream_file_sound.cpp +++ b/code/nel/src/sound/stream_file_sound.cpp @@ -32,6 +32,7 @@ // NeL includes // #include +#include // Project includes @@ -84,6 +85,7 @@ void CStreamFileSound::setMusicFilePath(const std::string &filePath, bool async, _MinDist = 1000.0f; m_Async = async; m_FilePath = filePath; + _GroupController = CGroupControllerRoot::getInstance()->getGroupController(NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER); } } /* namespace NLSOUND */ diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 057e20894..35377f0db 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -182,6 +182,16 @@ bool CStreamFileSource::isEnded() return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused); } +float CStreamFileSource::getLength() +{ + return m_AudioDecoder->getLength(); +} + +bool CStreamFileSource::isLoadingAsync() +{ + return m_WaitingForPlay; +} + void CStreamFileSource::prepareDecoder() { // creates a new decoder or keeps going with the current decoder if the stream was paused @@ -208,6 +218,9 @@ void CStreamFileSource::prepareDecoder() } this->setFormat(m_AudioDecoder->getChannels(), m_AudioDecoder->getBitsPerSample(), (uint32)m_AudioDecoder->getSamplesPerSec()); } + uint samples, bytes; + this->getRecommendedBufferSize(samples, bytes); + this->preAllocate(bytes * 2); } void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 7f6b0c608..7d35d06db 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -50,8 +50,11 @@ CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCal CAudioMixerUser *mixer = CAudioMixerUser::instance(); ISoundDriver *driver = mixer->getSoundDriver(); m_Buffers[0] = driver->createBuffer(); + m_Buffers[0]->setStorageMode(IBuffer::StorageSoftware); m_Buffers[1] = driver->createBuffer(); + m_Buffers[1]->setStorageMode(IBuffer::StorageSoftware); m_Buffers[2] = driver->createBuffer(); + m_Buffers[2]->setStorageMode(IBuffer::StorageSoftware); } CStreamSource::~CStreamSource() @@ -431,6 +434,19 @@ bool CStreamSource::hasFilledBuffersAvailable() const return m_FreeBuffers < 3; } +void CStreamSource::preAllocate(uint capacity) +{ + uint8 *b0 = m_Buffers[0]->lock(capacity); + memset(b0, 0, capacity); + m_Buffers[0]->unlock(capacity); + uint8 *b1 = m_Buffers[1]->lock(capacity); + memset(b1, 0, capacity); + m_Buffers[1]->unlock(capacity); + uint8 *b2 = m_Buffers[2]->lock(capacity); + memset(b2, 0, capacity); + m_Buffers[2]->unlock(capacity); +} + } /* namespace NLSOUND */ /* end of file */ From c03655b0ba1574f27d0935b534e25bee9bf6253a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 20:20:02 +0200 Subject: [PATCH 51/77] Added: Some sort of implementation for CPThread::isRunning() --HG-- branch : sound_dev --- code/nel/include/nel/misc/p_thread.h | 3 ++- code/nel/src/misc/p_thread.cpp | 37 ++++++++++++++++------------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/code/nel/include/nel/misc/p_thread.h b/code/nel/include/nel/misc/p_thread.h index 82f6a4ee3..a4db1f55f 100644 --- a/code/nel/include/nel/misc/p_thread.h +++ b/code/nel/include/nel/misc/p_thread.h @@ -59,8 +59,9 @@ public: /// Internal use IRunnable *Runnable; + uint8 _StateV2; // 0=not created, 1=started, 2=ended, 3=finished + private: - uint8 _State; // 0=not created, 1=started, 2=finished uint32 _StackSize; pthread_t _ThreadHandle; }; diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index e20981c98..77866471e 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -84,6 +84,8 @@ static void *ProxyFunc( void *arg ) // Run the code of the thread parent->Runnable->run(); + parent->_StateV2 = 2; + // Allow some clean // pthread_exit(0); return NULL; @@ -96,7 +98,7 @@ static void *ProxyFunc( void *arg ) */ CPThread::CPThread(IRunnable *runnable, uint32 stackSize) : Runnable(runnable), - _State(0), + _StateV2(0), _StackSize(stackSize) {} @@ -106,10 +108,10 @@ CPThread::CPThread(IRunnable *runnable, uint32 stackSize) */ CPThread::~CPThread() { - if(_State == 1) + if(_StateV2 == 1 || _StateV2 == 2) terminate(); // force the end of the thread if not already ended - if(_State > 0) + if(_StateV2 > 0) pthread_detach(_ThreadHandle); // free allocated resources only if it was created } @@ -132,13 +134,14 @@ void CPThread::start() { throw EThread("Cannot start new thread"); } - _State = 1; + _StateV2 = 1; } bool CPThread::isRunning() { - // TODO : need a real implementation here that check thread status - return _State == 1; + // ExTODO : need a real implementation here that check thread status + // DONE : some sort of implementation + return _StateV2 == 1; } /* @@ -146,11 +149,11 @@ bool CPThread::isRunning() */ void CPThread::terminate() { - if(_State == 1) + if (_StateV2 == 1 || _StateV2 == 2) { // cancel only if started pthread_cancel(_ThreadHandle); - _State = 2; // set to finished + _StateV2 = 3; // set to finished } } @@ -159,13 +162,13 @@ void CPThread::terminate() */ void CPThread::wait () { - if(_State == 1) + if (_StateV2 == 1 || _StateV2 == 2) { if(pthread_join(_ThreadHandle, 0) != 0) { throw EThread( "Cannot join with thread" ); } - _State = 2; // set to finished + _StateV2 = 3; // set to finished } } @@ -209,27 +212,29 @@ uint64 CPThread::getCPUMask() void CPThread::setPriority(TThreadPriority priority) { - // TODO: Verify and test this + // TODO: Test this + sched_param sp; switch (priority) { case ThreadPriorityHigh: { int minPrio = sched_get_priority_min(SCHED_FIFO); int maxPrio = sched_get_priority_max(SCHED_FIFO); - int prio = ((maxPrio - minPrio) / 4) + minPrio; - pthread_setschedparam(_ThreadHandle, SCHED_FIFO, prio); + sp.sched_priority = ((maxPrio - minPrio) / 4) + minPrio; + pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp); break; } case ThreadPriorityHighest: { int minPrio = sched_get_priority_min(SCHED_FIFO); int maxPrio = sched_get_priority_max(SCHED_FIFO); - int prio = ((maxPrio - minPrio) / 2) + minPrio; - pthread_setschedparam(_ThreadHandle, SCHED_FIFO, prio); + sp.sched_priority = ((maxPrio - minPrio) / 2) + minPrio; + pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp); break; } default: - pthread_setschedparam(_ThreadHandle, SCHED_OTHER, 0); + sp.sched_priority = 0; + pthread_setschedparam(_ThreadHandle, SCHED_OTHER, &sp); } } From 41968b06747fbf54bf317332e2b641ed31801171 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 21:49:25 +0200 Subject: [PATCH 52/77] Fixed: Compile error --HG-- branch : sound_dev --- code/ryzom/client/src/bg_downloader_access.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/client/src/bg_downloader_access.cpp b/code/ryzom/client/src/bg_downloader_access.cpp index 699f01f3a..84fb0af5a 100644 --- a/code/ryzom/client/src/bg_downloader_access.cpp +++ b/code/ryzom/client/src/bg_downloader_access.cpp @@ -764,7 +764,7 @@ bool CBGDownloaderAccess::CDownloadCoTask::defaultMessageHandling(BGDownloader:: { case BGD_Priority: { - TThreadPriority tp; + BGDownloader::TThreadPriority tp; msg.serialEnum(tp); if (tp != Parent->_DownloadThreadPriority) { From 1b49b59e0244d02abf55ad8b8de7c456d23be059 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 21:51:01 +0200 Subject: [PATCH 53/77] Fixed: #795 Hang when stopping file stream source that is synchronously waiting for play --HG-- branch : sound_dev --- code/nel/src/sound/stream_file_source.cpp | 14 ++++++++++---- code/nel/src/sound/stream_source.cpp | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 35377f0db..6a8c6b7d2 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -62,10 +62,10 @@ void CStreamFileSource::play() { // note: CStreamSource will assert crash if already physically playing! - nldebug("play"); if (m_Thread->isRunning() && m_WaitingForPlay) { + nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str()); if (m_NextBuffer || !m_FreeBuffers) { CStreamSource::play(); @@ -79,6 +79,7 @@ void CStreamFileSource::play() } else if (!_Playing) { + nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str()); if (!m_WaitingForPlay) { // thread may be stopping from stop call @@ -86,7 +87,7 @@ void CStreamFileSource::play() } else { - nlwarning("Already playing"); + nlwarning("Already waiting for play"); } if (!getStreamFileSound()->getAsync()) prepareDecoder(); @@ -97,7 +98,7 @@ void CStreamFileSource::play() if (!getStreamFileSound()->getAsync()) { // wait until at least one buffer is ready - while (!(m_NextBuffer || !m_FreeBuffers)) + while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay) NLMISC::nlSleep(10); CStreamSource::play(); if (!_Playing) @@ -111,6 +112,11 @@ void CStreamFileSource::play() mixer->addSourceWaitingForPlay(this); } } + else + { + nlwarning("Already playing"); + } + /*if (!m_WaitingForPlay) { @@ -127,7 +133,7 @@ void CStreamFileSource::play() void CStreamFileSource::stop() { - nldebug("stop"); + nldebug("stop %s", getStreamFileSound()->getFilePath().c_str()); CStreamSource::stop(); diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 7d35d06db..1cd1ddcbc 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -264,6 +264,8 @@ void CStreamSource::stop() _SpawnEndCb(this, _CbUserParam); delete this; } + + m_WaitingForPlay = false; } void CStreamSource::setPos(const NLMISC::CVector& pos) From c0c5ee6d93cafe0208cf5fb41cf2ef58ee189413 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 21:55:33 +0200 Subject: [PATCH 54/77] Fixed: Implementation for CPThread::isRunning() --HG-- branch : sound_dev --- code/nel/include/nel/misc/p_thread.h | 10 +++- code/nel/src/misc/p_thread.cpp | 85 +++++++++++++++++++++------- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/code/nel/include/nel/misc/p_thread.h b/code/nel/include/nel/misc/p_thread.h index a4db1f55f..7e6a4d5a5 100644 --- a/code/nel/include/nel/misc/p_thread.h +++ b/code/nel/include/nel/misc/p_thread.h @@ -36,6 +36,12 @@ namespace NLMISC { class CPThread : public IThread { public: + enum TThreadState + { + ThreadStateNone, + ThreadStateRunning, + ThreadStateFinished, + }; /// Constructor CPThread( IRunnable *runnable, uint32 stackSize); @@ -59,11 +65,11 @@ public: /// Internal use IRunnable *Runnable; - uint8 _StateV2; // 0=not created, 1=started, 2=ended, 3=finished + TThreadState _State; + pthread_t _ThreadHandle; private: uint32 _StackSize; - pthread_t _ThreadHandle; }; /** diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index 77866471e..9524ca0bb 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -84,7 +84,16 @@ static void *ProxyFunc( void *arg ) // Run the code of the thread parent->Runnable->run(); - parent->_StateV2 = 2; + { + pthread_t thread_self = pthread_self(); + // Make sure the parent still cares + // If this thread was replaced with a new thread (which should not happen), + // and the IThread object has been deleted, this will likely crash. + if (parent->_ThreadHandle == thread_self) + parent->_State = CPThread::ThreadStateFinished; + else + throw EThread("Thread ended after being detached, this should not happen"); + } // Allow some clean // pthread_exit(0); @@ -98,7 +107,7 @@ static void *ProxyFunc( void *arg ) */ CPThread::CPThread(IRunnable *runnable, uint32 stackSize) : Runnable(runnable), - _StateV2(0), + _State(ThreadStateNone), _StackSize(stackSize) {} @@ -108,10 +117,9 @@ CPThread::CPThread(IRunnable *runnable, uint32 stackSize) */ CPThread::~CPThread() { - if(_StateV2 == 1 || _StateV2 == 2) - terminate(); // force the end of the thread if not already ended + terminate(); // force the end of the thread if not already ended - if(_StateV2 > 0) + if (_State != ThreadStateNone) pthread_detach(_ThreadHandle); // free allocated resources only if it was created } @@ -121,27 +129,51 @@ CPThread::~CPThread() void CPThread::start() { pthread_attr_t tattr; - pthread_t tid; int ret; - /* initialized with default attributes */ - ret = pthread_attr_init(&tattr); + if (_StackSize != 0) + { + /* initialized with default attributes */ + ret = pthread_attr_init(&tattr); - /* setting the size of the stack also */ - ret = pthread_attr_setstacksize(&tattr, _StackSize); + /* setting the size of the stack also */ + ret = pthread_attr_setstacksize(&tattr, _StackSize); + } + + bool detach_old_thread = false; + pthread_t old_thread_handle; + if (_State != ThreadStateNone) + { + if (_State == ThreadStateRunning) + { + // I don't know if this behaviour is allowed, but neither thread implementations + // check the start function, and both simply let the existing running thread for what it is... + // From now on, this is not allowed. + throw EThread("Starting a thread that is already started, existing thread will continue running, this should not happen"); + } + detach_old_thread = true; + old_thread_handle = _ThreadHandle; + } - if(pthread_create(&_ThreadHandle, _StackSize != 0 ? &tattr : 0, ProxyFunc, this) != 0) + if (pthread_create(&_ThreadHandle, _StackSize != 0 ? &tattr : NULL, ProxyFunc, this) != 0) { throw EThread("Cannot start new thread"); } - _StateV2 = 1; + _State = ThreadStateRunning; + + if (detach_old_thread) + { + // Docs don't say anything about what happens when pthread_create is called with existing handle referenced. + if (old_thread_handle == _ThreadHandle) + throw EThread("Thread handle did not change, this should not happen"); + // Don't care about old thread, free resources when it terminates. + pthread_detach(old_thread_handle); + } } bool CPThread::isRunning() { - // ExTODO : need a real implementation here that check thread status - // DONE : some sort of implementation - return _StateV2 == 1; + return _State == ThreadStateRunning; } /* @@ -149,11 +181,11 @@ bool CPThread::isRunning() */ void CPThread::terminate() { - if (_StateV2 == 1 || _StateV2 == 2) + if (_State == ThreadStateRunning) { // cancel only if started pthread_cancel(_ThreadHandle); - _StateV2 = 3; // set to finished + _State = ThreadStateFinished; // set to finished } } @@ -162,13 +194,24 @@ void CPThread::terminate() */ void CPThread::wait () { - if (_StateV2 == 1 || _StateV2 == 2) + if (_State == ThreadStateRunning) { - if(pthread_join(_ThreadHandle, 0) != 0) + int error = pthread_join(_ThreadHandle, 0) != 0; + switch (error) { - throw EThread( "Cannot join with thread" ); + case 0: + break; + case EINVAL: + throw EThread("Thread is not joinable"); + case ESRCH: + throw EThread("No thread found with this id"); + case EDEADLK: + throw EThread("Deadlock detected or calling thread waits for itself"); + default: + throw EThread("Unknown thread join error"); } - _StateV2 = 3; // set to finished + if(_State != ThreadStateFinished) + throw EThread("Thread did not finish, this should not happen"); } } From 80bf70efbb2159658e02436239e94283a33cfe35 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 22:05:18 +0200 Subject: [PATCH 55/77] Changed: Make CWinThread::start more sane, and fixed a typo --HG-- branch : sound_dev --- code/nel/src/misc/p_thread.cpp | 2 +- code/nel/src/misc/win_thread.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/code/nel/src/misc/p_thread.cpp b/code/nel/src/misc/p_thread.cpp index 9524ca0bb..f752f4bdf 100644 --- a/code/nel/src/misc/p_thread.cpp +++ b/code/nel/src/misc/p_thread.cpp @@ -196,7 +196,7 @@ void CPThread::wait () { if (_State == ThreadStateRunning) { - int error = pthread_join(_ThreadHandle, 0) != 0; + int error = pthread_join(_ThreadHandle, 0); switch (error) { case 0: diff --git a/code/nel/src/misc/win_thread.cpp b/code/nel/src/misc/win_thread.cpp index f8a961a74..c9bfc90a1 100644 --- a/code/nel/src/misc/win_thread.cpp +++ b/code/nel/src/misc/win_thread.cpp @@ -190,6 +190,9 @@ CWinThread::~CWinThread () void CWinThread::start () { + if (isRunning()) + throw EThread("Starting a thread that is already started, existing thread will continue running, this should not happen"); + // ThreadHandle = (void *) ::CreateThread (NULL, _StackSize, ProxyFunc, this, 0, (DWORD *)&ThreadId); ThreadHandle = (void *) ::CreateThread (NULL, 0, ProxyFunc, this, 0, (DWORD *)&ThreadId); // nldebug("NLMISC: thread %x started for runnable '%x'", typeid( Runnable ).name()); From b486e22a96293d10fad3c92fabdce27f241193c4 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 23:34:36 +0200 Subject: [PATCH 56/77] Fixed: Typos and warnings --HG-- branch : sound_dev --- code/nel/include/nel/sound/group_controller.h | 2 +- code/nel/include/nel/sound/stream_file_sound.h | 2 +- code/nel/src/sound/audio_decoder_vorbis.cpp | 2 +- code/nel/src/sound/driver/CMakeLists.txt | 2 ++ code/nel/src/sound/group_controller.cpp | 3 ++- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/code/nel/include/nel/sound/group_controller.h b/code/nel/include/nel/sound/group_controller.h index c3ac4838f..08d474129 100644 --- a/code/nel/include/nel/sound/group_controller.h +++ b/code/nel/include/nel/sound/group_controller.h @@ -52,7 +52,7 @@ namespace NLSOUND { class CGroupController : public UGroupController { public: - friend CGroupControllerRoot; + friend class CGroupControllerRoot; private: CGroupController *m_Parent; diff --git a/code/nel/include/nel/sound/stream_file_sound.h b/code/nel/include/nel/sound/stream_file_sound.h index 2429c3382..8c1cc1634 100644 --- a/code/nel/include/nel/sound/stream_file_sound.h +++ b/code/nel/include/nel/sound/stream_file_sound.h @@ -48,7 +48,7 @@ namespace NLSOUND { class CStreamFileSound : public CStreamSound { public: - friend CSourceMusicChannel; + friend class CSourceMusicChannel; public: CStreamFileSound(); diff --git a/code/nel/src/sound/audio_decoder_vorbis.cpp b/code/nel/src/sound/audio_decoder_vorbis.cpp index c54ce0ade..e0b950fc4 100644 --- a/code/nel/src/sound/audio_decoder_vorbis.cpp +++ b/code/nel/src/sound/audio_decoder_vorbis.cpp @@ -102,7 +102,7 @@ static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { }; CAudioDecoderVorbis::CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop) -: _Stream(stream), _Loop(loop), _StreamSize(0), _IsMusicEnded(false) +: _Stream(stream), _Loop(loop), _IsMusicEnded(false), _StreamSize(0) { _StreamOffset = stream->getPos(); stream->seek(0, NLMISC::IStream::end); diff --git a/code/nel/src/sound/driver/CMakeLists.txt b/code/nel/src/sound/driver/CMakeLists.txt index d1d82169c..90fbbb562 100644 --- a/code/nel/src/sound/driver/CMakeLists.txt +++ b/code/nel/src/sound/driver/CMakeLists.txt @@ -3,6 +3,8 @@ FILE(GLOB HEADERS ../../../include/nel/sound/driver/*.h) NL_TARGET_LIB(nelsnd_lowlevel ${HEADERS} ${SRC}) +TARGET_LINK_LIBRARIES(nelsnd_lowlevel nelmisc) + SET_TARGET_PROPERTIES(nelsnd_lowlevel PROPERTIES LINK_INTERFACE_LIBRARIES "") NL_DEFAULT_PROPS(nelsnd_lowlevel "NeL, Library: Sound Lowlevel") NL_ADD_RUNTIME_FLAGS(nelsnd_lowlevel) diff --git a/code/nel/src/sound/group_controller.cpp b/code/nel/src/sound/group_controller.cpp index 1cce28bf0..fef67a01b 100644 --- a/code/nel/src/sound/group_controller.cpp +++ b/code/nel/src/sound/group_controller.cpp @@ -87,7 +87,8 @@ std::string CGroupController::getPath() // overridden by root return returnPath; } } - + nlerror("Group Controller not child of parent"); + return ""; } void CGroupController::calculateFinalGain() // overridden by root From d0314c9e0e1f600f3fca3a11a4297b6d32299c36 Mon Sep 17 00:00:00 2001 From: kervala Date: Wed, 11 Apr 2012 23:43:11 +0200 Subject: [PATCH 57/77] Fixed: Servers compilation if using static mysql library compiled with OpenSSL --- code/CMakeModules/FindMySQL.cmake | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/CMakeModules/FindMySQL.cmake b/code/CMakeModules/FindMySQL.cmake index b9970f63c..a00e36992 100644 --- a/code/CMakeModules/FindMySQL.cmake +++ b/code/CMakeModules/FindMySQL.cmake @@ -54,10 +54,14 @@ ELSE(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) IF(MYSQL_INCLUDE_DIR) IF(MYSQL_LIBRARY_RELEASE) - SET(MYSQL_LIBRARIES "optimized;${MYSQL_LIBRARY_RELEASE}") + SET(MYSQL_LIBRARIES optimized ${MYSQL_LIBRARY_RELEASE}) IF(MYSQL_LIBRARY_DEBUG) - SET(MYSQL_LIBRARIES "${MYSQL_LIBRARIES};debug;${MYSQL_LIBRARY_DEBUG}") + SET(MYSQL_LIBRARIES ${MYSQL_LIBRARIES} debug ${MYSQL_LIBRARY_DEBUG}) ENDIF(MYSQL_LIBRARY_DEBUG) + FIND_PACKAGE(OpenSSL) + IF(OPENSSL_FOUND) + SET(MYSQL_LIBRARIES ${MYSQL_LIBRARIES} ${OPENSSL_LIBRARIES}) + ENDIF(OPENSSL_FOUND) ENDIF(MYSQL_LIBRARY_RELEASE) ENDIF(MYSQL_INCLUDE_DIR) From eeecc066018690fec6bc0ace47ed8d436759ffa8 Mon Sep 17 00:00:00 2001 From: kervala Date: Wed, 11 Apr 2012 23:59:55 +0200 Subject: [PATCH 58/77] Changed: #1448 Compilation with Clang --- code/CMakeModules/nel.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index e6277c2b5..590b07b71 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -477,7 +477,11 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m64") ENDIF(HOST_CPU STREQUAL "x86" AND TARGET_CPU STREQUAL "x86_64") - SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + + IF(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -ansi") + ENDIF(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") IF(WITH_COVERAGE) SET(PLATFORM_CFLAGS "-fprofile-arcs -ftest-coverage ${PLATFORM_CFLAGS}") From 1cd4936cebcda02cdc1faae88b98ae50db201bd0 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 00:25:26 +0200 Subject: [PATCH 59/77] Changed: #795 #1460 Make sure the streaming thread safely stops in all cases --HG-- branch : sound_dev --- .../include/nel/sound/stream_file_source.h | 2 +- code/nel/include/nel/sound/stream_source.h | 3 + code/nel/src/sound/source_music_channel.cpp | 3 + code/nel/src/sound/stream_file_source.cpp | 72 +++++++++++++++---- code/nel/src/sound/stream_source.cpp | 20 ++++-- 5 files changed, 80 insertions(+), 20 deletions(-) diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h index 4bce66719..64c2d5228 100644 --- a/code/nel/include/nel/sound/stream_file_source.h +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -86,7 +86,7 @@ public: private: void prepareDecoder(); - void bufferMore(uint bytes); + inline bool bufferMore(uint bytes); private: CStreamFileSource(const CStreamFileSource &); diff --git a/code/nel/include/nel/sound/stream_source.h b/code/nel/include/nel/sound/stream_source.h index 1c94a5b7a..40ed82e11 100644 --- a/code/nel/include/nel/sound/stream_source.h +++ b/code/nel/include/nel/sound/stream_source.h @@ -55,6 +55,9 @@ public: virtual void setLooping(bool l); /// Play virtual void play(); +protected: + void stopInt(); +public: /// Stop playing virtual void stop(); /// Get playing state. Return false even if the source has stopped on its own. diff --git a/code/nel/src/sound/source_music_channel.cpp b/code/nel/src/sound/source_music_channel.cpp index 4395f3b5e..bd8f152e0 100644 --- a/code/nel/src/sound/source_music_channel.cpp +++ b/code/nel/src/sound/source_music_channel.cpp @@ -60,6 +60,9 @@ bool CSourceMusicChannel::play(const std::string &filepath, bool async, bool loo m_Sound.setMusicFilePath(filepath, async, loop); m_Source = new CStreamFileSource(&m_Sound, false, NULL, NULL, NULL, NULL); + m_Source->setSourceRelativeMode(true); + m_Source->setPos(NLMISC::CVector::Null); + m_Source->setRelativeGain(m_Gain); m_Source->play(); diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 6a8c6b7d2..2b7bd94ee 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -65,13 +65,18 @@ void CStreamFileSource::play() if (m_Thread->isRunning() && m_WaitingForPlay) { - nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str()); if (m_NextBuffer || !m_FreeBuffers) { + nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str()); CStreamSource::play(); + if (!_Playing && !m_WaitingForPlay) + { + nldebug("playing not possible or necessary for some reason"); + } } else { + nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str()); m_WaitingForPlay = true; CAudioMixerUser *mixer = CAudioMixerUser::instance(); mixer->addSourceWaitingForPlay(this); @@ -79,7 +84,7 @@ void CStreamFileSource::play() } else if (!_Playing) { - nldebug("play waiting %s", getStreamFileSound()->getFilePath().c_str()); + nldebug("play go %s", getStreamFileSound()->getFilePath().c_str()); if (!m_WaitingForPlay) { // thread may be stopping from stop call @@ -98,12 +103,18 @@ void CStreamFileSource::play() if (!getStreamFileSound()->getAsync()) { // wait until at least one buffer is ready - while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay) - NLMISC::nlSleep(10); - CStreamSource::play(); - if (!_Playing) + while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay && m_Thread->isRunning()) + { + nldebug("wait buffer"); + NLMISC::nlSleep(100); + } + if (m_WaitingForPlay && m_Thread->isRunning()) { - nlwarning("Failed to synchronously start playing a file stream source. This happens when all physical tracks are in use. Use a Highest Priority sound"); + CStreamSource::play(); + if (!_Playing) + { + nlwarning("Failed to synchronously start playing a file stream source. This happens when all physical tracks are in use. Use a Highest Priority sound"); + } } } else @@ -135,7 +146,19 @@ void CStreamFileSource::stop() { nldebug("stop %s", getStreamFileSound()->getFilePath().c_str()); - CStreamSource::stop(); + CStreamSource::stopInt(); + + nldebug("stopInt ok"); + + if (_Spawn) + { + if (_SpawnEndCb != NULL) + _SpawnEndCb(this, _CbUserParam); + m_Thread->wait(); + delete this; + } + + nldebug("stop ok"); // thread will check _Playing to stop } @@ -229,30 +252,41 @@ void CStreamFileSource::prepareDecoder() this->preAllocate(bytes * 2); } -void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) +inline bool CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) { uint8 *buffer = this->lock(bytes * 2); if (buffer) { uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2); this->unlock(result); + return true; } + return false; } void CStreamFileSource::run() { - nldebug("run"); + nldebug("run %s", getStreamFileSound()->getFilePath().c_str()); + uint dumpI = 0; bool looping = _Looping; if (getStreamFileSound()->getAsync()) prepareDecoder(); uint samples, bytes; this->getRecommendedBufferSize(samples, bytes); - bufferMore(bytes); + uint32 recSleep = 40; + uint32 doSleep = 10; while (_Playing || m_WaitingForPlay) { if (!m_AudioDecoder->isMusicEnded()) { + ++dumpI; + if (!(dumpI % 100)) + { + nldebug("buffer %s %s %s", _Playing ? "PLAYING" : "NP", m_WaitingForPlay ? "WAITING" : "NW", getStreamFileSound()->getFilePath().c_str()); + nldebug("gain %f", hasPhysicalSource() ? getPhysicalSource()->getGain() : -1.0f); + } + bool newLooping = _Looping; if (looping != newLooping) { @@ -260,14 +294,19 @@ void CStreamFileSource::run() looping = newLooping; } - bufferMore(bytes); - NLMISC::nlSleep(this->getRecommendedSleepTime()); + // reduce sleeping time if nothing was buffered + if (bufferMore(bytes)) recSleep = doSleep = this->getRecommendedSleepTime(); + else doSleep = recSleep >> 2; // /4 + NLMISC::nlSleep(doSleep); } else { // wait until done playing buffers - while (this->hasFilledBuffersAvailable()) + while (this->hasFilledBuffersAvailable() && (_Playing || m_WaitingForPlay)) + { + nldebug("music ended, wait until done %s", getStreamFileSound()->getFilePath().c_str()); NLMISC::nlSleep(40); + } // stop the physical source // if (hasPhysicalSource()) // getPhysicalSource()->stop(); @@ -284,6 +323,11 @@ void CStreamFileSource::run() delete m_AudioDecoder; m_AudioDecoder = NULL; } + // drop buffers + m_FreeBuffers = 3; + m_NextBuffer = 0; + + nldebug("run end %s", getStreamFileSound()->getFilePath().c_str()); } } /* namespace NLSOUND */ diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 1cd1ddcbc..c71e7d8e2 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -160,7 +160,8 @@ void CStreamSource::play() _SpawnEndCb(this, _CbUserParam); delete this; } - // nldebug("CStreamSource %p : play FAILED !", (CAudioMixerUser::IMixerEvent*)this); + nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this); + m_WaitingForPlay = false; return; } @@ -216,6 +217,7 @@ void CStreamSource::play() _SpawnEndCb(this, _CbUserParam); delete this; } + m_WaitingForPlay = false; return; } } @@ -232,8 +234,7 @@ void CStreamSource::play() #endif } -/// Stop playing -void CStreamSource::stop() +void CStreamSource::stopInt() { CAutoMutex autoMutex(m_BufferMutex); @@ -248,7 +249,10 @@ void CStreamSource::stop() } if (!_Playing) + { + m_WaitingForPlay = false; return; + } if (hasPhysicalSource()) releasePhysicalSource(); @@ -258,14 +262,20 @@ void CStreamSource::stop() m_FreeBuffers = 3; m_NextBuffer = 0; + m_WaitingForPlay = false; +} + +/// Stop playing +void CStreamSource::stop() +{ + stopInt(); + if (_Spawn) { if (_SpawnEndCb != NULL) _SpawnEndCb(this, _CbUserParam); delete this; } - - m_WaitingForPlay = false; } void CStreamSource::setPos(const NLMISC::CVector& pos) From 1b41357a434773d7ed5df30aa80b50c6ea92a50a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 00:27:03 +0200 Subject: [PATCH 60/77] Fixed: #795 #1460 Linux compile of new sound samples --HG-- branch : sound_dev --- .../samples/sound/stream_file/stream_file.cpp | 13 +++++++++++- .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 21 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/code/nel/samples/sound/stream_file/stream_file.cpp b/code/nel/samples/sound/stream_file/stream_file.cpp index 6ded512eb..14314c16d 100644 --- a/code/nel/samples/sound/stream_file/stream_file.cpp +++ b/code/nel/samples/sound/stream_file/stream_file.cpp @@ -18,7 +18,11 @@ // STL includes #include -#include +#ifdef NL_OS_WINDOWS +# include +#else +# include +#endif // NeL includes #include @@ -110,9 +114,16 @@ static void runSample() printf("Press ANY other key to exit\n"); for (; ; ) { +#ifdef NL_OS_WINDOWS if (_kbhit()) { switch (_getch()) +#else + char ch; + if (read(0, &ch, 1)) + { + switch (ch) +#endif { case '+': s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 2709a0b81..b3645913c 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -18,7 +18,11 @@ // STL includes #include -#include +#ifdef NL_OS_WINDOWS +# include +#else +# include +#endif // NeL includes #include @@ -138,9 +142,16 @@ static void runSample() printf("Press ANY other key to exit\n"); while (!s_AudioDecoder->isMusicEnded()) { +#ifdef NL_OS_WINDOWS if (_kbhit()) { switch (_getch()) +#else + char ch; + if (read(0, &ch, 1)) + { + switch (ch) +#endif { case '+': s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); @@ -172,7 +183,13 @@ static void runSample() printf("End of song\n"); printf("Press ANY key to exit\n"); - while (!_kbhit()) { s_AudioMixer->update(); nlSleep(10); } _getch(); +#ifdef NL_OS_WINDOWS + while (!_kbhit()) +#else + char ch; + while (!read(0, &ch, 1)) +#endif + { s_AudioMixer->update(); nlSleep(10); } return; } From 015f47d71d87873f1dc8bbbd4e4e2b44b360876a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 12:06:03 +0200 Subject: [PATCH 61/77] Changed: #795 Handle safely when audio decoder fails to be created --HG-- branch : sound_dev --- .../include/nel/sound/stream_file_source.h | 2 +- .../data/soundbank/stream_file.sound | 2 +- .../samples/sound/stream_file/stream_file.cpp | 5 +- code/nel/src/sound/audio_mixer_user.cpp | 4 +- code/nel/src/sound/stream_file_source.cpp | 59 ++++++++++++------- code/nel/src/sound/stream_source.cpp | 13 ++-- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h index 64c2d5228..0cfee14d1 100644 --- a/code/nel/include/nel/sound/stream_file_source.h +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -85,7 +85,7 @@ public: // TODO: getTime private: - void prepareDecoder(); + bool prepareDecoder(); inline bool bufferMore(uint bytes); private: diff --git a/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound b/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound index 1b196d749..a98ae3a3c 100644 --- a/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound +++ b/code/nel/samples/sound/stream_file/data/soundbank/stream_file.sound @@ -5,7 +5,7 @@ - + diff --git a/code/nel/samples/sound/stream_file/stream_file.cpp b/code/nel/samples/sound/stream_file/stream_file.cpp index 14314c16d..2ace9dc6b 100644 --- a/code/nel/samples/sound/stream_file/stream_file.cpp +++ b/code/nel/samples/sound/stream_file/stream_file.cpp @@ -46,7 +46,8 @@ #define NL_SOUND_DATA "." #endif // NL_SOUND_DATA -#define SAMPLE_OGG "D:/source/kaetemi/toverhex/src/samples/music_stream/data/aeon_1_10_mystic_river.ogg" +#define RYZOM_DATA "P:/data" +#define SAMPLE_OGG "Pyr Entrance.ogg" using namespace std; using namespace NLMISC; @@ -93,6 +94,8 @@ static void initSample() CVector upvec(0.0f, 0.0f, 1.0f); s_AudioMixer->getListener()->setPos(initpos); s_AudioMixer->getListener()->setOrientation(frontvec, upvec); + + CPath::addSearchPath(RYZOM_DATA, true, false); //NLMISC::CHTimer::startBench(); diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index f47f70413..8bec4442c 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -1651,6 +1651,7 @@ void CAudioMixerUser::update() _MusicChannelFaders[i].update(); // Check all playing track and stop any terminated buffer. + std::list::size_type nbWaitingSources = _Sources.size(); for (i=0; i<_Tracks.size(); ++i) { if (!_Tracks[i]->isPlaying()) @@ -1662,13 +1663,14 @@ void CAudioMixerUser::update() } // try to play any waiting source. - if (!_SourceWaitingForPlay.empty()) + if (!_SourceWaitingForPlay.empty() && nbWaitingSources) { // check if the source still exist before trying to play it if (_Sources.find(_SourceWaitingForPlay.front()) != _Sources.end()) _SourceWaitingForPlay.front()->play(); // nldebug("Before POP Sources waiting : %u", _SourceWaitingForPlay.size()); _SourceWaitingForPlay.pop_front(); + --nbWaitingSources; // nldebug("After POP Sources waiting : %u", _SourceWaitingForPlay.size()); } } diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 2b7bd94ee..0f60819bd 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -63,39 +63,53 @@ void CStreamFileSource::play() // note: CStreamSource will assert crash if already physically playing! - if (m_Thread->isRunning() && m_WaitingForPlay) + if (m_WaitingForPlay) { - if (m_NextBuffer || !m_FreeBuffers) + if (m_Thread->isRunning()) { - nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str()); - CStreamSource::play(); - if (!_Playing && !m_WaitingForPlay) + if (m_NextBuffer || !m_FreeBuffers) { - nldebug("playing not possible or necessary for some reason"); + nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str()); + CStreamSource::play(); + if (!_Playing && !m_WaitingForPlay) + { + nldebug("playing not possible or necessary for some reason"); + } + } + else + { + nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str()); + m_WaitingForPlay = true; + CAudioMixerUser *mixer = CAudioMixerUser::instance(); + mixer->addSourceWaitingForPlay(this); } } else { - nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str()); - m_WaitingForPlay = true; - CAudioMixerUser *mixer = CAudioMixerUser::instance(); - mixer->addSourceWaitingForPlay(this); + // thread went kaboom while not started playing yet, probably the audiodecoder cannot be started + // don't play + m_WaitingForPlay = false; } } else if (!_Playing) { nldebug("play go %s", getStreamFileSound()->getFilePath().c_str()); - if (!m_WaitingForPlay) - { + //if (!m_WaitingForPlay) + //{ // thread may be stopping from stop call m_Thread->wait(); - } - else + //} + //else + //{ + // nlwarning("Already waiting for play"); + //} + if (!getStreamFileSound()->getAsync()) { - nlwarning("Already waiting for play"); + if (!prepareDecoder()) + { + return; + } } - if (!getStreamFileSound()->getAsync()) - prepareDecoder(); // else load audiodecoder in thread m_WaitingForPlay = true; m_Thread->start(); @@ -221,7 +235,7 @@ bool CStreamFileSource::isLoadingAsync() return m_WaitingForPlay; } -void CStreamFileSource::prepareDecoder() +bool CStreamFileSource::prepareDecoder() { // creates a new decoder or keeps going with the current decoder if the stream was paused @@ -243,13 +257,15 @@ void CStreamFileSource::prepareDecoder() if (!m_AudioDecoder) { nlwarning("Failed to create IAudioDecoder, likely invalid format"); - return; + return false; } this->setFormat(m_AudioDecoder->getChannels(), m_AudioDecoder->getBitsPerSample(), (uint32)m_AudioDecoder->getSamplesPerSec()); } uint samples, bytes; this->getRecommendedBufferSize(samples, bytes); this->preAllocate(bytes * 2); + + return true; } inline bool CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum) @@ -271,7 +287,10 @@ void CStreamFileSource::run() bool looping = _Looping; if (getStreamFileSound()->getAsync()) - prepareDecoder(); + { + if (!prepareDecoder()) + return; + } uint samples, bytes; this->getRecommendedBufferSize(samples, bytes); uint32 recSleep = 40; diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index c71e7d8e2..b353a4bb6 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -161,7 +161,7 @@ void CStreamSource::play() delete this; } nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this); - m_WaitingForPlay = false; + // m_WaitingForPlay = false; // not necessary, delete ensures waiting for thread stop return; } @@ -182,12 +182,9 @@ void CStreamSource::play() // pSource->setPos( _Position, false); pSource->setPos(getVirtualPos(), false); - if (!m_Buffers[0]->isStereo()) - { - pSource->setMinMaxDistances(m_StreamSound->getMinDistance(), m_StreamSound->getMaxDistance(), false); - setDirection(_Direction); // because there is a workaround inside - pSource->setVelocity(_Velocity); - } + pSource->setMinMaxDistances(m_StreamSound->getMinDistance(), m_StreamSound->getMaxDistance(), false); + setDirection(_Direction); // because there is a workaround inside + pSource->setVelocity(_Velocity); pSource->setGain(getFinalGain()); pSource->setSourceRelativeMode(_RelativeMode); // pSource->setLooping(_Looping); @@ -435,7 +432,7 @@ uint32 CStreamSource::getRecommendedSleepTime() const { if (m_FreeBuffers > 0) return 0; uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) * m_PitchInv); - clamp(sleepTime, (uint32)0, (uint32)1000); + clamp(sleepTime, (uint32)0, (uint32)80); return sleepTime; } From 8e70469541736e03ce152a87cf4c76d74076ec28 Mon Sep 17 00:00:00 2001 From: kervala Date: Thu, 12 Apr 2012 16:01:38 +0200 Subject: [PATCH 62/77] Changed: Define Release configuration for compilation checks and as default if not defined (must be defined before PROJECT) --- code/CMakeModules/nel.cmake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 590b07b71..1b3377511 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -1,3 +1,11 @@ +# Force Release configuration for compiler checks +SET(CMAKE_TRY_COMPILE_CONFIGURATION "Release") + +# Force Release configuration by default +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) + ### # Helper macro that generates .pc and installs it. # Argument: name - the name of the .pc package, e.g. "nel-pacs.pc" From b6025fc00880958129a92b3a8ec998289c0fac8e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 17:24:06 +0200 Subject: [PATCH 63/77] Fixed: Always commit 3d positioning when calling play on sources. This avoids having to wait for update call. Sources no longer cause loud noise when ryzom client finishes loading --HG-- branch : sound_dev --- code/nel/include/nel/sound/driver/source.h | 2 +- .../nel/src/sound/driver/openal/source_al.cpp | 27 ++++++++---- code/nel/src/sound/driver/openal/source_al.h | 2 + .../sound/driver/xaudio2/source_xaudio2.cpp | 11 ++++- code/nel/src/sound/stream_source.cpp | 41 ++++++++++++++++++- 5 files changed, 69 insertions(+), 14 deletions(-) diff --git a/code/nel/include/nel/sound/driver/source.h b/code/nel/include/nel/sound/driver/source.h index 1858e6f24..e3a5d552c 100644 --- a/code/nel/include/nel/sound/driver/source.h +++ b/code/nel/include/nel/sound/driver/source.h @@ -353,7 +353,7 @@ public: * In streaming mode, the time spent during buffer outruns is not * counted towards the playback time, and the playback time is * be the current time position in the entire submitted queue. - * When using static buffers, the result is the tot time that the + * When using static buffers, the result is the total time that the * attached buffer has been playing. If the source is looping, the * time will be the total of all playbacks of the buffer. * When the source is stopped, this will return the time where the diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index 19ef1780f..4558626e3 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -30,7 +30,7 @@ namespace NLSOUND { CSourceAL::CSourceAL(CSoundDriverAL *soundDriver) : _SoundDriver(NULL), _Buffer(NULL), _Source(AL_NONE), _DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL), -_IsPlaying(false), _IsPaused(false), _StartTime(0), +_IsPlaying(false), _IsPaused(false), _StartTime(0), _IsStreaming(false), _Pos(0.0f, 0.0f, 0.0f), _Gain(NLSOUND_DEFAULT_GAIN), _Alpha(1.0), _MinDistance(1.0f), _MaxDistance(numeric_limits::max()), _Effect(NULL), _Direct(true), @@ -100,7 +100,7 @@ void CSourceAL::updateManualRolloff() } /// Enable or disable streaming mode. Source must be stopped to call this. -void CSourceAL::setStreaming(bool /* streaming */) +void CSourceAL::setStreaming(bool streaming) { nlassert(isStopped()); @@ -108,6 +108,7 @@ void CSourceAL::setStreaming(bool /* streaming */) alSourcei(_Source, AL_BUFFER, AL_NONE); alTestError(); _Buffer = NULL; + _IsStreaming = streaming; } /* Set the buffer that will be played (no streaming) @@ -209,28 +210,36 @@ bool CSourceAL::getLooping() const /// Play the static buffer (or stream in and play) bool CSourceAL::play() { - if ( _Buffer != NULL ) + // Commit 3D changes before starting play + if (_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) + updateManualRolloff(); + + if (_Buffer) { // Static playing mode _IsPaused = false; alSourcePlay(_Source); - _IsPlaying = alGetError() == AL_NO_ERROR; + _IsPlaying = (alGetError() == AL_NO_ERROR); if (_IsPlaying) _StartTime = CTime::getLocalTime(); return _IsPlaying; } - else + else if (_IsStreaming) { - // TODO: Verify streaming mode? _IsPaused = false; alSourcePlay(_Source); - _IsPlaying = true; - _StartTime = CTime::getLocalTime(); // TODO: Played time should freeze when buffering fails, and be calculated based on the number of buffers played plus passed time. This is necessary for synchronizing animation with sound. - return true; + _IsPlaying = (alGetError() == AL_NO_ERROR); + if (_IsPlaying) + _StartTime = CTime::getLocalTime(); // TODO: Played time should freeze when buffering fails, and be calculated based on the number of buffers played plus passed time. This is necessary for synchronizing animation with sound. + return _IsPlaying; // Streaming mode //nlwarning("AL: Cannot play null buffer; streaming not implemented" ); //nlstop; } + else + { + nlwarning("Invalid play call, not streaming and no static buffer assigned"); + } } /// Stop playing diff --git a/code/nel/src/sound/driver/openal/source_al.h b/code/nel/src/sound/driver/openal/source_al.h index cae9ff042..483a9b145 100644 --- a/code/nel/src/sound/driver/openal/source_al.h +++ b/code/nel/src/sound/driver/openal/source_al.h @@ -60,6 +60,8 @@ private: bool _IsPaused; NLMISC::TTime _StartTime; + bool _IsStreaming; + NLMISC::CVector _Pos; float _Gain; double _Alpha; diff --git a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp index 7ee723787..e8d4bdfb9 100644 --- a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp +++ b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp @@ -493,8 +493,10 @@ bool CSourceXAudio2::initFormat(IBuffer::TBufferFormat bufferFormat, uint8 chann _SourceVoice->SetVolume(_Gain, _OperationSet); setupVoiceSends(); _SoundDriver->getXAudio2()->CommitChanges(_OperationSet); - - + + // Also commit any 3D settings that were already done + commit3DChanges(); + // test //XAUDIO2_VOICE_DETAILS voice_details; //_SourceVoice->GetVoiceDetails(&voice_details); @@ -581,6 +583,11 @@ bool CSourceXAudio2::play() { // nldebug(NLSOUND_XAUDIO2_PREFIX "play"); + // Commit 3D changes before starting play + if (_SourceVoice) + commit3DChanges(); + // else it is commit by the preparePlay > initFormat function + if (_IsPaused) { if (SUCCEEDED(_SourceVoice->Start(0))) _IsPaused = false; diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index b353a4bb6..3833b8439 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -183,8 +183,17 @@ void CStreamSource::play() // pSource->setPos( _Position, false); pSource->setPos(getVirtualPos(), false); pSource->setMinMaxDistances(m_StreamSound->getMinDistance(), m_StreamSound->getMaxDistance(), false); - setDirection(_Direction); // because there is a workaround inside - pSource->setVelocity(_Velocity); + if (!m_Buffers[0]->isStereo()) + { + setDirection(_Direction); // because there is a workaround inside + pSource->setVelocity(_Velocity); + } + else + { + pSource->setDirection(NLMISC::CVector::Null); + pSource->setCone(float(Pi * 2), float(Pi * 2), 1.0f); + pSource->setVelocity(NLMISC::CVector::Null); + } pSource->setGain(getFinalGain()); pSource->setSourceRelativeMode(_RelativeMode); // pSource->setLooping(_Looping); @@ -223,6 +232,34 @@ void CStreamSource::play() { CSourceCommon::play(); m_WaitingForPlay = false; +#if 1 + // Dump source info + nlwarning("--- DUMP SOURCE INFO ---"); + nlwarning(" * getLooping: %s", getPhysicalSource()->getLooping() ? "YES" : "NO"); + nlwarning(" * isPlaying: %s", getPhysicalSource()->isPlaying() ? "YES" : "NO"); + nlwarning(" * isStopped: %s", getPhysicalSource()->isStopped() ? "YES" : "NO"); + nlwarning(" * isPaused: %s", getPhysicalSource()->isPaused() ? "YES" : "NO"); + nlwarning(" * getPos: %f, %f, %f", getPhysicalSource()->getPos().x, getPhysicalSource()->getPos().y, getPhysicalSource()->getPos().z); + NLMISC::CVector v; + getPhysicalSource()->getVelocity(v); + nlwarning(" * getVelocity: %f, %f, %f", v.x, v.y, v.z); + getPhysicalSource()->getDirection(v); + nlwarning(" * getDirection: %f, %f, %f", v.x, v.y, v.z); + nlwarning(" * getGain: %f", getPhysicalSource()->getGain()); + nlwarning(" * getPitch: %f", getPhysicalSource()->getPitch()); + nlwarning(" * getSourceRelativeMode: %s", getPhysicalSource()->getSourceRelativeMode() ? "YES" : "NO"); + float a, b, c; + getPhysicalSource()->getMinMaxDistances(a, b); + nlwarning(" * getMinMaxDistances: %f, %f", a, b); + getPhysicalSource()->getCone(a, b, c); + nlwarning(" * getCone: %f, %f", a, b, c); + nlwarning(" * getDirect: %s", getPhysicalSource()->getDirect() ? "YES" : "NO"); + nlwarning(" * getDirectGain: %f", getPhysicalSource()->getDirectGain()); + nlwarning(" * isDirectFilterEnabled: %s", getPhysicalSource()->isDirectFilterEnabled() ? "YES" : "NO"); + nlwarning(" * getEffect: %s", getPhysicalSource()->getEffect() ? "YES" : "NO"); + nlwarning(" * getEffectGain: %f", getPhysicalSource()->getEffectGain()); + nlwarning(" * isEffectFilterEnabled: %s", getPhysicalSource()->isEffectFilterEnabled() ? "YES" : "NO"); +#endif } } From d63bda28f479c335992fa9671340248ef0248a4d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 17:48:56 +0200 Subject: [PATCH 64/77] Fixed: Relative positioning mode was not implemented in OpenAL library driver with manual rolloff enabled --HG-- branch : sound_dev --- .../nel/src/sound/driver/openal/source_al.cpp | 45 ++++++++++++++----- code/nel/src/sound/driver/openal/source_al.h | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index 4558626e3..64961e439 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -22,6 +22,8 @@ #include "source_al.h" #include "ext_al.h" +// #define NLSOUND_DEBUG_GAIN + using namespace std; using namespace NLMISC; @@ -30,7 +32,7 @@ namespace NLSOUND { CSourceAL::CSourceAL(CSoundDriverAL *soundDriver) : _SoundDriver(NULL), _Buffer(NULL), _Source(AL_NONE), _DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL), -_IsPlaying(false), _IsPaused(false), _StartTime(0), _IsStreaming(false), +_IsPlaying(false), _IsPaused(false), _StartTime(0), _IsStreaming(false), _RelativeMode(false), _Pos(0.0f, 0.0f, 0.0f), _Gain(NLSOUND_DEFAULT_GAIN), _Alpha(1.0), _MinDistance(1.0f), _MaxDistance(numeric_limits::max()), _Effect(NULL), _Direct(true), @@ -92,11 +94,17 @@ void CSourceAL::release() /// (Internal) Update the 3d changes. void CSourceAL::updateManualRolloff() { - CVector distanceVector = _Pos - CListenerAL::getInstance()->getPos(); + CVector distanceVector = _RelativeMode ? _Pos : (_Pos - CListenerAL::getInstance()->getPos()); float distanceSquare = distanceVector.sqrnorm(); float rolloff = ISource::computeManualRolloff(_Alpha, distanceSquare, _MinDistance, _MaxDistance); alSourcef(_Source, AL_GAIN, _Gain * rolloff); alTestError(); +#ifdef NLSOUND_DEBUG_GAIN + ALfloat gain; + alGetSourcef(_Source, AL_GAIN, &gain); + nlwarning("Called updateManualRolloff(), physical gain is %f, configured gain is %f, distanceSquare is %f, rolloff is %f", gain, _Gain, distanceSquare, rolloff); + alTestError(); +#endif } /// Enable or disable streaming mode. Source must be stopped to call this. @@ -210,6 +218,13 @@ bool CSourceAL::getLooping() const /// Play the static buffer (or stream in and play) bool CSourceAL::play() { +#ifdef NLSOUND_DEBUG_GAIN + if (_IsStreaming) + { + nlwarning("Called play on a streaming source"); + } +#endif + // Commit 3D changes before starting play if (_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) updateManualRolloff(); @@ -422,15 +437,23 @@ void CSourceAL::setGain(float gain) alSourcef(_Source, AL_GAIN, _Gain); alTestError(); } +#ifdef NLSOUND_DEBUG_GAIN + else + { + nlwarning("Called setGain(), manual rolloff, commit deferred to play or update, physical gain is %f, configured gain is %f", gain, _Gain); + } +#endif } /// Get the gain float CSourceAL::getGain() const { - //ALfloat gain; - //alGetSourcef(_Source, AL_GAIN, &gain); - //alTestError(); - //return gain; +#ifdef NLSOUND_DEBUG_GAIN + ALfloat gain; + alGetSourcef(_Source, AL_GAIN, &gain); + nlwarning("Called getGain(), physical gain is %f, configured gain is %f", gain, _Gain); + alTestError(); +#endif return _Gain; } @@ -453,6 +476,7 @@ float CSourceAL::getPitch() const /// Set the source relative mode. If true, positions are interpreted relative to the listener position. void CSourceAL::setSourceRelativeMode( bool mode ) { + _RelativeMode = mode; alSourcei(_Source, AL_SOURCE_RELATIVE, mode?AL_TRUE:AL_FALSE ); alTestError(); } @@ -460,10 +484,11 @@ void CSourceAL::setSourceRelativeMode( bool mode ) /// Get the source relative mode (3D mode only) bool CSourceAL::getSourceRelativeMode() const { - ALint b; - alGetSourcei(_Source, AL_SOURCE_RELATIVE, &b ); - alTestError(); - return (b==AL_TRUE); + //ALint b; + //alGetSourcei(_Source, AL_SOURCE_RELATIVE, &b ); + //alTestError(); + //return (b==AL_TRUE); + return _RelativeMode; } /// Set the min and max distances (3D mode only) diff --git a/code/nel/src/sound/driver/openal/source_al.h b/code/nel/src/sound/driver/openal/source_al.h index 483a9b145..53c77cb7f 100644 --- a/code/nel/src/sound/driver/openal/source_al.h +++ b/code/nel/src/sound/driver/openal/source_al.h @@ -61,6 +61,7 @@ private: NLMISC::TTime _StartTime; bool _IsStreaming; + bool _RelativeMode; NLMISC::CVector _Pos; float _Gain; From f9745080b56fd9b354a18a5787dbbf2d67359d16 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 17:56:54 +0200 Subject: [PATCH 65/77] Removed: Warning messages --HG-- branch : sound_dev --- .../nel/src/sound/driver/openal/source_al.cpp | 1 + code/nel/src/sound/stream_file_source.cpp | 32 ++++++++++++++++++- code/nel/src/sound/stream_source.cpp | 8 +++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index 64961e439..c19f71639 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -254,6 +254,7 @@ bool CSourceAL::play() else { nlwarning("Invalid play call, not streaming and no static buffer assigned"); + return false; } } diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 0f60819bd..87164a9cd 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -40,6 +40,8 @@ using namespace std; // using namespace NLMISC; +// #define NLSOUND_STREAM_FILE_DEBUG + namespace NLSOUND { CStreamFileSource::CStreamFileSource(CStreamFileSound *streamFileSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) @@ -69,16 +71,20 @@ void CStreamFileSource::play() { if (m_NextBuffer || !m_FreeBuffers) { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("play waiting, play stream %s", getStreamFileSound()->getFilePath().c_str()); +#endif CStreamSource::play(); if (!_Playing && !m_WaitingForPlay) { - nldebug("playing not possible or necessary for some reason"); + nldebug("Stream file source playback not possible or necessary for some reason"); } } else { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("play waiting, hop onto waiting list %s", getStreamFileSound()->getFilePath().c_str()); +#endif m_WaitingForPlay = true; CAudioMixerUser *mixer = CAudioMixerUser::instance(); mixer->addSourceWaitingForPlay(this); @@ -93,7 +99,9 @@ void CStreamFileSource::play() } else if (!_Playing) { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("play go %s", getStreamFileSound()->getFilePath().c_str()); +#endif //if (!m_WaitingForPlay) //{ // thread may be stopping from stop call @@ -119,7 +127,9 @@ void CStreamFileSource::play() // wait until at least one buffer is ready while (!(m_NextBuffer || !m_FreeBuffers) && m_WaitingForPlay && m_Thread->isRunning()) { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("wait buffer"); +#endif NLMISC::nlSleep(100); } if (m_WaitingForPlay && m_Thread->isRunning()) @@ -158,11 +168,15 @@ void CStreamFileSource::play() void CStreamFileSource::stop() { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("stop %s", getStreamFileSound()->getFilePath().c_str()); +#endif CStreamSource::stopInt(); +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("stopInt ok"); +#endif if (_Spawn) { @@ -172,21 +186,27 @@ void CStreamFileSource::stop() delete this; } +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("stop ok"); +#endif // thread will check _Playing to stop } bool CStreamFileSource::isPlaying() { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("isPlaying"); +#endif return m_Thread->isRunning(); } void CStreamFileSource::pause() { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("pause"); +#endif if (!m_Paused) { @@ -206,7 +226,9 @@ void CStreamFileSource::pause() void CStreamFileSource::resume() { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("resume"); +#endif if (m_Paused) { @@ -282,8 +304,10 @@ inline bool CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (mini void CStreamFileSource::run() { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("run %s", getStreamFileSound()->getFilePath().c_str()); uint dumpI = 0; +#endif bool looping = _Looping; if (getStreamFileSound()->getAsync()) @@ -299,12 +323,14 @@ void CStreamFileSource::run() { if (!m_AudioDecoder->isMusicEnded()) { +#ifdef NLSOUND_STREAM_FILE_DEBUG ++dumpI; if (!(dumpI % 100)) { nldebug("buffer %s %s %s", _Playing ? "PLAYING" : "NP", m_WaitingForPlay ? "WAITING" : "NW", getStreamFileSound()->getFilePath().c_str()); nldebug("gain %f", hasPhysicalSource() ? getPhysicalSource()->getGain() : -1.0f); } +#endif bool newLooping = _Looping; if (looping != newLooping) @@ -323,7 +349,9 @@ void CStreamFileSource::run() // wait until done playing buffers while (this->hasFilledBuffersAvailable() && (_Playing || m_WaitingForPlay)) { +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("music ended, wait until done %s", getStreamFileSound()->getFilePath().c_str()); +#endif NLMISC::nlSleep(40); } // stop the physical source @@ -346,7 +374,9 @@ void CStreamFileSource::run() m_FreeBuffers = 3; m_NextBuffer = 0; +#ifdef NLSOUND_STREAM_FILE_DEBUG nldebug("run end %s", getStreamFileSound()->getFilePath().c_str()); +#endif } } /* namespace NLSOUND */ diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 3833b8439..1397e87ac 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -26,6 +26,8 @@ // using namespace std; using namespace NLMISC; +// #define NLSOUND_DEBUG_STREAM + namespace NLSOUND { CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) @@ -160,7 +162,9 @@ void CStreamSource::play() _SpawnEndCb(this, _CbUserParam); delete this; } +#ifdef NLSOUND_DEBUG_STREAM nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this); +#endif // m_WaitingForPlay = false; // not necessary, delete ensures waiting for thread stop return; } @@ -190,7 +194,7 @@ void CStreamSource::play() } else { - pSource->setDirection(NLMISC::CVector::Null); + pSource->setDirection(NLMISC::CVector::I); pSource->setCone(float(Pi * 2), float(Pi * 2), 1.0f); pSource->setVelocity(NLMISC::CVector::Null); } @@ -232,7 +236,7 @@ void CStreamSource::play() { CSourceCommon::play(); m_WaitingForPlay = false; -#if 1 +#ifdef NLSOUND_DEBUG_STREAM // Dump source info nlwarning("--- DUMP SOURCE INFO ---"); nlwarning(" * getLooping: %s", getPhysicalSource()->getLooping() ? "YES" : "NO"); From b371588b93c2bda36c53ccb93454bf3ee39eb316 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 18:06:33 +0200 Subject: [PATCH 66/77] Added: Log warnings when setting excessively high max distances on sound sources --HG-- branch : sound_dev --- code/nel/src/sound/driver/openal/source_al.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index c19f71639..b56276ec2 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -496,6 +496,14 @@ bool CSourceAL::getSourceRelativeMode() const void CSourceAL::setMinMaxDistances( float mindist, float maxdist, bool /* deferred */) { nlassert( (mindist >= 0.0f) && (maxdist >= 0.0f) ); + + static float maxSqrt = sqrt(std::numeric_limits::max()); + if (maxdist >= maxSqrt) + { + nlwarning("SOUND_DEV (OpenAL): Ridiculously high max distance set on source"); + maxdist = maxSqrt; + } + _MinDistance = mindist; _MaxDistance = maxdist; if (!_SoundDriver->getOption(ISoundDriver::OptionManualRolloff)) From 4a02d20a4c430199e665b7a101f684b0d8f988e7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 18:16:53 +0200 Subject: [PATCH 67/77] Fixed: The default max distance for sources is now sqrt(numeric_limits::max()) --HG-- branch : sound_dev --- code/nel/include/nel/sound/driver/source.h | 2 +- code/nel/src/sound/driver/openal/source_al.cpp | 2 +- code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/nel/include/nel/sound/driver/source.h b/code/nel/include/nel/sound/driver/source.h index e3a5d552c..b43c83914 100644 --- a/code/nel/include/nel/sound/driver/source.h +++ b/code/nel/include/nel/sound/driver/source.h @@ -397,7 +397,7 @@ public: virtual void setSourceRelativeMode(bool mode = true) = 0; /// Get the source relative mode virtual bool getSourceRelativeMode() const = 0; - /// Set the min and max distances (default: 1, MAX_FLOAT) (3D mode only) + /// Set the min and max distances (default: 1, sqrt(MAX_FLOAT)) (3D mode only) virtual void setMinMaxDistances(float mindist, float maxdist, bool deferred = true) = 0; /// Get the min and max distances virtual void getMinMaxDistances(float& mindist, float& maxdist) const = 0; diff --git a/code/nel/src/sound/driver/openal/source_al.cpp b/code/nel/src/sound/driver/openal/source_al.cpp index b56276ec2..dab3123b1 100644 --- a/code/nel/src/sound/driver/openal/source_al.cpp +++ b/code/nel/src/sound/driver/openal/source_al.cpp @@ -34,7 +34,7 @@ _SoundDriver(NULL), _Buffer(NULL), _Source(AL_NONE), _DirectFilter(AL_FILTER_NULL), _EffectFilter(AL_FILTER_NULL), _IsPlaying(false), _IsPaused(false), _StartTime(0), _IsStreaming(false), _RelativeMode(false), _Pos(0.0f, 0.0f, 0.0f), _Gain(NLSOUND_DEFAULT_GAIN), _Alpha(1.0), -_MinDistance(1.0f), _MaxDistance(numeric_limits::max()), +_MinDistance(1.0f), _MaxDistance(sqrt(numeric_limits::max())), _Effect(NULL), _Direct(true), _DirectGain(NLSOUND_DEFAULT_DIRECT_GAIN), _EffectGain(NLSOUND_DEFAULT_EFFECT_GAIN), _DirectFilterType(ISource::FilterLowPass), _EffectFilterType(ISource::FilterLowPass), diff --git a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp index e8d4bdfb9..318a65a1a 100644 --- a/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp +++ b/code/nel/src/sound/driver/xaudio2/source_xaudio2.cpp @@ -42,7 +42,7 @@ _DirectFilterPassGain(NLSOUND_DEFAULT_FILTER_PASS_GAIN), _EffectFilterPassGain(N _DirectFilterLowFrequency(NLSOUND_DEFAULT_FILTER_PASS_LF), _DirectFilterHighFrequency(NLSOUND_DEFAULT_FILTER_PASS_HF), _EffectFilterLowFrequency(NLSOUND_DEFAULT_FILTER_PASS_LF), _EffectFilterHighFrequency(NLSOUND_DEFAULT_FILTER_PASS_HF), _IsPlaying(false), _IsPaused(false), _IsLooping(false), _Pitch(1.0f), -_Gain(1.0f), _MinDistance(1.0f), _MaxDistance(numeric_limits::max()), +_Gain(1.0f), _MinDistance(1.0f), _MaxDistance(sqrt(numeric_limits::max())), _AdpcmUtility(NULL), _Channels(0), _BitsPerSample(0), _BufferStreaming(false) { // nlwarning(NLSOUND_XAUDIO2_PREFIX "Inititializing CSourceXAudio2"); From b13f0dcb3436556e4120b1e45e2c4bbd1ea8e84e Mon Sep 17 00:00:00 2001 From: kaetemi Date: Thu, 12 Apr 2012 21:38:28 +0200 Subject: [PATCH 68/77] Fixed: #1298 Replace assert with warning when physical sound source fails to play --- code/nel/src/sound/simple_source.cpp | 8 ++++++++ code/nel/src/sound/stream_source.cpp | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/code/nel/src/sound/simple_source.cpp b/code/nel/src/sound/simple_source.cpp index 7c76533e3..cce62cffd 100644 --- a/code/nel/src/sound/simple_source.cpp +++ b/code/nel/src/sound/simple_source.cpp @@ -134,6 +134,7 @@ void CSimpleSource::play() || (_RelativeMode ? getPos().sqrnorm() : (mixer->getListenPosVector() - getPos()).sqrnorm()) > _SimpleSound->getMaxDistance() * _SimpleSound->getMaxDistance()) { // The sample buffer is not available, don't play (we don't know the length) + _WaitingForPlay = false; if (_Spawn) { if (_SpawnEndCb != 0) @@ -175,7 +176,14 @@ void CSimpleSource::play() // and play the sound bool play = pSource->play(); + +#ifdef NL_DEBUG nlassert(play); +#else + if (!play) + nlwarning("Failed to play physical sound source. This is a serious error"); +#endif + // nldebug("CSimpleSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this); } else diff --git a/code/nel/src/sound/stream_source.cpp b/code/nel/src/sound/stream_source.cpp index 1397e87ac..221b0529e 100644 --- a/code/nel/src/sound/stream_source.cpp +++ b/code/nel/src/sound/stream_source.cpp @@ -156,6 +156,7 @@ void CStreamSource::play() if ((_RelativeMode ? getPos().sqrnorm() : (mixer->getListenPosVector() - getPos()).sqrnorm()) > m_StreamSound->getMaxDistance() * m_StreamSound->getMaxDistance()) { // Source is too far to play + m_WaitingForPlay = false; if (_Spawn) { if (_SpawnEndCb != NULL) @@ -165,7 +166,6 @@ void CStreamSource::play() #ifdef NLSOUND_DEBUG_STREAM nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this); #endif - // m_WaitingForPlay = false; // not necessary, delete ensures waiting for thread stop return; } @@ -221,13 +221,13 @@ void CStreamSource::play() else { // No source available, kill. + m_WaitingForPlay = false; if (_Spawn) { if (_SpawnEndCb != NULL) _SpawnEndCb(this, _CbUserParam); delete this; } - m_WaitingForPlay = false; return; } } @@ -269,6 +269,9 @@ void CStreamSource::play() #ifdef NL_DEBUG nlassert(play); +#else + if (!play) + nlwarning("Failed to play physical sound source. This is a serious error"); #endif } From ac948b8a53d8f1b3fb26ea00932153fb0d172963 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 03:06:52 +0200 Subject: [PATCH 69/77] Changed: #1461 Bad minimum value for comparing fast swim speed causes random switching between fast and slow swim speed particle effects --- code/ryzom/client/src/ground_fx_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ryzom/client/src/ground_fx_manager.cpp b/code/ryzom/client/src/ground_fx_manager.cpp index 92267dd2a..2d3ce3810 100644 --- a/code/ryzom/client/src/ground_fx_manager.cpp +++ b/code/ryzom/client/src/ground_fx_manager.cpp @@ -64,7 +64,7 @@ CGroundFXManager::CGroundFXManager() : _MinSpeed(1.5f), _MaxSpeed(6.f), _SpeedWaterWalkFast(3.f), - _SpeedWaterSwimFast(3.f), + _SpeedWaterSwimFast(2.f), _MaxDist(50.f), _MaxNumFX(10), _NumFX(0), @@ -495,7 +495,7 @@ void CGroundFXManager::update(const NLMISC::CVectorD &camPos) break; case CInstance::Swim: if (speed == 0.f) fxName = "StepSwimIdle.ps"; - else if (speed > _SpeedWaterWalkFast) + else if (speed > _SpeedWaterSwimFast) { fxName = "StepSwimSpeed.ps"; fxNameUnderWater = "StepSwimSpeedUnderWater.ps"; From de4e298fb6c8bf7d49b88a2c2950a04dfbd07e3c Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 03:10:13 +0200 Subject: [PATCH 70/77] Changed: Provide a fake sound name for fake CSound created by music channel --- code/nel/src/sound/source_music_channel.cpp | 15 ++++++++++++++- code/nel/src/sound/stream_file_sound.cpp | 5 +++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/code/nel/src/sound/source_music_channel.cpp b/code/nel/src/sound/source_music_channel.cpp index bd8f152e0..677370dd3 100644 --- a/code/nel/src/sound/source_music_channel.cpp +++ b/code/nel/src/sound/source_music_channel.cpp @@ -54,6 +54,8 @@ CSourceMusicChannel::~CSourceMusicChannel() bool CSourceMusicChannel::play(const std::string &filepath, bool async, bool loop) { + // delete previous source if any + // note that this waits for the source's thread to finish if the source was still playing if (m_Source) delete m_Source; @@ -71,12 +73,14 @@ bool CSourceMusicChannel::play(const std::string &filepath, bool async, bool loo void CSourceMusicChannel::stop() { + // stop but don't delete the source, deleting source may cause waiting for thread if (m_Source) m_Source->stop(); } void CSourceMusicChannel::reset() { + // forces the source to be deleted, happens when audio mixer is reset delete m_Source; m_Source = NULL; } @@ -96,7 +100,16 @@ void CSourceMusicChannel::resume() bool CSourceMusicChannel::isEnded() { if (m_Source) - return m_Source->isEnded(); + { + if (m_Source->isEnded()) + { + // we can delete the source now without worrying about thread wait + delete m_Source; + m_Source = NULL; + return true; + } + return false; + } return true; } diff --git a/code/nel/src/sound/stream_file_sound.cpp b/code/nel/src/sound/stream_file_sound.cpp index 4fc17b51c..44da5a31d 100644 --- a/code/nel/src/sound/stream_file_sound.cpp +++ b/code/nel/src/sound/stream_file_sound.cpp @@ -73,6 +73,11 @@ void CStreamFileSound::serial(NLMISC::IStream &s) void CStreamFileSound::setMusicFilePath(const std::string &filePath, bool async, bool loop) { +#if !FINAL_VERSION + _Name = NLMISC::CStringMapper::map(std::string(""); +#else + _Name = NLMISC::CStringMapper::map(""); +#endif _ConeInnerAngle = NLMISC::Pi * 2; _ConeOuterAngle = NLMISC::Pi * 2; _Looping = loop; From 1b6c957c8113681e85f7ae96ca0c1e756ee108d3 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 13 Apr 2012 09:36:51 +0200 Subject: [PATCH 71/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/ryzom/client/src/interface_v3/interface_element.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/client/src/interface_v3/interface_element.h b/code/ryzom/client/src/interface_v3/interface_element.h index 24982cad2..0202618ba 100644 --- a/code/ryzom/client/src/interface_v3/interface_element.h +++ b/code/ryzom/client/src/interface_v3/interface_element.h @@ -361,7 +361,7 @@ public: */ virtual void forceOpen() { setActive(true); } - virtual void enableBlink(int /* numBlinks */ = 0) {} + virtual void enableBlink(uint /* numBlinks */ = 0) {} virtual void disableBlink() {} virtual bool getBlink() const { return false; } From e3170b57bcb39fc4baa71a4aa35842faec94fec0 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 13 Apr 2012 10:07:06 +0200 Subject: [PATCH 72/77] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/include/nel/misc/bit_mem_stream.h | 6 ++-- code/nel/include/nel/misc/mem_stream.h | 4 +-- code/nel/src/misc/bit_mem_stream.cpp | 2 +- code/nel/src/misc/bitmap_jpeg.cpp | 2 +- code/nel/src/misc/co_task.cpp | 3 ++ code/nel/src/misc/command.cpp | 3 ++ code/nel/src/misc/common.cpp | 38 +++++++++++++++++++++- 7 files changed, 50 insertions(+), 8 deletions(-) diff --git a/code/nel/include/nel/misc/bit_mem_stream.h b/code/nel/include/nel/misc/bit_mem_stream.h index 3bddc20f7..b294d5454 100644 --- a/code/nel/include/nel/misc/bit_mem_stream.h +++ b/code/nel/include/nel/misc/bit_mem_stream.h @@ -394,7 +394,7 @@ public: * If you are using the stream only in output mode, you can use this method as a faster version * of clear() *if you don't serialize pointers*. */ - void resetBufPos() + virtual void resetBufPos() { // This is ensured in CMemStream::CMemStream() and CMemStream::clear() //if ( (!isReading()) && _Buffer.empty() ) @@ -477,7 +477,7 @@ public: } /// See doc in CMemStream::bufferToFill() - uint8 *bufferToFill( uint32 msgsize ) + virtual uint8 *bufferToFill( uint32 msgsize ) { _FreeBits = 8; _DbgInfo.clear(); @@ -654,7 +654,7 @@ public: virtual void serial(ucstring &b); virtual void serial(CBitMemStream &b) { serialMemStream(b); } - virtual void serialMemStream(CBitMemStream &b); + virtual void serialMemStream(CMemStream &b); //@} diff --git a/code/nel/include/nel/misc/mem_stream.h b/code/nel/include/nel/misc/mem_stream.h index 2102129dd..1da99a48d 100644 --- a/code/nel/include/nel/misc/mem_stream.h +++ b/code/nel/include/nel/misc/mem_stream.h @@ -301,7 +301,7 @@ public: * If you are using the stream only in output mode, you can use this method as a faster version * of clear() *if you don't serialize pointers*. */ - void resetBufPos() { _Buffer.Pos = 0; } + virtual void resetBufPos() { _Buffer.Pos = 0; } /** * Resize the message buffer and fill data at position 0. @@ -340,7 +340,7 @@ public: * fill it with raw data using any filling function (warning: don't fill more than 'msgsize' * bytes!), then you are ready to read, using serial(), the data you've just filled. */ - uint8 *bufferToFill( uint32 msgsize ) + virtual uint8 *bufferToFill( uint32 msgsize ) { #ifdef NL_DEBUG nlassert( isReading() ); diff --git a/code/nel/src/misc/bit_mem_stream.cpp b/code/nel/src/misc/bit_mem_stream.cpp index c050d9c61..007b95cbc 100644 --- a/code/nel/src/misc/bit_mem_stream.cpp +++ b/code/nel/src/misc/bit_mem_stream.cpp @@ -599,7 +599,7 @@ void CBitMemStream::append( const CBitMemStream& newBits ) /* * Serial bitmemstream */ -void CBitMemStream::serialMemStream(CBitMemStream &b) +void CBitMemStream::serialMemStream(CMemStream &b) { #ifdef LOG_ALL_TRAFFIC sint32 bitpos = getPosInBit(); diff --git a/code/nel/src/misc/bitmap_jpeg.cpp b/code/nel/src/misc/bitmap_jpeg.cpp index 835110d86..a4eaaa0b9 100644 --- a/code/nel/src/misc/bitmap_jpeg.cpp +++ b/code/nel/src/misc/bitmap_jpeg.cpp @@ -109,7 +109,7 @@ static void jpgDecompressSkip(j_decompress_ptr cinfo, long num_bytes) } } -static void jpgDecompressTerm(j_decompress_ptr cinfo) +static void jpgDecompressTerm(j_decompress_ptr /* cinfo */) { } diff --git a/code/nel/src/misc/co_task.cpp b/code/nel/src/misc/co_task.cpp index 1880be06c..c66941a40 100644 --- a/code/nel/src/misc/co_task.cpp +++ b/code/nel/src/misc/co_task.cpp @@ -237,12 +237,15 @@ namespace NLMISC _PImpl = new TCoTaskData(this); // _PImpl->_TaskThreadId = 0; // _PImpl->_ParentThreadId = 0; + nlunreferenced(stackSize); #else //NL_USE_THREAD_COTASK // allocate platform specific data storage _PImpl = new TCoTaskData; + nlunreferenced(stackSize); #if defined (NL_OS_WINDOWS) _PImpl->_Fiber = NULL; _PImpl->_ParentFiber = NULL; + nlunreferenced(stackSize); #elif defined(NL_OS_UNIX) // allocate the stack _PImpl->_Stack = new uint8[stackSize]; diff --git a/code/nel/src/misc/command.cpp b/code/nel/src/misc/command.cpp index 80d074386..f2b422d69 100644 --- a/code/nel/src/misc/command.cpp +++ b/code/nel/src/misc/command.cpp @@ -650,6 +650,9 @@ ICommand *CCommandRegistry::getCommand(const std::string &commandName) NLMISC_CATEGORISED_COMMAND(nel,help,"display help on a specific variable/commands or on all variables and commands", "[|]") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); // nlassert (_Commands != NULL); // make sure we have a valid number of parameters diff --git a/code/nel/src/misc/common.cpp b/code/nel/src/misc/common.cpp index 6d37c19ab..d89708c4e 100644 --- a/code/nel/src/misc/common.cpp +++ b/code/nel/src/misc/common.cpp @@ -373,6 +373,10 @@ uint32 humanReadableToBytes (const string &str) NLMISC_CATEGORISED_COMMAND(nel,btohr, "Convert a bytes number into an human readable number", "") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if (args.size() != 1) return false; @@ -384,6 +388,10 @@ NLMISC_CATEGORISED_COMMAND(nel,btohr, "Convert a bytes number into an human read NLMISC_CATEGORISED_COMMAND(nel,hrtob, "Convert a human readable number into a bytes number", "
") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if (args.size() != 1) return false; @@ -446,6 +454,10 @@ uint32 fromHumanReadable (const std::string &str) NLMISC_CATEGORISED_COMMAND(nel,stohr, "Convert a second number into an human readable time", "") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if (args.size() != 1) return false; @@ -829,6 +841,7 @@ int nlfseek64( FILE *stream, sint64 offset, int origin ) return fsetpos (stream, &pos64); #else // NL_OS_WINDOWS + // TODO: to fix for Linux and Mac OS X // This code doesn't work under windows : fseek() implementation uses a signed 32 bits offset. What ever we do, it can't seek more than 2 Go. // For the moment, i don't know if it works under linux for seek of more than 2 Go. @@ -871,6 +884,9 @@ sint64 nlftell64(FILE *stream) } else return -1; #else + nlunreferenced(stream); + + // TODO: implement for Linux and Mac OS X nlerror("Not implemented"); return -1; #endif @@ -885,9 +901,14 @@ sint64 nlftell64(FILE *stream) NLMISC_CATEGORISED_COMMAND(nel, sleep, "Freeze the service for N seconds (for debug purpose)", "") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if(args.size() != 1) return false; - sint32 n = atoi (args[0].c_str()); + sint32 n; + fromString(args[0], n); log.displayNL ("Sleeping during %d seconds", n); @@ -897,6 +918,10 @@ NLMISC_CATEGORISED_COMMAND(nel, sleep, "Freeze the service for N seconds (for de NLMISC_CATEGORISED_COMMAND(nel, system, "Execute the command line using system() function call (wait until the end of the command)", "") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if(args.size() != 1) return false; string cmd = args[0]; @@ -915,6 +940,10 @@ NLMISC_CATEGORISED_COMMAND(nel, system, "Execute the command line using system() NLMISC_CATEGORISED_COMMAND(nel, launchProgram, "Execute the command line using launcProgram() function call (launch in background task without waiting the end of the execution)", " ") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if(args.size() != 2) return false; string cmd = args[0]; @@ -927,6 +956,10 @@ NLMISC_CATEGORISED_COMMAND(nel, launchProgram, "Execute the command line using l NLMISC_CATEGORISED_COMMAND(nel, killProgram, "kill a program given the pid", "") { + nlunreferenced(rawCommandString); + nlunreferenced(quiet); + nlunreferenced(human); + if(args.size() != 1) return false; uint32 pid; fromString(args[0], pid); @@ -1030,6 +1063,9 @@ bool openDoc (const char *document) } else return true; +#else + // TODO: implement for Linux and Mac OS X + nlunreferenced(document); #endif // NL_OS_WINDOWS return false; } From cdb719130feda380e6e67063111f8e701e064da2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 11:54:20 +0200 Subject: [PATCH 73/77] Changed: #1459 Simplify the sound group controller interface --- code/nel/include/nel/sound/group_controller.h | 27 +++++++++---------- .../include/nel/sound/u_group_controller.h | 15 ++++------- .../samples/sound/stream_file/stream_file.cpp | 6 ++--- .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 6 ++--- code/nel/src/sound/group_controller.cpp | 6 ++--- code/nel/src/sound/group_controller_root.cpp | 13 ++++++--- code/ryzom/client/src/sound_manager.cpp | 10 +++++-- code/ryzom/client/src/sound_manager.h | 5 +++- 8 files changed, 47 insertions(+), 41 deletions(-) diff --git a/code/nel/include/nel/sound/group_controller.h b/code/nel/include/nel/sound/group_controller.h index 08d474129..25b7034b6 100644 --- a/code/nel/include/nel/sound/group_controller.h +++ b/code/nel/include/nel/sound/group_controller.h @@ -57,9 +57,11 @@ public: private: CGroupController *m_Parent; std::map m_Children; - - float m_DevGain; - float m_UserGain; + + /// Gain as set by the interface + float m_Gain; + + /// Gain including parent gain float m_FinalGain; int m_NbSourcesInclChild; @@ -70,27 +72,22 @@ public: /// \name UGroupController //@{ - virtual void setDevGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); m_DevGain = gain; updateSourceGain(); } - virtual float getDevGain() { return m_DevGain; } - - virtual void setUserGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); m_UserGain = gain; updateSourceGain(); } - virtual float getUserGain() { return m_UserGain; } - - virtual void setGain(float devGain, float userGain) { NLMISC::clamp(devGain, 0.0f, 1.0f); NLMISC::clamp(userGain, 0.0f, 1.0f); m_DevGain = devGain; m_UserGain = userGain; updateSourceGain(); } + virtual void setGain(float gain) { NLMISC::clamp(gain, 0.0f, 1.0f); if (m_Gain != gain) { m_Gain = gain; updateSourceGain(); } } + virtual float getGain() { return m_Gain; } //@} - + inline float getFinalGain() const { return m_FinalGain; } - + void addSource(CSourceCommon *source); void removeSource(CSourceCommon *source); - + virtual std::string getPath(); - + protected: virtual ~CGroupController(); // subnodes can only be deleted by the root private: - inline float calculateTotalGain() { return m_DevGain * m_UserGain; } + inline float calculateTotalGain() { return m_Gain; } virtual void calculateFinalGain(); virtual void increaseSources(); virtual void decreaseSources(); diff --git a/code/nel/include/nel/sound/u_group_controller.h b/code/nel/include/nel/sound/u_group_controller.h index 54a28b835..5aadeb25c 100644 --- a/code/nel/include/nel/sound/u_group_controller.h +++ b/code/nel/include/nel/sound/u_group_controller.h @@ -35,9 +35,9 @@ // Project includes -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER "effects" -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER "music" -#define NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER "dialog" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_GROUP_CONTROLLER "sound:effects:game" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_MUSIC_GROUP_CONTROLLER "sound:music:game" +#define NLSOUND_SHEET_V1_DEFAULT_SOUND_STREAM_GROUP_CONTROLLER "sound:dialog:game" namespace NLSOUND { @@ -50,13 +50,8 @@ namespace NLSOUND { class UGroupController { public: - virtual void setDevGain(float gain) = 0; - virtual float getDevGain() = 0; - - virtual void setUserGain(float gain) = 0; - virtual float getUserGain() = 0; - - virtual void setGain(float devGain, float userGain) = 0; + virtual void setGain(float gain) = 0; + virtual float getGain() = 0; protected: virtual ~UGroupController() { } diff --git a/code/nel/samples/sound/stream_file/stream_file.cpp b/code/nel/samples/sound/stream_file/stream_file.cpp index 2ace9dc6b..623ee4ff0 100644 --- a/code/nel/samples/sound/stream_file/stream_file.cpp +++ b/code/nel/samples/sound/stream_file/stream_file.cpp @@ -106,7 +106,7 @@ static void initSample() // s_Source->setSourceRelativeMode(true); // s_Source->setPitch(2.0f); - s_GroupController = s_AudioMixer->getGroupController("dialog"); + s_GroupController = s_AudioMixer->getGroupController("sound:dialog"); } static void runSample() @@ -129,10 +129,10 @@ static void runSample() #endif { case '+': - s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); + s_GroupController->setGain(s_GroupController->getGain() + 0.1f); break; case '-': - s_GroupController->setUserGain(s_GroupController->getUserGain() - 0.1f); + s_GroupController->setGain(s_GroupController->getGain() - 0.1f); break; case 'x': s_Source->stop(); diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index b3645913c..e0f0df705 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -100,7 +100,7 @@ static void initSample() s_StreamSource->setFormat(s_AudioDecoder->getChannels(), s_AudioDecoder->getBitsPerSample(), (uint32)s_AudioDecoder->getSamplesPerSec()); //s_StreamSource->setPitch(2.0f); - s_GroupController = s_AudioMixer->getGroupController("dialog"); + s_GroupController = s_AudioMixer->getGroupController("sound:dialog"); } //CMutex *s_Mutex = NULL; @@ -154,10 +154,10 @@ static void runSample() #endif { case '+': - s_GroupController->setUserGain(s_GroupController->getUserGain() + 0.1f); + s_GroupController->setGain(s_GroupController->getGain() + 0.1f); break; case '-': - s_GroupController->setUserGain(s_GroupController->getUserGain() - 0.1f); + s_GroupController->setGain(s_GroupController->getGain() - 0.1f); break; default: return; diff --git a/code/nel/src/sound/group_controller.cpp b/code/nel/src/sound/group_controller.cpp index fef67a01b..d01e29252 100644 --- a/code/nel/src/sound/group_controller.cpp +++ b/code/nel/src/sound/group_controller.cpp @@ -42,7 +42,7 @@ using namespace std; namespace NLSOUND { CGroupController::CGroupController(CGroupController *parent) : - m_Parent(parent), m_DevGain(1.0f), m_UserGain(1.0f), m_NbSourcesInclChild(0) + m_Parent(parent), m_Gain(1.0f), m_NbSourcesInclChild(0) { } @@ -81,9 +81,7 @@ std::string CGroupController::getPath() // overridden by root if (it->second == this) { const std::string &name = it->first; - std::string returnPath = m_Parent->getPath() + "/" + name; - if (returnPath[0] == '/') - returnPath = returnPath.substr(1); + std::string returnPath = m_Parent->getPath() + ":" + name; return returnPath; } } diff --git a/code/nel/src/sound/group_controller_root.cpp b/code/nel/src/sound/group_controller_root.cpp index f49a40fff..f1344e195 100644 --- a/code/nel/src/sound/group_controller_root.cpp +++ b/code/nel/src/sound/group_controller_root.cpp @@ -39,6 +39,8 @@ using namespace std; // using namespace NLMISC; +#define NLSOUND_GROUP_CONTROLLER_ROOT_PATH "sound" + namespace NLSOUND { CGroupControllerRoot::CGroupControllerRoot() : CGroupController(NULL) @@ -53,7 +55,8 @@ CGroupControllerRoot::~CGroupControllerRoot() std::string CGroupControllerRoot::getPath() { - return ""; + // The root node is always called sound + return NLSOUND_GROUP_CONTROLLER_ROOT_PATH; } void CGroupControllerRoot::calculateFinalGain() @@ -78,9 +81,13 @@ void CGroupControllerRoot::decreaseSources() CGroupController *CGroupControllerRoot::getGroupController(const std::string &path) { std::vector pathNodes; - NLMISC::splitString(NLMISC::toLower(path), "/", pathNodes); + NLMISC::splitString(NLMISC::toLower(path), ":", pathNodes); CGroupController *active = this; - for (std::vector::iterator it(pathNodes.begin()), end(pathNodes.end()); it != end; ++it) + if (pathNodes[0] != NLSOUND_GROUP_CONTROLLER_ROOT_PATH) + { + nlerror("Root node for group controller must always be 'sound', invalid group '%s' requested", path.c_str()); + } + for (std::vector::iterator it(pathNodes.begin() + 1), end(pathNodes.end()); it != end; ++it) { if (!(*it).empty()) { diff --git a/code/ryzom/client/src/sound_manager.cpp b/code/ryzom/client/src/sound_manager.cpp index a82e4b422..182a28cb1 100644 --- a/code/ryzom/client/src/sound_manager.cpp +++ b/code/ryzom/client/src/sound_manager.cpp @@ -107,6 +107,8 @@ enum TFilterMapping //----------------------------------------------- CSoundManager::CSoundManager(IProgressCallback * /* progressCallBack */) : _AudioMixer(NULL), + _GroupControllerEffects(NULL), + _GroupControllerEffectsGame(NULL), _EnvSoundRoot(NULL), _UserEntitySoundLevel(1.0f), _Sources(NULL) @@ -139,6 +141,7 @@ CSoundManager::~CSoundManager() NL3D::UParticleSystemSound::setPSSound(NULL); _GroupControllerEffects = NULL; + _GroupControllerEffectsGame = NULL; // free the audio mixer (and delete all sources) delete _AudioMixer; @@ -407,6 +410,7 @@ void CSoundManager::reset () NL3D::UParticleSystemSound::setPSSound(NULL); _GroupControllerEffects = NULL; + _GroupControllerEffectsGame = NULL; delete _AudioMixer; _AudioMixer = NULL; @@ -482,7 +486,8 @@ void CSoundManager::init(IProgressCallback *progressCallBack) new CSoundAnimManager(_AudioMixer); // get the controller group for effects - _GroupControllerEffects = _AudioMixer->getGroupController("effects"); + _GroupControllerEffects = _AudioMixer->getGroupController("sound:effects"); + _GroupControllerEffectsGame = _AudioMixer->getGroupController("sound:effects:game"); // restore the volume SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); @@ -1543,7 +1548,8 @@ void CSoundManager::updateVolume() _AudioMixer->setEventMusicVolume(_GameMusicVolume); // update sfx volume - _GroupControllerEffects->setGain(_FadeSFXVolume, _SFXVolume); + _GroupControllerEffects->setGain(_SFXVolume); + _GroupControllerEffectsGame->setGain(_FadeSFXVolume); } } diff --git a/code/ryzom/client/src/sound_manager.h b/code/ryzom/client/src/sound_manager.h index e5db85648..80401ea57 100644 --- a/code/ryzom/client/src/sound_manager.h +++ b/code/ryzom/client/src/sound_manager.h @@ -332,9 +332,12 @@ private: /// Pointer on the audio mixer object NLSOUND::UAudioMixer *_AudioMixer; - /// The root effects group controller for volume settings + /// The root effects group controller for effects volume settings by the user NLSOUND::UGroupController *_GroupControllerEffects; + /// The root effects group controller for effects fading by the game + NLSOUND::UGroupController *_GroupControllerEffectsGame; + /// Pointer on the root of the environmental sounds tree (if any) NLSOUND::UEnvSound *_EnvSoundRoot; From f17f7e726d8dc0495a87fbe17fa9836d70c4bb74 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 13:59:24 +0200 Subject: [PATCH 74/77] Changed: #1459 Reserve functional group controller node names --- .../include/nel/sound/group_controller_root.h | 1 + code/nel/src/sound/group_controller_root.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/code/nel/include/nel/sound/group_controller_root.h b/code/nel/include/nel/sound/group_controller_root.h index 2cc86f5b3..2081fa28a 100644 --- a/code/nel/include/nel/sound/group_controller_root.h +++ b/code/nel/include/nel/sound/group_controller_root.h @@ -59,6 +59,7 @@ protected: virtual void calculateFinalGain(); virtual void increaseSources(); virtual void decreaseSources(); + static bool isReservedName(const std::string &nodeName); }; /* class CGroupControllerRoot */ diff --git a/code/nel/src/sound/group_controller_root.cpp b/code/nel/src/sound/group_controller_root.cpp index f1344e195..7ba7173e4 100644 --- a/code/nel/src/sound/group_controller_root.cpp +++ b/code/nel/src/sound/group_controller_root.cpp @@ -78,6 +78,18 @@ void CGroupControllerRoot::decreaseSources() --m_NbSourcesInclChild; } +bool CGroupControllerRoot::isReservedName(const std::string &nodeName) +{ + // These node names are reserved, in case these category controllers can be integrated with CDB. + // I do not forsee any functionality for changing environment effect settings for entire categories. + // The nodeName parameter is already lowercase, see CGroupControllerRoot::getGroupController. + if (nodeName == NLSOUND_GROUP_CONTROLLER_ROOT_PATH) return true; // Root node name can only used by root. + if (nodeName == "gain") return true; + if (nodeName == "pitch") return true; + if (nodeName == "enable" || nodeName == "enabled") return true; + return true; +} + CGroupController *CGroupControllerRoot::getGroupController(const std::string &path) { std::vector pathNodes; @@ -91,6 +103,10 @@ CGroupController *CGroupControllerRoot::getGroupController(const std::string &pa { if (!(*it).empty()) { + if (isReservedName(*it)) + { + nlerror("Attempt to use reserved node name '%s' in group controller path '%s'", (*it).c_str(), path.c_str()); + } std::map::iterator found = active->m_Children.find(*it); if (found == active->m_Children.end()) { From adfffad5518c97b2846bcfceb76f291680de6727 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 14:15:04 +0200 Subject: [PATCH 75/77] Fixed: Typo --- code/nel/src/sound/group_controller_root.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/src/sound/group_controller_root.cpp b/code/nel/src/sound/group_controller_root.cpp index 7ba7173e4..0f1c58e63 100644 --- a/code/nel/src/sound/group_controller_root.cpp +++ b/code/nel/src/sound/group_controller_root.cpp @@ -87,7 +87,7 @@ bool CGroupControllerRoot::isReservedName(const std::string &nodeName) if (nodeName == "gain") return true; if (nodeName == "pitch") return true; if (nodeName == "enable" || nodeName == "enabled") return true; - return true; + return false; } CGroupController *CGroupControllerRoot::getGroupController(const std::string &path) From e6116663915dc8a770774903aaf4995e8c2d0b95 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 23:29:18 +0200 Subject: [PATCH 76/77] Fixed: Warnings --- code/ryzom/client/src/sound_manager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ryzom/client/src/sound_manager.cpp b/code/ryzom/client/src/sound_manager.cpp index 182a28cb1..4d8827875 100644 --- a/code/ryzom/client/src/sound_manager.cpp +++ b/code/ryzom/client/src/sound_manager.cpp @@ -110,8 +110,8 @@ CSoundManager::CSoundManager(IProgressCallback * /* progressCallBack */) _GroupControllerEffects(NULL), _GroupControllerEffectsGame(NULL), _EnvSoundRoot(NULL), - _UserEntitySoundLevel(1.0f), - _Sources(NULL) + _Sources(NULL), + _UserEntitySoundLevel(1.0f) { _EnableBackgroundMusicAtTime= 0; _GameMusicVolume= 1.f; @@ -747,7 +747,7 @@ nldebug("nb sources = %d", _Sources.size() ); { if ( (*it).second == sourceId ) { - (*it).second = NULL; + (*it).second = 0; // itOld = it; // ++it; From b3260314ac1ef5a271979226750db3ea87881216 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 13 Apr 2012 23:30:35 +0200 Subject: [PATCH 77/77] Fixed: Crash on exit caused by incomplete music channel reset --- code/nel/include/nel/sound/audio_mixer_user.h | 2 ++ code/nel/src/sound/audio_mixer_user.cpp | 30 ++++++++++++++++++- code/nel/src/sound/source_music_channel.cpp | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/code/nel/include/nel/sound/audio_mixer_user.h b/code/nel/include/nel/sound/audio_mixer_user.h index 1316fe34a..9c9fd5c86 100644 --- a/code/nel/include/nel/sound/audio_mixer_user.h +++ b/code/nel/include/nel/sound/audio_mixer_user.h @@ -230,6 +230,8 @@ public: virtual uint getSourcesInstanceCount() const { return (uint)_Sources.size(); } /// Return the number of playing sources (slow) virtual uint getPlayingSourcesCount() const; + uint countPlayingSimpleSources() const; // debug + uint countSimpleSources() const; // debug /// Return the number of available tracks virtual uint getAvailableTracksCount() const; /// Return the number of used tracks diff --git a/code/nel/src/sound/audio_mixer_user.cpp b/code/nel/src/sound/audio_mixer_user.cpp index 8bec4442c..1a6c2d322 100644 --- a/code/nel/src/sound/audio_mixer_user.cpp +++ b/code/nel/src/sound/audio_mixer_user.cpp @@ -221,6 +221,7 @@ void CAudioMixerUser::writeProfile(std::string& out) */ out += "Sound mixer: \n"; out += "\tPlaying sources: " + toString (getPlayingSourcesCount()) + " \n"; + out += "\tPlaying simple sources: " + toString(countPlayingSimpleSources()) + " / " + toString(countSimpleSources()) + " \n"; out += "\tAvailable tracks: " + toString (getAvailableTracksCount()) + " \n"; out += "\tUsed tracks: " + toString (getUsedTracksCount()) + " \n"; // out << "\tMuted sources: " << nb << " \n"; @@ -270,7 +271,8 @@ void CAudioMixerUser::reset() _SourceWaitingForPlay.clear(); - _MusicChannelFaders->reset(); + for (uint i = 0; i < _NbMusicChannelFaders; ++i) + _MusicChannelFaders[i].reset(); // Stop tracks uint i; @@ -2173,6 +2175,32 @@ uint CAudioMixerUser::getPlayingSourcesCount() const return _PlayingSources; } + +// ****************************************************************** + +uint CAudioMixerUser::countPlayingSimpleSources() const +{ + uint count = 0; + for (TSourceContainer::const_iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it) + { + if ((*it)->getType() == CSourceCommon::SOURCE_SIMPLE && (*it)->isPlaying()) + ++count; + } + return count; +} + +uint CAudioMixerUser::countSimpleSources() const +{ + uint count = 0; + for (TSourceContainer::const_iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it) + { + if ((*it)->getType() == CSourceCommon::SOURCE_SIMPLE) + ++count; + } + return count; +} + + // ****************************************************************** uint CAudioMixerUser::getAvailableTracksCount() const diff --git a/code/nel/src/sound/source_music_channel.cpp b/code/nel/src/sound/source_music_channel.cpp index 677370dd3..34735f113 100644 --- a/code/nel/src/sound/source_music_channel.cpp +++ b/code/nel/src/sound/source_music_channel.cpp @@ -48,6 +48,7 @@ CSourceMusicChannel::CSourceMusicChannel() : m_Source(NULL), m_Gain(1.0f) CSourceMusicChannel::~CSourceMusicChannel() { + nlassert(!m_Source); delete m_Source; m_Source = NULL; }