// 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.h" #include "nel/sound/sound_bank.h" #include "nel/sound/simple_sound.h" #include "nel/sound/complex_sound.h" #include "nel/sound/context_sound.h" #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" #include "nel/georges/u_form.h" #include "nel/misc/path.h" #include "nel/sound/driver/buffer.h" #include "nel/georges/load_form.h" using namespace std; using namespace NLMISC; using namespace NLGEORGES; namespace NLSOUND { void CSoundBank::bufferUnloaded(const NLMISC::TStringId &bufferName) { TBufferAssocContainer::iterator it(_BufferAssoc.find(bufferName)); if (it != _BufferAssoc.end()) { // ok, found some sound associated with this buffer. // update all sounds. TSimpleSoundContainer::iterator first(it->second.begin()), last(it->second.end()); for (; first != last; ++first) { // remove the associated buffer. CSimpleSound *ss = const_cast(*(first)); ss->setBuffer(NULL); } } } //void CSoundBank::bufferLoaded(const std::string bufferName, IBuffer *buffer) void CSoundBank::bufferLoaded(const NLMISC::TStringId &/* bufferName */, IBuffer *buffer) { // std::map >::iterator it(_BufferAssoc.find(buffer->getName())); TBufferAssocContainer::iterator it(_BufferAssoc.find(buffer->getName())); if (it != _BufferAssoc.end()) { // ok, found some sound associated with this buffer. // update all sounds. TSimpleSoundContainer::iterator first(it->second.begin()), last(it->second.end()); for (; first != last; ++first) { CSimpleSound *ss = const_cast(*(it->second.begin())); // restore the associated buffer. ss->setBuffer(buffer); } } } void CSoundBank::registerBufferAssoc(CSimpleSound *sound, IBuffer *buffer) { if (buffer != NULL) { const NLMISC::TStringId &bufferName = buffer->getName(); _BufferAssoc[bufferName].insert(sound); } } void CSoundBank::unregisterBufferAssoc(CSimpleSound *sound, IBuffer * buffer) { if (buffer != NULL) { const TStringId &bufferName = buffer->getName(); TBufferAssocContainer::iterator it(_BufferAssoc.find(bufferName)); if (it != _BufferAssoc.end()) { TSimpleSoundContainer::iterator it2(it->second.find(sound)); nlassert(it2 != it->second.end()); it->second.erase(it2); if (it->second.empty()) { // last sound refenrecing this buffer _BufferAssoc.erase(it); } } } } /* * Destructor */ CSoundBank::~CSoundBank() { unload(); } void CSoundBank::addSound(CSound *sound) { // nlassert(_Sounds.size() > sound->getName().getShortId()); // nldebug("SOUNDBANK: Add %s", sound->getName().toString().c_str()); if (_Sounds.size() <= sound->getName().getShortId()) _Sounds.resize(sound->getName().getShortId() + 1); _Sounds[sound->getName().getShortId()] = sound; } void CSoundBank::removeSound(const NLMISC::CSheetId &sheetId) { _Sounds[sheetId.getShortId()] = NULL; } /** Pseudo serializer for packed sheet loading/saving. * This class act as a wrapper to create sound from * xml document being read, call the serialisation method * to read/write packed sheet and to delete sounds that are * no more needed. */ class CSoundSerializer { public: /// The sound beeing managed by this serializer. CSound *Sound; /// Default constructor. CSoundSerializer() : Sound(0) {} // load the values using the george sheet (called by GEORGE::loadForm) void readGeorges (const NLMISC::CSmartPtr &form, const std::string &name) { // just call the sound creation method with the xml form. Sound = CSound::createSound(name, form->getRootNode()); // success ? // if (_Sound != 0) // CSoundBank::instance()->addSound(_Sound); } // load/save the values using the serial system (called by GEORGE::loadForm) void serial (NLMISC::IStream &s) { if (s.isReading()) { // read the first item to find the type CSound::TSOUND_TYPE type = CSound::SOUND_SIMPLE; s.serialEnum(type); // read the sound name // std::string name; // s.serial(name); // Instantiate the corresponding sound. switch(CSound::TSOUND_TYPE(type)) { case CSound::SOUND_SIMPLE: Sound = new CSimpleSound(); break; case CSound::SOUND_COMPLEX: Sound = new CComplexSound(); break; case CSound::SOUND_CONTEXT: Sound = new CContextSound(); break; case CSound::SOUND_BACKGROUND: Sound = new CBackgroundSound(); break; case CSound::SOUND_MUSIC: Sound = new CMusicSound(); break; case CSound::SOUND_STREAM: Sound = new CStreamSound(); break; case CSound::SOUND_STREAM_FILE: Sound = new CStreamFileSound(); break; default: Sound = 0; } // nlassert(_Sound != 0); if (Sound) { // read the sound data Sound->serial(s); // CSoundBank::instance()->addSound(_Sound); } } else { if (Sound == 0) { // the sound doesn't exist uint32 i = std::numeric_limits::max(); s.serialEnum(i); // s.serial(std::string("bad sound")); } else { // write the sound type. CSound::TSOUND_TYPE type = Sound->getSoundType(); s.serialEnum(type); // write the sound name // s.serial(const_cast(_Sound->getName())); // and write the sound data Sound->serial(s); } } } /** called by GEORGE::loadForm when a sheet read from the packed sheet is no more in * the directories. */ void removed() { if (Sound != 0) { // we remove the sound from the bank and delete it. // CSoundBank::instance()->removeSound(_Sound->getName()); delete Sound; } } // return the version of this class, increments this value when the content of this class changed static uint getVersion () { return 3; } }; /** Load all the sound samples. * * Can throw EPathNotFound or ESoundFileNotFound (check Exception) */ void CSoundBank::load(const std::string &packedSheetDir, bool packedSheetUpdate) { // this structure is fill by the loadForm() function and will contain all you need std::map container; // load the old way for compatibility nlassert(!_Loaded); // Just call the GEORGE::loadFrom method to read all available sounds ::loadForm("sound", packedSheetDir + "sounds.packed_sheets", container, packedSheetUpdate, false); _Loaded = true; // get the largest sheet id needed and init the sound bank uint32 maxShortId = 0; { std::map::iterator first(container.begin()), last(container.end()); for (; first != last; ++first) { if (first->second.Sound != 0) if (first->second.Sound->getName().getShortId() > maxShortId) maxShortId = first->second.Sound->getName().getShortId(); } ++maxShortId; // inc for size = last idx + 1 if (container.size() == 0) { nlwarning("NLSOUND: No sound sheets have been loaded, missing sound sheet directory or packed sound sheets file"); } else { nlassert(maxShortId < (container.size() * 8)); // ensure no ridiculous sheet id values if (maxShortId > _Sounds.size()) _Sounds.resize(maxShortId); } } // add all the loaded sound in the sound banks { std::map::iterator first(container.begin()), last(container.end()); for (; first != last; ++first) { if (first->second.Sound != 0) addSound(first->second.Sound); } } container.clear(); } /* * Unload all the sound samples in this bank. */ void CSoundBank::unload() { nlassert(_Loaded); for (TSoundTable::size_type i = 0; i < _Sounds.size(); ++i) { delete _Sounds[i]; } _Sounds.clear(); _Loaded = false; /* vector vec; TSoundTable::iterator map_iter; for (map_iter = _Sounds.begin(); map_iter != _Sounds.end(); ++map_iter) { // We can't delete directly second because the map is based on second->getName() vec.push_back( (*map_iter).second ); } _Sounds.clear(); vector::iterator vec_iter; for (vec_iter = vec.begin(); vec_iter != vec.end(); ++vec_iter) { CSound *sound = *vec_iter; delete sound; } _Loaded = false; */ } /* * Returns true if the samples in this bank have been loaded. */ bool CSoundBank::isLoaded() { return _Loaded; } /* * Return a sound sample corresponding to a name. */ CSound* CSoundBank::getSound(const NLMISC::CSheetId &sheetId) { if (sheetId == NLMISC::CSheetId::Unknown) return NULL; // nlassert(sheetId.getShortId() < _Sounds.size()); if (sheetId.getShortId() >= _Sounds.size()) { std::string sheetName = sheetId.toString(); nldebug("NLSOUND: Sound sheet id '%s' exceeds loaded sound sheets", sheetName.c_str()); return NULL; } return _Sounds[sheetId.getShortId()]; } /** * Return the names of the sounds */ void CSoundBank::getNames( std::vector &sheetIds ) { for (TSoundTable::size_type i = 0; i < _Sounds.size(); ++i) { if (_Sounds[i]) sheetIds.push_back(_Sounds[i]->getName()); } } /* * Return the number of buffers in this bank. */ uint CSoundBank::countSounds() { return (uint)_Sounds.size(); } } // namespace NLSOUND