OpenAL sound implementation.
This commit is contained in:
@@ -1,29 +1,35 @@
|
||||
namespace DaggerFramework.Audio
|
||||
{
|
||||
public abstract class AudioBackend
|
||||
public abstract class AudioBackend : IDisposable
|
||||
{
|
||||
public abstract void Initialize();
|
||||
public abstract void Update();
|
||||
public abstract void Shutdown();
|
||||
// BUS
|
||||
public abstract void CreateBus(string busName);
|
||||
public abstract void SetBusVolume(string busName, float volume);
|
||||
public abstract float GetBusVolume(string busName);
|
||||
|
||||
// SOUND
|
||||
protected abstract void PlaySound(Sound sound, string bus = "Master", float pitch = 1.0f, float volume = 1.0f);
|
||||
public void PlaySound(Sound sound, string bus = "Master") => PlaySound(sound, bus, sound.PitchScale, sound.Volume);
|
||||
public void PlaySoundVariation(Sound sound, string bus = "Master", float pitchVariation = 0.1f)
|
||||
public abstract void PlaySound(Sound sound, string bus = "Master", float pitch = 1.0f, float volume = 1.0f);
|
||||
public void PlaySound(Sound sound, string bus = "Master") => PlaySound(sound, bus, default, default);
|
||||
public void PlaySoundVariation(Sound sound, string bus = "Master", float pitchVariation = 0.1f, float volume = 1.0f)
|
||||
{
|
||||
var maxPitch = sound.PitchScale + pitchVariation;
|
||||
var minPitch = sound.PitchScale - pitchVariation;
|
||||
var maxPitch = 1.0f + pitchVariation;
|
||||
var minPitch = 1.0f - pitchVariation;
|
||||
|
||||
var pitch = (float)_random.NextDouble() * (maxPitch - minPitch) + minPitch;
|
||||
PlaySound(sound, bus, pitch, sound.Volume);
|
||||
PlaySound(sound, bus, pitch, volume);
|
||||
}
|
||||
|
||||
// EFFECTS
|
||||
public abstract void AddBusEffect<T>(T effect, string bus = "Master") where T : AudioEffect;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
private LehmerRandom _random = new LehmerRandom();
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,17 @@ namespace DaggerFramework.Audio
|
||||
return;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void PlaySound(Sound sound, string bus = "Master", float pitch = 1, float volume = 1)
|
||||
public override void PlaySound(Sound sound, string bus = "Master", float pitch = 1, float volume = 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
125
Source/Audio/OpenALAudioBackend.cs
Normal file
125
Source/Audio/OpenALAudioBackend.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Silk.NET.OpenAL;
|
||||
|
||||
namespace DaggerFramework.Audio
|
||||
{
|
||||
public class OpenALAudioBackend : AudioBackend
|
||||
{
|
||||
public unsafe override void Initialize()
|
||||
{
|
||||
_alc = ALContext.GetApi();
|
||||
_al = AL.GetApi();
|
||||
_alDevice = _alc.OpenDevice("");
|
||||
|
||||
_context = _alc.CreateContext(_alDevice, null);
|
||||
_alc.MakeContextCurrent(_context);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe override void Shutdown()
|
||||
{
|
||||
_alc.CloseDevice(_alDevice);
|
||||
_al.Dispose();
|
||||
_alc.Dispose();
|
||||
}
|
||||
|
||||
public override void AddBusEffect<T>(T effect, string bus = "Master")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void CreateBus(string busName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetBusVolume(string busName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetBusVolume(string busName, float volume)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void PlaySound(Sound sound, string bus = "Master", float pitch = 1, float volume = 1)
|
||||
{
|
||||
ALSound alSound;
|
||||
|
||||
if (_alSoundsForDaggerSounds.ContainsKey(sound))
|
||||
{
|
||||
alSound = _alSoundsForDaggerSounds[sound];
|
||||
PlayALSound(alSound, pitch);
|
||||
return;
|
||||
}
|
||||
|
||||
alSound = CreateALSound(sound);
|
||||
_alSoundsForDaggerSounds.Add(sound, alSound);
|
||||
|
||||
PlayALSound(alSound, pitch);
|
||||
}
|
||||
|
||||
private void PlayALSound(ALSound sound, float pitch, float volume = 1.0f)
|
||||
{
|
||||
_al.SetSourceProperty(sound.SourceHandle, SourceFloat.Pitch, pitch);
|
||||
// TODO: Add gain.
|
||||
// _al.SetSourceProperty(sound.SourceHandle, SourceFloat.Gain, 0.0f);
|
||||
_al.SourcePlay(sound.SourceHandle);
|
||||
}
|
||||
|
||||
private unsafe ALSound CreateALSound(Sound sound)
|
||||
{
|
||||
ALSound result;
|
||||
uint source = _al.GenSource();
|
||||
uint buffer = _al.GenBuffer();
|
||||
|
||||
fixed (byte* pData = sound.Buffer)
|
||||
{
|
||||
BufferFormat bufferFormat = BufferFormat.Stereo16;
|
||||
|
||||
if (sound.Format == SoundFormat.Mono)
|
||||
{
|
||||
bufferFormat = BufferFormat.Mono16;
|
||||
}
|
||||
else if (sound.Format == SoundFormat.Stereo)
|
||||
{
|
||||
bufferFormat = BufferFormat.Stereo16;
|
||||
}
|
||||
|
||||
_al.BufferData(buffer, bufferFormat, pData, sound.BufferSize, sound.BufferSize);
|
||||
}
|
||||
|
||||
result = new ALSound(buffer, source);
|
||||
|
||||
_al.SetSourceProperty(source, SourceInteger.Buffer, buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void DeleteALSound(ALSound sound)
|
||||
{
|
||||
_al.DeleteSource(sound.SourceHandle);
|
||||
_al.DeleteBuffer(sound.BufferHandle);
|
||||
}
|
||||
|
||||
private Dictionary<Sound, ALSound> _alSoundsForDaggerSounds = new();
|
||||
|
||||
private ALContext _alc;
|
||||
private AL _al;
|
||||
private unsafe Device* _alDevice;
|
||||
private unsafe Context* _context;
|
||||
|
||||
private struct ALSound
|
||||
{
|
||||
public uint BufferHandle { get; set; }
|
||||
public uint SourceHandle { get; set; }
|
||||
public ALSound(uint bufferHandle, uint sourceHandle)
|
||||
{
|
||||
BufferHandle = bufferHandle;
|
||||
SourceHandle = sourceHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,47 @@
|
||||
using StbVorbisSharp;
|
||||
|
||||
namespace DaggerFramework
|
||||
{
|
||||
public class SoundLoader : ResourceLoader<Sound>
|
||||
{
|
||||
public override Sound Load(string path)
|
||||
{
|
||||
// TODO
|
||||
// var data = File.ReadAllBytes(path);
|
||||
var sound = new Sound(path, new byte[] { });
|
||||
sound.Path = path;
|
||||
Vorbis vorbis;
|
||||
Sound result;
|
||||
|
||||
return sound;
|
||||
var fileBuffer = File.ReadAllBytes(path);
|
||||
vorbis = Vorbis.FromMemory(fileBuffer);
|
||||
|
||||
vorbis.SubmitBuffer();
|
||||
|
||||
if (vorbis.Decoded == 0)
|
||||
{
|
||||
vorbis.Restart();
|
||||
vorbis.SubmitBuffer();
|
||||
}
|
||||
|
||||
var audioShort = vorbis.SongBuffer;
|
||||
int length = vorbis.Decoded * vorbis.Channels;
|
||||
byte[] audioData = new byte[length * 2];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (i * 2 >= audioData.Length) break;
|
||||
|
||||
var b1 = (byte)(audioShort[i] >> 8);
|
||||
var b2 = (byte)(audioShort[i] & 256);
|
||||
|
||||
audioData[i * 2] = b2;
|
||||
audioData[i * 2 + 1] = b1;
|
||||
}
|
||||
|
||||
result = new Sound(path, audioData);
|
||||
result.Format = (SoundFormat)vorbis.Channels - 1;
|
||||
result.SampleRate = vorbis.SampleRate;
|
||||
result.BufferSize = length;
|
||||
|
||||
vorbis.Dispose();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,18 @@ namespace DaggerFramework
|
||||
{
|
||||
public class Sound : Resource
|
||||
{
|
||||
public float PitchScale { get; set; } = 1.0f;
|
||||
public float Volume { get; set; } = 1.0f;
|
||||
public SoundFormat Format { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
public Sound(string path, byte[] buffer) : base(path, buffer)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public enum SoundFormat
|
||||
{
|
||||
Mono,
|
||||
Stereo
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user