// Ryzom - 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 "stdpch.h" ////////////// // INCLUDES // ////////////// // client #include "sound_manager.h" #include "pacs_client.h" #include "light_cycle_manager.h" #include "weather_manager_client.h" #include "user_entity.h" #include "entities.h" #include "time_client.h" #include "weather.h" #include "global.h" // game share #include "game_share/cst_loader.h" // If you compile using the nel distrib and you don't have the source, you must undef this symbol #if !FINAL_VERSION # define DEBUG_SOUND_IN_GAME #endif #if defined(DEBUG_SOUND_IN_GAME) //sound # include "nel/sound/clustered_sound.h" # include "nel/sound/audio_mixer_user.h" // 3d # include "nel/3d/cluster.h" # include "nel/3d/portal.h" # include "nel/3d/dru.h" # include "nel/3d/material.h" # include "nel/3d/driver.h" # include "nel/3d/driver_user.h" # include "view.h" #endif extern CLightCycleManager LightCycleManager; extern NL3D::UDriver *Driver; extern NL3D::UCamera MainCam; extern NLLIGO::CLigoConfig LigoConfig; ////////////// // USING // ////////////// using namespace NLMISC; using namespace NLPACS; using namespace NLSOUND; using namespace std; using namespace NL3D; extern NL3D::UScene *Scene; // The interpolation distance for the portals : 1 m const float PORTAL_INTERPOLATE = 1.0f; // Filter mapping enum TFilterMapping { DAY = 0, NIGHT = 1, MORNING = 2, DUSK = 3, SPRING = 4, SUMMER = 5, AUTUMN = 6, WINTER = 7, MIST = 8, FAIR1 = 9, FAIR2 = 10, FAIR3 = 11, CLOUDS = 12, RAIN1 = 13, RAIN2 = 14, SNOW1 = 15, THUNDER1 = 16, THUNDERSEVE1 = 17, THUNDERSAND1 = 18, THUNDERSAND2 = 19, THUNDERSAND3 = 20, BAD = 21, GOOD = 22, QUICK_MUTE = 23 }; //----------------------------------------------- // constructor //----------------------------------------------- CSoundManager::CSoundManager(IProgressCallback * /* progressCallBack */) : _AudioMixer(NULL), _EnvSoundRoot(NULL), _UserEntitySoundLevel(1.0f), _Sources(NULL) { _EnableBackgroundMusicAtTime= 0; _GameMusicVolume= 1.f; _UserMusicVolume= 1.f; _FadeGameMusicVolume= 1.f; _FadeSFXVolume= 1.f; _UseGameMusicVolume= true; _EventMusicEnabled= true; _EventMusicLoop= false; _FadeGameMusicVolumeDueToEvent= 1.f; // init(progressCallBack); } // CSoundManager //----------------------------------------------- // Destructor //----------------------------------------------- CSoundManager::~CSoundManager() { _Sources.clear(); _AttachedSources.clear(); // attached sources already deleted (they are also in the _Sources map) // detach the sound from the particule system NL3D::UParticleSystemSound::setPSSound(NULL); // free the audio mixer (and delete all sources) if (_AudioMixer) delete _AudioMixer; // release sound anim properly releaseSoundAnim(); } // ~CSoundManager // // *************************************************************************** void CSoundManager::releaseSoundAnim() { // Parse all Entities to reset their current sound id EntitiesMngr.resetAllSoundAnimId(); // Parse all animation to reset their Id if(EAM) EAM->resetAllSoundAnimId(); // Delete the sound anim Singleton CSoundAnimManager::release(); } // *************************************************************************** void CSoundManager::drawSounds(float camHeight) { #if defined(DEBUG_SOUND_IN_GAME) // prepare the cameras for cluster display // draw the cluster bounding box on screen CMatrix newCam; // get the user pos CVector pos(View.viewPos()); pos += CVector(0,0,camHeight); // get the view vector ? CVector lookAt(View.view()); lookAt.z = 0; lookAt.normalize(); if (lookAt.y <0) newCam.rotateZ(float(Pi+/*-Pi/2+*/asin(lookAt.x))); else newCam.rotateZ(float(Pi+/*-Pi/2+*/Pi-asin(lookAt.x))); newCam.rotateX(float(-Pi/2)); newCam.setPos(pos); Driver->setMatrixMode3D(MainCam); Driver->setViewMatrix(newCam.inverted()); Driver->clearZBuffer(); CMatrix model; model.identity(); model.translate(-pos); // model.rotateX(mapRX); // model.rotateY(mapRY); model.translate(pos); Driver->setModelMatrix(model); // UAudioMixer *amu = UAudioMixer::instance(); IDriver *idriver = static_cast(Driver)->getDriver(); CClusteredSound *cs = static_cast(_AudioMixer)->getClusteredSound(); // Draw the audible clusters { CMaterial mat; CMaterial mat2; CClusteredSound::TClusterStatusMap::const_iterator first(cs->getAudibleClusters().begin()), last(cs->getAudibleClusters().end()); for (; first != last; ++first) { NL3D::CCluster *cluster = first->first; const CAABBox &box = cluster->getBBox(); CVector center(box.getCenter()); CVector size(box.getHalfSize()); CVector a(center+size), b(center-size); CVector s[8]; s[0].set(a.x, a.y, a.z); s[1].set(a.x, b.y, a.z); s[2].set(b.x, b.y, a.z); s[3].set(b.x, a.y, a.z); s[4].set(a.x, a.y, b.z); s[5].set(a.x, b.y, b.z); s[6].set(b.x, b.y, b.z); s[7].set(b.x, a.y, b.z); CLine lines[12] = { CLine(s[0], s[1]), CLine(s[1], s[2]), CLine(s[2], s[3]), CLine(s[3], s[0]), CLine(s[4], s[5]), CLine(s[5], s[6]), CLine(s[6], s[7]), CLine(s[7], s[4]), CLine(s[0], s[4]), CLine(s[1], s[5]), CLine(s[2], s[6]), CLine(s[3], s[7]), }; mat.setColor(CRGBA(255,0,0,255)); mat.setZFunc(CMaterial::less); NL3D::CDRU::drawLinesUnlit(lines, 12, mat, *idriver); mat.setColor(CRGBA(80,0,0,255)); mat.setZFunc(CMaterial::greater); NL3D::CDRU::drawLinesUnlit(lines, 12, mat, *idriver); mat2.setColor(CRGBA(0,0,255,50)); mat2.setZFunc(CMaterial:: always); mat2.setSrcBlend(CMaterial::srcalpha); mat2.setDstBlend(CMaterial::one); mat2.setBlend(true); mat.setColor(CRGBA(0,0,255,255)); mat.setZFunc(CMaterial::always); // draw the portals for (uint k = 0; kgetNbPortals(); ++k) { CPortal *portal = cluster->getPortal(k); std::vector poly; portal->getPoly(poly); if (poly.size() > 2) { for (uint i=1; igetMatrix().getI(); // NL3D::CDRU::drawLine(pos, pos+front*4, CRGBA(255,255,0,255), *idriver); } // draw the virtual sound sound { CClusteredSound::TClusterStatusMap::const_iterator first(cs->getAudibleClusters().begin()), last(cs->getAudibleClusters().end()); for (; first != last; ++first ) { const CClusteredSound::CClusterSoundStatus &css = first->second; if (css.Direction != CVector::Null) { CVector dest = pos+css.Direction*css.Dist; NL3D::CDRU::drawLine(pos, dest, CRGBA(0,255,255,255), *idriver); NL3D::CDRU::drawLine(dest+CVector(0.5f,0.5f,0), dest+CVector(-0.5f,-0.5f,0), CRGBA(0, 255,255,255), *idriver); NL3D::CDRU::drawLine(dest+CVector(-0.5f,0.5f,0), dest+CVector(0.5f,-0.5f,0), CRGBA(0, 255,255,255), *idriver); } } } // draw the audio path { idriver->setupMaterial(mat); const std::vector > &lines = cs->getAudioPath(); std::vector >::const_iterator first(lines.begin()), last(lines.end()); for (; first != last; ++first) { NL3D::CDRU::drawLine(first->first, first->second, CRGBA(0,255,0,255), *idriver); } } // draw the sound source position { std::vector > soundPos; _AudioMixer->getPlayingSoundsPos(true, soundPos); std::vector >::iterator first(soundPos.begin()), last(soundPos.end()); for (; first != last; ++first) { NL3D::CDRU::drawLine(first->second + CVector(0.5f,0.5f,0), first->second + CVector(-0.5f,-0.5f,0), CRGBA(255,0,255,255), *idriver); NL3D::CDRU::drawLine(first->second + CVector(0.5f,-0.5f,0), first->second + CVector(-0.5f,0.5f,0), CRGBA(255,0,255,255), *idriver); } } #endif // DEBUG_SOUND_IN_GAME } UAudioMixer *CSoundManager::getMixer() { return _AudioMixer; } void CSoundManager::switchSoundState () { _PlaySound = !_PlaySound; _AudioMixer->enable(_PlaySound); } void CSoundManager::loadContinent(const string &name, const NLMISC::CVector& pos) { if (!_AudioMixer) return; // load already stop all sound and unload them before loading new one _AudioMixer->loadBackgroundSound (name, LigoConfig); _AudioMixer->setListenerPos(pos); } void CSoundManager::reset () { if (!_AudioMixer) return; NL3D::UParticleSystemSound::setPSSound(NULL); delete _AudioMixer; _AudioMixer = NULL; // release sound anim properly releaseSoundAnim(); init(); // _AudioMixer->reset (); } //--------------------------------------------------- // init : // Initialize the audio mixer, load the sound banks //--------------------------------------------------- void CSoundManager::init(IProgressCallback *progressCallBack) { _EnvSoundRoot = NULL; _PlaySound = true; _UserEntitySoundLevel = ClientCfg.UserEntitySoundLevel; const std::string &packedSheetPath = ClientCfg.SoundPackedSheetPath; const std::string &samplePath = ClientCfg.SampleBankDir; // try // { // reset particle sound NL3D::UParticleSystemSound::setPSSound(NULL); /* * Create the audio mixer object and init it. * If the sound driver cannot be loaded, an exception is thrown. */ _AudioMixer = UAudioMixer::createAudioMixer(); if (!_AudioMixer) return; // Set the path to the sample directory _AudioMixer->setSamplePath(samplePath); // Set the path to the packed sheets if (ClientCfg.UpdatePackedSheet) _AudioMixer->setPackedSheetOption(packedSheetPath, true); else _AudioMixer->setPackedSheetOption("", false); UAudioMixer::TDriver driverType= UAudioMixer::DriverAuto; if(ClientCfg.DriverSound==CClientConfig::SoundDrvFMod) driverType= UAudioMixer::DriverFMod; else if(ClientCfg.DriverSound==CClientConfig::SoundDrvOpenAL) driverType= UAudioMixer::DriverOpenAl; else if(ClientCfg.DriverSound==CClientConfig::SoundDrvDirectSound) driverType= UAudioMixer::DriverDSound; else if(ClientCfg.DriverSound==CClientConfig::SoundDrvXAudio2) driverType= UAudioMixer::DriverXAudio2; _AudioMixer->init(ClientCfg.MaxTrack, ClientCfg.UseEax, ClientCfg.UseADPCM, progressCallBack, false, driverType, ClientCfg.SoundForceSoftwareBuffer); /* int nbVoice = _AudioMixer->getPolyphony(); _AudioMixer->setPriorityReserve(HighPri, max(1, nbVoice /2)); nbVoice -= nbVoice /2; _AudioMixer->setPriorityReserve(MidPri, max(1, nbVoice /2)); nbVoice -= nbVoice /2; _AudioMixer->setPriorityReserve(LowPri, max(1, nbVoice /2)); */ // when they are only 1 track available, enter in limiting mode. _AudioMixer->setLowWaterMark(1); /* * Create the CSoundAnimManager. The CSoundAnimManager is a singleton. * Access the singleton with CSoundAnimManager::instance(). */ new CSoundAnimManager(_AudioMixer); // restore the volume SoundMngr->setSFXVolume(ClientCfg.SoundSFXVolume); SoundMngr->setGameMusicVolume(ClientCfg.SoundGameMusicVolume); // Init clustered sound system _AudioMixer->initClusteredSound(Scene, 0.01f, 100.0f, PORTAL_INTERPOLATE); // Init the background flags; for (uint i=0; isetBackgroundFlags(_BackgroundFlags); /* * Initialize listener's position */ CVector initpos ( 0.0f, 0.0f, 0.0f ); //_AudioMixer->getListener()->setPos( initpos ); _AudioMixer->setListenerPos(initpos); /* * Init the particule sound system */ // attach the sound from the particule system NL3D::UParticleSystemSound::setPSSound(_AudioMixer); //loadPositionedSounds(); // reload Sound Animation (if EAM already init) if(EAM) EAM->reloadAllSoundAnim(); // Special option for DEV (easier to test when disabled) _AudioMixer->enableBackgroundMusicTimeConstraint(ClientCfg.EnableBackgroundMusicTimeConstraint); /* } catch(const Exception &e) { nlwarning( "Error: %s", e.what() ); } */ } // init // //--------------------------------------------------- // getSoundName: //--------------------------------------------------- /*bool CSoundManager::getSoundName( TSound sound, std::string &name, const CVector& pos ) const { /// \todo Malkav: take ground type into account, and all the different sounds switch (sound) { case WALK: { //name = "walk"; UGlobalPosition globalPos = GR->retrievePosition( pos ); uint32 material = GR->getMaterial( globalPos ); } break; case RUN: name = "run"; break; case USER_WALK: name = "user_walk"; break; case USER_RUN: name = "user_run"; break; default: return false; } return true; } // getSoundName // */ //--------------------------------------------------- // addSource : // 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( TSound sound, const NLMISC::CVector &position, bool play, bool loop, const CEntityId &parentId ) { static std::string name; static TSource source; uint32 retValue = 0; if ( ! getSoundName ( sound, name, position ) ) return 0; USource *pSource = _AudioMixer->createSource( name ); if ( pSource != NULL ) { pSource ->setPos( position ); pSource ->setLooping( loop ); if (play) pSource ->play(); //create the TSource object source.sound = sound; source.pSource = pSource; // attach the source to the entity, if specified if (parentId != CEntityId::Unknown) _AttachedSources.insert( TMultiMapEntityToSource::value_type( parentId, source ) ); // set source id retValue = _NextId; _Sources.insert( TMapIdToSource::value_type( _NextId, source ) ); ++_NextId; } else { nlwarning( "Sound '%s' not found", name ); } return retValue; } // addSource // */ //----------------------------------------------- // addSource : // add a new source to the world, attached to the specified entity // return 0 if creation failed, sound id if creation was successful //----------------------------------------------- CSoundManager::TSourceId CSoundManager::addSource( const NLMISC::TStringId &soundName, const NLMISC::CVector &position, bool play, bool loop, const CEntityId &id) { uint32 retValue = 0; // Create a source USource *pSource = _AudioMixer->createSource( soundName ); // If the source is valid. if(pSource == 0) { nlwarning("Sound '%s' not found !", CStringMapper::unmap(soundName).c_str()); return retValue; } // Load the properties for this sound. // REMOVED because the pitch and the gain are now in the .nss (don't need .sdf anymore) // loadProperties(soundName, pSource); // Set the source position. pSource->setPos( position ); // Is the source looping ? pSource->setLooping( loop ); // Play the sound immedialty if asked. if(play) { 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, sourceId ) ); } // return the id of the source return sourceId; } // addSource // //----------------------------------------------- // spawn a new source to the world // return false if creation failed, true if creation was successful //----------------------------------------------- bool CSoundManager::spawnSource(const NLMISC::TStringId &soundName, CSoundContext &context) { if (!_PlaySound) return false; // Create a source // TODO : find the correct cluster USource *pSource = _AudioMixer->createSource( soundName, true, NULL, NULL, NULL, &context); // If the source is valid. if(pSource == 0) { nlwarning("Sound '%s' not found !", soundName); return false; } // Set the source position. pSource->setPos (context.Position); pSource->play(); ////nlinfo ("%.3f spawn source %p", (float)ryzomGetLocalTime ()/1000.0f, pSource); return true; } // addSource // //----------------------------------------------- // spawn a new source to the world // return false if creation failed, true if creation was successful //----------------------------------------------- bool CSoundManager::spawnSource(const NLMISC::TStringId &soundName, const NLMISC::CVector &position) { if (!_PlaySound) return false; // Create a source USource *pSource = _AudioMixer->createSource( soundName, true); // If the source is valid. if(pSource == 0) { nlwarning("Sound '%s' not found !", CStringMapper::unmap(soundName).c_str ()); return false; } // Set the source position. pSource->setPos (position); pSource->play(); ////nlinfo ("%.3f spawn source %p", (float)ryzomGetLocalTime ()/1000.0f, pSource); return true; } // addSource // //--------------------------------------------------- // removeSource: // remove a source //--------------------------------------------------- void CSoundManager::removeSource(CSoundManager::TSourceId sourceId) { nldebug("remove the source : %d", sourceId); /// \todo Malkav : optimize speed nldebug("nb sources = %d", _Sources.size() ); USource *pSource = _Sources.get(sourceId); if (pSource) { TMultiMapEntityToSource::iterator it = _AttachedSources.begin();//, itOld; for ( ; it != _AttachedSources.end() ; ++it) { if ( (*it).second == sourceId ) { (*it).second = NULL; // itOld = it; // ++it; _AttachedSources.erase( it/*Old*/ ); break; // if (it == _AttachedSources.end() ) // break; } } // delete the source delete pSource; // i think there was something going on here _Sources.erase(sourceId); } } // removeSource // //--------------------------------------------------- // getSounds : // //--------------------------------------------------- /*bool CSoundManager::getSounds( uint32 material, TMoveType moveType, bool soft, std::vector& sounds ) { return _StepSounds.getSounds( material, moveType, soft, sounds ); } // getSounds //*/ //--------------------------------------------------- // updateEntityPos : // update the pos of all the sounds attached to that entity //--------------------------------------------------- void CSoundManager::updateEntityPos( const CEntityId &id, const NLMISC::CVector &pos) { TMultiMapEntityToSource::iterator it; const std::pair range = _AttachedSources.equal_range( id ); for ( it = range.first; it != range.second ; ++it) { _Sources.get((*it).second)->setPos( pos ); } } // updateEntityPos // //--------------------------------------------------- // updateEntityVelocity : // update the velocity of all the sounds attached to that entity //--------------------------------------------------- void CSoundManager::updateEntityVelocity( const CEntityId &id, const NLMISC::CVector &velocity) { TMultiMapEntityToSource::iterator it; const std::pair range = _AttachedSources.equal_range( id ); for ( it = range.first; it != range.second ; ++it) { _Sources.get((*it).second)->setVelocity( velocity ); } } // updateEntityVelocity // //--------------------------------------------------- // updateEntityDirection : // update the direction of all the sounds attached to that entity //--------------------------------------------------- void CSoundManager::updateEntityDirection( const CEntityId &id, const NLMISC::CVector &dir ) { TMultiMapEntityToSource::iterator it; const std::pair range = _AttachedSources.equal_range( id ); for ( it = range.first; it != range.second ; ++it) { _Sources.get((*it).second)->setDirection( dir ); } } // updateEntityOrientation // //--------------------------------------------------- // removeEntity : // remove an entity from the view : delete all the sources attached to that entity //--------------------------------------------------- void CSoundManager::removeEntity( const CEntityId &id) { /// \todo Malkav : optimize speed TMultiMapEntityToSource::iterator it; const std::pair range = _AttachedSources.equal_range( id ); for ( it = range.first; it != range.second ; ++it) { TSourceId sourceId = (*it).second; if (sourceId) { USource *pSource = _Sources.get(sourceId); delete pSource; _Sources.erase(sourceId); } } _AttachedSources.erase( range.first, range.second ); } // removeEntity // //--------------------------------------------------- // setSoundPosition : //--------------------------------------------------- void CSoundManager::setSoundPosition(TSourceId sourceId, const NLMISC::CVector &position) { if (!_PlaySound) return; USource *pSource = _Sources.get(sourceId); if (pSource) pSource->setPos(position); } // setSoundPosition // //--------------------------------------------------- // loopSound : //--------------------------------------------------- void CSoundManager::loopSound(TSourceId sourceId, bool loop) { if (!_PlaySound) return; USource *pSource = _Sources.get(sourceId); if (pSource) pSource->setLooping(loop); } // loopSound // //--------------------------------------------------- // playSound : // start or stop playing sound //--------------------------------------------------- void CSoundManager::playSound(TSourceId sourceId, bool play) { if (!_PlaySound) return; USource *pSource = _Sources.get(sourceId); if (pSource) { if (play) pSource->play(); else pSource->stop(); } } // loopSound // //--------------------------------------------------- // isPlaying : // return true if the source is playing //--------------------------------------------------- bool CSoundManager::isPlaying(TSourceId sourceId) { USource *pSource = _Sources.get(sourceId); if (pSource) return pSource->isPlaying(); return false; } // isPlaying // //--------------------------------------------------- // setSoundForSource : // set the sound associated to the specified source //--------------------------------------------------- /* bool CSoundManager::setSoundForSource( uint32 sourceId, TSound sound, const CVector& pos ) { // find the sound name static std::string soundName; soundName.clear(); if ( ! getSoundName( sound , soundName, pos) ) return false; TMapIdToSource::iterator it = _Sources.find( sourceId ); if (it != _Sources.end() ) { nlassert( (*it).second.pSource ); // get the sound TSoundId soundId = _AudioMixer->getSoundId( soundName.c_str() ); if (soundId != NULL) { (*it).second.pSource->setSound( soundId ); (*it).second.sound = sound; nlinfo("sound has been set to %d",sound); return true; } } return false; } // setSoundForSource // */ //--------------------------------------------------- // getSoundFromSource : // get the sound type currently associated to a source //--------------------------------------------------- /*CSoundManager::TSound CSoundManager::getSoundFromSource( uint32 sourceId ) const { TMapIdToSource::const_iterator it = _Sources.find( sourceId ); if (it != _Sources.end() ) { nlassert( (*it).second.pSource ); return ( (*it).second.sound ); } return NONE; } // getSoundFromSource // */ //--------------------------------------------------- // setSourceGain : // set the gain of the specified source //--------------------------------------------------- void CSoundManager::setSourceGain(TSourceId sourceId, float gain) { USource *pSource = _Sources.get(sourceId); if (pSource) pSource->setGain( gain ); } // setSourceGain // //--------------------------------------------------- // getSourceGain : // get the gain of the specified source (-1 if source not found) //--------------------------------------------------- float CSoundManager::getSourceGain(TSourceId sourceId) { USource *pSource = _Sources.get(sourceId); if (pSource) return pSource->getGain(); return -1; } // getSourceGain // //--------------------------------------------------- // setSourcePitch : // set the Pitch of the specified source //--------------------------------------------------- void CSoundManager::setSourcePitch(TSourceId sourceId, float Pitch) { USource *pSource = _Sources.get(sourceId); if (pSource) pSource->setPitch(Pitch); } // setSourcePitch // //--------------------------------------------------- // getSourcePitch : // get the Pitch of the specified source (-1 if source not found) //--------------------------------------------------- float CSoundManager::getSourcePitch(TSourceId sourceId) { USource *pSource = _Sources.get(sourceId); if (pSource) return pSource->getPitch(); return -1; } // getSourcePitch // //--------------------------------------------------- // loadProperties : // Load the properties for this sound and apply them. //--------------------------------------------------- void CSoundManager::loadProperties(const string &soundName, USource *source) { // If the source does not exist -> return nothing to do. if(!source) return; // Search for the file. string filePath = CPath::lookup(soundName+".sdf"); ifstream file(filePath.c_str(), ios::in); // Try to open the file. if(file.is_open()) { char tmpBuff[260]; char delimiterBox[] = "\t "; // While the end of the file is not reached. while(!file.eof()) { // Get a line (teh line should not be more than _MAX_LINE_SIZE). file.getline(tmpBuff, 260); char *token = strtok(tmpBuff, delimiterBox); while(token != NULL) { // Get the pitch. if(strcmp(token, "Pitch:") == 0) { token = strtok(NULL, delimiterBox); if(token) { float pitch; fromString(token, pitch); nlinfo("Pitch: %f", pitch); source->setPitch(pitch); } } // Get the Gain. else if(strcmp(token, "Gain:") == 0) { token = strtok(NULL, delimiterBox); if(token) { float gain; fromString(token, gain); nlinfo("Gain: %f", gain); source->setGain(gain); } } // Next property. token = strtok(NULL, delimiterBox); } } // Close the file. file.close(); } }// loadProperties // //--------------------------------------------------- // loadPositionedSounds : // //--------------------------------------------------- /*void CSoundManager::loadPositionedSounds() { nlstopex(("class CStepSounds is not used anymore")); // cst loader CSTLoader cstl; // build file format map fileFormat; fileFormat.insert( make_pair( string( "sound name" ) , CSTLoader::STRING )); fileFormat.insert( make_pair( string( "x" ) , CSTLoader::FLOAT )); fileFormat.insert( make_pair( string( "y" ) , CSTLoader::FLOAT )); fileFormat.insert( make_pair( string( "z" ) , CSTLoader::FLOAT )); fileFormat.insert( make_pair( string( "loop" ) , CSTLoader::BOOL )); string str = CPath::lookup("positioned_sounds.txt", false); if(str.empty()) { nlwarning ("cant load positioned_sounds"); return; } // init loader cstl.init( str, fileFormat ); // read the file while( cstl.readLine() ) { string soundName; cstl.getStringValue( "sound name", soundName ); CVector sourcePos; cstl.getValue( "x", sourcePos.x ); cstl.getValue( "y", sourcePos.y ); cstl.getValue( "z", sourcePos.z ); bool loop; cstl.getBoolValue( "loop", loop ); uint32 sourceId = addSource( soundName, sourcePos, false , loop ); if(sourceId != 0) { _PositionedSounds.push_back( sourceId ); } else { nlwarning ("positioned sound: can't find sound: '%s'", soundName.c_str()); } } cstl.close(); } // chooseSoundOnMaterial // */ //--------------------------------------------------- // playPositionedSounds : // //--------------------------------------------------- void CSoundManager::playPositionedSounds( const CVector& /* pos */ ) { if (!_PlaySound) return; list::iterator itPSnd; for( itPSnd = _PositionedSounds.begin(); itPSnd != _PositionedSounds.end(); ++itPSnd ) { USource *pSource = _Sources.get(*itPSnd); if (!pSource) { nlwarning(" : The source %d is unknown",*itPSnd); } else { /* if( (pos - (*itSrc).second.getPos()).norm() < ...) { if( (*itSrc).second.pSource->isPlaying() == false ) { (*itSrc).second.pSource->play(); } } */ if (!pSource->isPlaying()) { pSource->play(); } } } } // playPositionedSounds // void CSoundManager::selectEnv( const std::string &/* tag */) { // TODO : boris : temporarily removed. /* if (_EnvSoundRoot==NULL) return; _EnvSoundRoot->selectEnv (tag.c_str(), true); */ } void CSoundManager::setFilterState(uint filterId, bool state) { nlassert(filterId < NLSOUND::UAudioMixer::TBackgroundFlags::NB_BACKGROUND_FLAGS); _BackgroundFlags.Flags[filterId] = state; _AudioMixer->setBackgroundFlags(_BackgroundFlags); } // *************************************************************************** void CSoundManager::update () { // Filters : day = 0, night = 1, morning = 2, Dusk = 3 // update the filter state for day/night management. switch(LightCycleManager.getState()) { case CLightCycleManager::Day: _BackgroundFlags.Flags[DAY] = true; _BackgroundFlags.Flags[NIGHT] = false; _BackgroundFlags.Flags[MORNING] = false; _BackgroundFlags.Flags[DUSK] = false; break; case CLightCycleManager::Night: _BackgroundFlags.Flags[DAY] = false; _BackgroundFlags.Flags[NIGHT] = true; _BackgroundFlags.Flags[MORNING] = false; _BackgroundFlags.Flags[DUSK] = false; break; case CLightCycleManager::DayToNight: _BackgroundFlags.Flags[DAY] = true; _BackgroundFlags.Flags[NIGHT] = false; _BackgroundFlags.Flags[MORNING] = true; _BackgroundFlags.Flags[DUSK] = false; break; case CLightCycleManager::NightToDay: _BackgroundFlags.Flags[DAY] = false; _BackgroundFlags.Flags[NIGHT] = false; _BackgroundFlags.Flags[MORNING] = false; _BackgroundFlags.Flags[DUSK] = true; break; case CLightCycleManager::StateUnknown: //nlwarning("Unknown light cycle state reached."); break; } // update the filter for season. EGSPD::CSeason::TSeason season = CurrSeason; switch(season) { case EGSPD::CSeason::Spring: _BackgroundFlags.Flags[SPRING] = true; _BackgroundFlags.Flags[SUMMER] = false; _BackgroundFlags.Flags[AUTUMN] = false; _BackgroundFlags.Flags[WINTER] = false; break; case EGSPD::CSeason::Summer: _BackgroundFlags.Flags[SPRING] = false; _BackgroundFlags.Flags[SUMMER] = true; _BackgroundFlags.Flags[AUTUMN] = false; _BackgroundFlags.Flags[WINTER] = false; break; case EGSPD::CSeason::Autumn: _BackgroundFlags.Flags[SPRING] = false; _BackgroundFlags.Flags[SUMMER] = false; _BackgroundFlags.Flags[AUTUMN] = true; _BackgroundFlags.Flags[WINTER] = false; break; case EGSPD::CSeason::Winter: _BackgroundFlags.Flags[SPRING] = false; _BackgroundFlags.Flags[SUMMER] = false; _BackgroundFlags.Flags[AUTUMN] = false; _BackgroundFlags.Flags[WINTER] = true; break; default: //nlwarning("Updating unknown season."); break; } // TODO : update the filter state for weather effet CWeatherState weatherState = WeatherManager.getCurrWeatherState(); enum TWeatherSoundEnv { WSEMist = 0, // not used WSEFair1, WSEFair2, WSEFair3, WSEClouds, WSERain1, WSERain2, WSESnow1, WSEThunder1, WSEThunderSeve, WSEThunderSand1, // not used WSEThunderSand2, // not used WSEThunderSand3, // not used WSECount }; // For each weather setup, gives the matching weather sound env to used, when there are precipitation or not class CWeatherSetupSoundEnv { public: NLMISC::TStringId SetupName; // setup nem as found is the .weather_setup sheets TWeatherSoundEnv Sound; CWeatherSetupSoundEnv(const char *setupName, TWeatherSoundEnv sound) { SetupName = NLMISC::CStringMapper::map(setupName); Sound = sound; } }; // association between weather setup names & sound env static const CWeatherSetupSoundEnv weatherSoundLUT[] = { CWeatherSetupSoundEnv("fair1", WSEFair1), CWeatherSetupSoundEnv("fair2", WSEFair2), CWeatherSetupSoundEnv("fair3", WSEFair3), CWeatherSetupSoundEnv("wind1", WSEClouds), CWeatherSetupSoundEnv("humidity1", WSERain1), CWeatherSetupSoundEnv("humidity2", WSERain2), CWeatherSetupSoundEnv("clouds1", WSERain1), CWeatherSetupSoundEnv("clouds2", WSERain2), CWeatherSetupSoundEnv("thunderseve", WSEThunderSeve), CWeatherSetupSoundEnv("storm", WSEThunder1), CWeatherSetupSoundEnv("snow", WSESnow1), }; const CWeatherSetupSoundEnv *weatherSetupSoundEnv = NULL; for(uint k = 0; k < sizeof(weatherSoundLUT) / sizeof(weatherSoundLUT[0]); ++k) { if (weatherSoundLUT[k].SetupName == weatherState.BestSetupName) { weatherSetupSoundEnv = &weatherSoundLUT[k]; break; } } // disable all weather setup sound flags except the current one for(uint k = 0; k < WSECount; ++k) { _BackgroundFlags.Flags[MIST+k] = false; } if (weatherSetupSoundEnv) { _BackgroundFlags.Flags[MIST + weatherSetupSoundEnv->Sound] = true; } else { static bool bDisplayOnce = false; if (!bDisplayOnce) { nlinfo("The current weather setup '%s' is unknown !", CStringMapper::unmap(weatherState.BestSetupName).c_str()); bDisplayOnce = true; } } // update the background flags in the mixer. _AudioMixer->setBackgroundFlags(_BackgroundFlags); // update background music enabling if(_EnableBackgroundMusicAtTime && CTime::getLocalTime()>_EnableBackgroundMusicAtTime) { setGameMusicMode(true, true); } // Special option for DEV (easier to test when disabled) _AudioMixer->enableBackgroundMusicTimeConstraint(ClientCfg.EnableBackgroundMusicTimeConstraint); // update game music volume because of event played or not updateEventAndGameMusicVolume(); // update audio mixer _AudioMixer->update (); } // *************************************************************************** void CSoundManager::updateAudioMixerOnly() { if(!_AudioMixer) return; _AudioMixer->update (); } // *************************************************************************** void CSoundManager::playBackgroundSound() { _AudioMixer->playBackgroundSound(); } // *************************************************************************** void CSoundManager::stopBackgroundSound() { _AudioMixer->stopBackgroundSound(); } // *************************************************************************** void CSoundManager::playMusic(const string &fileName, uint xFadeTime, bool async, bool loop, bool forceGameMusicVolume) { if(fileName.empty()) { stopMusic(xFadeTime); } else { if(_AudioMixer) { // disable before the background music (before is important else may have bug) setGameMusicMode(false, forceGameMusicVolume); // play _AudioMixer->playMusic(fileName, xFadeTime, async, loop); } } } // *************************************************************************** void CSoundManager::stopMusic(uint xFadeTime) { if(_AudioMixer) { // stop the music _AudioMixer->stopMusic(xFadeTime); // and reenable the background music system. after a delay if xFadeTime if(xFadeTime>0) { // enable background music later _EnableBackgroundMusicAtTime= CTime::getLocalTime()+xFadeTime; } else { // else enable now setGameMusicMode(true, true); } } } // *************************************************************************** void CSoundManager::playEventMusic(const string &fileName, uint xFadeTime, bool loop) { // if event music not enabled (mp3 player), abort if(!_EventMusicEnabled) return; if(fileName.empty()) { stopEventMusic("", xFadeTime); } else { if(_AudioMixer) { // if the music is not already currently playing (with same loop state) if(_EventMusicPlayed.empty() || nlstricmp(fileName, _EventMusicPlayed)!=0 || loop!=_EventMusicLoop) { // play _AudioMixer->playEventMusic(fileName, xFadeTime, true, loop); _EventMusicPlayed= fileName; _EventMusicLoop= loop; } } } } // *************************************************************************** void CSoundManager::stopEventMusic(const string &fileName, uint xFadeTime) { if(_AudioMixer) { // if the event music is the one currently played, or if empty fileName if(fileName.empty() || nlstricmp(_EventMusicPlayed,fileName)==0) { // stop the music _AudioMixer->stopEventMusic(xFadeTime); // music no more played _EventMusicPlayed.clear(); } } } // *************************************************************************** void CSoundManager::pauseMusic() { if(_AudioMixer) _AudioMixer->pauseMusic(); } // *************************************************************************** void CSoundManager::resumeMusic() { if(_AudioMixer) _AudioMixer->resumeMusic(); } // *************************************************************************** bool CSoundManager::isMusicEnded() { if(_AudioMixer) return _AudioMixer->isMusicEnded(); return false; } // *************************************************************************** void CSoundManager::setGameMusicVolume(float val) { clamp(val,0.f,1.f); _GameMusicVolume= val; updateVolume(); } // *************************************************************************** void CSoundManager::setUserMusicVolume(float val) { clamp(val,0.f,1.f); _UserMusicVolume= val; updateVolume(); } // *************************************************************************** void CSoundManager::setSFXVolume(float val) { clamp(val,0.f,1.f); _SFXVolume= val; updateVolume(); } // *************************************************************************** void CSoundManager::setGameMusicMode(bool enableBackground, bool useGameMusicVolume) { // update background music state _EnableBackgroundMusicAtTime= 0; _AudioMixer->enableBackgroundMusic(enableBackground); _UseGameMusicVolume= useGameMusicVolume; // event music enabled if the background music is also enabled _EventMusicEnabled= enableBackground; // stop any event music if disabled if(!_EventMusicEnabled) stopEventMusic("", 500); // update music volume state updateVolume(); } // *************************************************************************** void CSoundManager::updateVolume() { if(_AudioMixer) { // update music volume if(_UseGameMusicVolume) // Game music (background music system) _AudioMixer->setMusicVolume(_GameMusicVolume*_FadeGameMusicVolume*_FadeGameMusicVolumeDueToEvent); else // User music volume (mp3 player) is not affected by fadein/fadeout _AudioMixer->setMusicVolume(_UserMusicVolume); // The event music volume get the game music volume, but is not faded (want music during teleport) _AudioMixer->setEventMusicVolume(_GameMusicVolume); // update sfx volume _AudioMixer->getListener()->setGain(_SFXVolume*_FadeSFXVolume); } } // *************************************************************************** void CSoundManager::fadeInGameSound(sint32 timeFadeMs) { // if already at max, noop if(_FadeGameMusicVolume==1.f && _FadeSFXVolume==1.f) return; fadeGameSound(true, timeFadeMs); } // *************************************************************************** void CSoundManager::fadeOutGameSound(sint32 timeFadeMs) { // if already at 0, noop if(_FadeGameMusicVolume==0.f && _FadeSFXVolume==0.f) return; fadeGameSound(false, timeFadeMs); } // *************************************************************************** void CSoundManager::fadeGameSound(bool fadeIn, sint32 timeFadeMs) { // fade in clamp(_FadeGameMusicVolume, 0.f, 1.f); clamp(_FadeSFXVolume, 0.f, 1.f); TTime t0= CTime::getLocalTime(); float dVolumeOverDt= 1.f / (timeFadeMs*0.001f); if(!fadeIn) dVolumeOverDt*= -1; while(fadeIn?(_FadeGameMusicVolume<1.f || _FadeSFXVolume<1.f):(_FadeGameMusicVolume>0.f || _FadeSFXVolume>0.f)) { // update volume vars TTime t1= CTime::getLocalTime(); float dt= (t1-t0)*0.001f; t0= t1; _FadeGameMusicVolume+= dVolumeOverDt*dt; _FadeSFXVolume+= dVolumeOverDt*dt; clamp(_FadeGameMusicVolume, 0.f, 1.f); clamp(_FadeSFXVolume, 0.f, 1.f); // update volume and sound updateVolume(); update(); } } // *************************************************************************** void CSoundManager::setupFadeSound(float sfxFade, float musicFade) { clamp(sfxFade, 0.f, 1.f); clamp(musicFade, 0.f, 1.f); _FadeGameMusicVolume= musicFade; _FadeSFXVolume= sfxFade; updateVolume(); } // *************************************************************************** void CSoundManager::updateEventAndGameMusicVolume() { // update event music played state if not looping if(!_EventMusicPlayed.empty() && !_EventMusicLoop) { if(!_AudioMixer) _EventMusicPlayed.clear(); else { if(_AudioMixer->isEventMusicEnded()) _EventMusicPlayed.clear(); } } // Fade const sint32 timeFadeMs= 500; static TTime t0= CTime::getLocalTime(); TTime t1= CTime::getLocalTime(); float dVolume= (t1-t0) / (float)timeFadeMs; t0= t1; // if event music is played, music lower the music if(!_EventMusicPlayed.empty()) dVolume*= -1; float f= _FadeGameMusicVolumeDueToEvent + dVolume; clamp(f, 0.f, 1.f); // if faded, update the volume if(f!=_FadeGameMusicVolumeDueToEvent) { _FadeGameMusicVolumeDueToEvent= f; updateVolume(); } } //--------------------------------------------------- // CStepSounds : // //--------------------------------------------------- /*CStepSounds::CStepSounds() { nlstopex(("class CStepSounds is not used anymore")); //init( CPath::lookup("materials_for_step_sounds.txt", false).c_str() ); } // CStepSounds // */ //--------------------------------------------------- // init : // //--------------------------------------------------- /*void CStepSounds::init( const char * fileName ) { nlstopex(("class CStepSounds is not used anymore")); if(fileName==NULL || strlen(fileName)==0 || fileName[0]=='\0') { nlwarning ("can't load step sound because no material step file"); return; } // get all the materials used for step sounds list materials; try { CConfigFile cf; cf.load( fileName ); CConfigFile::CVar &cvMaterials = cf.getVar("Materials"); for(sint i = 0; i < cvMaterials.size(); i++) { materials.push_back( cvMaterials.asInt(i) ); } } catch (const EConfigFile &e) { nlerror("Problem in the file %s : %s", fileName,e.what ()); } // load all sounds for each of these material list::const_iterator itMaterials; for( itMaterials = materials.begin(); itMaterials != materials.end(); ++itMaterials ) { CMaterialStepSounds materialStepSounds; materialStepSounds.load( CPath::lookup(string("sndmat_") + toString(*itMaterials) + string(".txt")) ); _StepSoundsByMaterial.insert( make_pair(*itMaterials,materialStepSounds) ); } } // init //*/ //--------------------------------------------------- // getSounds : // //--------------------------------------------------- /*bool CStepSounds::getSounds( uint32 material, TMoveType moveType, bool soft, std::vector& sounds ) { nlstopex(("class CStepSounds is not used anymore")); map::iterator itMatSounds = _StepSoundsByMaterial.find( material ); if( itMatSounds == _StepSoundsByMaterial.end() ) { return false; } else { return (*itMatSounds).second.getSounds( moveType, soft, sounds ); } } // getSounds //*/ //--------------------------------------------------- // readVar : // //--------------------------------------------------- /*void CMaterialStepSounds::readVar( CConfigFile& cf, TMoveType moveType, bool soft, char * varName ) { try { vector sounds; CConfigFile::CVar &cvSounds = cf.getVar( varName ); for(sint i = 0; i < cvSounds.size(); i++) { sounds.push_back( cvSounds.asString(i) ); } _Sounds.insert( make_pair( make_pair(moveType,soft), sounds) ); } catch (const EConfigFile &e) { nlwarning("Problem in the sounds by material config file : %s", e.what ()); } } // readVar // */ //--------------------------------------------------- // load : // //--------------------------------------------------- /*void CMaterialStepSounds::load( string fileName ) { CConfigFile cf; cf.load(CPath::lookup(fileName.c_str())); // walk soft sounds readVar(cf,WALK,true,"walk_sounds_soft"); // walk hard sounds readVar(cf,WALK,false,"walk_sounds_hard"); // run soft sounds readVar(cf,RUN,true,"run_sounds_soft"); // run hard sounds readVar(cf,RUN,false,"run_sounds_hard"); // user walk soft sounds readVar(cf,USER_WALK,true,"user_walk_sounds_soft"); // user walk hard sounds readVar(cf,USER_WALK,false,"user_walk_sounds_hard"); // user run soft sounds readVar(cf,USER_RUN,true,"user_run_sounds_soft"); // user run hard sounds readVar(cf,USER_RUN,false,"user_run_sounds_hard"); } // load // */ /*//--------------------------------------------------- // getSounds : // //--------------------------------------------------- bool CMaterialStepSounds::getSounds( TMoveType moveType, bool soft, vector& sounds ) { map,vector >::iterator itSounds = _Sounds.find( make_pair(moveType,soft) ); if( itSounds == _Sounds.end() ) { return false; } uint i; uint sz = (*itSounds).second.size(); sounds.clear(); sounds.resize( sz ); for( i=0; i < sz; i++ ) { sounds[i] = (*itSounds).second[i]; } return true; } // getSounds //*/