SoundHandler

Monogame implementation of FMOD
mail@pastecode.io avatar
unknown
csharp
8 days ago
10 kB
27
Indexable
Never
//Code by HalfLucid, free to use for any purpose
/*
Call InitFmod in your initialization method
Call LoadSound for each sound during your load method, loading sounds into some type of permanent/static storage
Call Update() from your update method.

You should also ensure your sounds are manually added to your project and that they are set to copy out to your output directory
You will need to download/install fmod API and copy fmod.dll fmodL.dll and fmodL_vc.lib 
from C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\lib\x86 into your root directory
and set those to copy to output directory also. Then add fmod.cs fmod_dsp.cs, and fmod_errors.cs to your solution also from
C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\inc

In my case I am loading sounds in like this from a static resource manager class from my load method where SoundName is a custom Enum I have created for each sound.


        public static Dictionary<SoundName, FMOD.Sound> SoundEffects = new();
        public static Dictionary<SongName, FMOD.Sound> Songs = new();
        
        private static void InitSoundEffects()
        {
            SoundEffects.Add(SoundName.ButtonAccept, LoadNewSound(@"Content\Sounds\Effects\UI\Menu-Click.wav"));
            SoundEffects.Add(SoundName.ButtonCancel, LoadNewSound(@"Content\Sounds\Effects\UI\Menu-Click.wav"));
            SoundEffects.Add(SoundName.ButtonLeft, LoadNewSound(@"Content\Sounds\Effects\UI\Menu-Click.wav"));
            SoundEffects.Add(SoundName.ButtonRight, LoadNewSound(@"Content\Sounds\Effects\UI\Menu-Click.wav"));
            SoundEffects.Add(SoundName.FormClose, LoadNewSound(@"Content\Sounds\Effects\UI\FormClose.wav"));
            SoundEffects.Add(SoundName.FormOpen, LoadNewSound(@"Content\Sounds\Effects\UI\FormOpen.wav"));
            SoundEffects.Add(SoundName.GamePaused, LoadNewSound(@"Content\Sounds\Effects\UI\GamePaused.wav"));
            SoundEffects.Add(SoundName.GameResumed, LoadNewSound(@"Content\Sounds\Effects\UI\GameUnpaused.wav"));
            SoundEffects.Add(SoundName.LoadGame, LoadNewSound(@"Content\Sounds\Effects\UI\LoadGame.wav"));
        }
        
        
        private static FMOD.Sound LoadNewSound(string soundPath)
        {
            SoundHandler.LoadSound(soundPath, out var tempSound);
            return tempSound;
        }


then you can play a sound as easily as


            SoundHandler.PlaySound(SoundName.GamePaused, startTimeDelay: 3000);
*/


using System;
using System.Collections.Generic;
using System.Linq;
using FMOD;

namespace YourProject
{
    public static class SoundHandler
    {
        private static SoundInstance _currentSong = new();
        private static Dictionary<SoundName, List<SoundInstance>> _playingSounds = new();

        private static FMOD.System _fSystem;
        private static FMOD.Channel _fChannel;
        private static FMOD.ChannelGroup _fChannelGroup;

        public static void InitFmod()
        {
            // Initialize FMOD system
            Factory.System_Create(out _fSystem);
            _fSystem.init(512, INITFLAGS.NORMAL, IntPtr.Zero);
            _fSystem.createChannelGroup(null, out _fChannelGroup);
        }

        public static void LoadSound(string soundPath, out Sound sound)
        {
            // Load a sound with FMOD
            _fSystem.createSound(soundPath, MODE.DEFAULT, out sound);
        }

        public static void PlaySound(SoundName soundName, bool allowMultiple = true, bool loop = false, float volume = 1.0f, double startTimeDelay = 0.0, double fadeInTime = 0.0)
        {
            if (!allowMultiple && _playingSounds.ContainsKey(soundName) && _playingSounds[soundName].Count > 0)
            {
                // If multiple instances are not allowed and the sound is already playing, do nothing
                return;
            }

            _fSystem.playSound(Dictionaries.SoundEffects[soundName], _fChannelGroup, true, out var channel);

            // Set looping
            channel.setMode(loop ? MODE.LOOP_NORMAL : MODE.LOOP_OFF);

            // Track the playing sound
            if (!_playingSounds.ContainsKey(soundName))
            {
                _playingSounds[soundName] = new List<SoundInstance>();
            }

            var instance = CreateSoundInstance(channel, volume, startTimeDelay, fadeInTime);

            _playingSounds[soundName].Add(instance);
        }

        public static void PlaySong(SongName songName, float volume = 1.0f, double startTimeDelay = 0.0, double fadeInTime = 0.0)
        {
            _currentSong.Channel.stop();
            _fSystem.playSound(Dictionaries.Songs[songName], _fChannelGroup, true, out var channel);
            _currentSong = CreateSoundInstance(channel, volume, startTimeDelay, fadeInTime);
        }

