Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / sound / sample_bank.cpp
blob53a6319e3562d37c6a13f2ae090e62f43d83498e
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdsound.h"
22 #include "nel/sound/sample_bank.h"
23 #include "nel/sound/sample_bank_manager.h"
24 #include "nel/sound/driver/sound_driver.h"
25 #include "nel/sound/driver/buffer.h"
26 #include "nel/misc/path.h"
27 #include "nel/misc/file.h"
28 #include "nel/sound/async_file_manager_sound.h"
29 #include "nel/sound/background_sound_manager.h"
30 #include "nel/sound/sound_bank.h"
33 using namespace std;
34 using namespace NLMISC;
37 namespace NLSOUND {
39 /// Constante for the number of file to load asynchronously at a time.
40 uint32 ASYNC_LOADING_SPLIT = 10; // 10 file by 10 file
43 // ********************************************************
45 CSampleBank::CSampleBank(NLMISC::TStringId name, CSampleBankManager *sampleBankManager)
46 : _SampleBankManager(sampleBankManager), _Name(name), _Loaded(false), _LoadingDone(true), _ByteSize(0)
48 _SampleBankManager->m_Banks.insert(make_pair(name, this));
52 // ********************************************************
54 CSampleBank::~CSampleBank()
56 _SampleBankManager->m_AudioMixer->unregisterUpdate(this);
57 while (!_LoadingDone)
59 // need to wait for loading end.
60 nlSleep(100);
63 if (_Loaded)
64 unload();
66 // remove the bank from the list of known banks
67 for (CSampleBankManager::TSampleBankContainer::iterator
68 it(_SampleBankManager->m_Banks.begin()),
69 end(_SampleBankManager->m_Banks.end()); it != end; ++it)
71 if (it->second == this)
73 _SampleBankManager->m_Banks.erase(it);
74 break;
79 // delete all the samples.
80 while (!_Samples.empty())
82 delete _Samples.begin()->second;
83 _Samples.erase(_Samples.begin());
86 _Samples.clear();
90 // ********************************************************
92 void CSampleBank::load(bool async)
94 // TODO : add async loading support !
96 CSampleBankManager::TVirtualBankCont::iterator it(_SampleBankManager->m_VirtualBanks.find(_Name));
97 if (it != _SampleBankManager->m_VirtualBanks.end())
99 // this is a virtual sample bank !
100 nlinfo("Loading virtual sample bank %s", CStringMapper::unmap(_Name).c_str());
102 const CAudioMixerUser::TBackgroundFlags &flags = _SampleBankManager->m_AudioMixer->getBackgroundFlags();
104 for (uint i=0; i<it->second.size(); ++i)
106 if (flags.Flags[it->second[i].Filter])
108 CSampleBank *bank = _SampleBankManager->findSampleBank(it->second[i].BankName);
109 if (bank)
110 bank->load(async);
115 //nlinfo("Loading sample bank %s %", CStringMapper::unmap(_Name).c_str(), async?"":"Asynchronously");
117 vector<string> filenames;
118 // vector<string>::iterator iter;
120 if (_Loaded)
122 nlwarning("Trying to load an already loaded bank : %s", CStringMapper::unmap(_Name).c_str ());
123 return;
127 // Load the sample bank from the builded sample_bank file.
128 string bankName(CStringMapper::unmap(_Name)+".sample_bank");
129 string filename = CPath::lookup(bankName, false);
130 if (filename.empty())
132 nlwarning("Could not find sample bank [%s]", bankName.c_str());
133 return;
139 CIFile sampleBank(filename);
141 CAudioMixerUser::TSampleBankHeader sbh;
142 sampleBank.serial(sbh);
143 _LoadingDone = false;
145 sint32 seekStart = sampleBank.getPos();
148 uint8 *data = 0;
149 uint i;
150 for (i=0; i<sbh.Name.size(); ++i)
152 IBuffer *ibuffer = _SampleBankManager->m_AudioMixer->getSoundDriver()->createBuffer();
153 nlassert(ibuffer);
155 TStringId nameId = CStringMapper::map(CFile::getFilenameWithoutExtension(sbh.Name[i]));
156 ibuffer->setName(nameId);
158 /* {
159 sint16 *data16 = new sint16[sbh.NbSample[i]];
160 IBuffer::TADPCMState state;
161 state.PreviousSample = 0;
162 state.StepIndex = 0;
163 uint count =0;
164 for (count=0; count+1024<sbh.NbSample[i]; count+=1024)
166 IBuffer::decodeADPCM(data+count/2, data16+count, 1024, state);
168 IBuffer::decodeADPCM(data+count/2, data16+count, sbh.NbSample[i]-count, state);
170 state.PreviousSample = 0;
171 state.StepIndex = 0;
172 sint16 *data16_2 = new sint16[sbh.NbSample[i]];
173 IBuffer::decodeADPCM(data, data16_2, sbh.NbSample[i], state);
175 for (uint j=0; j<sbh.NbSample[i]; ++j)
177 if (data16[j] != data16_2[j])
179 nlwarning("Sample differ at %u", j);
183 _SoundDriver->readRawBuffer(ibuffer, sbh.Name[i], (uint8*)data16, sbh.NbSample[i]*2, Mono16, sbh.Freq[i]);
184 delete [] data16;
185 delete [] data16_2;
189 if (_SampleBankManager->m_AudioMixer->useAPDCM())
191 data = (uint8*) realloc(data, sbh.SizeAdpcm[i]);
192 sampleBank.seek(seekStart + sbh.OffsetAdpcm[i], CIFile::begin);
193 sampleBank.serialBuffer(data, sbh.SizeAdpcm[i]);
194 ibuffer->setFormat(IBuffer::FormatDviAdpcm, 1, 16, sbh.Freq[i]);
195 if (!ibuffer->fill(data, sbh.SizeAdpcm[i]))
196 nlwarning("AM: ibuffer->fill returned false with FormatADPCM");
198 else
200 data = (uint8*) realloc(data, sbh.SizeMono16[i]);
201 sampleBank.seek(seekStart + sbh.OffsetMono16[i], CIFile::begin);
202 sampleBank.serialBuffer(data, sbh.SizeMono16[i]);
203 ibuffer->setFormat(IBuffer::FormatPcm, 1, 16, sbh.Freq[i]);
204 if (!ibuffer->fill(data, sbh.SizeMono16[i]))
205 nlwarning("AM: ibuffer->fill returned false with FormatPCM");
208 _ByteSize += ibuffer->getSize();
210 _Samples[nameId] = ibuffer;
212 // Warn the sound bank that the sample are available.
213 CAudioMixerUser::getInstance()->getSoundBank()->bufferLoaded(nameId, ibuffer);
215 free(data);
217 _SampleBankManager->m_LoadedSize += _ByteSize;
219 catch(const Exception &e)
221 // loading failed !
222 nlwarning("Exception %s during loading of sample bank %s", e.what(), filename.c_str());
224 if (_SampleBankManager->m_AudioMixer->getPackedSheetUpdate())
226 nlinfo("Deleting offending sound bank, you need to restart to recreate it!");
227 CFile::deleteFile(filename);
231 _Loaded = true;
232 _LoadingDone = true;
236 ///////////////////////////////////////// OLD Version //////////////////////////////////////
239 std::string list = CPath::lookup(CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt, false);
240 if (list.empty())
242 nlwarning("File %s not found to load sample bank %s", (CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt).c_str(), CStringMapper::unmap(_Name).c_str());
243 return;
247 NLMISC::CIFile sampleBankList(list);
248 sampleBankList.serialCont(filenames);
250 for (iter = filenames.begin(); iter != filenames.end(); iter++)
252 IBuffer* ibuffer = NULL;
255 ibuffer = _SoundDriver->createBuffer();
256 nlassert(ibuffer);
258 // std::string sampleName(CFile::getFilenameWithoutExtension(*iter));
259 NLMISC::TStringId sampleName(CStringMapper::map(CFile::getFilenameWithoutExtension(*iter)));
261 if (async)
263 ibuffer->presetName(sampleName);
264 nldebug("Preloading sample [%s]", CStringMapper::unmap(sampleName).c_str());
266 else
268 std::string fullName = NLMISC::CPath::lookup(*iter, false);
269 if (!fullName.empty())
271 NLMISC::CIFile ifile(fullName);
272 uint size = ifile.getFileSize();
273 uint8 *buffer = new uint8[ifile.getFileSize()];
274 ifile.serialBuffer(buffer, size);
276 _SoundDriver->readWavBuffer(ibuffer, fullName, buffer, size);
277 _ByteSize += ibuffer->getSize();
279 delete [] buffer;
282 _Samples[sampleName] = ibuffer ;
284 // Warn the sound bank that the sample are available.
285 CSoundBank::instance()->bufferLoaded(sampleName, ibuffer);
287 catch (const ESoundDriver &e)
289 if (ibuffer != NULL) {
290 delete ibuffer;
291 ibuffer = NULL;
293 nlwarning("Problem with file '%s': %s", (*iter).c_str(), e.what());
297 _Loaded = true;
299 if (!async)
301 _LoadingDone = true;
302 // compute the sample bank size.
303 _LoadedSize += _ByteSize;
305 else
307 // fill the loading list.
308 TSampleTable::iterator first(_Samples.begin()), last(_Samples.end());
309 for (; first != last; ++first)
311 _LoadList.push_back(make_pair(first->second, first->first));
313 _SplitLoadDone = false;
314 // send the first files
315 for (uint i=0; i<ASYNC_LOADING_SPLIT && !_LoadList.empty(); ++i)
317 CAsyncFileManagerSound::getInstance().loadWavFile(_LoadList.front().first, CStringMapper::unmap(_LoadList.front().second)+".wav");
318 _LoadList.pop_front();
320 // add a end loading event...
321 CAsyncFileManagerSound::getInstance().signal(&_SplitLoadDone);
322 // and register for update on the mixer
323 CAudioMixerUser::instance()->registerUpdate(this);
328 void CSampleBank::onUpdate()
330 if (_SplitLoadDone)
332 nldebug("Some samples have been loaded");
333 if (_LoadList.empty())
335 // all the samples are loaded, we can compute the bank size.
336 TSampleTable::iterator first(_Samples.begin()), last(_Samples.end());
337 for (; first != last; ++first)
339 _ByteSize += first->second->getSize();
342 _SampleBankManager->m_LoadedSize += _ByteSize;
344 // stop the update.
345 _SampleBankManager->m_AudioMixer->unregisterUpdate(this);
346 _LoadingDone = true;
348 // Force an update in the background manager (can restar stopped sound).
349 _SampleBankManager->m_AudioMixer->getBackgroundSoundManager()->updateBackgroundStatus();
351 nlinfo("Sample bank %s loaded.", CStringMapper::unmap(_Name).c_str());
353 else
355 _SplitLoadDone = false;
356 for (uint i=0; i<ASYNC_LOADING_SPLIT && !_LoadList.empty(); ++i)
358 CAsyncFileManagerSound::getInstance().loadWavFile(_LoadList.front().first, CStringMapper::unmap(_LoadList.front().second)+".wav");
359 _LoadList.pop_front();
361 // add a end loading event...
362 CAsyncFileManagerSound::getInstance().signal(&_SplitLoadDone);
367 // ********************************************************
369 bool CSampleBank::unload()
371 vector<IBuffer*> vec;
372 TSampleTable::iterator it;
374 if (!_Loaded)
376 nlwarning("Trying to unload an already unloaded bank : %s", CStringMapper::unmap(_Name).c_str ());
377 return true;
380 // need to wait end of load ?
381 if (!_LoadingDone)
382 return false;
384 //nlinfo("Unloading sample bank %s", CStringMapper::unmap(_Name).c_str());
386 for (it = _Samples.begin(); it != _Samples.end(); ++it)
388 IBuffer *buffer = it->second;
389 if (buffer)
391 const NLMISC::TStringId & bufferName = buffer->getName();
393 CAudioMixerUser *audioMixer = _SampleBankManager->m_AudioMixer;
394 // Warn the mixer to stop any track playing this buffer.
395 audioMixer->bufferUnloaded(buffer);
397 // Warn the sound banks about this buffer.
398 audioMixer->getSoundBank()->bufferUnloaded(bufferName);
400 // delete
401 it->second = NULL;
402 delete buffer;
406 _Loaded = false;
408 _SampleBankManager->m_LoadedSize -= _ByteSize;
409 _ByteSize = 0;
411 return true;
414 // ********************************************************
416 bool CSampleBank::isLoaded()
418 return _Loaded;
421 // ********************************************************
423 IBuffer* CSampleBank::getSample(const NLMISC::TStringId &name)
426 /* // dump the sample list.
427 TSampleTable::iterator it (_Samples.begin()), last(_Samples.end());
428 std::string s;
430 // while (first != last)
431 for (it = _Samples.begin(); it != _Samples.end(); ++it)
433 s += std::string(" [")+it->first+"] ";
434 //first++;
437 nldebug("getSample(%s) : sample list = [%s]", name, s.c_str());
441 // Find sound
442 TSampleTable::iterator iter = _Samples.find(name);
443 if ( iter == _Samples.end() )
445 return 0;
447 else
449 return (*iter).second;
453 // ********************************************************
455 uint CSampleBank::countSamples()
457 return (uint)_Samples.size();
460 // ********************************************************
462 uint CSampleBank::getSize()
464 uint size = 0;
466 TSampleTable::const_iterator iter;
467 for (iter = _Samples.begin(); iter != _Samples.end(); iter++)
469 size += (*iter).second->getSize();
472 return size;
477 } // namespace NLSOUND