public Soundtrack(psai.ProtoBuf_PsaiCoreSoundtrack pbSoundtrack) : this() { for (int i = 0; i < pbSoundtrack.themes.Count; i++) { psai.ProtoBuf_Theme pbTheme = pbSoundtrack.themes[i]; Theme tempTheme = new Theme(pbTheme); m_themes[tempTheme.id] = tempTheme; } for (int i = 0; i < pbSoundtrack.snippets.Count; i++) { psai.ProtoBuf_Snippet pbSnippet = pbSoundtrack.snippets[i]; Segment tempSnippet = new Segment(pbSnippet); m_snippets[tempSnippet.Id] = tempSnippet; Theme tmpTheme = m_themes[tempSnippet.ThemeId]; if (tmpTheme != null) { tmpTheme.m_segments.Add(tempSnippet); } else { #if !(PSAI_NOLOG) { if (LogLevel.errors <= Logger.Instance.LogLevel) { string s = "INTERNAL ERROR! could not find Theme for Theme id " + tempSnippet.ThemeId + " of snippet" + tempSnippet.Id; Logger.Instance.Log(s, LogLevel.errors); } } #endif } } }
public psai.net.Theme CreatePsaiDotNetVersion() { psai.net.Theme netTheme = new psai.net.Theme(); netTheme.id = this.Id; netTheme.Name = this.Name; netTheme.themeType = (psai.net.ThemeType) this.ThemeTypeInt; netTheme.intensityAfterRest = this.IntensityAfterRest; netTheme.musicDurationGeneral = this.MusicPhaseSecondsGeneral; netTheme.musicDurationAfterRest = this.MusicPhaseSecondsAfterRest; netTheme.restSecondsMin = this.RestSecondsMin; netTheme.restSecondsMax = this.RestSecondsMax; netTheme.priority = this.Priority; netTheme.weightings.switchGroups = this.WeightingSwitchGroups; netTheme.weightings.intensityVsVariety = this.WeightingIntensityVsVariance; netTheme.weightings.lowPlaycountVsRandom = this.WeightingLowPlaycountVsRandom; return(netTheme); }
public psai.net.Theme CreatePsaiDotNetVersion() { psai.net.Theme netTheme = new psai.net.Theme(); netTheme.id = this.Id; netTheme.Name = this.Name; netTheme.themeType = (psai.net.ThemeType)this.ThemeTypeInt; netTheme.intensityAfterRest = this.IntensityAfterRest; netTheme.musicDurationGeneral = this.MusicPhaseSecondsGeneral; netTheme.musicDurationAfterRest = this.MusicPhaseSecondsAfterRest; netTheme.restSecondsMin = this.RestSecondsMin; netTheme.restSecondsMax = this.RestSecondsMax; netTheme.priority = this.Priority; netTheme.weightings.switchGroups = this.WeightingSwitchGroups; netTheme.weightings.intensityVsVariety = this.WeightingIntensityVsVariance; netTheme.weightings.lowPlaycountVsRandom = this.WeightingLowPlaycountVsRandom; return netTheme; }
/** returns the id of the theme set as baseTheme. * If no baseTheme has been triggered yet, the baseTheme with the lowest id is returned, or -1 if no base theme was found at all. */ internal int GetLastBasicMoodId() { if (m_lastBasicMood != null) { return m_lastBasicMood.id; } else { foreach(Theme tmpThema in m_soundtrack.m_themes.Values) { if (tmpThema.themeType == ThemeType.basicMood) { m_lastBasicMood = tmpThema; return tmpThema.id; } } return -1; } }
void SetThemeAsLastBasicMood(Theme latestBasicMood) { if (latestBasicMood != null) { #if !(PSAI_NOLOG) { if (m_lastBasicMood == null || (m_lastBasicMood != null && m_lastBasicMood!= latestBasicMood)) if (LogLevel.info <= Logger.Instance.LogLevel) { Logger.Instance.Log( "Setting Theme " + latestBasicMood.id + " as the Last Basic Mood", LogLevel.info); } } #endif m_lastBasicMood = latestBasicMood; } else { #if !(PSAI_NOLOG) { if (LogLevel.warnings <= Logger.Instance.LogLevel) { Logger.Instance.Log("SetThemeAsLatestBasicMood(): invalid theme argument ! ", LogLevel.warnings); } } #endif } }
private void EnterSilenceMode() { // enter silence mode #if !(PSAI_NOLOG) if (LogLevel.info <= Logger.Instance.LogLevel) { Logger.Instance.Log("entering Silence Mode", LogLevel.info); } #endif m_timerStartSnippetPlayback.Stop(); m_timerSegmentEndApproaching.Stop(); m_targetSegment = null; m_effectiveTheme = null; m_scheduleFadeoutUponSnippetPlayback = false; m_psaiStateIntended = PsaiState.silence; m_psaiState = PsaiState.silence; }
private void InitMembersAfterSoundtrackHasLoaded() { m_themeQueue.Clear(); m_fadeVoices.Clear(); foreach (Segment segment in m_soundtrack.m_snippets.Values) { segment.audioData.filePathRelativeToProjectDir = m_platformLayer.ConvertFilePathForPlatform(segment.audioData.filePathRelativeToProjectDir); #if !(PSAI_NOLOG) { if (LogLevel.debug <= Logger.Instance.LogLevel) { Logger.Instance.Log("converted path of segment " + segment.Name + " to " + segment.audioData.filePathRelativeToProjectDir, LogLevel.debug); } } #endif } m_soundtrack.UpdateMaxPreBeatMsOfCompatibleMiddleOrBridgeSnippets(); m_lastBasicMood = m_soundtrack.getThemeById(GetLastBasicMoodId()); m_psaiState = PsaiState.silence; m_psaiStateIntended = PsaiState.silence; m_psaiPlayMode = PsaiPlayMode.regular; m_psaiPlayModeIntended = PsaiPlayMode.regular; m_returnToLastBasicMoodFlag = false; m_holdIntensity = false; m_nonInterruptingTriggerOfHighestPriority.themeId = -1; m_soundtrack.BuildAllIndirectionSequences(); }
internal Logik() { #if PSAI_STANDALONE m_platformLayer = new PlatformLayerStandalone(this); #else m_platformLayer = new PlatformLayerUnity(); #endif m_platformLayer.Initialize(); m_soundtrack = new Soundtrack(); m_themeQueue = new List<ThemeQueueEntry>(); m_fadeVoices = new List<FadeData>(); for (int i = 0; i < PSAI_CHANNEL_COUNT; i++) { m_playbackChannels[i] = new PlaybackChannel(); } m_hilightVoiceIndex = -1; m_lastRegularVoiceNumberReturned = -1; m_currentVoiceNumber = -1; m_targetVoice = -1; m_psaiMasterVolume = 1.0f; m_effectiveTheme = null; m_currentSegmentPlaying = null; m_currentSnippetTypeRequested = 0; m_targetSegment = null; m_targetSegmentSuitabilitiesRequested = 0; m_psaiState = PsaiState.notready; m_psaiStateIntended = PsaiState.notready; m_paused = false; m_fullVersionString = "psai Version " + PSAI_VERSION; #if !(PSAI_NOLOG) Logger.Instance.LogLevel = LogLevel.info; Logger.Instance.Log(m_fullVersionString, LogLevel.info); #endif s_instance = this; }
Segment GetBestStartSegmentForTheme_internal(Theme theme, float intensity) { //boost::recursive_mutex::scoped_lock block(m_pnxLogicMutex); float maxDeltaIntensity = 0.0f; int minAbgespielt = 0; int maxAbgespielt = 0; // Vergleichsliste aufbauen List<Follower> segmentList = new List<Follower>(); int teilstueckeCount = theme.m_segments.Count; for (int i=0; i < teilstueckeCount; i++) { Segment tempTeil = theme.m_segments[i]; if (tempTeil != null) { if ( ((int)SegmentSuitability.start & tempTeil.SnippetTypeBitfield ) > 0 ) { if (i==0) { minAbgespielt = tempTeil.Playcount; } else { if (tempTeil.Playcount < minAbgespielt) minAbgespielt = tempTeil.Playcount; } if (tempTeil.Playcount > maxAbgespielt) maxAbgespielt = tempTeil.Playcount; float tDeltaIntensity = intensity - tempTeil.Intensity; if (tDeltaIntensity < 0.0f) tDeltaIntensity = tDeltaIntensity * -1.0f; if (tDeltaIntensity > maxDeltaIntensity) maxDeltaIntensity = tDeltaIntensity; // kreiere eine snippetType Nachfolger Follower vorfolger = new Follower(); vorfolger.snippetId = tempTeil.Id; vorfolger.compatibility = 1.0f; // compatiblity is ignored for Start-Segments and therefore set to 1.0f segmentList.Add(vorfolger); } } else { #if !(PSAI_NOLOG) { if (LogLevel.errors <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("INTERNAL ERROR ! GetBestStartSegmentForTheme_internal() - a Segment with id "); sb.Append(tempTeil.Id); sb.Append(" could not be found!"); Logger.Instance.Log(sb.ToString(), LogLevel.errors); } } #endif } } Weighting weighting = theme.weightings; return ChooseBestSegmentFromList(segmentList, weighting, intensity, maxAbgespielt, minAbgespielt, maxDeltaIntensity); }
PsaiResult HandleNonInterruptingTriggerCall(Theme argTheme, float intensity, int musicDuration) { #if !(PSAI_NOLOG) { if (LogLevel.debug <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("HandleNonInterruptingTriggerCall() argTheme="); sb.Append(argTheme.Name); sb.Append(" intensity="); sb.Append(intensity); sb.Append(" musicDuration="); sb.Append(musicDuration); Logger.Instance.Log(sb.ToString(), LogLevel.debug); } } #endif bool updateTqe = false; if (m_nonInterruptingTriggerOfHighestPriority.themeId != -1) { m_nonInterruptingTriggerOfHighestPriority.themeId = -1; updateTqe = true; /* #if !(PSAI_NOLOG) { if (LogLevel.debug <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("previouslyTriggeredTheme was null"); Logger.Instance.Log(sb.ToString(), LogLevel.debug); } } #endif */ } else { // check if the trigger call differs from the last one if ( m_nonInterruptingTriggerOfHighestPriority.themeId != argTheme.id || m_nonInterruptingTriggerOfHighestPriority.startIntensity != intensity ) { #if !(PSAI_NOLOG) { /* if (LogLevel.debug <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("differing trigger -> update"); Logger.Instance.Log(sb.ToString(), LogLevel.debug); } */ } #endif updateTqe = true; } } if (updateTqe) { m_nonInterruptingTriggerOfHighestPriority.themeId = argTheme.id; m_nonInterruptingTriggerOfHighestPriority.startIntensity = intensity; m_nonInterruptingTriggerOfHighestPriority.musicDuration = musicDuration; m_psaiStateIntended = PsaiState.playing; return PsaiResult.OK; } else { #if !(PSAI_NOLOG) if (LogLevel.debug <= Logger.Instance.LogLevel) { Logger.Instance.Log("... no update necessary", LogLevel.debug); } #endif return PsaiResult.OK; } }
/* sets the psaiState to PSAISTATE_REST, where no music is played for a period defined in the themes.psai file. * param themeId the theme that will affect the duration of the rest, and will be played automatically when the rest is over. * param restMillis the milliseconds to rest. Pass 0 to have the resting millis calculated based on the authored data. */ void EnterRestMode(int themeIdToWakeUpWith, int themeIdToUseForRestingTimeCalculation) { //boost::recursive_mutex::scoped_lock block(m_pnxLogicMutex); #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("--- Entering rest mode. Will wake up with Theme "); sb.Append(themeIdToWakeUpWith); Logger.Instance.Log(sb.ToString(), LogLevel.info); } } #endif m_psaiState = PsaiState.rest; m_holdIntensity = false; m_timerStartSnippetPlayback.Stop(); // may be necessary if we have been fading out immediately m_timerSegmentEndApproaching.Stop(); // may be necessary if we have been fading out immediately m_timerWakeUpFromRest.Stop(); m_effectiveTheme = m_soundtrack.getThemeById(themeIdToWakeUpWith); // the effective Theme is also valid during Rest Mode if (m_effectiveTheme != null) { int millisTimerRest = 0; if (m_restModeSecondsOverride > 0) { #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("--- resting time is based on override values."); Logger.Instance.Log(sb.ToString(), LogLevel.info); } } #endif millisTimerRest = m_restModeSecondsOverride; m_restModeSecondsOverride = -1; } else { Theme themeRest = m_soundtrack.getThemeById(themeIdToUseForRestingTimeCalculation); if (themeRest != null) { #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("--- resting time is based on Theme "); sb.Append(themeRest.Name); Logger.Instance.Log(sb.ToString(), LogLevel.info); } } #endif millisTimerRest = GetRandomInt(themeRest.restSecondsMin, themeRest.restSecondsMax) * 1000; } else { #if !(PSAI_NOLOG) { if (LogLevel.warnings <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("--- resting time is based on Theme "); sb.Append(m_effectiveTheme.Name); sb.Append("(themeIdToUseForRestingTimeCalculation was not found: "); sb.Append(themeIdToUseForRestingTimeCalculation); sb.Append(" )"); Logger.Instance.Log(sb.ToString(), LogLevel.warnings); } } #endif millisTimerRest = GetRandomInt(m_effectiveTheme.restSecondsMin, m_effectiveTheme.restSecondsMax) * 1000; } } if (millisTimerRest > 0) { m_timeStampRestStart = GetTimestampMillisElapsedSinceInitialisation(); #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("...resting for "); sb.Append(millisTimerRest); sb.Append(" ms"); Logger.Instance.Log(sb.ToString(), LogLevel.info); } } #endif m_timerWakeUpFromRest.SetTimer(millisTimerRest, 0); } else { #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { Logger.Instance.Log("resting time is zero, starting again immediately.", LogLevel.info); } } #endif WakeUpFromRestHandler(); } } else { #if !(PSAI_NOLOG) { if (LogLevel.errors <= Logger.Instance.LogLevel) { Logger.Instance.Log("can't go to rest because Theme wasn't found!", LogLevel.errors); } } #endif } }
/** internal function to initiate the playback of a theme. * @param themeId id of the theme to be played * @param intensity the desired intensity level [0.0f ... 1.0f] * @param immediately true: play instantly false: play at end of current snippet * @param SnippetType * @param recalculateIntensitySlope pass true if the intensity slope needs to be reinitialized to the full musical period as defined in the authoring software. * @param holdIntensity pass true if the intensity should be held on a constant level, false otherwise (default is false) * @return PsaiResult.OK ok, theme will be played * PSAI_INFO_TRIGGER_DENIED trigger was denied as a theme transition is not guaranteed due to missing snippet transitions, and error handling was set to DENY_TRIGGER * PSAI_ERR_ESSENTIAL_SNIPPET_MISSING a transition could not be achieved because there was no compatible follower to the current snippet, and error handling was set to ABORT or STOP_MUSIC * */ PsaiResult PlayThemeNowOrAtEndOfCurrentSegment(int themeId, float intensity, int musicDuration, bool immediately, bool holdIntensity) { #if !(PSAI_NOLOG) { if (LogLevel.debug <= Logger.Instance.LogLevel) { Theme theme = m_soundtrack.getThemeById(themeId); StringBuilder sb = new StringBuilder(); sb.Append("PlayThemeNowOrAtEndOfCurrentSegment() themeId="); sb.Append(themeId); if (theme == null) { sb.Append("THEME NOT FOUND!"); } else { sb.Append(" ["); sb.Append(theme.Name); sb.Append("] themeType="); sb.Append(theme.themeType); } sb.Append( " intensity="); sb.Append(intensity); sb.Append(" immediately="); sb.Append(immediately); sb.Append(" holdIntensity="); sb.Append(holdIntensity); sb.Append(" musicDuration="); sb.Append(musicDuration); sb.Append(" m_currentSegmentPlaying="); sb.Append(m_currentSegmentPlaying); Logger.Instance.Log(sb.ToString(), LogLevel.debug); } } #endif SetCurrentIntensityAndMusicDuration(intensity, musicDuration, true); m_psaiStateIntended = PsaiState.playing; m_heldIntensity = intensity; // if we're interrupting rest mode, kill the rest wake up timer if (m_psaiState == PsaiState.rest) { m_timerWakeUpFromRest.Stop(); } // choice of Segment Suitability: // * Use Start Segments: // 1. when playing out of silence // 2. if a pure END-Segment is currently playing // * Use Middle Segments when staying within the current Theme. // * Use Bridge or Middle Segments when transitioning to another Theme. m_targetSegmentSuitabilitiesRequested = (int)SegmentSuitability.start; if (m_psaiState == PsaiState.playing) { if (m_currentSegmentPlaying != null) { if (m_currentSegmentPlaying.IsUsableOnlyAs(SegmentSuitability.end)) { m_targetSegmentSuitabilitiesRequested = (int)SegmentSuitability.start; } else { if (getEffectiveThemeId() == themeId) { // we stay within the same Theme, so choose a MIDDLE Segment // NOTE: this may only happen if we return from the Menu or a CutScene to the same Theme. Otherwise, // PlayThemeNowOrAtEndOfCurrentSegment should not be called explicitly m_targetSegmentSuitabilitiesRequested = (int)SegmentSuitability.middle; } else { // upon Theme changes we play BRIDGE or MIDDLE Segments m_targetSegmentSuitabilitiesRequested = SNIPPET_TYPE_MIDDLE_OR_BRIDGE; } } } } m_effectiveTheme = m_soundtrack.getThemeById(themeId); Segment targetSnippet; if ((m_targetSegmentSuitabilitiesRequested & (int)SegmentSuitability.start) > 0 || GetEffectiveSegment() == null) { targetSnippet = GetBestStartSegmentForTheme(themeId, intensity); } else { targetSnippet = GetBestCompatibleSegment(GetEffectiveSegment(), themeId, intensity, m_targetSegmentSuitabilitiesRequested); } ////////////////////////////////////////////////////////////////////////// // no compatible Segment could be found ! ////////////////////////////////////////////////////////////////////////// if (targetSnippet == null) { #if !(PSAI_NOLOG) { Logger.Instance.Log("essential Segment could not be found! Trying to substitute...", LogLevel.errors); } #endif targetSnippet = substituteSegment(themeId); if (targetSnippet == null) { #if !(PSAI_NOLOG) { Logger.Instance.Log("failed to substitute Segment. Stopping music.", LogLevel.errors); } #endif StopMusic(true); return PsaiResult.essential_segment_missing; } } // //////////////////////////////////////////////// // at this point, the target Segment has to be set! (internal error otherwise) // //////////////////////////////////////////////// m_holdIntensity = holdIntensity; // starting a Theme immediately when any other Theme is already playing should result in // fading out that other Theme. if (immediately && GetEffectiveSegment() != null) { m_scheduleFadeoutUponSnippetPlayback = true; } if (targetSnippet != null) { return PlaySegment(targetSnippet, immediately); } else { #if !(PSAI_NOLOG) { Logger.Instance.Log("fatal internal error! entered code section in PlayTheme that is supposed to be unreachable!", LogLevel.errors); } #endif return PsaiResult.internal_error; } }
/** * * @return PSAI_OK ok hightlight is being played PSAI_INFO_TRIGGER_IGNORED_LOW_PRIORITY trigger ignored, because a highlight of a higher priority is currently being played. PSAI_ERR_ESSENTIAL_SEGMENT_MISSING ignored, because no compatible Segment could be found. */ PsaiResult startHighlight(Theme highlightTheme) { //boost::recursive_mutex::scoped_lock blockieren(m_pnxLogicMutex); #if !(PSAI_NOLOG) if (LogLevel.debug <= Logger.Instance.LogLevel) { Logger.Instance.Log("startHighlight()", LogLevel.debug); } #endif // Highlights are always SNIPPET_TYPE_MIDDLE if (highlightTheme.m_segments.Count > 0) { // deine omma Segment tempTeil; if (m_currentSegmentPlaying != null) { tempTeil = GetBestCompatibleSegment(m_currentSegmentPlaying, highlightTheme.id, getCurrentIntensity(), (int)SegmentSuitability.whatever); } else { int randomSegmentIndex = GetRandomInt(0, highlightTheme.m_segments.Count); // note: GetRandomInt will never return the max value. tempTeil = m_soundtrack.GetSegmentById(highlightTheme.m_segments[randomSegmentIndex].Id); } if (tempTeil != null) { //PsaiResult psaiResult; // TODO: check if we need to fade out highlights // is currently a highlight playing, has it a lower priority and is in need of a fade? /* bool playing = false; if (m_hilightVoiceIndex > -1) { psaiResult = m_audioPlaybackLayer->isPlaying(m_hilightVoiceIndex, &playing); if (playing) { if ( m_priorityOfCurrentHighlight > highlightTheme->priority) return PSAI_RC_TRIGGER_IGNORED_LOW_PRIORITY; // no need to do anything further else startFade(m_hilightVoiceIndex, PSAI_FADEOUTMILLIS_HIGHLIGHT_INTERRUPTED); } } */ PlaySegmentLayeredAndImmediately(tempTeil); tempTeil.Playcount++; return PsaiResult.OK; } else { #if !(PSAI_NOLOG) { if (LogLevel.warnings <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("the triggered Highlight Layer "); sb.Append(highlightTheme.id); sb.Append(" does not contain a compatible Highlight Segment."); if (m_currentSegmentPlaying != null) { sb.Append(" Current Segment playing:"); sb.Append(m_currentSegmentPlaying.Name); } else { sb.Append(" (No Segment is currently playing.)"); } Logger.Instance.Log(sb.ToString(), LogLevel.warnings); } } #endif return PsaiResult.essential_segment_missing; } } else { #if !(PSAI_NOLOG) { if (LogLevel.warnings <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("the triggered Highlight Theme "); sb.Append(highlightTheme.id); sb.Append(" does not contain any Segments."); Logger.Instance.Log(sb.ToString(), LogLevel.warnings); } } #endif return PsaiResult.essential_segment_missing; } }
internal PsaiResult TriggerMusicTheme(Theme argTheme, float argIntensity, int argMusicDuration) { if (m_initializationFailure) { #if !(PSAI_NOLOG) { if (LogLevel.errors <= Logger.Instance.LogLevel) { Logger.Instance.Log(LOGMESSAGE_ABORTION_DUE_TO_INITIALIZATION_FAILURE, LogLevel.errors); } } #endif return PsaiResult.initialization_error; } ////////////////////////////////////////////////////////////////////////// if (m_paused) { setPaused(false); } Segment effectiveSegment = GetEffectiveSegment(); // special treatment for Highlights if (argTheme.themeType == ThemeType.highlightLayer) { if (CheckIfAnyThemeIsCurrentlyPlaying() == true) { if (m_effectiveTheme != null && effectiveSegment != null && effectiveSegment.CheckIfAtLeastOneDirectTransitionOrLayeringIsPossible(m_soundtrack, argTheme.id) == false) { #if !(PSAI_NOLOG) { Logger.Instance.Log(LOGMESSAGE_TRIGGER_DENIED, LogLevel.warnings); } #endif return PsaiResult.triggerDenied; }; } return startHighlight(argTheme); } // return immediately in menu mode if (m_psaiPlayMode == PsaiPlayMode.menuMode) { #if !(PSAI_NOLOG) if (LogLevel.warnings <= Logger.Instance.LogLevel) { Logger.Instance.Log("TriggerMusicTheme() ignored: Menu Mode is active", LogLevel.warnings); } #endif return PsaiResult.commandIgnoredMenuModeActive; } else if (m_psaiPlayModeIntended == PsaiPlayMode.cutScene) { #if !(PSAI_NOLOG) if (LogLevel.warnings <= Logger.Instance.LogLevel) { Logger.Instance.Log("TriggerMusicTheme() ignored: Cutscene is active", LogLevel.warnings); } #endif return PsaiResult.commandIgnoredCutsceneActive; } else if (m_psaiPlayMode == PsaiPlayMode.cutScene && m_psaiStateIntended == PsaiState.silence && m_currentSegmentPlaying != null) { #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { Logger.Instance.Log("special case: Cutscene Theme is still playing, continuing with theme " + argTheme.Name, LogLevel.info); } } #endif m_psaiState = PsaiState.playing; m_psaiStateIntended = PsaiState.playing; return PlayThemeNowOrAtEndOfCurrentSegment(argTheme.id, argIntensity, argMusicDuration, true, false); } ////////////////////////////////////////////////////////////////////////// // regular Mode ////////////////////////////////////////////////////////////////////////// // if we trigger a BasicMood shortly after ReturnToBasicMood(by End) has been called, we don't want to cancel the // return, but instead switch the Last Basic Mood to return to. In all other cases, cancel the Return process. if (m_returnToLastBasicMoodFlag) { if (argTheme.themeType != ThemeType.basicMood) { m_returnToLastBasicMoodFlag = false; } } if (argTheme.themeType == ThemeType.basicMood) { SetThemeAsLastBasicMood(argTheme); } // always clear the Theme Queue, as the most recent trigger call is the one that counts. removeFirstFollowingThemeQueueEntry(); // nothing is playing -> play immediately if (effectiveSegment == null || m_psaiState == PsaiState.silence || m_psaiState == PsaiState.rest) { return PlayThemeNowOrAtEndOfCurrentSegment(argTheme.id, argIntensity, argMusicDuration, true, false); } // special case: StopMusic(by End Segment) is in progress, and a Theme of lower or same priority is triggered // -> conduct a seamless transition. if (m_psaiStateIntended == PsaiState.silence && effectiveSegment != null) { Theme effectiveTheme1 = m_soundtrack.getThemeById(effectiveSegment.ThemeId); ThemeInterruptionBehavior tib = Theme.GetThemeInterruptionBehavior(effectiveTheme1.themeType, argTheme.themeType); if (tib == ThemeInterruptionBehavior.at_end_of_current_snippet || tib == ThemeInterruptionBehavior.never) { m_psaiStateIntended = PsaiState.playing; return PlayThemeNowOrAtEndOfCurrentSegment(argTheme.id, argIntensity, argMusicDuration, false, false); } } // the effective Theme was triggered again? if (effectiveSegment.ThemeId == argTheme.id) { #if !(PSAI_NOLOG) { /* if (LogLevel.debug <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("theme "); sb.Append(argTheme.Name); sb.Append(" is already playing and the SegmentEndApproaching timer is pending. Updating intensity to "); sb.Append(argIntensity); Logger.Instance.Log(sb.ToString(), LogLevel.debug); } */ } #endif m_nonInterruptingTriggerOfHighestPriority.themeId = -1; // clear any non-interrupting trigger call we may have received recently! SetCurrentIntensityAndMusicDuration(argIntensity, argMusicDuration, true); m_psaiStateIntended = PsaiState.playing; return PsaiResult.OK; } Theme effectiveTheme = m_soundtrack.getThemeById(effectiveSegment.ThemeId); ThemeInterruptionBehavior themeInteruptionBehavior = Theme.GetThemeInterruptionBehavior(effectiveTheme.themeType, argTheme.themeType); switch (themeInteruptionBehavior) { case ThemeInterruptionBehavior.immediately: { // don't push the interrupted Theme on the stack if GoToRest() or StopMusic() has just been called. if (argTheme.themeType == ThemeType.shock && m_psaiStateIntended == PsaiState.playing) { PushEffectiveThemeToThemeQueue(PsaiPlayMode.regular); } else { m_nonInterruptingTriggerOfHighestPriority.themeId = -1; } return PlayThemeNowOrAtEndOfCurrentSegment(argTheme.id, argIntensity, argMusicDuration, true, false); } //break; case ThemeInterruptionBehavior.at_end_of_current_snippet: { return HandleNonInterruptingTriggerCall(argTheme, argIntensity, argMusicDuration); } //break; case ThemeInterruptionBehavior.never: { if (argTheme.themeType != ThemeType.basicMood) { pushThemeToThemeQueue(argTheme.id, argIntensity, argMusicDuration, false, 0, PsaiPlayMode.regular, false); #if !(PSAI_NOLOG) { if (LogLevel.info <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("Theme "); sb.Append(argTheme.Name); sb.Append(" has been queued for direct playback after the intensity of the current Theme has dropped to zero."); Logger.Instance.Log(sb.ToString(), LogLevel.info); } } #endif } return PsaiResult.OK; } //break; } #if !(PSAI_NOLOG) { if (LogLevel.errors <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("INTERNAL ERROR: end of TriggerMusicTheme() reached without returning a proper returnCode. "); sb.Append("argThemeId="); sb.Append(argTheme.id); sb.Append(" m_currentTheme="); sb.Append(m_effectiveTheme); if (m_effectiveTheme != null) { sb.Append(" m_currentTheme id="); sb.Append(m_effectiveTheme.id); sb.Append(" m_currentTheme themeType="); sb.Append(m_effectiveTheme.themeType); } Logger.Instance.Log(sb.ToString(), LogLevel.errors); } } #endif return PsaiResult.internal_error; }
/** * @param listOfSnippetsWithValidTransitionSequencesToTargetTheme - a list of Snippets that have a valid Transition-Sequence to or directly compatible followers in a given TargetTheme. */ private void SetTheNextSegmentToShortestTransitionSequenceToTargetThemeForAllSourceSegmentsOfTheSegmentsInThisList(Segment[] listOfSnippetsWithValidTransitionSequencesToTargetTheme, Soundtrack soundtrack, Theme targetTheme) { /* #if !(PSAI_NOLOG) if (LogLevel.debug <= Logger.Instance.LogLevel) { Logger.Instance.Log("SetTheNextSegmentToShortestTransitionSequenceToTargetThemeForAllSourceSegmentsOfTheSegmentsInThisList() called, listOfSnippets.Length=" + listOfSnippetsWithValidEndSequences.Length, LogLevel.debug); StringBuilder sb = new StringBuilder(); foreach (Snippet argSnippet in listOfSnippetsWithValidEndSequences) { sb.Append(argSnippet.name); sb.Append(" intensity="); sb.Append(argSnippet.intensity); sb.Append(" nextSnippetToShEndSeq="); sb.Append(argSnippet.nextSnippetToShortestEndSequence); sb.Append(" "); } Logger.Instance.Log(sb.ToString(), LogLevel.debug); } #endif */ Dictionary<Segment, List<Segment>> mapWaypointAlternativesForSnippet = new Dictionary<Segment, List<Segment>>(); foreach (Segment transitionSnippet in listOfSnippetsWithValidTransitionSequencesToTargetTheme) { List<Segment> sourceSnippets = GetSetOfAllSourceSegmentsCompatibleToSegment(transitionSnippet, Logik.COMPATIBILITY_PERCENTAGE_SAME_GROUP, SegmentSuitability.none); sourceSnippets.Remove(transitionSnippet); foreach (Segment sourceSnippet in sourceSnippets) { if (sourceSnippet.MapOfNextTransitionSegmentToTheme.ContainsKey(targetTheme.id) == false && sourceSnippet.CheckIfAtLeastOneDirectTransitionOrLayeringIsPossible(soundtrack, targetTheme.id) == false && sourceSnippet.ThemeId == transitionSnippet.ThemeId) { if (mapWaypointAlternativesForSnippet.ContainsKey(sourceSnippet) == false) { mapWaypointAlternativesForSnippet[sourceSnippet] = new List<Segment>(); } mapWaypointAlternativesForSnippet[sourceSnippet].Add(transitionSnippet); } } } foreach (Segment snippet in mapWaypointAlternativesForSnippet.Keys) { snippet.MapOfNextTransitionSegmentToTheme[targetTheme.id] = snippet.ReturnSegmentWithLowestIntensityDifference(mapWaypointAlternativesForSnippet[snippet]); } Segment[] snippetsAdded = new Segment[mapWaypointAlternativesForSnippet.Count]; mapWaypointAlternativesForSnippet.Keys.CopyTo(snippetsAdded, 0); if (snippetsAdded.Length > 0) { SetTheNextSegmentToShortestTransitionSequenceToTargetThemeForAllSourceSegmentsOfTheSegmentsInThisList(snippetsAdded, soundtrack, targetTheme); } }
/* This method is responsible to link each Segment of a Theme to the Target Theme * in the shortest path possible. If multiple compatible Segments exist, the one with the * best-matching intensity will be chosen. * After the algorithm has finished, each Snippet's _mapCompatibleSegmentsToTheme will hold * the Segment to be played next when a Theme Transition is in progress, or NULL * if either no transition is possible at all, or if a direct transition is possible. */ internal void BuildSequencesToTargetThemeForAllSegments(Soundtrack soundtrack, Theme targetTheme) { foreach (Segment snippet in m_segments) { snippet.MapOfNextTransitionSegmentToTheme.Remove(targetTheme.id); } List<Segment> snippetsAddedInLastTier = new List<Segment>(); foreach (Segment snippet in m_segments) { if (snippet.CheckIfAtLeastOneDirectTransitionOrLayeringIsPossible(soundtrack, targetTheme.id) == true) { snippetsAddedInLastTier.Add(snippet); } } SetTheNextSegmentToShortestTransitionSequenceToTargetThemeForAllSourceSegmentsOfTheSegmentsInThisList(snippetsAddedInLastTier.ToArray(), soundtrack, targetTheme); #if !(PSAI_NOLOG) { if (LogLevel.debug <= Logger.Instance.LogLevel) { StringBuilder sb = new StringBuilder(); sb.Append("BuildSequencesToTargetThemeForAllSegments completed for Theme "); sb.Append(this); sb.Append(" to Theme "); sb.Append(targetTheme); sb.Append("\n"); foreach (Segment snippet in m_segments) { sb.Append(snippet); sb.Append(" -> "); if (snippet.MapOfNextTransitionSegmentToTheme.ContainsKey(targetTheme.id) == false) { sb.Append(" DirectTransition:"); sb.Append(snippet.CheckIfAtLeastOneDirectTransitionOrLayeringIsPossible(soundtrack, targetTheme.id)); } else { sb.Append(snippet.MapOfNextTransitionSegmentToTheme[targetTheme.id].ToString()); } sb.Append("\n"); } Logger.Instance.Log(sb.ToString(), LogLevel.debug); } } #endif }