sample_bank.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdsound.h"
00027 
00028 #include "sample_bank.h"
00029 #include "driver/sound_driver.h"
00030 #include "driver/buffer.h"
00031 #include "nel/misc/path.h"
00032 #include "nel/misc/file.h"
00033 #include "async_file_manager_sound.h"
00034 #include "background_sound_manager.h"
00035 #include "sound_bank.h"
00036 
00037 
00038 using namespace std;
00039 using namespace NLMISC;
00040 
00041 
00042 namespace NLSOUND {
00043 
00045 uint32          ASYNC_LOADING_SPLIT = 10;               // 10 file by 10 file
00046 
00047 CSampleBank::TSampleBankContainer       CSampleBank::_Banks;
00048 uint    CSampleBank::_LoadedSize = 0;
00049 
00050 CSampleBank::TVirtualBankCont           CSampleBank::_VirtualBanks;
00051 
00052 
00053 
00054 
00055 void                            CSampleBank::init(NLGEORGES::UFormElm *mixerConfig)
00056 {
00057         NL_ALLOC_CONTEXT(NLSOUND_CSampleBank);
00058         if (mixerConfig == 0)
00059                 return;
00060 
00061         NLGEORGES::UFormElm     *virtualBanks;
00062         mixerConfig->getNodeByName(&virtualBanks, ".VirtualBanks");
00063         if (virtualBanks == 0)
00064                 return;
00065 
00066         uint size;
00067         virtualBanks->getArraySize(size);
00068 
00069         for (uint i=0; i<size; ++i)
00070         {
00071                 NLGEORGES::UFormElm     *virtualBank;
00072                 virtualBanks->getArrayNode(&virtualBank, i);
00073 
00074                 if (virtualBank != 0)
00075                 {
00076                         std::vector<TFilteredBank> vfb;
00077                         std::string virtualName;
00078                         virtualBank->getValueByName(virtualName, ".VirtualName");
00079                         NLGEORGES::UFormElm     *realBanks;
00080                         virtualBank->getNodeByName(&realBanks, ".FilteredBank");
00081                         if (realBanks != 0)
00082                         {
00083                                 uint size2;
00084                                 realBanks->getArraySize(size2);
00085 
00086                                 for (uint j=0; j<size2; ++j)
00087                                 {
00088                                         TFilteredBank fb;
00089                                         std::string     bankName;
00090                                         NLGEORGES::UFormElm     *realBank;
00091                                         realBank->getArrayNode(&realBank, j);
00092 
00093                                         realBank->getValueByName(bankName, ".SampleBank");
00094                                         fb.BankName = CStringMapper::map(bankName);
00095                                         realBank->getValueByName(fb.Filter, ".Filter");
00096 
00097                                         vfb.push_back(fb);
00098                                 }
00099                         }
00100 
00101                         if (!vfb.empty())
00102                         {
00103                                 _VirtualBanks.insert(std::make_pair(NLMISC::CStringMapper::map(virtualName), vfb));
00104                                 // create the sample bank
00105                                 new CSampleBank(virtualName, NULL);
00106                         }
00107                 }
00108         }
00109 }
00110 
00111 
00112 
00113 
00114 // ********************************************************
00115 CSampleBank *CSampleBank::findSampleBank(const NLMISC::TStringId &filename)
00116 {
00117         TSampleBankContainer::iterator  it(_Banks.find(filename));
00118 
00119         if (it != _Banks.end())
00120                 return it->second;
00121 
00122         return NULL;
00123 }
00124 
00125 // ********************************************************
00126 
00127 IBuffer*                CSampleBank::get(const NLMISC::TStringId &name)
00128 {
00129         IBuffer* buffer;
00130         TSampleBankContainer::iterator iter;
00131 
00132         for (iter = _Banks.begin(); iter != _Banks.end(); ++iter)
00133         {
00134                 buffer = iter->second->getSample(name);
00135                 if (buffer != 0)
00136                 {
00137                         return buffer;
00138                 }
00139         }
00140         
00141         //nlwarning ("Try to get an unknown sample '%s'", name);
00142         return 0;
00143 }
00144 
00145 void     CSampleBank::reload(bool async)
00146 {
00147         TSampleBankContainer::iterator first(_Banks.begin()), last(_Banks.end());
00148 
00149         for (; first != last; ++first)
00150         {
00151                 first->second->unload();
00152                 first->second->load(async);
00153         }
00154 }
00155 
00156 
00157 void CSampleBank::getLoadedSampleBankInfo(std::vector<std::pair<std::string, uint> > &result)
00158 {
00159         result.clear();
00160 
00161         TSampleBankContainer::iterator first(_Banks.begin()), last(_Banks.end());
00162         for (; first != last; ++first)
00163         {
00164                 std::pair<std::string, uint> p;
00165                 if (first->second->isLoaded())
00166                 {
00167                         p.first = NLMISC::CStringMapper::unmap(first->first);
00168                         p.second = first->second->getSize();
00169                         result.push_back(p);
00170                 }
00171         }
00172 }
00173 
00174 
00175 
00176 
00177 
00178 // ********************************************************
00179 
00180 CSampleBank::CSampleBank(const std::string& name, ISoundDriver *sd) 
00181 : _SoundDriver(sd), _Name(CStringMapper::map(name)), _Loaded(false), _LoadingDone(true), _ByteSize(0)
00182 {
00183 //      _Name = CFile::getFilenameWithoutExtension(_Path);
00184         _Banks.insert(make_pair(_Name, this));
00185 }
00186 
00187 
00188 // ********************************************************
00189 
00190 CSampleBank::~CSampleBank()
00191 {
00192         CAudioMixerUser::instance()->unregisterUpdate(this);
00193         while (!_LoadingDone)
00194         {
00195                 // need to wait for loading end.
00196                 nlSleep(100);
00197         }
00198 
00199         if (_Loaded)
00200                 unload();
00201 
00202         // remove the bank from the list of known banks
00203         TSampleBankContainer::iterator iter(_Banks.begin()), end(_Banks.end());
00204 
00205         for (; iter != end; ++iter)
00206         {
00207                 if (iter->second == this)
00208                 {
00209                         _Banks.erase(iter);
00210                         break;
00211                 }
00212         }
00213 
00214 
00215         // delete all the samples.
00216         while (!_Samples.empty())
00217         {
00218                 delete _Samples.begin()->second;
00219                 _Samples.erase(_Samples.begin());
00220         }
00221 
00222         _Samples.clear();
00223 }
00224 
00225 
00226 // ********************************************************
00227 
00228 void                            CSampleBank::load(bool async)
00229 {
00230         // TODO : add async loading support !
00231 
00232         TVirtualBankCont::iterator it(_VirtualBanks.find(_Name));
00233         if (it != _VirtualBanks.end())
00234         {
00235                 // this is a virtual sample bank !
00236                 nlinfo("Loading virtual sample bank %s", CStringMapper::unmap(_Name).c_str());
00237 
00238                 CAudioMixerUser *mixer = CAudioMixerUser::instance();
00239                 const CAudioMixerUser::TBackgroundFlags &flags = mixer->getBackgroundFlags();
00240 
00241                 for (uint i=0; i<it->second.size(); ++i)
00242                 {
00243                         if (flags.Flags[it->second[i].Filter])
00244                         {
00245                                 CSampleBank *bank = findSampleBank(it->second[i].BankName);
00246                                 if (bank)
00247                                         bank->load(async);
00248                         }
00249                 }
00250         }
00251 
00252         nlinfo("Loading sample bank %s %", CStringMapper::unmap(_Name).c_str(), async?"":"Asynchronously");
00253 
00254         vector<string> filenames;
00255 //      vector<string>::iterator iter;
00256 
00257         if (_Loaded)
00258         {
00259                 nlwarning("Trying to load an already loaded bank : %s", CStringMapper::unmap(_Name).c_str ());
00260                 return;
00261         }
00262 
00263 
00264         // Load the sample bank from the builded sample_bank file.
00265         string bankName(CStringMapper::unmap(_Name)+".sample_bank");
00266         string filename = CPath::lookup(bankName);
00267         if (filename.empty())
00268         {
00269                 nlwarning("Trying to load an unknown sample bank [%s]", bankName.c_str());
00270                 return;
00271         }
00272 
00273         try
00274         {
00275 
00276                 CIFile  sampleBank(filename);
00277 
00278                 CAudioMixerUser::TSampleBankHeader sbh;
00279                 sampleBank.serial(sbh);
00280                 _LoadingDone = false;
00281 
00282                 sint32 seekStart = sampleBank.getPos();
00283 
00284 
00285                 uint8   *data = 0;
00286                 uint    i;
00287                 for (i=0; i<sbh.Name.size(); ++i)
00288                 {
00289                         IBuffer *ibuffer = _SoundDriver->createBuffer();
00290                         nlassert(ibuffer);
00291 
00292                         TStringId       nameId = CStringMapper::map(CFile::getFilenameWithoutExtension(sbh.Name[i]));
00293                         ibuffer->presetName(nameId);
00294                         
00295         /*              {
00296                                 sint16 *data16 = new sint16[sbh.NbSample[i]];
00297                                 IBuffer::TADPCMState    state;
00298                                 state.PreviousSample = 0;
00299                                 state.StepIndex = 0;
00300                                 uint count =0;
00301                                 for (count=0; count+1024<sbh.NbSample[i]; count+=1024)
00302                                 {
00303                                         IBuffer::decodeADPCM(data+count/2, data16+count, 1024, state);
00304                                 }
00305                                 IBuffer::decodeADPCM(data+count/2, data16+count, sbh.NbSample[i]-count, state);
00306 
00307                                 state.PreviousSample = 0;
00308                                 state.StepIndex = 0;
00309                                 sint16  *data16_2 = new sint16[sbh.NbSample[i]];
00310                                 IBuffer::decodeADPCM(data, data16_2, sbh.NbSample[i], state);
00311 
00312                                 for (uint j=0; j<sbh.NbSample[i]; ++j)
00313                                 {
00314                                         if (data16[j] != data16_2[j])
00315                                         {
00316                                                 nlwarning("Sample differ at %u", j);
00317                                         }
00318                                 }
00319 
00320                                 _SoundDriver->readRawBuffer(ibuffer, sbh.Name[i], (uint8*)data16, sbh.NbSample[i]*2, Mono16, sbh.Freq[i]);
00321                                 delete [] data16;
00322                                 delete [] data16_2;
00323                         }
00324         */
00325 
00326                         if (CAudioMixerUser::instance()->useAPDCM())
00327                         {
00328                                 data = (uint8*) realloc(data, sbh.SizeAdpcm[i]);
00329                                 sampleBank.seek(seekStart + sbh.OffsetAdpcm[i], CIFile::begin);
00330                                 sampleBank.serialBuffer(data, sbh.SizeAdpcm[i]);
00331                                 _SoundDriver->readRawBuffer(ibuffer, sbh.Name[i], data, sbh.SizeAdpcm[i], Mono16ADPCM, sbh.Freq[i]);
00332                         }
00333                         else
00334                         {
00335                                 data = (uint8*) realloc(data, sbh.SizeMono16[i]);
00336                                 sampleBank.seek(seekStart + sbh.OffsetMono16[i], CIFile::begin);
00337                                 sampleBank.serialBuffer(data, sbh.SizeMono16[i]);
00338                                 _SoundDriver->readRawBuffer(ibuffer, sbh.Name[i], data, sbh.SizeMono16[i], Mono16, sbh.Freq[i]);
00339                         }
00340                         
00341                         _ByteSize += ibuffer->getSize();
00342 
00343                         _Samples[nameId] = ibuffer;
00344 
00345                         // Warn the sound bank that the sample are available.
00346                         CSoundBank::instance()->bufferLoaded(nameId, ibuffer);
00347                 }
00348                 free(data);
00349 
00350                 _LoadedSize += _ByteSize;
00351         }
00352         catch(Exception &e)
00353         {
00354                 // loading failed !
00355                 nlwarning("Exception %s during loading of sample bank %s", e.what(), filename.c_str());
00356                 CAudioMixerUser *mixer = CAudioMixerUser::instance();
00357 
00358                 if (mixer->getPackedSheetUpdate())
00359                 {
00360                         nlinfo("Deleting offending sound bank, you need to restart to recreate it!");
00361                         CFile::deleteFile(filename);
00362                 }
00363         }
00364 
00365         _Loaded = true;
00366         _LoadingDone = true;
00367 
00368 
00369 
00371 /*
00372 
00373         std::string list = CPath::lookup(CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt, false);
00374         if (list.empty())
00375         {
00376                 nlwarning("File %s not found to load sample bank %s", (CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt).c_str(), CStringMapper::unmap(_Name).c_str());
00377                 return;
00378         }
00379 
00380 
00381         NLMISC::CIFile sampleBankList(list);
00382         sampleBankList.serialCont(filenames);
00383 
00384         for (iter = filenames.begin(); iter != filenames.end(); iter++)
00385         {
00386                 IBuffer* ibuffer = NULL;
00387                 try
00388                 {
00389                         ibuffer = _SoundDriver->createBuffer();
00390                         nlassert(ibuffer);
00391 
00392 //                      std::string sampleName(CFile::getFilenameWithoutExtension(*iter));
00393                         NLMISC::TStringId sampleName(CStringMapper::map(CFile::getFilenameWithoutExtension(*iter)));
00394 
00395                         if (async)
00396                         {
00397                                 ibuffer->presetName(sampleName);
00398                                 nldebug("Preloading sample [%s]", CStringMapper::unmap(sampleName).c_str());
00399                         }
00400                         else
00401                         {
00402                                 std::string fullName = NLMISC::CPath::lookup(*iter, false);
00403                                 if (!fullName.empty())
00404                                 {
00405                                         NLMISC::CIFile  ifile(fullName);
00406                                         uint size = ifile.getFileSize();
00407                                         uint8 *buffer = new uint8[ifile.getFileSize()];
00408                                         ifile.serialBuffer(buffer, size);
00409 
00410                                         _SoundDriver->readWavBuffer(ibuffer, fullName, buffer, size);
00411                                         _ByteSize += ibuffer->getSize();
00412 
00413                                         delete [] buffer;
00414                                 }
00415                         }
00416                         _Samples[sampleName] = ibuffer ;
00417 
00418                         // Warn the sound bank that the sample are available.
00419                         CSoundBank::instance()->bufferLoaded(sampleName, ibuffer);
00420                 }
00421                 catch (ESoundDriver &e)
00422                 {
00423                         if (ibuffer != NULL) {
00424                                 delete ibuffer;
00425                                 ibuffer = NULL;
00426                         }
00427                         nlwarning("Problem with file '%s': %s", (*iter).c_str(), e.what());
00428                 }
00429         }
00430 
00431         _Loaded = true;
00432 
00433         if (!async)
00434         {
00435                 _LoadingDone = true;
00436                 // compute the sample bank size.
00437                 _LoadedSize += _ByteSize;
00438         }
00439         else
00440         {
00441                 // fill the loading list.
00442                 TSampleTable::iterator first(_Samples.begin()), last(_Samples.end());
00443                 for (; first != last; ++first)
00444                 {
00445                         _LoadList.push_back(make_pair(first->second, first->first));
00446                 }
00447                 _SplitLoadDone = false;
00448                 // send the first files
00449                 for (uint i=0; i<ASYNC_LOADING_SPLIT && !_LoadList.empty(); ++i)
00450                 {
00451                         CAsyncFileManagerSound::getInstance().loadWavFile(_LoadList.front().first, CStringMapper::unmap(_LoadList.front().second)+".wav");
00452                         _LoadList.pop_front();
00453                 }
00454                 // add a end loading event...
00455                 CAsyncFileManagerSound::getInstance().signal(&_SplitLoadDone);
00456                 // and register for update on the mixer
00457                 CAudioMixerUser::instance()->registerUpdate(this);
00458         }
00459         */
00460 }
00461 
00462 void CSampleBank::onUpdate()
00463 {
00464         if (_SplitLoadDone)
00465         {
00466                 nldebug("Some samples have been loaded");
00467                 if (_LoadList.empty())
00468                 {
00469                         // all the samples are loaded, we can compute the bank size.
00470                         TSampleTable::iterator  first(_Samples.begin()), last(_Samples.end());
00471                         for (; first != last; ++first)
00472                         {
00473                                 _ByteSize += first->second->getSize();
00474                         }
00475                 
00476                         _LoadedSize += _ByteSize;
00477 
00478                         // stop the update.
00479                         CAudioMixerUser::instance()->unregisterUpdate(this);
00480                         _LoadingDone = true;
00481 
00482                         // Force an update in the background manager (can restar stoped sound).
00483                         CAudioMixerUser::instance()->getBackgroundSoundManager()->updateBackgroundStatus();
00484 
00485                         nlinfo("Sample bank %s loaded.", CStringMapper::unmap(_Name).c_str());
00486                 }
00487                 else
00488                 {
00489                         _SplitLoadDone = false;
00490                         for (uint i=0; i<ASYNC_LOADING_SPLIT && !_LoadList.empty(); ++i)
00491                         {
00492                                 CAsyncFileManagerSound::getInstance().loadWavFile(_LoadList.front().first, CStringMapper::unmap(_LoadList.front().second)+".wav");
00493                                 _LoadList.pop_front();
00494                         }
00495                         // add a end loading event...
00496                         CAsyncFileManagerSound::getInstance().signal(&_SplitLoadDone);
00497                 }
00498         }
00499 }
00500 
00501 // ********************************************************
00502 
00503 bool                            CSampleBank::unload()
00504 {
00505         vector<IBuffer*> vec;
00506         TSampleTable::iterator it;
00507 
00508         if (!_Loaded)
00509         {
00510                 nlwarning("Trying to unload an already unloaded bank : %s", CStringMapper::unmap(_Name).c_str ());
00511                 return  true;
00512         }
00513 
00514         // need to wait end of load ?
00515         if (!_LoadingDone)
00516                 return false;
00517 
00518         nlinfo("Unloading sample bank %s", CStringMapper::unmap(_Name).c_str());
00519 
00520         for (it = _Samples.begin(); it != _Samples.end(); ++it)
00521         {
00522                 IBuffer *buffer = it->second;
00523                 if (buffer)
00524                 {
00525                         const NLMISC::TStringId & bufferName = buffer->getName();
00526 
00527                         // Warn the mixer to stop any track playing this buffer.
00528                         CAudioMixerUser::instance()->bufferUnloaded(buffer);
00529                         // Warn the sound banks abount this buffer.
00530                         CSoundBank::instance()->bufferUnloaded(bufferName);
00531 
00532                         // delete
00533                         it->second = NULL;
00534                         delete buffer;
00535                 }
00536         }
00537 
00538         _Loaded = false;
00539 
00540         _LoadedSize -= _ByteSize;
00541         _ByteSize = 0;
00542 
00543         return true;
00544 }
00545 
00546 // ********************************************************
00547 
00548 bool                            CSampleBank::isLoaded()
00549 {
00550         return _Loaded;
00551 }
00552 
00553 // ********************************************************
00554 
00555 IBuffer*                        CSampleBank::getSample(const NLMISC::TStringId &name)
00556 {
00557         {
00558 /*              // dump the sample list.
00559                 TSampleTable::iterator it (_Samples.begin()), last(_Samples.end());
00560                 std::string s;
00561 
00562 //              while (first != last)
00563                 for (it = _Samples.begin(); it != _Samples.end(); ++it)
00564                 {
00565                         s += std::string(" [")+it->first+"] ";
00566                         //first++;
00567                 }
00568 
00569                 nldebug("getSample(%s) : sample list = [%s]", name, s.c_str());
00570 */
00571         }
00572 
00573         // Find sound
00574         TSampleTable::iterator iter = _Samples.find(name);
00575         if ( iter == _Samples.end() )
00576         {
00577                 return 0;
00578         }
00579         else
00580         {
00581                 return (*iter).second;
00582         }
00583 }
00584 
00585 // ********************************************************
00586 
00587 uint                            CSampleBank::countSamples()
00588 {
00589         return _Samples.size();
00590 }
00591 
00592 // ********************************************************
00593 
00594 uint                            CSampleBank::getSize()
00595 {
00596         uint size = 0;
00597 
00598         TSampleTable::const_iterator iter;
00599         for (iter = _Samples.begin(); iter != _Samples.end(); iter++)
00600         {
00601                 size += (*iter).second->getSize();
00602         }
00603 
00604         return size;
00605 }
00606 
00607 void                            CSampleBank::releaseAll()
00608 {
00609         nldebug( "SampleBanks: Releasing..." );
00610 
00611         while (!_Banks.empty())
00612         {
00613                 delete _Banks.begin()->second;
00614         }
00615         nldebug( "SampleBanks: Released" );
00616 }
00617 
00618 
00619 
00620 } // namespace NLSOUND
00621 

Generated on Tue Mar 16 06:35:55 2004 for NeL by doxygen 1.3.6