예제 #1
0
        /// <returns>Returns true if this ambient sound is playable</returns>
        public static bool GetPlaybackInfoFor(AnimationSet animationSet, Position position, bool facingLeft, int radius, float volume, float pitch, float pan, Camera camera, IGameState gameState, int localPlayerBits, out FadePitchPan fadePitchPan)
        {
            //
            // Global ambient audio
            //

            if (radius < 0)
            {
                fadePitchPan = new FadePitchPan(1f);
                return(true);
            }


            //
            // Nominal audio position:
            //

            fadePitchPan        = new FadePitchPan(camera.WorldToAudio(position));
            fadePitchPan.fade  *= volume;
            fadePitchPan.pitch *= pitch;
            fadePitchPan.pan   *= pan;

            if (fadePitchPan.fade < 0.001f) // Far off-camera
            {
                fadePitchPan.fade = 0;
                return(false);
            }


            //
            // Distance to a listening player:
            //

            if (localPlayerBits == 0) // No one to listen
            {
                fadePitchPan.fade = 0;
                return(false);
            }

            int distanceSquared = GetDistanceSquaredToLocalPlayer(animationSet, position, facingLeft, gameState, localPlayerBits);

            if (distanceSquared > radius * radius)
            {
                fadePitchPan.fade = 0;
                return(false);
            }


            //
            // Modulate by listening player distance and apply settings:
            //

            // Trying linear fade-out here
            float listenerFade = MathHelper.Clamp(1f - (float)Math.Sqrt(distanceSquared) / (float)radius, 0f, 1f);

            fadePitchPan.fade *= listenerFade;

            return(true);
        }
예제 #2
0
        public static void TriedToPlayMissingCue(FadePitchPan fpp)
        {
            SafeSoundEffect sound;

            lock (lockObject)
                sound = missingSoundEffect;

            if (sound != null)
            {
                sound.Play(fpp.fade, fpp.pitch, fpp.pan);
            }
        }
예제 #3
0
        /// <summary>Call this only when PlayCueParameters have been validated</summary>
        public static void PlayCueSkipMissingCheck(IAudioDefinitions definitions, Cue cue, PlayCueParameters parameters,
                                                   FadePitchPan fpp, bool loop = false)
        {
            fpp.fade  = AudioMath.Clamp(fpp.fade * cue.volume, 0, 1);
            fpp.pitch = AudioMath.Clamp(fpp.pitch + parameters.cuePitch, -1, 1);
            fpp.pan   = AudioMath.Clamp(fpp.pan + cue.pan, -1, 1);

            if (fpp.fade < 0.01f)
            {
                return;                 // too quiet to care about
            }
            if (cue.SoundCount == 0)
            {
                return;                 // <- nothing to play
            }
            lock (lockObject)
            {
                switch (cue.type)
                {
                case CueType.Parallel:
                {
                    for (var i = 0; i < cue.SoundCount; i++)
                    {
                        AddAndStartPlaying(definitions.GetSound(cue, i), fpp, loop);
                    }
                }
                break;

                case CueType.Serial:
                {
                    // Build the queue for the cue:
                    var queueHead = -1;
                    for (var i = cue.SoundCount - 1; i >= 1; i--)
                    {
                        var q = AllocateQueuedSound();
                        queuedSounds[q].sound = definitions.GetSound(cue, i);
                        queuedSounds[q].next  = queueHead;
                        queueHead             = q;
                    }

                    AddAndStartPlaying(definitions.GetSound(cue, 0), fpp, loop, queueHead);
                }
                break;

                default:
                {
                    AddAndStartPlaying(definitions.GetSound(cue, parameters.soundIndex), fpp, loop);
                }
                break;
                }
            }
        }
예제 #4
0
        public static void PlayCue(IAudioDefinitions definitions, Cue cue, PlayCueParameters parameters,
                                   FadePitchPan fpp, bool loop = false)
        {
            if (parameters.soundIndex == PlayCueParameters.MISSING_CUE)
            {
                MissingAudio.TriedToPlayMissingCue(fpp);
                return;
            }

            if (parameters.soundIndex < 0)
            {
                return;
            }

            PlayCueSkipMissingCheck(definitions, cue, parameters, fpp, loop);
        }
예제 #5
0
        public void PlayCueSkipMissingCheck(IAudioDefinitions definitions, Cue cue, Position?worldPosition,
                                            FadePitchPan fpp, PlayCueParameters parameters, bool playsLocally)
        {
            if (parameters.soundIndex < 0)
            {
                return;
            }

            if (!playsLocally || !AudioDevice.Available)
            {
                return;                                       // <- nothing to do!
            }
            if (doingPrediction)                              // Re-prediction following rollback
            {
                if (activeFrame >= liveFrame - DontCareLimit) // <- new enough that we could still be tracking it
                {
                    if (!TryKillCueExact(cue, activeFrame, worldPosition))
                    {
                        if (activeFrame >= liveFrame - MaximumSoundShift)                         // <- new enough that we may play it
                        {
                            PendingCue pending;
                            pending.cue        = cue;
                            pending.parameters = parameters;
                            pending.frame      = activeFrame;
                            pending.position   = worldPosition;
                            pending.fpp        = fpp;

                            pendingCues.Add(pending);
                        }
                    }
                }
            }
            else             // Standard playback
            {
                Debug.Assert(activeFrame == liveFrame);

                if (!rollbackAware || !TryKillCueFuzzy(cue, activeFrame, worldPosition))
                {
                    if (!doingStartupPrediction)
                    {
                        SoundEffectManager.PlayCueSkipMissingCheck(definitions, cue, parameters, fpp);
                    }
                    AddLiveCueNow(cue, worldPosition);
                }
            }
        }
