internal float INTERNAL_calculateVolume() { float retval = 1.0f; for (int i = 0; i < INTERNAL_activeSound.RPCCodes.Count; i += 1) { foreach (uint curCode in INTERNAL_activeSound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); if (curRPC.Parameter != RPCParameter.Volume) { continue; } float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { result = curRPC.CalculateRPC(GetVariable(curRPC.Variable)); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } retval *= XACTCalculator.CalculateAmplitudeRatio(result / 100.0); } } return(retval); }
internal float INTERNAL_calculateVolume() { float retval = 0.0f; for (int i = 0; i < INTERNAL_activeSound.Sound.RPCCodes.Count; i += 1) { foreach (uint curCode in INTERNAL_activeSound.Sound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); if (curRPC.Parameter != RPCParameter.Volume) { continue; } float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { float variableValue; if (curRPC.Variable.Equals("AttackTime")) { long elapsedFromPlay = INTERNAL_timer.ElapsedMilliseconds; variableValue = elapsedFromPlay; } else if (curRPC.Variable.Equals("ReleaseTime")) { if (INTERNAL_fadeMode == FadeMode.ReleaseRpc) { long elapsedFromStop = INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart; variableValue = elapsedFromStop; } else { variableValue = 0.0f; } } else { variableValue = GetVariable(curRPC.Variable); } result = curRPC.CalculateRPC(variableValue); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } retval += result; } } return(retval); }
internal bool INTERNAL_update() { // If we're not running, save some instructions... if (!INTERNAL_timer.IsRunning) { return(true); } elapsedFrames += 1; // Play events when the timestamp has been hit. for (int i = 0; i < INTERNAL_eventList.Count; i += 1) { if (!INTERNAL_eventPlayed[i] && INTERNAL_timer.ElapsedMilliseconds > INTERNAL_eventList[i].Timestamp) { uint type = INTERNAL_eventList[i].Type; if (type == 1) { PlayWave((PlayWaveEvent)INTERNAL_eventList[i]); } else if (type == 2) { eventVolume = ((SetVolumeEvent)INTERNAL_eventList[i]).GetVolume(); } else if (type == 3) { eventPitch = ((SetPitchEvent)INTERNAL_eventList[i]).GetPitch(); } else { throw new NotImplementedException("Unhandled XACTEvent type!"); } INTERNAL_eventPlayed[i] = true; } } // Clear out sound effect instances as they finish for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { if (INTERNAL_instancePool[i].State == SoundState.Stopped) { // Get the event that spawned this instance... PlayWaveEvent evt = (PlayWaveEvent)INTERNAL_waveEventSounds[INTERNAL_instancePool[i]]; float prevVolume = INTERNAL_instanceVolumes[i]; float prevPitch = INTERNAL_instancePitches[i]; // Then delete all the guff INTERNAL_waveEventSounds.Remove(INTERNAL_instancePool[i]); INTERNAL_instancePool[i].Dispose(); INTERNAL_instancePool.RemoveAt(i); INTERNAL_instanceVolumes.RemoveAt(i); INTERNAL_instancePitches.RemoveAt(i); INTERNAL_rpcTrackVolumes.RemoveAt(i); INTERNAL_rpcTrackPitches.RemoveAt(i); // Increment the loop counter, try to get another loop INTERNAL_eventLoops[evt] += 1; PlayWave(evt, prevVolume, prevPitch); // Removed a wave, have to step back... i -= 1; } } // Fade in/out float fadePerc = 1.0f; if (INTERNAL_fadeMode != FadeMode.None) { if (INTERNAL_fadeMode == FadeMode.FadeOut) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = (INTERNAL_fadeEnd - (INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart)) / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc <= 0.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = INTERNAL_timer.ElapsedMilliseconds / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc > 1.0f) { fadePerc = 1.0f; INTERNAL_fadeMode = FadeMode.None; } } } // User control updates if (INTERNAL_data.IsUserControlled) { string varName = INTERNAL_data.UserControlVariable; if (INTERNAL_userControlledPlaying && (INTERNAL_baseEngine.INTERNAL_isGlobalVariable(varName) ? !MathHelper.WithinEpsilon(INTERNAL_controlledValue, INTERNAL_baseEngine.GetGlobalVariable(varName)) : !MathHelper.WithinEpsilon(INTERNAL_controlledValue, GetVariable(INTERNAL_data.UserControlVariable)))) { // TODO: Crossfading foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Stop(); sfi.Dispose(); } INTERNAL_instancePool.Clear(); INTERNAL_instanceVolumes.Clear(); INTERNAL_instancePitches.Clear(); INTERNAL_rpcTrackVolumes.Clear(); INTERNAL_rpcTrackPitches.Clear(); if (!INTERNAL_calculateNextSound()) { // Nothing to play, bail. return(true); } INTERNAL_activeSound.GatherEvents(INTERNAL_eventList); foreach (XACTEvent evt in INTERNAL_eventList) { INTERNAL_eventPlayed.Add(false); INTERNAL_eventLoops.Add(evt, 0); } INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_timer.Start(); } if (INTERNAL_activeSound == null) { return(INTERNAL_userControlledPlaying); } } // If everything has been played and finished, we're done here. if (INTERNAL_instancePool.Count == 0) { bool allPlayed = true; foreach (bool played in INTERNAL_eventPlayed) { if (!played) { allPlayed = false; break; } } if (allPlayed) { // If this is managed, we're done completely. if (INTERNAL_isManaged) { Dispose(); } else { INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_category.INTERNAL_removeActiveCue(this); } return(INTERNAL_userControlledPlaying); } } // RPC updates float rpcVolume = 1.0f; float rpcPitch = 0.0f; float hfGain = 1.0f; float lfGain = 1.0f; for (int i = 0; i < INTERNAL_activeSound.RPCCodes.Count; i += 1) { if (i > INTERNAL_instancePool.Count) { break; } if (i > 0) { INTERNAL_rpcTrackVolumes[i - 1] = 1.0f; INTERNAL_rpcTrackPitches[i - 1] = 0.0f; } foreach (uint curCode in INTERNAL_activeSound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { result = curRPC.CalculateRPC(GetVariable(curRPC.Variable)); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } if (curRPC.Parameter == RPCParameter.Volume) { float vol = XACTCalculator.CalculateAmplitudeRatio(result / 100.0); if (i == 0) { rpcVolume *= vol; } else { INTERNAL_rpcTrackVolumes[i - 1] *= vol; } } else if (curRPC.Parameter == RPCParameter.Pitch) { float pitch = result / 1000.0f; if (i == 0) { rpcPitch += pitch; } else { INTERNAL_rpcTrackPitches[i - 1] += pitch; } } else if (curRPC.Parameter == RPCParameter.FilterFrequency) { // FIXME: Just listening to the last RPC! float hf = result / 20000.0f; float lf = 1.0f - hf; if (i == 0) { hfGain = hf; lfGain = lf; } else { throw new NotImplementedException("Per-track filter RPCs!"); } } else { throw new NotImplementedException("RPC Parameter Type: " + curRPC.Parameter.ToString()); } } } // Sound effect instance updates for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { /* The final volume should be the combination of the * authored volume, category volume, RPC/Event volumes, and fade. */ INTERNAL_instancePool[i].Volume = ( INTERNAL_instanceVolumes[i] * INTERNAL_category.INTERNAL_volume.Value * rpcVolume * INTERNAL_rpcTrackVolumes[i] * eventVolume * fadePerc ); /* The final pitch should be the combination of the * authored pitch and RPC/Event pitch results. */ INTERNAL_instancePool[i].Pitch = ( INTERNAL_instancePitches[i] + rpcPitch + eventPitch + INTERNAL_rpcTrackPitches[i] ); /* The final filter is determined by the instance's filter type, * in addition to our calculation of the HF/LF gain values. */ byte fType = INTERNAL_instancePool[i].FilterType; if (fType == 0xFF) { // No-op, no filter! } else if (fType == 0) { INTERNAL_instancePool[i].INTERNAL_applyLowPassFilter(hfGain); } else if (fType == 1) { INTERNAL_instancePool[i].INTERNAL_applyHighPassFilter(lfGain); } else if (fType == 2) { INTERNAL_instancePool[i].INTERNAL_applyBandPassFilter(hfGain, lfGain); } else { throw new InvalidOperationException("Unhandled filter type!"); } // Update 3D position, if applicable if (INTERNAL_isPositional) { INTERNAL_instancePool[i].Apply3D( INTERNAL_listener, INTERNAL_emitter ); } } return(true); }
internal bool INTERNAL_update() { // If this is our first update, time to play! if (INTERNAL_queuedPlayback) { INTERNAL_queuedPlayback = false; foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Play(); if (INTERNAL_queuedPaused) { sfi.Pause(); } } INTERNAL_queuedPaused = false; } for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { if (INTERNAL_instancePool[i].INTERNAL_timer.ElapsedMilliseconds > INTERNAL_instancePool[i].INTERNAL_delayMS) { // Okay, play this NOW! INTERNAL_instancePool[i].Play(); if (IsPaused) { INTERNAL_instancePool[i].Pause(); } } if (INTERNAL_instancePool[i].State == SoundState.Stopped) { INTERNAL_instancePool[i].Dispose(); INTERNAL_instancePool.RemoveAt(i); INTERNAL_instanceVolumes.RemoveAt(i); INTERNAL_instancePitches.RemoveAt(i); i -= 1; } } // User control updates if (INTERNAL_data.IsUserControlled) { if (INTERNAL_userControlledPlaying && INTERNAL_controlledValue != GetVariable(INTERNAL_data.UserControlVariable)) { // TODO: Crossfading foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Stop(); sfi.Dispose(); } INTERNAL_instancePool.Clear(); INTERNAL_instanceVolumes.Clear(); INTERNAL_instancePitches.Clear(); if (!INTERNAL_calculateNextSound()) { // Nothing to play, bail. return(true); } INTERNAL_setupSounds(); foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Play(); } } if (INTERNAL_activeSound == null) { return(INTERNAL_userControlledPlaying); } } if (INTERNAL_isPositional) { foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Apply3D( INTERNAL_listener, INTERNAL_emitter ); } } float rpcVolume = 1.0f; float rpcPitch = 0.0f; foreach (uint curCode in INTERNAL_activeSound.RPCCodes) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { result = curRPC.CalculateRPC(GetVariable(curRPC.Variable)); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } if (curRPC.Parameter == RPCParameter.Volume) { rpcVolume *= XACTCalculator.CalculateAmplitudeRatio(result / 100.0); } else if (curRPC.Parameter == RPCParameter.Pitch) { rpcPitch += result / 1000.0f; } else if (curRPC.Parameter == RPCParameter.FilterFrequency) { // TODO: Filters? } else { throw new Exception("RPC Parameter Type: " + curRPC.Parameter.ToString()); } } for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { /* The final volume should be the combination of the * authored volume, Volume variable and RPC volume results. */ INTERNAL_instancePool[i].Volume = INTERNAL_instanceVolumes[i] * GetVariable("Volume") * rpcVolume; /* The final pitch should be the combination of the * authored pitch and RPC pitch results. */ INTERNAL_instancePool[i].Pitch = INTERNAL_instancePitches[i] + rpcPitch; } // Finally, check if we're still active. if (IsStopped && !INTERNAL_queuedPlayback && !INTERNAL_userControlledPlaying) { // If this is managed, we're done here. if (INTERNAL_isManaged) { Dispose(); } return(false); } return(true); }
internal bool INTERNAL_update() { // If we're not running, save some instructions... if (!INTERNAL_timer.IsRunning) { return(true); } elapsedFrames += 1; // User control updates if (INTERNAL_data.IsUserControlled) { string varName = INTERNAL_data.UserControlVariable; if (INTERNAL_userControlledPlaying && (INTERNAL_baseEngine.INTERNAL_isGlobalVariable(varName) ? !MathHelper.WithinEpsilon(INTERNAL_controlledValue, INTERNAL_baseEngine.GetGlobalVariable(varName)) : !MathHelper.WithinEpsilon(INTERNAL_controlledValue, GetVariable(INTERNAL_data.UserControlVariable)))) { // TODO: Crossfading foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Stop(); sfi.Dispose(); } INTERNAL_instancePool.Clear(); INTERNAL_instanceVolumes.Clear(); INTERNAL_instancePitches.Clear(); INTERNAL_rpcTrackVolumes.Clear(); INTERNAL_rpcTrackPitches.Clear(); if (!INTERNAL_calculateNextSound()) { // Nothing to play, bail. return(true); } INTERNAL_activeSound.InitializeClips(); INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_timer.Start(); } if (INTERNAL_activeSound == null) { return(INTERNAL_userControlledPlaying); } } // Trigger events for each track foreach (XACTClipInstance clip in INTERNAL_activeSound.Clips) { // Play events when the timestamp has been hit. for (int i = 0; i < clip.Events.Count; i += 1) { EventInstance evt = clip.Events[i]; if (!evt.Played && INTERNAL_timer.ElapsedMilliseconds > evt.Timestamp) { evt.Apply( this, null, INTERNAL_timer.ElapsedMilliseconds / 1000.0f ); } } } // Clear out sound effect instances as they finish for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { if (INTERNAL_instancePool[i].State == SoundState.Stopped) { // Get the event that spawned this instance... PlayWaveEventInstance evtInstance = INTERNAL_playWaveEventBySound[INTERNAL_instancePool[i]]; double prevVolume = INTERNAL_instanceVolumes[i]; short prevPitch = INTERNAL_instancePitches[i]; // Then delete all the guff INTERNAL_playWaveEventBySound.Remove(INTERNAL_instancePool[i]); INTERNAL_instancePool[i].Dispose(); INTERNAL_instancePool.RemoveAt(i); INTERNAL_instanceVolumes.RemoveAt(i); INTERNAL_instancePitches.RemoveAt(i); INTERNAL_rpcTrackVolumes.RemoveAt(i); INTERNAL_rpcTrackPitches.RemoveAt(i); // Increment the loop counter, try to get another loop evtInstance.LoopCount += 1; PlayWave(evtInstance, prevVolume, prevPitch); // Removed a wave, have to step back... i -= 1; } } // Fade in/out float fadePerc = 1.0f; if (INTERNAL_fadeMode != FadeMode.None) { if (INTERNAL_fadeMode == FadeMode.FadeOut) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = ( INTERNAL_fadeEnd - ( INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart ) ) / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc <= 0.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else if (INTERNAL_fadeMode == FadeMode.FadeIn) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = INTERNAL_timer.ElapsedMilliseconds / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc > 1.0f) { fadePerc = 1.0f; INTERNAL_fadeMode = FadeMode.None; } } else if (INTERNAL_fadeMode == FadeMode.ReleaseRpc) { float releasePerc = ( INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart ) / (float)INTERNAL_maxRpcReleaseTime; if (releasePerc > 1.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else { throw new NotImplementedException("Unsupported FadeMode!"); } } // If everything has been played and finished, we're done here. if (INTERNAL_instancePool.Count == 0) { bool allPlayed = true; foreach (XACTClipInstance clipInstance in INTERNAL_activeSound.Clips) { foreach (EventInstance evt in clipInstance.Events) { if (!evt.Played) { allPlayed = false; break; } } } if (allPlayed) { // If this is managed, we're done completely. if (INTERNAL_isManaged) { Dispose(); } else { KillCue(); } if (INTERNAL_userControlledPlaying) { // We're "still" "playing" right now... return(true); } IsStopped = true; return(false); } } // RPC updates float rpcVolume = 0.0f; float rpcPitch = 0.0f; float hfGain = 1.0f; float lfGain = 1.0f; for (int i = 0; i < INTERNAL_activeSound.Sound.RPCCodes.Count; i += 1) { // Are we processing an RPC targeting the sound itself rather than a track? bool isSoundRpc = i == 0 && INTERNAL_activeSound.Sound.HasSoundRpcs; // If there is an RPC targeting the sound instance itself, it is handled in rpcVolume/rpcPitch, and the first track is at i-1. int trackRpcIndex = INTERNAL_activeSound.Sound.HasSoundRpcs ? i - 1 : i; // If this RPC Code is for a track that is not active yet, we have nothing to do. if (trackRpcIndex >= INTERNAL_instancePool.Count) { // FIXME: This presumes that tracks start in order, which doesn't have to be true. break; } if (!isSoundRpc) { INTERNAL_rpcTrackVolumes[trackRpcIndex] = 0.0f; INTERNAL_rpcTrackPitches[trackRpcIndex] = 0.0f; } foreach (uint curCode in INTERNAL_activeSound.Sound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { float variableValue; if (curRPC.Variable.Equals("AttackTime")) { PlayWaveEvent playWaveEvent = (PlayWaveEvent)INTERNAL_activeSound.Sound.INTERNAL_clips[trackRpcIndex].Events[0]; long elapsedFromPlay = INTERNAL_timer.ElapsedMilliseconds - playWaveEvent.Timestamp; variableValue = elapsedFromPlay; } else if (curRPC.Variable.Equals("ReleaseTime")) { if (INTERNAL_fadeMode == FadeMode.ReleaseRpc) { long elapsedFromStop = INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart; variableValue = elapsedFromStop; } else { variableValue = 0.0f; } } else { variableValue = GetVariable(curRPC.Variable); } result = curRPC.CalculateRPC(variableValue); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } if (curRPC.Parameter == RPCParameter.Volume) { // If this RPC targets the sound instance itself then apply to the dedicated variable. if (isSoundRpc) { rpcVolume += result; } else { INTERNAL_rpcTrackVolumes[trackRpcIndex] += result; } } else if (curRPC.Parameter == RPCParameter.Pitch) { float pitch = result; if (isSoundRpc) { rpcPitch += pitch; } else { INTERNAL_rpcTrackPitches[trackRpcIndex] += pitch; } } else if (curRPC.Parameter == RPCParameter.FilterFrequency) { // FIXME: Just listening to the last RPC! float hf = result / 20000.0f; float lf = 1.0f - hf; if (isSoundRpc) { hfGain = hf; lfGain = lf; } else { throw new NotImplementedException("Per-track filter RPCs!"); } } else { throw new NotImplementedException( "RPC Parameter Type: " + curRPC.Parameter.ToString() ); } } } // Sound effect instance updates for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { /* The final volume should be the combination of the * authored volume, category volume, RPC sound/track * volumes, event volumes, and fade. */ INTERNAL_instancePool[i].Volume = XACTCalculator.CalculateAmplitudeRatio( INTERNAL_instanceVolumes[i] + rpcVolume + INTERNAL_rpcTrackVolumes[i] + eventVolume ) * INTERNAL_category.INTERNAL_volume.Value * fadePerc; /* The final pitch should be the combination of the * authored pitch, RPC sound/track pitches, and event * pitch. * * XACT uses -1200 to 1200 (+/- 12 semitones), * XNA uses -1.0f to 1.0f (+/- 1 octave). */ INTERNAL_instancePool[i].Pitch = ( INTERNAL_instancePitches[i] + rpcPitch + INTERNAL_rpcTrackPitches[i] + eventPitch ) / 1200.0f; /* The final filter is determined by the instance's filter type, * in addition to our calculation of the HF/LF gain values. */ byte fType = INTERNAL_instancePool[i].FilterType; if (fType == 0xFF) { // No-op, no filter! } else if (fType == 0) { INTERNAL_instancePool[i].INTERNAL_applyLowPassFilter(hfGain); } else if (fType == 1) { INTERNAL_instancePool[i].INTERNAL_applyHighPassFilter(lfGain); } else if (fType == 2) { INTERNAL_instancePool[i].INTERNAL_applyBandPassFilter(hfGain, lfGain); } else { throw new InvalidOperationException("Unhandled filter type!"); } // Update 3D position, if applicable if (INTERNAL_isPositional) { INTERNAL_instancePool[i].Apply3D( INTERNAL_listener, INTERNAL_emitter ); } } return(true); }