2010-05-06 00:08:41 +00:00
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
# include "stdsound.h"
2010-05-10 20:28:57 +00:00
# include "nel/sound/sample_bank.h"
# include "nel/sound/sample_bank_manager.h"
# include "nel/sound/driver/sound_driver.h"
# include "nel/sound/driver/buffer.h"
2010-05-06 00:08:41 +00:00
# include "nel/misc/path.h"
# include "nel/misc/file.h"
2010-05-10 20:28:57 +00:00
# include "nel/sound/async_file_manager_sound.h"
# include "nel/sound/background_sound_manager.h"
# include "nel/sound/sound_bank.h"
2010-05-06 00:08:41 +00:00
using namespace std ;
using namespace NLMISC ;
namespace NLSOUND {
/// Constante for the number of file to load asynchronously at a time.
uint32 ASYNC_LOADING_SPLIT = 10 ; // 10 file by 10 file
// ********************************************************
CSampleBank : : CSampleBank ( NLMISC : : TStringId name , CSampleBankManager * sampleBankManager )
: _SampleBankManager ( sampleBankManager ) , _Name ( name ) , _Loaded ( false ) , _LoadingDone ( true ) , _ByteSize ( 0 )
{
_SampleBankManager - > m_Banks . insert ( make_pair ( name , this ) ) ;
}
// ********************************************************
CSampleBank : : ~ CSampleBank ( )
{
_SampleBankManager - > m_AudioMixer - > unregisterUpdate ( this ) ;
while ( ! _LoadingDone )
{
// need to wait for loading end.
nlSleep ( 100 ) ;
}
if ( _Loaded )
unload ( ) ;
// remove the bank from the list of known banks
for ( CSampleBankManager : : TSampleBankContainer : : iterator
it ( _SampleBankManager - > m_Banks . begin ( ) ) ,
end ( _SampleBankManager - > m_Banks . end ( ) ) ; it ! = end ; + + it )
{
if ( it - > second = = this )
{
_SampleBankManager - > m_Banks . erase ( it ) ;
break ;
}
}
// delete all the samples.
while ( ! _Samples . empty ( ) )
{
delete _Samples . begin ( ) - > second ;
_Samples . erase ( _Samples . begin ( ) ) ;
}
_Samples . clear ( ) ;
}
// ********************************************************
void CSampleBank : : load ( bool async )
{
// TODO : add async loading support !
CSampleBankManager : : TVirtualBankCont : : iterator it ( _SampleBankManager - > m_VirtualBanks . find ( _Name ) ) ;
if ( it ! = _SampleBankManager - > m_VirtualBanks . end ( ) )
{
// this is a virtual sample bank !
nlinfo ( " Loading virtual sample bank %s " , CStringMapper : : unmap ( _Name ) . c_str ( ) ) ;
const CAudioMixerUser : : TBackgroundFlags & flags = _SampleBankManager - > m_AudioMixer - > getBackgroundFlags ( ) ;
for ( uint i = 0 ; i < it - > second . size ( ) ; + + i )
{
if ( flags . Flags [ it - > second [ i ] . Filter ] )
{
CSampleBank * bank = _SampleBankManager - > findSampleBank ( it - > second [ i ] . BankName ) ;
if ( bank )
bank - > load ( async ) ;
}
}
}
//nlinfo("Loading sample bank %s %", CStringMapper::unmap(_Name).c_str(), async?"":"Asynchronously");
vector < string > filenames ;
// vector<string>::iterator iter;
if ( _Loaded )
{
nlwarning ( " Trying to load an already loaded bank : %s " , CStringMapper : : unmap ( _Name ) . c_str ( ) ) ;
return ;
}
// Load the sample bank from the builded sample_bank file.
string bankName ( CStringMapper : : unmap ( _Name ) + " .sample_bank " ) ;
string filename = CPath : : lookup ( bankName , false ) ;
if ( filename . empty ( ) )
{
nlwarning ( " Could not find sample bank [%s] " , bankName . c_str ( ) ) ;
return ;
}
try
{
CIFile sampleBank ( filename ) ;
CAudioMixerUser : : TSampleBankHeader sbh ;
sampleBank . serial ( sbh ) ;
_LoadingDone = false ;
sint32 seekStart = sampleBank . getPos ( ) ;
uint8 * data = 0 ;
uint i ;
for ( i = 0 ; i < sbh . Name . size ( ) ; + + i )
{
IBuffer * ibuffer = _SampleBankManager - > m_AudioMixer - > getSoundDriver ( ) - > createBuffer ( ) ;
nlassert ( ibuffer ) ;
TStringId nameId = CStringMapper : : map ( CFile : : getFilenameWithoutExtension ( sbh . Name [ i ] ) ) ;
ibuffer - > setName ( nameId ) ;
/* {
sint16 * data16 = new sint16 [ sbh . NbSample [ i ] ] ;
IBuffer : : TADPCMState state ;
state . PreviousSample = 0 ;
state . StepIndex = 0 ;
uint count = 0 ;
for ( count = 0 ; count + 1024 < sbh . NbSample [ i ] ; count + = 1024 )
{
IBuffer : : decodeADPCM ( data + count / 2 , data16 + count , 1024 , state ) ;
}
IBuffer : : decodeADPCM ( data + count / 2 , data16 + count , sbh . NbSample [ i ] - count , state ) ;
state . PreviousSample = 0 ;
state . StepIndex = 0 ;
sint16 * data16_2 = new sint16 [ sbh . NbSample [ i ] ] ;
IBuffer : : decodeADPCM ( data , data16_2 , sbh . NbSample [ i ] , state ) ;
for ( uint j = 0 ; j < sbh . NbSample [ i ] ; + + j )
{
if ( data16 [ j ] ! = data16_2 [ j ] )
{
nlwarning ( " Sample differ at %u " , j ) ;
}
}
_SoundDriver - > readRawBuffer ( ibuffer , sbh . Name [ i ] , ( uint8 * ) data16 , sbh . NbSample [ i ] * 2 , Mono16 , sbh . Freq [ i ] ) ;
delete [ ] data16 ;
delete [ ] data16_2 ;
}
*/
if ( _SampleBankManager - > m_AudioMixer - > useAPDCM ( ) )
{
data = ( uint8 * ) realloc ( data , sbh . SizeAdpcm [ i ] ) ;
sampleBank . seek ( seekStart + sbh . OffsetAdpcm [ i ] , CIFile : : begin ) ;
sampleBank . serialBuffer ( data , sbh . SizeAdpcm [ i ] ) ;
ibuffer - > setFormat ( IBuffer : : FormatDviAdpcm , 1 , 16 , sbh . Freq [ i ] ) ;
if ( ! ibuffer - > fill ( data , sbh . SizeAdpcm [ i ] ) )
nlwarning ( " AM: ibuffer->fill returned false with FormatADPCM " ) ;
}
else
{
data = ( uint8 * ) realloc ( data , sbh . SizeMono16 [ i ] ) ;
sampleBank . seek ( seekStart + sbh . OffsetMono16 [ i ] , CIFile : : begin ) ;
sampleBank . serialBuffer ( data , sbh . SizeMono16 [ i ] ) ;
ibuffer - > setFormat ( IBuffer : : FormatPcm , 1 , 16 , sbh . Freq [ i ] ) ;
if ( ! ibuffer - > fill ( data , sbh . SizeMono16 [ i ] ) )
nlwarning ( " AM: ibuffer->fill returned false with FormatPCM " ) ;
}
_ByteSize + = ibuffer - > getSize ( ) ;
_Samples [ nameId ] = ibuffer ;
// Warn the sound bank that the sample are available.
CAudioMixerUser : : getInstance ( ) - > getSoundBank ( ) - > bufferLoaded ( nameId , ibuffer ) ;
}
free ( data ) ;
_SampleBankManager - > m_LoadedSize + = _ByteSize ;
}
catch ( Exception & e )
{
// loading failed !
nlwarning ( " Exception %s during loading of sample bank %s " , e . what ( ) , filename . c_str ( ) ) ;
if ( _SampleBankManager - > m_AudioMixer - > getPackedSheetUpdate ( ) )
{
nlinfo ( " Deleting offending sound bank, you need to restart to recreate it! " ) ;
CFile : : deleteFile ( filename ) ;
}
}
_Loaded = true ;
_LoadingDone = true ;
///////////////////////////////////////// OLD Version //////////////////////////////////////
/*
std : : string list = CPath : : lookup ( CStringMapper : : unmap ( _Name ) + CAudioMixerUser : : SampleBankListExt , false ) ;
if ( list . empty ( ) )
{
nlwarning ( " File %s not found to load sample bank %s " , ( CStringMapper : : unmap ( _Name ) + CAudioMixerUser : : SampleBankListExt ) . c_str ( ) , CStringMapper : : unmap ( _Name ) . c_str ( ) ) ;
return ;
}
NLMISC : : CIFile sampleBankList ( list ) ;
sampleBankList . serialCont ( filenames ) ;
for ( iter = filenames . begin ( ) ; iter ! = filenames . end ( ) ; iter + + )
{
IBuffer * ibuffer = NULL ;
try
{
ibuffer = _SoundDriver - > createBuffer ( ) ;
nlassert ( ibuffer ) ;
// std::string sampleName(CFile::getFilenameWithoutExtension(*iter));
NLMISC : : TStringId sampleName ( CStringMapper : : map ( CFile : : getFilenameWithoutExtension ( * iter ) ) ) ;
if ( async )
{
ibuffer - > presetName ( sampleName ) ;
nldebug ( " Preloading sample [%s] " , CStringMapper : : unmap ( sampleName ) . c_str ( ) ) ;
}
else
{
std : : string fullName = NLMISC : : CPath : : lookup ( * iter , false ) ;
if ( ! fullName . empty ( ) )
{
NLMISC : : CIFile ifile ( fullName ) ;
uint size = ifile . getFileSize ( ) ;
uint8 * buffer = new uint8 [ ifile . getFileSize ( ) ] ;
ifile . serialBuffer ( buffer , size ) ;
_SoundDriver - > readWavBuffer ( ibuffer , fullName , buffer , size ) ;
_ByteSize + = ibuffer - > getSize ( ) ;
delete [ ] buffer ;
}
}
_Samples [ sampleName ] = ibuffer ;
// Warn the sound bank that the sample are available.
CSoundBank : : instance ( ) - > bufferLoaded ( sampleName , ibuffer ) ;
}
catch ( ESoundDriver & e )
{
if ( ibuffer ! = NULL ) {
delete ibuffer ;
ibuffer = NULL ;
}
nlwarning ( " Problem with file '%s': %s " , ( * iter ) . c_str ( ) , e . what ( ) ) ;
}
}
_Loaded = true ;
if ( ! async )
{
_LoadingDone = true ;
// compute the sample bank size.
_LoadedSize + = _ByteSize ;
}
else
{
// fill the loading list.
TSampleTable : : iterator first ( _Samples . begin ( ) ) , last ( _Samples . end ( ) ) ;
for ( ; first ! = last ; + + first )
{
_LoadList . push_back ( make_pair ( first - > second , first - > first ) ) ;
}
_SplitLoadDone = false ;
// send the first files
for ( uint i = 0 ; i < ASYNC_LOADING_SPLIT & & ! _LoadList . empty ( ) ; + + i )
{
CAsyncFileManagerSound : : getInstance ( ) . loadWavFile ( _LoadList . front ( ) . first , CStringMapper : : unmap ( _LoadList . front ( ) . second ) + " .wav " ) ;
_LoadList . pop_front ( ) ;
}
// add a end loading event...
CAsyncFileManagerSound : : getInstance ( ) . signal ( & _SplitLoadDone ) ;
// and register for update on the mixer
CAudioMixerUser : : instance ( ) - > registerUpdate ( this ) ;
}
*/
}
void CSampleBank : : onUpdate ( )
{
if ( _SplitLoadDone )
{
nldebug ( " Some samples have been loaded " ) ;
if ( _LoadList . empty ( ) )
{
// all the samples are loaded, we can compute the bank size.
TSampleTable : : iterator first ( _Samples . begin ( ) ) , last ( _Samples . end ( ) ) ;
for ( ; first ! = last ; + + first )
{
_ByteSize + = first - > second - > getSize ( ) ;
}
_SampleBankManager - > m_LoadedSize + = _ByteSize ;
// stop the update.
_SampleBankManager - > m_AudioMixer - > unregisterUpdate ( this ) ;
_LoadingDone = true ;
// Force an update in the background manager (can restar stoped sound).
_SampleBankManager - > m_AudioMixer - > getBackgroundSoundManager ( ) - > updateBackgroundStatus ( ) ;
nlinfo ( " Sample bank %s loaded. " , CStringMapper : : unmap ( _Name ) . c_str ( ) ) ;
}
else
{
_SplitLoadDone = false ;
for ( uint i = 0 ; i < ASYNC_LOADING_SPLIT & & ! _LoadList . empty ( ) ; + + i )
{
CAsyncFileManagerSound : : getInstance ( ) . loadWavFile ( _LoadList . front ( ) . first , CStringMapper : : unmap ( _LoadList . front ( ) . second ) + " .wav " ) ;
_LoadList . pop_front ( ) ;
}
// add a end loading event...
CAsyncFileManagerSound : : getInstance ( ) . signal ( & _SplitLoadDone ) ;
}
}
}
// ********************************************************
bool CSampleBank : : unload ( )
{
vector < IBuffer * > vec ;
TSampleTable : : iterator it ;
if ( ! _Loaded )
{
nlwarning ( " Trying to unload an already unloaded bank : %s " , CStringMapper : : unmap ( _Name ) . c_str ( ) ) ;
return true ;
}
// need to wait end of load ?
if ( ! _LoadingDone )
return false ;
//nlinfo("Unloading sample bank %s", CStringMapper::unmap(_Name).c_str());
for ( it = _Samples . begin ( ) ; it ! = _Samples . end ( ) ; + + it )
{
IBuffer * buffer = it - > second ;
if ( buffer )
{
const NLMISC : : TStringId & bufferName = buffer - > getName ( ) ;
CAudioMixerUser * audioMixer = _SampleBankManager - > m_AudioMixer ;
// Warn the mixer to stop any track playing this buffer.
audioMixer - > bufferUnloaded ( buffer ) ;
// Warn the sound banks about this buffer.
audioMixer - > getSoundBank ( ) - > bufferUnloaded ( bufferName ) ;
// delete
it - > second = NULL ;
delete buffer ;
}
}
_Loaded = false ;
_SampleBankManager - > m_LoadedSize - = _ByteSize ;
_ByteSize = 0 ;
return true ;
}
// ********************************************************
bool CSampleBank : : isLoaded ( )
{
return _Loaded ;
}
// ********************************************************
IBuffer * CSampleBank : : getSample ( const NLMISC : : TStringId & name )
{
{
/* // dump the sample list.
TSampleTable : : iterator it ( _Samples . begin ( ) ) , last ( _Samples . end ( ) ) ;
std : : string s ;
// while (first != last)
for ( it = _Samples . begin ( ) ; it ! = _Samples . end ( ) ; + + it )
{
s + = std : : string ( " [ " ) + it - > first + " ] " ;
//first++;
}
nldebug ( " getSample(%s) : sample list = [%s] " , name , s . c_str ( ) ) ;
*/
}
// Find sound
TSampleTable : : iterator iter = _Samples . find ( name ) ;
if ( iter = = _Samples . end ( ) )
{
return 0 ;
}
else
{
return ( * iter ) . second ;
}
}
// ********************************************************
uint CSampleBank : : countSamples ( )
{
2010-05-13 20:23:34 +00:00
return ( uint ) _Samples . size ( ) ;
2010-05-06 00:08:41 +00:00
}
// ********************************************************
uint CSampleBank : : getSize ( )
{
uint size = 0 ;
TSampleTable : : const_iterator iter ;
for ( iter = _Samples . begin ( ) ; iter ! = _Samples . end ( ) ; iter + + )
{
size + = ( * iter ) . second - > getSize ( ) ;
}
return size ;
}
} // namespace NLSOUND