예제 #6
0
        private static void AddAndStartPlaying(SafeSoundEffect sound, FadePitchPan fpp, bool loop = false, int queue = -1)
        {
            Debug.Assert(playingSoundCount <= playingSounds.Length);
            if (playingSoundCount == playingSounds.Length)
            {
                Array.Resize(ref playingSounds, playingSounds.Length * 2);
            }

            Debug.Assert(playingSounds[playingSoundCount].sound == null);    // <- got cleared properly
            Debug.Assert(playingSounds[playingSoundCount].instance == null); // <- got cleared properly
            Debug.Assert(playingSounds[playingSoundCount].frameCount == 0);  // <- got cleared properly


            // If we are about to play multiple identical sounds at about the same time, stop them from overlapping:
            float quashFade = 1f;

            for (int i = playingSoundCount - 1; i >= 0; i--)
            {
                if (playingSounds[i].frameCount >= 3)
                {
                    break; // <- Reaching sounds that are too old
                }
                if (ReferenceEquals(playingSounds[i].sound, sound))
                {
                    quashFade -= (1f - ((float)playingSounds[i].frameCount / 3f));
                }
            }
            // TODO: The following is ugly, because it kills sequential sounds (but odds are they would be killed anyway - and because we just use `fpp.fade`, below, they'd get killed anyway)
            // If a sound would be quashed completely, just don't play it -- this is required because otherwise the quashed sounds would be taking up simulated channels
            if (quashFade < 0.1f)
            {
                while (queue != -1) // Don't leak the queue, if any
                {
                    queue = FreeQueuedSound(queue);
                }
                return;
            }
            // TODO: This is ugly because sequential sounds will inherit this fade level
            fpp.fade *= MathHelper.Clamp(quashFade, 0f, 1f);



            if (loop)
            {
                playingSounds[playingSoundCount].instance = sound.CreateInstance();
            }
            else
            {
                playingSounds[playingSoundCount].instance = sound.SoundEffectManager_GetInstance();
            }

            if (playingSounds[playingSoundCount].instance == null)
            {
                while (queue != -1) // Don't leak the queue, if any
                {
                    queue = FreeQueuedSound(queue);
                }
                return; // Failed to create sound instance... oh well.
            }

            Debug.Assert(playingSounds[playingSoundCount].instance.IsLooped == false); // <- instance was properly cleared
            if (loop)                                                                  // <- Cannot set on used instances (even to the same value)
            {
                playingSounds[playingSoundCount].instance.IsLooped = true;
            }

            playingSounds[playingSoundCount].sound      = sound;
            playingSounds[playingSoundCount].fpp        = fpp;
            playingSounds[playingSoundCount].queue      = queue;
            playingSounds[playingSoundCount].linkToNext = true; // <- all sounds on a given frame get linked!
            playingSounds[playingSoundCount].fade       = 1f;   // <- NOTE: assumed by channel ducking code
            playingSounds[playingSoundCount].frameCount = 0;

            fpp.ApplyTo(playingSounds[playingSoundCount].instance, SafeSoundEffect.SoundEffectVolume);
            playingSounds[playingSoundCount].instance.Play();

            playingSoundCount++;
        }
예제 #7
0
        /// <param name="random">IMPORTANT: This may be part of the game state</param>
        /// <param name="cueStates">IMPORTANT: This may be part of the game state</param>
        /// <param name="playLocally">
        ///     True if this sound should be played, false if the sound is for a remote player (not for us).
        ///     Allows for local-only UI sounds.
        /// </param>
        public void PlayCue(IAudioDefinitions definitions, Cue cue, Position?worldPosition, FadePitchPan fpp,
                            PlayCueParameters parameters, bool playsLocally)
        {
            if (parameters.soundIndex == PlayCueParameters.MISSING_CUE)
            {
                if (!doingPrediction
                    )             // <- poor man's "first time simulated" (Missing music doesn't get rollback-aware sound handling)
                {
                    MissingAudio.TriedToPlayMissingCue(fpp);
                }
                return;
            }

            PlayCueSkipMissingCheck(definitions, cue, worldPosition, fpp, parameters, playsLocally);
        }
예제 #8
0
 public bool Play(FadePitchPan fpp)
 {
     return(inner != null && inner.Play(fpp.fade * SoundEffectVolume, fpp.pitch, fpp.pan));
 }
예제 #9
0
 public bool Play(FadePitchPan fpp)
 {
     return(soundEffect != null && soundEffect.Play(fpp.fade * _sfxVolume, fpp.pitch, fpp.pan));
 }