OpenAL sound implementation.
This commit is contained in:
@@ -10,5 +10,6 @@
|
|||||||
<PackageReference Include="Raylib-cs" Version="4.2.0.1" />
|
<PackageReference Include="Raylib-cs" Version="4.2.0.1" />
|
||||||
<PackageReference Include="Silk.NET" Version="2.17.0" />
|
<PackageReference Include="Silk.NET" Version="2.17.0" />
|
||||||
<PackageReference Include="StbImageSharp" Version="2.27.13" />
|
<PackageReference Include="StbImageSharp" Version="2.27.13" />
|
||||||
|
<PackageReference Include="StbVorbisSharp" Version="1.22.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.0.31903.59
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,29 +1,35 @@
|
|||||||
namespace DaggerFramework.Audio
|
namespace DaggerFramework.Audio
|
||||||
{
|
{
|
||||||
public abstract class AudioBackend
|
public abstract class AudioBackend : IDisposable
|
||||||
{
|
{
|
||||||
public abstract void Initialize();
|
public abstract void Initialize();
|
||||||
public abstract void Update();
|
public abstract void Update();
|
||||||
|
public abstract void Shutdown();
|
||||||
// BUS
|
// BUS
|
||||||
public abstract void CreateBus(string busName);
|
public abstract void CreateBus(string busName);
|
||||||
public abstract void SetBusVolume(string busName, float volume);
|
public abstract void SetBusVolume(string busName, float volume);
|
||||||
public abstract float GetBusVolume(string busName);
|
public abstract float GetBusVolume(string busName);
|
||||||
|
|
||||||
// SOUND
|
// SOUND
|
||||||
protected abstract void PlaySound(Sound sound, string bus = "Master", float pitch = 1.0f, float volume = 1.0f);
|
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, sound.PitchScale, sound.Volume);
|
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)
|
public void PlaySoundVariation(Sound sound, string bus = "Master", float pitchVariation = 0.1f, float volume = 1.0f)
|
||||||
{
|
{
|
||||||
var maxPitch = sound.PitchScale + pitchVariation;
|
var maxPitch = 1.0f + pitchVariation;
|
||||||
var minPitch = sound.PitchScale - pitchVariation;
|
var minPitch = 1.0f - pitchVariation;
|
||||||
|
|
||||||
var pitch = (float)_random.NextDouble() * (maxPitch - minPitch) + minPitch;
|
var pitch = (float)_random.NextDouble() * (maxPitch - minPitch) + minPitch;
|
||||||
PlaySound(sound, bus, pitch, sound.Volume);
|
PlaySound(sound, bus, pitch, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EFFECTS
|
// EFFECTS
|
||||||
public abstract void AddBusEffect<T>(T effect, string bus = "Master") where T : AudioEffect;
|
public abstract void AddBusEffect<T>(T effect, string bus = "Master") where T : AudioEffect;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
private LehmerRandom _random = new LehmerRandom();
|
private LehmerRandom _random = new LehmerRandom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,12 +27,17 @@ namespace DaggerFramework.Audio
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
return;
|
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;
|
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
|
namespace DaggerFramework
|
||||||
{
|
{
|
||||||
public class SoundLoader : ResourceLoader<Sound>
|
public class SoundLoader : ResourceLoader<Sound>
|
||||||
{
|
{
|
||||||
public override Sound Load(string path)
|
public override Sound Load(string path)
|
||||||
{
|
{
|
||||||
// TODO
|
Vorbis vorbis;
|
||||||
// var data = File.ReadAllBytes(path);
|
Sound result;
|
||||||
var sound = new Sound(path, new byte[] { });
|
|
||||||
sound.Path = path;
|
|
||||||
|
|
||||||
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 class Sound : Resource
|
||||||
{
|
{
|
||||||
public float PitchScale { get; set; } = 1.0f;
|
public SoundFormat Format { get; set; }
|
||||||
public float Volume { get; set; } = 1.0f;
|
public int SampleRate { get; set; }
|
||||||
|
public int BufferSize { get; set; }
|
||||||
|
|
||||||
public Sound(string path, byte[] buffer) : base(path, buffer)
|
public Sound(string path, byte[] buffer) : base(path, buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SoundFormat
|
||||||
|
{
|
||||||
|
Mono,
|
||||||
|
Stereo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BIN
TestGame/Resources/sounds/test_sound.ogg
Normal file
BIN
TestGame/Resources/sounds/test_sound.ogg
Normal file
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
using DaggerFramework;
|
using DaggerFramework;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using DaggerFramework.Rendering;
|
using DaggerFramework.Rendering;
|
||||||
|
using DaggerFramework.Audio;
|
||||||
|
|
||||||
public class TestGame : Game
|
public class TestGame : Game
|
||||||
{
|
{
|
||||||
@@ -8,18 +9,26 @@ public class TestGame : Game
|
|||||||
protected override void OnStart()
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
_renderer = new RaylibRenderer();
|
_renderer = new RaylibRenderer();
|
||||||
|
_audioBackend = new OpenALAudioBackend();
|
||||||
|
_inputHandler = new RaylibInputHandler();
|
||||||
|
|
||||||
_renderer.CreateAndInitialize(new WindowSettings()
|
_renderer.CreateAndInitialize(new WindowSettings()
|
||||||
{
|
{
|
||||||
Title = "Test Game",
|
Title = "Test Game",
|
||||||
Size = new Vector2(1280, 720)
|
Size = new Vector2(1280, 720)
|
||||||
}, RendererSettings.Default);
|
}, RendererSettings.Default);
|
||||||
|
_renderer.SetTargetFps(60);
|
||||||
|
|
||||||
|
_audioBackend.Initialize();
|
||||||
|
|
||||||
|
_inputHandler.AddInputMapping("play", new InputAction[] { new KeyInputAction(KeyboardKey.Spacebar) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void LoadResources()
|
protected override void LoadResources()
|
||||||
{
|
{
|
||||||
|
_soundLoader = new SoundLoader();
|
||||||
|
_testSound = _soundLoader.Load($"{ResourceRoot}sounds/test_sound.ogg");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void MainLoop()
|
protected override void MainLoop()
|
||||||
@@ -28,6 +37,12 @@ public class TestGame : Game
|
|||||||
{
|
{
|
||||||
_renderer.BeginFrame();
|
_renderer.BeginFrame();
|
||||||
_renderer.ClearBackground(Color.Black);
|
_renderer.ClearBackground(Color.Black);
|
||||||
|
|
||||||
|
if (_inputHandler.IsActionJustPressed("play"))
|
||||||
|
{
|
||||||
|
_audioBackend.PlaySoundVariation(_testSound, default, 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
_renderer.SetTransform(new Vector2(640, 480));
|
_renderer.SetTransform(new Vector2(640, 480));
|
||||||
_renderer.DrawCircle(16f, Color.Chocolate);
|
_renderer.DrawCircle(16f, Color.Chocolate);
|
||||||
_renderer.EndFrame();
|
_renderer.EndFrame();
|
||||||
@@ -39,4 +54,8 @@ public class TestGame : Game
|
|||||||
_renderer.Shutdown();
|
_renderer.Shutdown();
|
||||||
}
|
}
|
||||||
private Renderer _renderer;
|
private Renderer _renderer;
|
||||||
|
private SoundLoader _soundLoader;
|
||||||
|
private Sound _testSound;
|
||||||
|
private OpenALAudioBackend _audioBackend;
|
||||||
|
private InputHandler _inputHandler;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user