OpenAL sound implementation.

This commit is contained in:
2023-06-17 00:56:17 +02:00
parent 7d5c5f822b
commit 36eae40926
9 changed files with 211 additions and 30 deletions

View File

@@ -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>

View File

@@ -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

View File

@@ -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();
} }
} }

View File

@@ -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;
} }

View 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;
}
}
}
}

View File

@@ -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;
} }
} }
} }

View File

@@ -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
}
} }

Binary file not shown.

View File

@@ -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;
} }