예제 #1
0
        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);
        }