using FMOD; using System.Runtime.InteropServices; namespace DaggerFramework.Audio { public class FmodAudioBackend : AudioBackend { public override void Initialize() { CreateSystem(); _loadedSounds = new Dictionary(); _channelGroups = new Dictionary(); CreateBus("Master"); } public override void Update() => _system.update(); public override void PlaySound(Sound sound, string bus = "Master", float pitch = 1, float volume = 1) { int channels = 0; if (sound.Format == SoundFormat.Mono) { channels = 1; } else if (sound.Format == SoundFormat.Stereo) { channels = 2; } var channel = PlaySoundFromBuffer(sound.Path, (int)sound.BufferSize, channels, sound.SampleRate, sound.Buffer, GetChannelGroup(bus)); channel.setVolume(volume); channel.setPitch(pitch); } public override void CreateBus(string busName) { ChannelGroup channelGroup; _system.createChannelGroup(busName, out channelGroup); _channelGroups.Add(busName, channelGroup); } public override void SetBusVolume(string busName, float volume) { var channel = GetChannelGroup(busName); channel.setVolume(volume); } public override float GetBusVolume(string busName) { float volume; GetChannelGroup(busName).getVolume(out volume); return volume; } public override void AddBusEffect(T effect, string bus = "Master") { var channelGroup = GetChannelGroup(bus); DSP dsp; switch (effect) { case AudioEffectReverb: dsp = CreateReverbDsp(effect as AudioEffectReverb); break; default: _system.createDSPByType(DSP_TYPE.UNKNOWN, out dsp); break; } channelGroup.addDSP(0, dsp); } private DSP CreateReverbDsp(AudioEffectReverb effectReverb) { DSP dsp; _system.createDSPByType(DSP_TYPE.SFXREVERB, out dsp); return dsp; } private void CreateSystem() { var result = FMOD.Factory.System_Create(out _system); _system.init(128, INITFLAGS.NORMAL, 0); } private Channel PlaySoundFromBuffer(string path, int length, int channels, int sampleRate, byte[] buffer, ChannelGroup channelGroup) { Channel fmodChannel; FMOD.Sound fmodSound = IsLoaded(path) ? GetSoundFromLoaded(path) : CreateSound(length, channels, sampleRate, path, buffer); _system.playSound(fmodSound, channelGroup, false, out fmodChannel); return fmodChannel; } private FMOD.Sound GetSoundFromLoaded(string path) => _loadedSounds[path]; private bool IsLoaded(string path) => _loadedSounds.ContainsKey(path); private FMOD.Sound CreateSound(int length, int channels, int sampleRate, string path, byte[] buffer) { FMOD.Sound sound; CREATESOUNDEXINFO info = new CREATESOUNDEXINFO() { numchannels = channels, defaultfrequency = sampleRate, format = SOUND_FORMAT.PCM16, length = (uint)length, cbsize = Marshal.SizeOf(typeof(CREATESOUNDEXINFO)) }; var result = _system.createSound(buffer, FMOD.MODE.OPENMEMORY | FMOD.MODE.OPENRAW | FMOD.MODE.CREATESAMPLE, ref info, out sound); AddToLoaded(path, sound); return sound; } private void AddToLoaded(string path, FMOD.Sound sound) => _loadedSounds.Add(path, sound); private ChannelGroup GetChannelGroup(string busName) { return _channelGroups[busName]; } public override void Shutdown() { throw new NotImplementedException(); } private FMOD.System _system; // TODO: use a different key for the dictionary, paths are not good :( (waste of memory lol) private Dictionary _loadedSounds; private Dictionary _channelGroups; } }