        public static void StopSound(SoundName soundName, double fadeOutTimeMilli = 0)
        {
            if (_playingSounds.ContainsKey(soundName))
            {
                foreach (var soundInstance in _playingSounds[soundName])
                {
                    ApplyFadeOutOrStop(soundInstance, fadeOutTimeMilli);
                }
            }
        }

        public static void StopSong(double fadeOutTimeMilli = 0)
        {
            if (_currentSong != null)
            {
                ApplyFadeOutOrStop(_currentSong, fadeOutTimeMilli);
            }
        }

        public static void StopAllSounds()
        {
            foreach (var pair in _playingSounds)
            {
                foreach (var sound in pair.Value)
                {
                    sound.Channel.stop();
                }
            }
            _playingSounds.Clear();
        }

        public static void Update()
        {
            _fSystem.update();
            double currentTimeMilli = Global.TimerMilli;

            UpdatePlayingSounds(currentTimeMilli);
            UpdateCurrentSong(currentTimeMilli);
        }

        private static void UpdatePlayingSounds(double currentTimeMilli)
        {
            foreach (var soundName in _playingSounds.Keys.ToList())
            {
                _playingSounds[soundName].RemoveAll(soundInstance =>
                {
                    return HandleSoundInstance(soundInstance, currentTimeMilli);
                });

                if (_playingSounds[soundName].Count == 0)
                {
                    _playingSounds.Remove(soundName);
                }
            }
        }

        private static void UpdateCurrentSong(double currentTimeMilli)
        {
            if (_currentSong != null)
            {
                if (HandleSoundInstance(_currentSong, currentTimeMilli))
                {
                    _currentSong = null;
                }
            }
        }

        private static SoundInstance CreateSoundInstance(Channel channel, float volume, double startTimeDelay, double fadeInTime)
        {
            double currentTimeMilli = Global.TimerMilli;
            var instance = new SoundInstance
            {
                Channel = channel,
                StartTime = currentTimeMilli + startTimeDelay,
                FadeInTime = fadeInTime,
                TargetVolume = volume,
                FadeStartTime = currentTimeMilli,
                HasPlayed = false
            };

            channel.setVolume(0);
            channel.setPaused(true);

            return instance;
        }

        private static void ApplyFadeOutOrStop(SoundInstance instance, double fadeOutTimeMilli)
        {
            if (fadeOutTimeMilli > Constants.TOLERANCE)
            {
                instance.FadeOutTime = fadeOutTimeMilli;
                instance.FadeStartTime = Global.TimerMilli;
            }
            else
            {
                instance.Channel.stop();
            }
        }

        private static bool HandleSoundInstance(SoundInstance instance, double currentTimeMilli)
        {
            bool isPlaying;
            instance.Channel.isPlaying(out isPlaying);

            if (instance.HasPlayed)
            {
                if (!isPlaying)
                {
                    instance.Channel.stop();
                    return true;
                }

                if (instance.FadeOutTime > Constants.TOLERANCE)
                {
                    return ApplyFadeOut(instance, currentTimeMilli);
                }

                return false;
            }

            if (currentTimeMilli >= instance.StartTime)
            {
                if (instance.FadeInTime > Constants.TOLERANCE)
                {
                    double elapsedTime = currentTimeMilli - instance.StartTime;
                    if (elapsedTime < instance.FadeInTime)
                    {
                        float newVolume = instance.TargetVolume * (float)(elapsedTime / instance.FadeInTime);
                        instance.Channel.setVolume(newVolume);
                    }
                    else
                    {
                        instance.Channel.setVolume(instance.TargetVolume);
                        instance.HasPlayed = true;
                        instance.Channel.setPaused(false);
                    }
                }
                else
                {
                    instance.Channel.setVolume(instance.TargetVolume);
                    instance.HasPlayed = true;
                    instance.Channel.setPaused(false);
                }
            }

            return false;
        }

        private static bool ApplyFadeOut(SoundInstance instance, double currentTimeMilli)
        {
            double elapsedTime = currentTimeMilli - instance.FadeStartTime;
            if (elapsedTime < instance.FadeOutTime)
            {
                float newVolume = instance.TargetVolume * (float)(1.0 - elapsedTime / instance.FadeOutTime);
                instance.Channel.setVolume(newVolume);
                return false;
            }
            return true;
        }
    }

    public class SoundInstance
    {
        public FMOD.Channel Channel { get; set; }
        public double StartTime { get; set; }
        public bool HasPlayed { get; set; }
        public double FadeInTime { get; set; }
        public double FadeOutTime { get; set; }
        public double FadeStartTime { get; set; }
        public float TargetVolume { get; set; }
    }
}
Leave a Comment