#include "StdAfx.h" #include "musicsystem.h" #include #include #include "PatternDecoder.h" #include "MusicLoadSink.h" /* * INFO: You should read the document CryMusicSystem.doc first to know about the terminology used. */ /* streaming-callback called by fmod. */ //signed char __cdecl _StreamingCallback(CS_STREAM *pStream, void *pBuffer, int nLength, INT_PTR nParam) #ifndef CS_VERSION_372 #ifdef CS_VERSION_361 signed char F_CALLBACKAPI _StreamingCallback(CS_STREAM *pStream, void *pBuffer, int nLength, int nParam) #else signed char F_CALLBACKAPI _StreamingCallback(CS_STREAM *pStream, void *pBuffer, int nLength, INT_PTR nParam) #endif #else signed char F_CALLBACKAPI _StreamingCallback(CS_STREAM *pStream, void *pBuffer, int nLength, void * nParam) #endif { if (!nParam) return FALSE; CMusicSystem *pMusicSystem=(CMusicSystem*)nParam; return pMusicSystem->StreamingCallback(pStream, pBuffer, nLength); } /* constructor */ CMusicSystem::CMusicSystem(ISystem *pSystem) { InitializeCriticalSection(&m_CS); m_pSystem=pSystem; m_pTimer=m_pSystem->GetITimer(); m_pLog=m_pSystem->GetILog(); m_pSoundSystem=m_pSystem->GetISoundSystem(); if (m_pSoundSystem) m_fMasterVolume=m_pSoundSystem->GetMusicVolume(); else m_fMasterVolume=0.0f; m_nSampleRate=44100; // m_fLatency=0.25f; m_fLatency=0.1f;//to let it peak less m_pStream=NULL; m_nChannel=-1; m_bPlaying = false; m_mapPatterns.clear(); m_mapThemes.clear(); m_pCurrPattern=NULL; m_pNextPattern=NULL; m_nLayeredRhythmicPatterns=0; m_nLayeredIncidentalPatterns=0; m_pMixBuffer=NULL; m_pThemeOverride=NULL; m_pCurrTheme=NULL; m_pNextTheme=NULL; m_pCurrMood=NULL; m_pNextMood=NULL; m_bForcePatternChange=false; m_fDefaultMoodTime=0.0f; m_sDefaultMood=""; m_pSink=NULL; m_pDataPtr=NULL; m_bDataLoaded=false; m_bPause=false; m_bBridging=false; m_bCurrPatternIsBridge=false; m_fCurrCrossfadeTime=DEFAULT_CROSSFADE_TIME; m_bOwnMusicData = false; m_pCVarDebugMusic=pSystem->GetIConsole()->CreateVariable("s_DebugMusic","0",0, "Toggles music-debugging on and off.\n" "Usage: s_DebugMusic [0/1]\n" "Default is 0 (off). Set to 1 to debug music."); m_pCVarMusicEnable=pSystem->GetIConsole()->CreateVariable("s_MusicEnable","1",VF_DUMPTODISK,"enable/disable music"); m_pCVarMusicMaxSimultaniousPatterns = pSystem->GetIConsole()->CreateVariable("s_MusicMaxPatterns","12",VF_DUMPTODISK, "Max simultaniously playing music patterns.\n" ); m_pCVarMusicStreamedData = pSystem->GetIConsole()->CreateVariable( "s_MusicStreamedData", "0", VF_DUMPTODISK, "Data used for streaming music data.\n0 - (AD)PCM\n1 - OGG" ); m_musicEnable = 1; Init(); } /* destructor; make sure everything gets unloaded */ CMusicSystem::~CMusicSystem() { Unload(); DeleteCriticalSection(&m_CS); } /* destroy system */ void CMusicSystem::Release() { Shutdown(); delete this; } /* set up notification-sink */ IMusicSystemSink* CMusicSystem::SetSink(IMusicSystemSink *pSink) { IMusicSystemSink *pOldSink=m_pSink; m_pSink=pSink; return pOldSink; } /* initialize system and create fmod-streaming callback */ bool CMusicSystem::Init() { GUARD_HEAP; CSmartCriticalSection SmartCriticalSection(m_CS); EnableEventProcessing(true); m_RandGen.Seed(GetTickCount() & 0xfffffffe); m_nBytesPerSample=4; // always use STEREO 16 BIT m_pMixBuffer=new char[(int)((float)m_nSampleRate*m_fLatency)*m_nBytesPerSample]; #if (defined CS_VERSION_372) m_pStream=CS_Stream_Create(_StreamingCallback, (int)((float)m_nSampleRate*m_fLatency)*m_nBytesPerSample, CS_STEREO | CS_16BITS | CS_SIGNED | CS_2D, 44100, (void *)this); #elif defined CS_VERSION_361 m_pStream=CS_Stream_Create(_StreamingCallback, (int)((float)m_nSampleRate*m_fLatency)*m_nBytesPerSample, CS_STEREO | CS_16BITS | CS_SIGNED | CS_2D, 44100, (int)this); #else m_pStream=CS_Stream_Create(_StreamingCallback, (int)((float)m_nSampleRate*m_fLatency)*m_nBytesPerSample, CS_STEREO | CS_16BITS | CS_SIGNED | CS_2D, 44100, (INT_PTR)this); #endif if (!m_pStream) return false;; return true; } /* counterpart of init(); destroy stream and cleanup allocated mem. */ void CMusicSystem::Shutdown() { CSmartCriticalSection SmartCriticalSection(m_CS); Silence(); StopPlaying(); if (m_pStream) { GUARD_HEAP; CS_Stream_Close(m_pStream); } m_pStream=NULL; m_nChannel=-1; m_bPlaying = false; if (m_pMixBuffer) delete[] m_pMixBuffer; m_pMixBuffer=NULL; FlushPatterns(); } /* push log-message in internal list, so it can be outputted on next update-cycle; this has been done due to multi-threading issues with the logging functions. */ void CMusicSystem::LogMsg(const char *pszFormat, ...) { if (m_pLog && m_pCVarDebugMusic->GetIVal()) { va_list ArgList; char szBuffer[2048]; va_start(ArgList, pszFormat); vsprintf(szBuffer, pszFormat, ArgList); va_end(ArgList); m_vecLog.push_back(szBuffer); } } /* flush all log-messages and delete internal list */ void CMusicSystem::FlushLog() { CSmartCriticalSection SmartCriticalSection(m_CS); if (m_pLog && m_pCVarDebugMusic->GetIVal()) { for (TStringVecIt It=m_vecLog.begin();It!=m_vecLog.end();++It) { m_pLog->Log((*It).c_str()); } } m_vecLog.clear(); } /* add (create) pattern to the music-system and create/open its decoder instance */ CMusicPattern* CMusicSystem::AddPattern(const char *pszName, const char *pszFilename) { CSmartCriticalSection SmartCriticalSection(m_CS); CMusicPattern *pPattern=new CMusicPattern(this, pszName,pszFilename); // convert name to lowercase //string sName=pszName; //strlwr((char*)sName.c_str()); // Check if file exist. /* FILE *pFile = m_pSystem->GetIPak()->FOpen(pszFilename, "rb"); if (!m_FileAccess.pFile) return false; */ /* if (!pPattern->Open(pszFilename)) { delete pPattern; LogMsg("[MusicSystem] WARNING: Cannot load music-pattern %s (%s) !", pszName, pszFilename); return NULL; } */ m_mapPatterns.insert(TPatternMapIt::value_type(pszName, pPattern)); return pPattern; } /* release all patterns */ bool CMusicSystem::FlushPatterns() { CSmartCriticalSection SmartCriticalSection(m_CS); if (m_bOwnMusicData) ReleaseData(); m_pDataPtr=NULL; m_mapThemes.clear(); m_pThemeOverride=NULL; m_pCurrTheme=NULL; m_pNextTheme=NULL; m_pCurrMood=NULL; m_pNextMood=NULL; m_vecPlayingPatterns.clear(); m_mapPatterns.clear(); m_pCurrPattern=NULL; m_pNextPattern=NULL; m_nLayeredRhythmicPatterns=0; m_nLayeredIncidentalPatterns=0; m_bForcePatternChange=false; m_fDefaultMoodTime=0.0f; m_sDefaultMood=""; m_bDataLoaded=false; m_bPause=false; m_bBridging=false; m_bCurrPatternIsBridge=false; for (TPatternMapIt It=m_mapPatterns.begin();It!=m_mapPatterns.end();++It) { CMusicPattern *pPattern=It->second; delete pPattern; } return true; } /* start streaming (so callback will be called) */ bool CMusicSystem::StartPlaying() { CSmartCriticalSection SmartCriticalSection(m_CS); if (!m_bDataLoaded) return false; if (m_bPause) return true; if (m_nChannel>=0) return false; if (m_pSoundSystem) m_fMasterVolume=m_pSoundSystem->GetMusicVolume(); if (m_fMasterVolume<=0) return true; GUARD_HEAP; m_bPlaying = true; m_nChannel=CS_Stream_Play(CS_FREE, m_pStream); if (m_nChannel<0) { FlushPatterns(); return false; } CS_SetVolume(m_nChannel, (int)(m_fMasterVolume*255.0f)); return true; } /* stop streaming; streaming-callback will not be called anymore */ bool CMusicSystem::StopPlaying() { if (m_nChannel<0) return false; CSmartCriticalSection SmartCriticalSection(m_CS); m_bPlaying = false; GUARD_HEAP; CS_Stream_Stop(m_pStream); m_nChannel=-1; return true; } /* set music-system data (eg. read from lua-file by gamecode) we convert all names to lowercase to make them not case-sensitive (faster lookup) we start streaming */ bool CMusicSystem::SetData(SMusicData *pMusicData,bool bNoRelease) { CSmartCriticalSection SmartCriticalSection(m_CS); m_bOwnMusicData = !bNoRelease; StopPlaying(); FlushPatterns(); m_pDataPtr=pMusicData; if (!m_pDataPtr) return true; // create patterns for (TPatternDefVecIt It=m_pDataPtr->vecPatternDef.begin();It!=m_pDataPtr->vecPatternDef.end();++It) { SPatternDef *pPatternDef=(*It); CMusicPattern *pPattern=AddPattern(pPatternDef->sName.c_str(), pPatternDef->sFilename.c_str()); if (!pPattern) { m_pSystem->Warning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,VALIDATOR_FLAG_SOUND,pPatternDef->sFilename.c_str(), "Music file %s failed to load",pPatternDef->sFilename.c_str() ); continue; //FlushPatterns(bNoRelease); //return false; } for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt) { pPattern->AddFadePoint(*IntIt); } pPattern->SetLayeringVolume(pPatternDef->nLayeringVolume); } // copy themes { m_mapThemes.clear(); for (TThemeMap::iterator it = m_pDataPtr->mapThemes.begin(); it != m_pDataPtr->mapThemes.end(); ++it) { m_mapThemes.insert( TThemeMap::value_type(it->first,it->second) ); } } /* // make all theme/mood/pattern names lowercase for (TThemeMapIt ThemeIt=m_mapThemes.begin();ThemeIt!=m_mapThemes.end();++ThemeIt) { SMusicTheme *pTheme=ThemeIt->second; strlwr((char*)ThemeIt->first.c_str()); // convert theme key strlwr((char*)pTheme->sName.c_str()); // convert theme name strlwr((char*)pTheme->sDefaultMood.c_str()); // convert default mood for (TMoodMapIt MoodIt=pTheme->mapMoods.begin();MoodIt!=pTheme->mapMoods.end();++MoodIt) { SMusicMood *pMood=MoodIt->second; pMood->pCurrPatternSet=NULL; strlwr((char*)MoodIt->first.c_str()); // convert mood key strlwr((char*)pMood->sName.c_str()); // convert mood name for (TPatternSetVecIt PatternSetIt=pMood->vecPatternSets.begin();PatternSetIt!=pMood->vecPatternSets.end();++PatternSetIt) { SMusicPatternSet *pPatterSet=(*PatternSetIt); for (TPatternDefVecIt PatternIt=pPatterSet->vecMainPatterns.begin();PatternIt!=pPatterSet->vecMainPatterns.end();++PatternIt) { SMusicPatternInfo &PatternInfo=(*PatternIt); strlwr((char*)PatternInfo.sName.c_str()); // convert pattern name } for (TPatternDefVecIt PatternIt=pPatterSet->vecRhythmicPatterns.begin();PatternIt!=pPatterSet->vecRhythmicPatterns.end();++PatternIt) { SMusicPatternInfo &PatternInfo=(*PatternIt); strlwr((char*)PatternInfo.sName.c_str()); // convert pattern name } for (TPatternDefVecIt PatternIt=pPatterSet->vecIncidentalPatterns.begin();PatternIt!=pPatterSet->vecIncidentalPatterns.end();++PatternIt) { SMusicPatternInfo &PatternInfo=(*PatternIt); strlwr((char*)PatternInfo.sName.c_str()); // convert pattern name } } } for (TThemeBridgeMapIt BridgeIt=pTheme->mapBridges.begin();BridgeIt!=pTheme->mapBridges.end();++BridgeIt) { strlwr((char*)BridgeIt->first.c_str()); // convert mood key strlwr((char*)BridgeIt->second.c_str()); // convert mood value } } */ m_bDataLoaded=true; // start playing the stream... if (m_nChannel<0) StartPlaying(); return true; } bool CMusicSystem::LoadMusicDataFromLUA(IScriptSystem* pScriptSystem, const char *pszFilename) { SMusicData *pMusicData=new SMusicData(); pScriptSystem->SetGlobalToNull("DynamicMusic"); if (!pScriptSystem->ExecuteFile(pszFilename, true, true)) return false; _SmartScriptObject pObj(pScriptSystem, true); if (!pScriptSystem->GetGlobalValue("DynamicMusic", pObj)) return false; CMusicLoadSink LoadSink(pMusicData, pScriptSystem, &pObj); pObj->Dump(&LoadSink); pScriptSystem->SetGlobalToNull("DynamicMusic"); if (!SetData(pMusicData)) return false; return true; } void CMusicSystem::ReleaseData() { if (!m_bOwnMusicData) return; SMusicData *pData = m_pDataPtr; if (!pData) return; // release pattern-defs for (TPatternDefVecIt PatternIt=pData->vecPatternDef.begin();PatternIt!=pData->vecPatternDef.end();++PatternIt) { SPatternDef *pPattern=(*PatternIt); delete pPattern; } // release themes/moods for (TThemeMapIt ThemeIt=pData->mapThemes.begin();ThemeIt!=pData->mapThemes.end();++ThemeIt) { SMusicTheme *pTheme=ThemeIt->second; for (TMoodMapIt MoodIt=pTheme->mapMoods.begin();MoodIt!=pTheme->mapMoods.end();++MoodIt) { SMusicMood *pMood=MoodIt->second; for (TPatternSetVecIt PatternSetIt=pMood->vecPatternSets.begin();PatternSetIt!=pMood->vecPatternSets.end();++PatternSetIt) { SMusicPatternSet *pPatternSet=(*PatternSetIt); delete pPatternSet; } delete pMood; } delete pTheme; } delete pData; } /* stop streaming and unload all data from the system */ void CMusicSystem::Unload() { // stop the stream... CSmartCriticalSection SmartCriticalSection(m_CS); Silence(); StopPlaying(); FlushPatterns(); } /* pause/resume streaming */ void CMusicSystem::Pause(bool bPause) { if (m_bPause!=bPause) { m_bPause=bPause; if (m_bPause) StopPlaying(); else StartPlaying(); } } /* get pointer to current mood descriptor */ SMusicMood* CMusicSystem::GetMood(SMusicTheme *pTheme, const char *pszMood) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pTheme) return NULL; TMoodMapIt It=pTheme->mapMoods.find(pszMood); if (It==pTheme->mapMoods.end()) return NULL; return It->second; } /* reset the theme override */ bool CMusicSystem::ResetThemeOverride() { m_pThemeOverride=NULL; return true; } /* set the current theme by pointer to descriptor */ bool CMusicSystem::SetTheme(SMusicTheme *pNewTheme, bool bOverride) { CSmartCriticalSection SmartCriticalSection(m_CS); if (pNewTheme==m_pCurrTheme) return true; // do nothing since we're already in the requested theme if (!((m_pCurrTheme==m_pNextTheme) || (pNewTheme))) // only switch to the new theme if there was no switch in this frame yet OR the new theme is NOT null return true; if (pNewTheme) LogMsg("[MusicSystem] Setting theme to %s", pNewTheme->sName.c_str()); else LogMsg("[MusicSystem] Setting theme to NULL"); if (bOverride) { m_pThemeOverride=pNewTheme; m_pNextTheme=m_pCurrTheme; }else { m_pNextTheme=pNewTheme; } return true; } /* set the current theme by name */ bool CMusicSystem::SetTheme(const char *pszTheme, bool bOverride) { CSmartCriticalSection SmartCriticalSection(m_CS); string sTheme=pszTheme; if (strlen(pszTheme)==0) // no theme return SetTheme((SMusicTheme*)NULL, false); TThemeMapIt It=m_mapThemes.find(sTheme); if (It==m_mapThemes.end()) return false; // theme not found SMusicTheme *pNewTheme=It->second; return SetTheme(pNewTheme, bOverride); } /* set the current mood by pointer to descriptor */ bool CMusicSystem::SetMood(SMusicMood *pNewMood) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pNewMood) { m_pCurrMood=pNewMood; m_pNextMood=NULL; m_pNextPattern=NULL; LogMsg("[MusicSystem] Setting mood to "); return true; } if (pNewMood==m_pCurrMood) return true; // do nothing since we're already in the requested mood LogMsg("[MusicSystem] Setting mood to %s", pNewMood->sName.c_str()); // adjust fading time accourding to new pattern m_fCurrCrossfadeTime=(double)pNewMood->fFadeOutTime; if (!UpdateCurrentPatternSet(pNewMood, 0, true)) { LogMsg("[MusicSystem] WARNING: Unable to find pattern-set in mood %s!", pNewMood->sName.c_str() ); return false; } m_pNextPattern=ChoosePattern(pNewMood); if (!m_pNextPattern) { LogMsg("[MusicSystem] WARNING: Unable to find pattern in mood %s ", pNewMood->sName.c_str() ); return false; } m_bForcePatternChange=(m_pCurrMood!=NULL) && (m_pCurrMood!=pNewMood); // try to get into the new mood asap if we're changing the mood m_pCurrMood = pNewMood; return true; } /* set the current mood by name */ bool CMusicSystem::SetMood(const char *pszMood) { CSmartCriticalSection SmartCriticalSection(m_CS); string sMood=pszMood; SMusicTheme *pTheme = m_pCurrTheme; if (m_pNextTheme) pTheme = m_pNextTheme; if (!pTheme) return false; // no theme set if (strlen(pszMood)==0) { SetMood((SMusicMood*)NULL); return true; } TMoodMapIt It = pTheme->mapMoods.find(sMood); if (It==pTheme->mapMoods.end()) return false; // mood not found SMusicMood *pNewMood=It->second; if (m_pNextTheme) m_pNextMood = pNewMood; return SetMood(pNewMood); } /* set the current default mood by name */ bool CMusicSystem::SetDefaultMood(const char *pszMood) { CSmartCriticalSection SmartCriticalSection(m_CS); m_sDefaultMood=pszMood; SMusicMood *pDefaultMood=GetDefaultMood(m_pCurrTheme); // force reselection of mood if we're currently playing the default mood if (m_fDefaultMoodTime<0.0f) { // we're currently playing the default mood so lets set the new default-mood m_fDefaultMoodTime=0.0f; }else { // we're currently playing some non-default mood, lets check if the new default mood-priority is higher than the currently playing one if (pDefaultMood && m_pCurrMood) { if (pDefaultMood->nPriority>m_pCurrMood->nPriority) m_fDefaultMoodTime=0.0f; // default-mood priority is higher so lets choose it } } if (m_sDefaultMood.empty()) LogMsg("[MusicSystem] Setting theme-specific default mood"); else LogMsg("[MusicSystem] Setting default mood to %s", pszMood); return true; } /* get a vector of strings containing all available themes */ IStringItVec* CMusicSystem::GetThemes() { CSmartCriticalSection SmartCriticalSection(m_CS); TStringVec Vec; for (TThemeMapIt It=m_mapThemes.begin();It!=m_mapThemes.end();++It) { Vec.push_back(It->first.c_str()); } return new CStringItVec(Vec); } /* get a vector of strings containing all available moods in the theme pszTheme */ IStringItVec* CMusicSystem::GetMoods(const char *pszTheme) { CSmartCriticalSection SmartCriticalSection(m_CS); TStringVec Vec; TThemeMapIt It=m_mapThemes.find(pszTheme); if (It==m_mapThemes.end()) return NULL; SMusicTheme *pTheme=It->second; for (TMoodMapIt It=pTheme->mapMoods.begin();It!=pTheme->mapMoods.end();++It) { Vec.push_back(It->first.c_str()); } return new CStringItVec(Vec); } /* add a mood-event */ bool CMusicSystem::AddMusicMoodEvent(const char *pszMood, float fTimeout) { CSmartCriticalSection SmartCriticalSection(m_CS); SMoodEventInfo EventInfo; EventInfo.sMood=pszMood; EventInfo.fTime=fTimeout; m_setFrameMoodEvents.insert(TMoodEventSetIt::value_type(EventInfo)); return true; } /* remove all patterns from the play-lists */ void CMusicSystem::Silence() { CSmartCriticalSection SmartCriticalSection(m_CS); m_pCurrPattern=NULL; m_pNextPattern=NULL; m_nLayeredRhythmicPatterns=0; m_nLayeredIncidentalPatterns=0; m_vecPlayingPatterns.clear(); } /* main update-loop; check comments in function for details */ void CMusicSystem::Update() { FlushLog(); if (!m_bDataLoaded) return; // no data loaded if (m_pCVarMusicEnable->GetIVal() != m_musicEnable) { m_musicEnable = m_pCVarMusicEnable->GetIVal(); if (!m_musicEnable) { Pause(true); Silence(); } else Pause(false); } if (!m_musicEnable) return; // get deltatime float fDeltaTime=m_pTimer->GetFrameTime(); // check if the volume has changed... if (m_pSoundSystem) { if (m_fMasterVolume!=m_pSoundSystem->GetMusicVolume()) { GUARD_HEAP; m_fMasterVolume=m_pSoundSystem->GetMusicVolume(); if (m_fMasterVolume>0.0f) { StartPlaying(); // start playing in case we're stopped (volume=0) CS_SetVolume(m_nChannel, (int)(m_fMasterVolume*255.0f)); }else { StopPlaying(); // volume=0, lets stop playing the stream to reduce overhead } } } CSmartCriticalSection SmartCriticalSection(m_CS); // check for theme change if ((m_pCurrTheme!=m_pNextTheme) || (m_pThemeOverride)) { SMusicTheme *pNext; if (m_pThemeOverride) pNext=m_pThemeOverride; else pNext=m_pNextTheme; m_bForcePatternChange=(m_pCurrTheme!=NULL) && (m_pCurrTheme!=pNext); // try to get into the new theme asap if we're changing the theme if (m_pCurrTheme && pNext && m_bForcePatternChange) // if we are already playing a theme we need to use a bridge { m_pNextPattern = ChooseBridge(m_pCurrTheme, pNext); if (!m_pNextPattern) { LogMsg("[MusicSystem] WARNING: Unable to find bridging pattern or no bridge defined from %s to %s !", m_pCurrTheme->sName.c_str(), m_pNextTheme->sName.c_str()); // No bridge pattern, } } m_pCurrTheme=pNext; if (m_pCurrTheme) { if (m_bForcePatternChange) { if (m_pNextMood) // reenter next mood to get it from the correct theme SetMood( m_pNextMood ); else m_pCurrMood=NULL; } }else { Silence(); } if (!m_pCurrMood) EnterDefaultMood(); // if we are in no mood right now, use default mood } m_pNextMood = NULL; // event-handling if (m_setFrameMoodEvents.empty()) { // no events occured this frame so lets delete the events-list if (!m_setMoodEvents.empty()) m_setMoodEvents.clear(); } // lets add all new events for (TMoodEventSetIt It=m_setFrameMoodEvents.begin();It!=m_setFrameMoodEvents.end();++It) { TMoodEventSetIt EventIt=m_setMoodEvents.find(TMoodEventSetIt::value_type(*It)); if (EventIt==m_setMoodEvents.end()) // event not in list yet m_setMoodEvents.insert(TMoodEventSetIt::value_type(*It)); } // lets not choose any mood which has a lower priority than the currently playing one... int nNewMoodPriority=-1; SMusicMood *pNewMood=NULL; for (TMoodEventSetIt It=m_setMoodEvents.begin();It!=m_setMoodEvents.end();) { // lets remove all events which didnt occur this frame... TMoodEventSetIt FrameIt=m_setFrameMoodEvents.find(TMoodEventSetIt::value_type(*It)); if (FrameIt==m_setFrameMoodEvents.end()) // event not in list of last frame { // the event didn't occur, so we remove it here TMoodEventSetIt itnext = It; itnext++; m_setMoodEvents.erase(It); It = itnext; }else { // the event occured, so lets check if we reached the timeout SMoodEventInfo &EventInfo= const_cast(*It); EventInfo.fTime-=fDeltaTime; if (EventInfo.fTime<=0.0f) { // time to switch to this mood, so we store its priority so we can choose the one with the highest priority later SMusicMood *pMood=GetMood(m_pCurrTheme, EventInfo.sMood.c_str()); if (pMood) { // does it have a higher priority than the one before ? if (pMood->nPriority>nNewMoodPriority) { // ...yes, store name and new priority nNewMoodPriority=pMood->nPriority; pNewMood=pMood; } }else { if (m_pCurrTheme) LogMsg("[MusicSystem] Invalid music mood '%s' (Theme: %s)", EventInfo.sMood.c_str(), m_pCurrTheme->sName.c_str()); else LogMsg("[MusicSystem] Invalid music mood '%s' (Theme: NULL)", EventInfo.sMood.c_str()); } } ++It; } } m_setFrameMoodEvents.clear(); // now we need to check if the current default mood has a higher priority than the current mood SMusicMood *pDefMood=GetDefaultMood(m_pCurrTheme); // select default mood, if... if (pDefMood // ...there is a default mood && (pDefMood->nPriority>nNewMoodPriority) // ...it has a higher priority than the new mood && (m_fDefaultMoodTime>=0.0f)) // ...we are not in the default mood already { // update default mood timing m_fDefaultMoodTime-=fDeltaTime; // is it time to enter the default mood ? if ((m_fDefaultMoodTime<0.0f) && (m_pCurrMood && (!m_pCurrMood->bPlaySingle)) && m_bEnableEventProcessing) EnterDefaultMood(); // ...yes }else { // one of the above criteria failed, so lets see if we can choose the new mood // do we have a new mood ? if (pNewMood) { // ...yes bool bEnterNewMood=true; // do we also have a default mood ? if (pDefMood && pNewMood) { // ...yes; does the default mood has a higher priority than the new one ? if (pDefMood->nPriority>pNewMood->nPriority) bEnterNewMood=false; // ...yes, so we dont enter the new mood } if (m_pCurrMood && pNewMood) { // If currently playing mood is Play Single and have higher priority, doesnt switch to new mood. if (m_pCurrMood->bPlaySingle && m_pCurrMood->nPriority > pNewMood->nPriority) bEnterNewMood = false; } // should we enter the new mood ? if (bEnterNewMood && m_bEnableEventProcessing) { // ...yes SetMood(pNewMood); if (m_pCurrTheme) m_fDefaultMoodTime=m_pCurrTheme->fDefaultMoodTimeout; } } } } /* get system-status of music-system; returning pointer only valid until next call to GetStatus() */ SMusicSystemStatus* CMusicSystem::GetStatus() { CSmartCriticalSection SmartCriticalSection(m_CS); m_Status.m_vecPlayingPatterns.clear(); m_Status.bPlaying=(m_nChannel!=-1); if (m_pCurrTheme) m_Status.sTheme=m_pCurrTheme->sName.c_str(); else m_Status.sTheme=""; if (m_pCurrMood) m_Status.sMood=m_pCurrMood->sName.c_str(); else m_Status.sMood=""; SPlayingPatternsStatus PatternStatus; // push current main-pattern if (m_pCurrPattern) { PatternStatus.nLayer=MUSICLAYER_MAIN; PatternStatus.sName=m_pCurrPattern->GetPattern()->GetName(); PatternStatus.nVolume=255; m_Status.m_vecPlayingPatterns.push_back(PatternStatus); } for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It) { SMusicPatternPlayInfo &PlayInfo=(*It); PatternStatus.nLayer=PlayInfo.nLayer; PatternStatus.sName=PlayInfo.pPatternInstance->GetPattern()->GetName(); PatternStatus.nVolume=PlayInfo.pPatternInstance->GetPattern()->GetLayeringVolume(); m_Status.m_vecPlayingPatterns.push_back(PatternStatus); } return &m_Status; } /* get pointer to mood-descriptor of current default mood (either theme specific or overriden default mood; whatever is currently set) */ SMusicMood* CMusicSystem::GetDefaultMood(SMusicTheme *pTheme) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pTheme) return NULL; if (m_sDefaultMood.empty()) return GetMood(pTheme, pTheme->sDefaultMood.c_str()); else { SMusicMood *pMood=GetMood(pTheme, m_sDefaultMood.c_str()); if (!pMood) return GetMood(pTheme, pTheme->sDefaultMood.c_str()); // return theme-specific mood if user defined default mood is not available else return pMood; } } /* enter the default mood (either theme specific or overriden default mood; whatever is currently set) */ void CMusicSystem::EnterDefaultMood() { CSmartCriticalSection SmartCriticalSection(m_CS); if (m_pCurrTheme) SetMood(GetDefaultMood(m_pCurrTheme)); else m_fDefaultMoodTime=0.0f; // no theme is set yet, so lets try it again in the next update... } /* add a pattern to the play-list */ void CMusicSystem::PushPatternToMixList(SMusicPatternPlayInfo &PlayInfo) { CSmartCriticalSection SmartCriticalSection(m_CS); MTRACE("Pushing pattern %s in mix-list.", PlayInfo.pPatternInstance->GetPattern()->GetName()); m_vecPlayingPatterns.push_back(PlayInfo); } /* main streaming callback this is the main state-machine; check comments in function for details */ signed char CMusicSystem::StreamingCallback(CS_STREAM *pStream, void *pBuffer, int nLength) { CSmartCriticalSection SmartCriticalSection(m_CS); if (m_pStream!=pStream) { // wrong stream; this should never happen CryError( " (CMusicSystem::StreamingCallback) Wrong Stream" ); return FALSE; } // Ignore if not playing. if (!m_bPlaying) return TRUE; if (!m_musicEnable) return TRUE; signed short *pOutput=(signed short*)pBuffer; int nOfs=0; int nSamples=nLength/m_nBytesPerSample; // check if theme and mood are valid if ((!m_pCurrTheme) || (!m_pCurrMood) || (!UpdateCurrentPatternSet(m_pCurrMood, nSamples, false))) { // no theme or mood set if (m_pCurrPattern) { SMusicPatternPlayInfo PlayInfo; PlayInfo.nLayer=MUSICLAYER_MAIN; PlayInfo.pPatternInstance=m_pCurrPattern; PlayInfo.eBlendType=EBlend_FadeOut; PushPatternToMixList(PlayInfo); m_pCurrPattern=NULL; } memset(&(pOutput[nOfs]), 0, nSamples*m_nBytesPerSample); MixStreams(&(pOutput[nOfs]), nSamples); return TRUE; } // if no patterns are chosen, lets choose one if (!m_pCurrPattern) m_pCurrPattern=ChoosePattern(m_pCurrMood); if (!m_pNextPattern) m_pNextPattern=ChoosePattern(m_pCurrMood); for (;;) { if (!m_pCurrPattern) { // there is no pattern available... weired memset(&(pOutput[nOfs]), 0, nSamples*m_nBytesPerSample); break; } // retrieve samples to next fade-point so we can decide if we wanna play additional layers int nSamplesToNextFade=m_pCurrPattern->GetSamplesToNextFadePoint(); // fast // lets add some other layers if ((!m_bBridging) && (nSamplesToNextFade<=nSamples)) // are we not bridging and is the fade point in this block ? { // adjust streams ref-count, so it will free up patterns which will finish this frame AdjustMixStreamsRefCount(nSamplesToNextFade); // ...yes, lets eventually choose some layered patterns // rhythmic layer if ((m_nLayeredRhythmicPatterns < m_pCurrMood->pCurrPatternSet->nMaxSimultaneousRhythmicPatterns) && (m_nLayeredRhythmicPatterns+m_nLayeredIncidentalPatterns) < m_pCVarMusicMaxSimultaniousPatterns->GetIVal()) { // should we layer now ? bool bIsTimeForLayer=m_RandGen.Rand(0.0f, 100.0f)pCurrPatternSet->fRhythmicLayerProbability; if (bIsTimeForLayer) { // ...yes CMusicPatternInstance *pLayeredPattern=ChoosePattern(m_pCurrMood, MUSICLAYER_RHYTHMIC); if (pLayeredPattern) { m_nLayeredRhythmicPatterns++; pLayeredPattern->Seek0(nSamplesToNextFade); // lets seek negative so it starts playing at the right time... // lets queue the new pattern so it gets automatically played till the end SMusicPatternPlayInfo PlayInfo; PlayInfo.nLayer=MUSICLAYER_RHYTHMIC; PlayInfo.pPatternInstance=pLayeredPattern; PlayInfo.eBlendType=EBlend_ToEnd; PlayInfo.pRefCount=&m_nLayeredRhythmicPatterns; PushPatternToMixList(PlayInfo); #ifdef _DEBUG SMusicPatternFileInfo FileInfo; pLayeredPattern->GetPattern()->GetFileInfo(FileInfo); MTRACE("Layering rhythmic-layer %s", FileInfo.sFilename.c_str()); #endif } } } // incidental layer if ((m_nLayeredIncidentalPatterns < m_pCurrMood->pCurrPatternSet->nMaxSimultaneousIncidentalPatterns) && (m_nLayeredRhythmicPatterns+m_nLayeredIncidentalPatterns) < m_pCVarMusicMaxSimultaniousPatterns->GetIVal()) { // should we layer now ? bool bIsTimeForLayer=m_RandGen.Rand(0.0f, 100.0f)pCurrPatternSet->fIncidentalLayerProbability; if (bIsTimeForLayer) { // ...yes CMusicPatternInstance *pLayeredPattern=ChoosePattern(m_pCurrMood, MUSICLAYER_INCIDENTAL); if (pLayeredPattern) { m_nLayeredIncidentalPatterns++; pLayeredPattern->Seek0(nSamplesToNextFade); // lets seek negative so it starts playing at the right time... // lets queue the new pattern so it gets automatically played till the end SMusicPatternPlayInfo PlayInfo; PlayInfo.nLayer=MUSICLAYER_INCIDENTAL; PlayInfo.pPatternInstance=pLayeredPattern; PlayInfo.eBlendType=EBlend_ToEnd; PlayInfo.pRefCount=&m_nLayeredIncidentalPatterns; PushPatternToMixList(PlayInfo); #ifdef _DEBUG SMusicPatternFileInfo FileInfo; pLayeredPattern->GetPattern()->GetFileInfo(FileInfo); MTRACE("Layering incidental-layer %s", FileInfo.sFilename.c_str()); #endif } } } } int nSamplesToFade; // samples to main-pattern change // slow or fast fade ? if (m_bForcePatternChange) nSamplesToFade=nSamplesToNextFade; // fast else nSamplesToFade=m_pCurrPattern->GetSamplesToEnd(); //GetSamplesToLastFadePoint(); // slow if (nSamplesToFade<=nSamples) // is the fade-point in this block ? { // ...yes m_pCurrPattern->GetPCMData((signed long*)&(pOutput[nOfs]), nSamplesToFade); // get pcm data from the current pattern, till we reach the fadepoint MixStreams(&(pOutput[nOfs]), nSamplesToFade); // also mix all queued patterns to the fadepoint // if the old stream is not at the end yet we push it in the PlayingStreams-list so it gets played and faded... // if (m_pCurrPattern->GetSamplesToEnd()>0) if (m_bForcePatternChange && (!m_bCurrPatternIsBridge)) { SMusicPatternPlayInfo PlayInfo; PlayInfo.nLayer=MUSICLAYER_MAIN; PlayInfo.pPatternInstance=m_pCurrPattern; PlayInfo.eBlendType=EBlend_FadeOut; PushPatternToMixList(PlayInfo); }else { // if this is a single-play mood we enter the default mood now, since we're done with the pattern if (m_pCurrMood->bPlaySingle) EnterDefaultMood(); } if (m_bBridging) { m_bCurrPatternIsBridge=true; MTRACE("Clearing bridging-flag."); }else { m_bCurrPatternIsBridge=false; } m_bBridging=false; UpdateCurrentPatternSet(m_pCurrMood, 0, true); // current<=next m_pCurrPattern=m_pNextPattern; // lets choose a new "next" pattern m_pNextPattern=ChoosePattern(m_pCurrMood); // we should always have a new current pattern at this point, but lets check in case there were non available if (m_pCurrPattern) { m_pCurrPattern->Seek0(); // seek to 0 #ifdef _DEBUG SMusicPatternFileInfo FileInfo; m_pCurrPattern->GetPattern()->GetFileInfo(FileInfo); MTRACE("Changing Music-Pattern to %s", FileInfo.sFilename.c_str()); #endif } // enhance positions nOfs+=nSamplesToFade<<1; nSamples-=nSamplesToFade; // update flags m_bForcePatternChange=false; }else { // no fadepoint in this block so we can use the current patterns without any hassle m_pCurrPattern->GetPCMData((signed long*)&(pOutput[nOfs]), nSamples); // get pcm data MixStreams(&(pOutput[nOfs]), nSamples); // mix queued patterns break; } } return TRUE; } /* lookahead for all patterns in play-list to see which ones exceed in the next nSamples */ void CMusicSystem::AdjustMixStreamsRefCount(int nSamples) { for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It) { SMusicPatternPlayInfo &PlayInfo=(*It); if (!PlayInfo.pRefCount) continue; int nSamplesToEnd=PlayInfo.pPatternInstance->GetSamplesToEnd(); if (nSamplesToEnd<=nSamples) { (*(PlayInfo.pRefCount))--; PlayInfo.bRefCountAdjusted=true; } } } /* mix all streams in the play-list into pBuffer; mixlength is nSamples the data is processed before mixing if needed (eg. ramping) */ void CMusicSystem::MixStreams(void *pBuffer, int nSamples) { for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();) { int nSamplesToRead=nSamples; SMusicPatternPlayInfo &PlayInfo=(*It); bool bStreamEnd=false; if (PlayInfo.eBlendType==EBlend_Stop) { bStreamEnd=true; }else { GUARD_HEAP; // AMD64 note: here was a Memory Corruption switch (PlayInfo.eBlendType) { case EBlend_ToEnd: { int nLength=PlayInfo.pPatternInstance->GetSamplesToEnd(); if (nSamplesToRead>=nLength) { nSamplesToRead=nLength; bStreamEnd=true; } break; } } PlayInfo.pPatternInstance->GetPCMData((signed long*)m_pMixBuffer, nSamplesToRead); switch (PlayInfo.eBlendType) { case EBlend_FadeOut: { if (FadeStream(PlayInfo, (signed short*)m_pMixBuffer, nSamplesToRead)) bStreamEnd=true; break; } } //const int nSize = 1024*1024*4; //static int arrBuffer[nSize]; //memset (arrBuffer, 0xCE, sizeof(arrBuffer)); //memcpy (pBuffer, m_pMixBuffer, nSamplesToRead*4); //memset(pBuffer, 0, nSamplesToRead*4); CS_DSP_MixBuffers(pBuffer, m_pMixBuffer, nSamplesToRead, m_nSampleRate, PlayInfo.pPatternInstance->GetPattern()->GetLayeringVolume(), 128, CS_16BITS | CS_STEREO); /* signed short *destptr = (signed short *)pBuffer; signed int *srcptr = (signed int *)pBuffer; for (int count = 0; count < nSamplesToRead; count++) { signed int lval,rval; lval = *srcptr++; rval = *srcptr++; *destptr++ = (signed short)( lval < -32768 ? -32768 : lval > 32767 ? 32767 : lval); *destptr++ = (signed short)( rval < -32768 ? -32768 : rval > 32767 ? 32767 : rval); } /**/ /*assert (arrBuffer[nSize-1] == 0xCECECECE); assert (IsHeapValid()); memcpy (pBuffer, arrBuffer, nSamplesToRead*4); assert (IsHeapValid());*/ } if (bStreamEnd) { if (PlayInfo.pRefCount && (!PlayInfo.bRefCountAdjusted)) (*(PlayInfo.pRefCount))--; It=m_vecPlayingPatterns.erase(It); }else ++It; } } /* volume ramping for a pattern */ bool CMusicSystem::FadeStream(SMusicPatternPlayInfo &PlayInfo, signed short *pBuffer, int nSamples) { double fFadeTime=PlayInfo.fFadeTime; if (!fFadeTime) fFadeTime=m_fCurrCrossfadeTime; double fPhaseAdder=(1.0/(double)m_nSampleRate)/fFadeTime; for (int i=0;isecond; return pPattern->CreateInstance(); } /* check if a new pattern-set needs to be chosen */ bool CMusicSystem::UpdateCurrentPatternSet(SMusicMood *pMood, int nSamples, bool bAllowChange) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pMood) return false; bool bChooseNewPatternSet=(pMood->pCurrPatternSet==NULL); pMood->fCurrPatternSetTime+=(float)nSamples/(float)m_nSampleRate; // update timing if (bAllowChange) { if (pMood->fCurrPatternSetTime>pMood->fCurrPatternSetTimeout) // did we reach the timeout ? bChooseNewPatternSet=true; // yes... lets choose a new pattern-set if (bChooseNewPatternSet) { ChoosePatternSet(pMood); return (pMood->pCurrPatternSet!=NULL); } } return true; } /* choose a new pattern-set and calc its timeout */ bool CMusicSystem::ChoosePatternSet(SMusicMood *pMood) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pMood) return false; SMusicPatternSet *pPrevPatternSet=pMood->pCurrPatternSet; int nPatternSets= (int)pMood->vecPatternSets.size(); SMusicPatternSet *pPatternSet=NULL; int nCount=0; switch (nPatternSets) { case 0: LogMsg("[MusicSystem] WARNING: Choosing NULL pattern-set."); return false; case 1: pPatternSet=*(pMood->vecPatternSets.begin()); break; default: int nPatternSet=(int)m_RandGen.Rand(0.0f, (float)nPatternSets) % nPatternSets; for (TPatternSetVecIt It=pMood->vecPatternSets.begin();It!=pMood->vecPatternSets.end();++It) { if (nCount==nPatternSet) { pPatternSet=(*It); while (pPatternSet==pPrevPatternSet) // make sure we select a different set { ++It; nCount++; if (It==pMood->vecPatternSets.end()) { It=pMood->vecPatternSets.begin(); nCount=0; } pPatternSet=(*It); } break; } nCount++; } //FadeOutAllSecondaryLayers(PATTERNSET_CHANGE_FADETIME); break; } pMood->fCurrPatternSetTime=0.0f; pMood->fCurrPatternSetTimeout=m_RandGen.Rand(pPatternSet->fMinTimeout, pPatternSet->fMaxTimeout); LogMsg("[MusicSystem] Choosing pattern-set %d with a timeout of %3.1f seconds.", nCount, pMood->fCurrPatternSetTimeout); pMood->pCurrPatternSet=pPatternSet; // we have to choose a new next pattern in order to reflect the new pattern set m_pNextPattern=ChoosePattern(pMood); return true; } /* return bridge from pCurrTheme to pNewTheme; NULL if no such bridge exist */ CMusicPatternInstance* CMusicSystem::ChooseBridge(SMusicTheme *pCurrTheme, SMusicTheme *pNewTheme) { CSmartCriticalSection SmartCriticalSection(m_CS); if ((!pCurrTheme) || (!pNewTheme)) return NULL; TThemeBridgeMapIt BridgeIt=pCurrTheme->mapBridges.find(pNewTheme->sName); if (BridgeIt==pCurrTheme->mapBridges.end()) return NULL; // no bridge defined CMusicPatternInstance *pBridgePattern=GetPatternInstance(BridgeIt->second.c_str()); if (pBridgePattern) m_bBridging=true; // set bridging flag if (m_bBridging) MTRACE("Setting bridging-flag."); return pBridgePattern; } /* choose a pattern and return its instance for pMood in nLayer */ CMusicPatternInstance* CMusicSystem::ChoosePattern(SMusicMood *pMood, int nLayer) { CSmartCriticalSection SmartCriticalSection(m_CS); if (!pMood) return NULL; if (m_mapPatterns.empty()) return NULL; TPatternDefVec *pPatternInfo=NULL; float fTotalProbability; switch (nLayer) { default: case MUSICLAYER_MAIN: if (m_bBridging) { MTRACE("Returning bridging pattern blindly..."); return m_pNextPattern; // if we're bridging we return the "NextPattern" since we want to continue the bridging } pPatternInfo=&(pMood->pCurrPatternSet->vecMainPatterns); fTotalProbability=pMood->pCurrPatternSet->fTotalMainPatternProbability; break; case MUSICLAYER_RHYTHMIC: pPatternInfo=&(pMood->pCurrPatternSet->vecRhythmicPatterns); fTotalProbability=pMood->pCurrPatternSet->fTotalRhythmicPatternProbability; break; case MUSICLAYER_INCIDENTAL: pPatternInfo=&(pMood->pCurrPatternSet->vecIncidentalPatterns); fTotalProbability=pMood->pCurrPatternSet->fTotalIncidentalPatternProbability; break; } if ((!pPatternInfo) || (pPatternInfo->empty())) return NULL; // no pattern could be found in the requested layer // lets choose one accourding to their probability float fProb = fTotalProbability; float fRand=m_RandGen.Rand(0.0f, fTotalProbability); fTotalProbability=0.0f; const char *sPattern = ""; // iterate through patterns and retrieve the one which was chosen by fTotalProbability TPatternDefVecIt InfoIt; for (InfoIt=pPatternInfo->begin();InfoIt!=pPatternInfo->end();++InfoIt) { SPatternDef *pPattern = (*InfoIt); fTotalProbability+=pPattern->fProbability; if (fRand<=fTotalProbability) { sPattern = pPattern->sName.c_str(); break; } } ASSERT( strlen(sPattern) > 0 ); if (nLayer==MUSICLAYER_INCIDENTAL) // make sure we dont choose a pattern which is already played { bool bPatternFound; int nTries=0; do { bPatternFound=true; for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It) { SMusicPatternPlayInfo &Info=(*It); if (stricmp(Info.pPatternInstance->GetPattern()->GetName(),sPattern )==0) // in use already { bPatternFound=false; break; } } if (!bPatternFound) { InfoIt++; if (InfoIt==pPatternInfo->end()) InfoIt=pPatternInfo->begin(); SPatternDef *pPattern = (*InfoIt); sPattern = pPattern->sName.c_str(); nTries++; if (nTries==(int)pPatternInfo->size()) return NULL; // nothing found } }while(!bPatternFound); } return GetPatternInstance( sPattern ); } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::GetMemoryUsage(class ICrySizer* pSizer) { if (!pSizer->Add(*this)) return; if (m_pMixBuffer) pSizer->AddObject(m_pMixBuffer, (int)((float)m_nSampleRate*m_fLatency)*m_nBytesPerSample); { SIZER_COMPONENT_NAME(pSizer, "Patterns"); for (TPatternMapIt PatternIt=m_mapPatterns.begin();PatternIt!=m_mapPatterns.end();++PatternIt) { CMusicPattern *pPattern=PatternIt->second; pPattern->GetMemoryUsage(pSizer); } } } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::RenamePattern( const char *sOldName,const char *sNewName ) { CSmartCriticalSection SmartCriticalSection(m_CS); CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns,sOldName,(CMusicPattern *)NULL); if (pPattern) { m_mapPatterns.erase(sOldName); m_mapPatterns[sNewName] = pPattern; } } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::UpdateTheme( SMusicTheme *pTheme ) { CSmartCriticalSection SmartCriticalSection(m_CS); Silence(); m_pCurrTheme = m_pNextTheme = m_pThemeOverride = 0; m_pCurrMood = 0; m_mapThemes = m_pDataPtr->mapThemes; } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::UpdateMood( SMusicMood *pMood ) { m_pCurrMood = 0; } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::UpdatePattern( SPatternDef *pPatternDef ) { CSmartCriticalSection SmartCriticalSection(m_CS); // Update pattern. CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns,pPatternDef->sName.c_str(), (CMusicPattern *)NULL); if (pPattern) { bool bStopped = false; // Update exising pattern. if (stricmp(pPatternDef->sFilename.c_str(),pPattern->GetFilename()) != 0) { Silence(); bStopped = StopPlaying(); pPattern->SetFilename( pPatternDef->sFilename.c_str() ); /* // Filename changed. const char *pszFilename = pPatternDef->sFilename.c_str(); if (!pPattern->Open(pszFilename)) { delete pPattern; LogMsg("[MusicSystem] WARNING: Cannot load music-pattern %s (%s) !", pPatternDef->sName.c_str(), pszFilename); return; } */ } pPattern->ClearFadePoints(); for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt) { pPattern->AddFadePoint(*IntIt); } pPattern->SetLayeringVolume(pPatternDef->nLayeringVolume); if (bStopped) StartPlaying(); } else { // Add this pattern. CMusicPattern *pPattern = AddPattern(pPatternDef->sName.c_str(),pPatternDef->sFilename.c_str()); if (pPattern) { for (TIntVecIt IntIt=pPatternDef->vecFadePoints.begin();IntIt!=pPatternDef->vecFadePoints.end();++IntIt) { pPattern->AddFadePoint(*IntIt); } pPattern->SetLayeringVolume(pPatternDef->nLayeringVolume); } } } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::PlayPattern( const char *sPattern,bool bStopPrevious ) { CSmartCriticalSection SmartCriticalSection(m_CS); CMusicPattern *pPattern = stl::find_in_map( m_mapPatterns,sPattern,(CMusicPattern *)NULL); if (pPattern) { m_bForcePatternChange = true; if (bStopPrevious) { Silence(); m_pCurrTheme = m_pNextTheme = m_pThemeOverride = 0; m_pCurrMood = 0; m_pCurrPattern = pPattern->CreateInstance(); m_pNextPattern = m_pCurrPattern; } else { CMusicPatternInstance *pLayeredPattern = pPattern->CreateInstance(); if (pLayeredPattern) { m_nLayeredIncidentalPatterns++; //pLayeredPattern->Seek0(nSamplesToNextFade); // lets seek negative so it starts playing at the right time... // lets queue the new pattern so it gets automatically played till the end SMusicPatternPlayInfo PlayInfo; PlayInfo.nLayer=MUSICLAYER_INCIDENTAL; PlayInfo.pPatternInstance=pLayeredPattern; PlayInfo.eBlendType=EBlend_ToEnd; PlayInfo.pRefCount=&m_nLayeredIncidentalPatterns; PushPatternToMixList(PlayInfo); } m_pNextPattern = pPattern->CreateInstance(); } } } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::DeletePattern( const char *sPattern ) { m_mapPatterns.erase( sPattern ); } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::LoadPatternFromXML( XmlNodeRef node,SPatternDef *pPattern ) { // Loading. pPattern->sName = node->getAttr( "Name" ); node->getAttr( "Probability",pPattern->fProbability ); node->getAttr( "LayeringVolume",pPattern->nLayeringVolume ); pPattern->sFilename = node->getAttr( "Filename" ); const char *sFadePoints = node->getAttr( "FadePoints" ); if (strlen(sFadePoints) > 0) { char sPoints[4096]; strncpy( sPoints,sFadePoints,sizeof(sPoints) ); sPoints[sizeof(sPoints)-1] = '\0'; char *token = strtok( sPoints,"," ); while( token != NULL ) { pPattern->vecFadePoints.push_back( atoi(token) ); token = strtok( NULL,"," ); } } // Add this pattern to music data. m_pDataPtr->vecPatternDef.push_back(pPattern); UpdatePattern( pPattern ); } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::LoadMoodFromXML( XmlNodeRef &node,SMusicMood *pMood ) { pMood->sName = node->getAttr( "Name" ); node->getAttr( "PlaySingle",pMood->bPlaySingle ); node->getAttr( "Priority",pMood->nPriority ); node->getAttr( "FadeOutTime",pMood->fFadeOutTime ); if (node->getChildCount() > 0) { // Save pattern sets. for (int i = 0; i < node->getChildCount(); i++) { XmlNodeRef nodePtrnSet = node->getChild(i); SMusicPatternSet *pPatternSet = new SMusicPatternSet; pMood->vecPatternSets.push_back(pPatternSet); nodePtrnSet->getAttr( "MaxTimeout",pPatternSet->fMaxTimeout ); nodePtrnSet->getAttr( "MinTimeout",pPatternSet->fMinTimeout ); nodePtrnSet->getAttr( "IncidentalLayerProbability",pPatternSet->fIncidentalLayerProbability ); nodePtrnSet->getAttr( "RhythmicLayerProbability",pPatternSet->fRhythmicLayerProbability ); nodePtrnSet->getAttr( "MaxSimultaneousIncidentalPatterns",pPatternSet->nMaxSimultaneousIncidentalPatterns ); nodePtrnSet->getAttr( "MaxSimultaneousRhythmicPatterns",pPatternSet->nMaxSimultaneousRhythmicPatterns ); // Save patterns. XmlNodeRef nodeMainLayer = nodePtrnSet->findChild( "MainLayer" ); XmlNodeRef nodeRhythmicLayer = nodePtrnSet->findChild( "RhythmicLayer" ); XmlNodeRef nodeIncidentalLayer = nodePtrnSet->findChild( "IncidentalLayer" ); if (nodeMainLayer) { for (int j = 0; j < nodeMainLayer->getChildCount(); j++) { XmlNodeRef childNode = nodeMainLayer->getChild(j); SPatternDef *pPattern = new SPatternDef; pPatternSet->vecMainPatterns.push_back(pPattern); LoadPatternFromXML( childNode,pPattern ); } } if (nodeRhythmicLayer) { for (int j = 0; j < nodeRhythmicLayer->getChildCount(); j++) { XmlNodeRef childNode = nodeRhythmicLayer->getChild(j); SPatternDef *pPattern = new SPatternDef; pPatternSet->vecRhythmicPatterns.push_back(pPattern); LoadPatternFromXML( childNode,pPattern ); } } if (nodeIncidentalLayer) { for (int j = 0; j < nodeIncidentalLayer->getChildCount(); j++) { XmlNodeRef childNode = nodeIncidentalLayer->getChild(j); SPatternDef *pPattern = new SPatternDef; pPatternSet->vecIncidentalPatterns.push_back(pPattern); LoadPatternFromXML( childNode,pPattern ); } } ////////////////////////////////////////////////////////////////////////// pPatternSet->fTotalMainPatternProbability = 0.0f; pPatternSet->fTotalRhythmicPatternProbability = 0.0f; pPatternSet->fTotalIncidentalPatternProbability = 0.0f; for (int j = 0; j < (int)pPatternSet->vecMainPatterns.size(); j++) { pPatternSet->fTotalMainPatternProbability += pPatternSet->vecMainPatterns[j]->fProbability; } for (int j = 0; j < (int)pPatternSet->vecRhythmicPatterns.size(); j++) { pPatternSet->fTotalRhythmicPatternProbability += pPatternSet->vecRhythmicPatterns[j]->fProbability; } for (int j = 0; j < (int)pPatternSet->vecIncidentalPatterns.size(); j++) { pPatternSet->fTotalIncidentalPatternProbability += pPatternSet->vecIncidentalPatterns[j]->fProbability; } } } } ////////////////////////////////////////////////////////////////////////// void CMusicSystem::LoadThemeFromXML( XmlNodeRef &node,SMusicTheme *pTheme ) { // Loading. pTheme->sName = node->getAttr( "Name" ); pTheme->sDefaultMood = node->getAttr( "DefaultMood" ); node->getAttr( "DefaultMoodTimeout",pTheme->fDefaultMoodTimeout ); XmlNodeRef nodeMoods = node->findChild("Moods"); if (nodeMoods) { for (int i = 0; i < nodeMoods->getChildCount(); i++) { XmlNodeRef childNode = nodeMoods->getChild(i); SMusicMood *pMood = new SMusicMood; LoadMoodFromXML( childNode,pMood ); pTheme->mapMoods[pMood->sName.c_str()] = pMood; } } XmlNodeRef nodeBridges = node->findChild( "Bridges" ); if (nodeBridges) { for (int i = 0; i < nodeBridges->getChildCount(); i++) { XmlNodeRef nodeBridge = nodeBridges->getChild(i); const char *sPattern = nodeBridge->getAttr( "Pattern" ); if (strlen(sPattern) > 0) { pTheme->mapBridges[nodeBridges->getTag()] = (const char*)sPattern; } } } // Add this pattern to music data. m_pDataPtr->mapThemes[pTheme->sName.c_str()] = pTheme; } ////////////////////////////////////////////////////////////////////////// bool CMusicSystem::LoadFromXML( const char *sFilename,bool bAddData ) { CSmartCriticalSection SmartCriticalSection(m_CS); // Loading. if (!bAddData) { Unload(); m_pDataPtr = new SMusicData; } m_bOwnMusicData = true; if (!m_pDataPtr) m_pDataPtr = new SMusicData; XmlNodeRef root = m_pSystem->LoadXmlFile( sFilename ); if (!root) { m_pSystem->Warning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,VALIDATOR_FLAG_SOUND,sFilename, "Music description XML file %s failed to load",sFilename ); return false; } // Iterate music themes for (int i = 0; i < root->getChildCount(); i++) { XmlNodeRef nodeTheme = root->getChild(i); if (nodeTheme->isTag("Theme")) { SMusicTheme *pTheme = new SMusicTheme; LoadThemeFromXML( nodeTheme,pTheme ); } } m_mapThemes = m_pDataPtr->mapThemes; m_bDataLoaded = true; // start playing the stream... if (m_nChannel<0) StartPlaying(); return true; } bool CMusicSystem::StreamOGG() { return( 1 == m_pCVarMusicStreamedData->GetIVal() ); }