NLS Engine  v0.1
The Next Logical Step in game engine design.
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines
SoundModuleXACT.cpp
Go to the documentation of this file.
00001 
00011 #pragma once
00012 
00013 #include "SoundModule.h"
00014 
00015 // System Library Includes
00016 #include <set>
00017 #include <string>
00018 
00019 // Application Library Includes
00020 #include <d3d9.h>
00021 #include <boost/foreach.hpp>
00022 #include <EngineConfig.h>
00023 
00024 // Local Includes
00025 #include "../sharedbase/MessageRouter.h"
00026 #include "../sharedbase/PropertyMap.h"
00027 #include "../sharedbase/Envelope.h"
00028 
00029 #include "Messages.h"
00030 
00031 // Prototypes
00032 namespace Sound {
00033   void WINAPI XACTNotificationCallback(const XACT_NOTIFICATION*);
00034 }
00035 
00036 // Helpers
00037 namespace Sound {
00038   // Taken from a comment on: http://notfaq.wordpress.com/2006/08/30/c-convert-int-to-string/
00039   template <class T>
00040   inline std::string to_string (const T& t) {
00041     std::stringstream ss;
00042     ss << t;
00043     return ss.str();
00044   }
00045 
00046   template <class T>
00047   inline std::string to_hex (const T& t) {
00048     std::stringstream ss;
00049     ss << hex << t;
00050     return ss.str();
00051   }
00052 }
00053 
00054 // Class Methods
00055 namespace Sound {
00056   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00057   XACTINDEX SoundModule::GetCueIndex(const std::string& bank, const std::string& cueName) {
00058     XACTINDEX index = XACTINDEX_INVALID;
00059     
00060     if (bank.length() > 0 && cueName.length() > 0) {
00061       Threading::ReadLock r_lock(this->soundBanksMutex);
00062       soundbank_map::iterator sbIter = this->soundBanks.find(bank);
00063       if (sbIter != this->soundBanks.end()) {
00064         IXACT3SoundBank* soundBank = sbIter->second;
00065         
00066         Threading::ReadLock r_lock(this->engineMutex);
00067         index = soundBank->GetCueIndex(cueName.c_str());
00068       }
00069     }
00070     
00071     return index;
00072   }
00073   
00074   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00075   void SoundModule::LoadXACTSettingsBuffer(std::string file) {
00076     HANDLE hFile;
00077     std::string errStr;
00078     
00079     file = NLS_ENGINE_DATA_PATH + file;
00080     
00081     LOG(LOG_PRIORITY::FLOW, "Loading setting file: " + file);
00082     
00083     unsigned int count;
00084     {
00085       Threading::ReadLock r_lock(this->waveBanksMutex);
00086       count = this->waveBanks.count(file);
00087     }
00088     
00089     if (count <= 0) {
00090       hFile = CreateFile( file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
00091       
00092       if( hFile != INVALID_HANDLE_VALUE ) {
00093         DWORD dwFileSize = GetFileSize( hFile, NULL );
00094         HANDLE hMapFile = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL );
00095         VOID* pSoundBankData = MapViewOfFile( hMapFile, FILE_MAP_READ, 0, 0, 0 );
00096         
00097         /*TODO
00098         EngineParameters.pGlobalSettingsBuffer = pvGlobalSettingsData;
00099         EngineParameters.globalSettingsBufferSize = dwGlobalSettingsFileSize;
00100         EngineParameters.globalSettingsFlags = XACT_FLAG_GLOBAL_SETTINGS_MANAGEDATA;
00101         */
00102         
00103       }
00104       else {
00105         errStr = "Couldn't find the settings file!!"; //*TODO: better error message
00106         LOG(LOG_PRIORITY::MISSRESS, errStr);
00107       }
00108     }
00109     else {
00110       errStr = "Settings file already loaded!!"; //*TODO: better error message
00111       LOG(LOG_PRIORITY::ERR, errStr);
00112     }
00113     
00114     if (errStr.compare("") != 0) {
00115       // Notify failure to load
00116       EnvelopeSPTR confirmation(new Envelope);
00117       confirmation->msgid = ACK_LOAD_XACT_SETTINGS_FILE;
00118       confirmation->AddData(file);
00119       confirmation->AddData(false);
00120       this->msgrouter->SendSP(confirmation);
00121     }
00122   }
00123   
00124   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00125   void SoundModule::LoadInMemoryWavebank(std::string file) {
00126     //*TODO: Load an in-memory wavebank
00127     
00128     file = NLS_ENGINE_DATA_PATH + file;
00129     
00130     LOG(LOG_PRIORITY::WARN, "Loading in-memory wavebank: " + file + " --- UNIMPLEMENTED!!!");
00131   }
00132   
00133   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00134   void SoundModule::LoadStreamingWavebank(std::string file) {
00135     IXACT3WaveBank* pStreamingWaveBank = NULL;
00136     HANDLE hFile;
00137     HRESULT hr;
00138     std::string errStr ("");
00139     
00140     file = NLS_ENGINE_DATA_PATH + file;
00141     
00142     LOG(LOG_PRIORITY::FLOW, "Loading streaming wavebank: " + file);
00143     
00144     unsigned int count;
00145     {
00146       Threading::ReadLock r_lock(this->waveBanksMutex);
00147       count = this->waveBanks.count(file);
00148     }
00149     
00150     if (count <= 0) {
00151       hFile = CreateFile( file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL );
00152       
00153       if( hFile != INVALID_HANDLE_VALUE ) {
00154         XACT_WAVEBANK_STREAMING_PARAMETERS wsParams;
00155         ZeroMemory( &wsParams, sizeof(XACT_WAVEBANK_STREAMING_PARAMETERS) );
00156         wsParams.file = hFile;
00157         wsParams.offset = 0;
00158         wsParams.packetSize = 64;
00159         
00160         {
00161           Threading::ReadLock r_lock(this->engineMutex);
00162           hr = this->engine->CreateStreamingWaveBank( &wsParams, &pStreamingWaveBank );
00163         }
00164         
00165         if (SUCCEEDED(hr)) {
00166           // Put the wave bank into the map
00167           Threading::WriteLock w_lock(this->waveBanksMutex);
00168           this->waveBanks[file] = pStreamingWaveBank;
00169         }
00170         else {
00171           errStr.assign("Couldn't load the wavebank!!"); //*TODO: better error message
00172         }
00173       }
00174       else {
00175         errStr.assign("Couldn't find the wavebank!!"); //*TODO: better error message
00176       }
00177     }
00178     else {
00179       errStr.assign("Wavebank already loaded!!"); //*TODO: better error message
00180     }
00181     
00182     if (errStr.compare("") != 0) {
00183       LOG(LOG_PRIORITY::ERR, errStr);
00184       
00185       // Notify failure to load
00186       EnvelopeSPTR confirmation(new Envelope);
00187       confirmation->msgid = ACK_LOAD_STREAMING_WAVEBANK;
00188       confirmation->AddData(file);
00189       confirmation->AddData(false);
00190       msgrouter->SendSP(confirmation);
00191     }
00192   }
00193 
00194   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00195   void SoundModule::LoadSoundbank(std::string file) {
00196     IXACT3SoundBank* pSoundBank;
00197     HANDLE hFile;
00198     HRESULT hr;
00199     std::string errStr ("");
00200     
00201     file = NLS_ENGINE_DATA_PATH + file;
00202     
00203     LOG(LOG_PRIORITY::FLOW, "Loading soundbank: " + file);
00204     
00205     unsigned int count;
00206     
00207     {
00208       Threading::ReadLock r_lock(this->soundBanksMutex);
00209       count = this->soundBanks.count(file);
00210     }
00211     
00212     if (count <= 0) {
00213       hFile = CreateFile(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
00214       
00215       if( hFile != INVALID_HANDLE_VALUE ) {
00216         DWORD dwFileSize = GetFileSize( hFile, NULL );
00217         VOID* pSoundBankData = new BYTE[dwFileSize];
00218         DWORD bytesRead;
00219         
00220         if (ReadFile(hFile, pSoundBankData, dwFileSize, &bytesRead, NULL)) {
00221           {
00222             Threading::ReadLock r_lock(this->engineMutex);
00223             hr = this->engine->CreateSoundBank(pSoundBankData, dwFileSize, 0, 0, &pSoundBank);
00224           }
00225           
00226           if (SUCCEEDED(hr)) {
00227             // Put the wave bank into the map
00228             Threading::WriteLock w_lock(this->soundBanksMutex);
00229             this->soundBanks[file] = pSoundBank;
00230           }
00231           else {
00232             errStr.assign("Couldn't load the soundbank!!"); //*TODO: better error message
00233             LOG(LOG_PRIORITY::ERR, errStr);
00234           }
00235         }
00236       }
00237       else {
00238         errStr.assign("Couldn't find the soundbank!!"); //*TODO: better error message
00239         LOG(LOG_PRIORITY::MISSRESS, errStr);
00240       }
00241     }
00242     else {
00243       errStr.assign("Soundbank already loaded!!"); //*TODO: better error message
00244       LOG(LOG_PRIORITY::ERR, errStr);
00245     }
00246     
00247     if (errStr.compare("") != 0) {
00248       // Notify failure to load
00249       EnvelopeSPTR confirmation(new Envelope);
00250       confirmation->msgid = ACK_LOAD_STREAMING_WAVEBANK;
00251       confirmation->AddData(file);
00252       confirmation->AddData(false);
00253       msgrouter->SendSP(confirmation);
00254     }
00255   }
00256   
00257   
00258   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00259   void SoundModule::RegisterNotification(XACTNOTIFICATIONTYPE type, BYTE flags, PVOID context) {
00260     XACT_NOTIFICATION_DESCRIPTION desc = {0};
00261     desc.type       = type;
00262     desc.flags      = flags;
00263     desc.pSoundBank = NULL;              // SoundBank instance
00264     desc.pWaveBank  = NULL;              // WaveBank instance
00265     desc.pCue       = NULL;              // Cue instance
00266     desc.pWave      = NULL;              // Wave instance
00267     desc.cueIndex   = XACTINDEX_INVALID; // Cue index
00268     desc.waveIndex  = XACTINDEX_INVALID; // Wave index
00269     desc.pvContext  = context;           // User context (optional)
00270     
00271     Threading::WriteLock w_lock(this->engineMutex);
00272     this->engine->RegisterNotification(&desc);
00273   }
00274   
00275   
00281   template <typename A, typename B>
00282   const A FindKeyByValue(std::map<A, B> map, B value) {
00283     for (std::map<A, B>::const_iterator it = map.begin(); it != map.end(); ++it) {
00284       if (it->second == value ){
00285         return (*it).first;
00286       }
00287     }
00288     return NULL;
00289   }
00290   
00291   /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00292   void WINAPI XACTNotificationCallback(const XACT_NOTIFICATION* notification) {
00293     SoundModule* audio_module = SoundModule::GetModuleInstance();
00294     
00295     if (!audio_module->engineInitialized) {
00296       // *HACK: This is getting called during dtor if there's a sound playing.  This prevents this from being an issue as the below code is unsafe during dtor.
00297       return;
00298     }
00299     
00300     if (notification->type == XACTNOTIFICATIONTYPE_WAVEBANKPREPARED) {
00301       std::string file;
00302       
00303       {
00304         Threading::ReadLock r_lock(audio_module->waveBanksMutex);
00305         file = FindKeyByValue<std::string, IXACT3WaveBank*>(audio_module->waveBanks, notification->waveBank.pWaveBank);
00306       }
00307       
00308       if (file.length() > 0) {
00309         // Notify completion of wavebank loadup
00310         EnvelopeSPTR confirmation(new Envelope);
00311         confirmation->msgid = ACK_LOAD_STREAMING_WAVEBANK;
00312         confirmation->AddData(file);
00313         confirmation->AddData(true);
00314         audio_module->msgrouter->SendSP(confirmation);
00315         
00316         std::string logStr;
00317         logStr.assign("Wavebank Prepared: Loaded wavebank: ").append(file);
00318         LOG(LOG_PRIORITY::FLOW, logStr);
00319       }
00320       else {
00321         std::string logStr;
00322         logStr.assign("Wavebank Prepared: Wavebank reported as loaded, but was not found in memory!");
00323         LOG(LOG_PRIORITY::ERR, logStr);
00324       }
00325     }
00326     else if (notification->type == XACTNOTIFICATIONTYPE_CUEPREPARED) {
00327       // Handle the cue prepared notification
00328       std::string file;
00329       
00330       {
00331         Threading::ReadLock r_lock(audio_module->soundBanksMutex);
00332         file = FindKeyByValue<std::string, IXACT3SoundBank*>(audio_module->soundBanks, notification->cue.pSoundBank);
00333       }
00334       
00335       if (file.length() > 0) {
00336         EnvelopeSPTR confirmation(new Envelope);
00337         confirmation->msgid = ACK_PREPARE_CUE;
00338         confirmation->AddData(file);
00339         confirmation->AddData(notification->cue.cueIndex);
00340         audio_module->msgrouter->SendSP(confirmation);
00341         
00342         std::string logStr;
00343         logStr.assign("Cue Prepared: Soundbank ").append(file).append(" with cue index ").append(to_string<int>(notification->cue.cueIndex));
00344         LOG(LOG_PRIORITY::FLOW, logStr);
00345       }
00346       else {
00347         std::string logStr;
00348         logStr.assign("Cue Prepared: Soundbank reported as loaded, but was not found in memory!");
00349         LOG(LOG_PRIORITY::ERR, logStr);
00350       }
00351     }
00352     else if (notification->type == XACTNOTIFICATIONTYPE_CUESTOP) {
00353       // Handle the cue stop notification
00354       std::string file;
00355       
00356       {
00357         Threading::ReadLock r_lock(audio_module->soundBanksMutex);
00358         file = FindKeyByValue<std::string, IXACT3SoundBank*>(audio_module->soundBanks, notification->cue.pSoundBank);
00359       }
00360       
00361       if (file.length() > 0) {
00362         EnvelopeSPTR confirmation(new Envelope);
00363         confirmation->msgid = ACK_STOP_CUE;
00364         confirmation->AddData(file);
00365         confirmation->AddData(notification->cue.cueIndex);
00366         audio_module->msgrouter->SendSP(confirmation);
00367         
00368         std::string logStr;
00369         logStr.assign("Cue Stopped: Soundbank ").append(file).append(" with cue index ").append(to_string<int>(notification->cue.cueIndex));
00370         LOG(LOG_PRIORITY::FLOW, logStr);
00371         
00372         // If the cue was previously prepared, then remove it from the list as it is no longer useable.
00373         std::pair<std::string, int> key (file, notification->cue.cueIndex);
00374         if (audio_module->preparedCues.count(key)) {
00375           audio_module->preparedCues.erase(key);
00376         }
00377       }
00378       else {
00379         std::string logStr;
00380         logStr.assign("Cue Stopped: Soundbank reported as loaded, but was not found in memory!");
00381         LOG(LOG_PRIORITY::FLOW, logStr);
00382       }
00383     }
00384   }
00385 }