Files
FC1/CrySoundSystem/MusicSystem.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1776 lines
53 KiB
C++

#include "StdAfx.h"
#include "musicsystem.h"
#include <CrySizer.h>
#include <ISystem.h>
#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 <none>");
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<SMoodEventInfo&>(*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="<none>";
if (m_pCurrMood)
m_Status.sMood=m_pCurrMood->sName.c_str();
else
m_Status.sMood="<none>";
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( "<CrySound> (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)<m_pCurrMood->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)<m_pCurrMood->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;i<nSamples;i++)
{
if (PlayInfo.fPhase<=0.0)
{
memset(pBuffer, 0, (nSamples-i)*m_nBytesPerSample);
PlayInfo.fPhase=0.0;
return true;
}
*pBuffer=(signed short)((float)(*pBuffer)*PlayInfo.fPhase);
pBuffer++;
*pBuffer=(signed short)((float)(*pBuffer)*PlayInfo.fPhase);
pBuffer++;
PlayInfo.fPhase-=fPhaseAdder;
}
return false;
}
/* all secondary layers (rhythmic, incidental) will be faded out over fFadeTime seconds
*/
void CMusicSystem::FadeOutAllSecondaryLayers(double fFadeTime)
{
for (TPatternPlayInfoVecIt It=m_vecPlayingPatterns.begin();It!=m_vecPlayingPatterns.end();++It)
{
SMusicPatternPlayInfo &PlayInfo=(*It);
if (PlayInfo.eBlendType==EBlend_Stop)
continue;
PlayInfo.eBlendType=EBlend_FadeOut;
PlayInfo.fFadeTime=fFadeTime;
}
}
/* create a new instance of pattern pszPattern
*/
CMusicPatternInstance* CMusicSystem::GetPatternInstance(const char *pszPattern)
{
CSmartCriticalSection SmartCriticalSection(m_CS);
TPatternMapIt PatternIt=m_mapPatterns.find(pszPattern);
if (PatternIt==m_mapPatterns.end())
{
LogMsg("[MusicSystem] WARNING: Pattern %s could not be found !", pszPattern);
return NULL;
}
CMusicPattern *pPattern=PatternIt->second;
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() );
}