Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio Buffers Freeing Up #5562

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions Robust.Client/Audio/AudioManager.Public.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Robust.Client.Audio;
internal partial class AudioManager
{
private float _zOffset;
private long _generatedBuffers;

public void SetZOffset(float offset)
{
Expand Down Expand Up @@ -89,8 +90,6 @@ public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
var vorbis = AudioLoaderOgg.LoadAudioData(stream);

var buffer = AL.GenBuffer();

ALFormat format;
// NVorbis only supports loading into floats.
// If this becomes a problem due to missing extension support (doubt it but ok),
Expand All @@ -108,19 +107,12 @@ public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
throw new InvalidOperationException("Unable to load audio with more than 2 channels.");
}

unsafe
{
fixed (short* ptr = vorbis.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(short),
(int) vorbis.SampleRate);
}
}

_checkAlError();

var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var handle = new ClydeHandle(_generatedBuffers++);
MakeVorbisCast(handle,
format,
vorbis.Data.Span.ToArray(),
vorbis.Data.Length * sizeof(short),
(int) vorbis.SampleRate);
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist);
}
Expand All @@ -130,8 +122,6 @@ public AudioStream LoadAudioWav(Stream stream, string? name = null)
{
var wav = AudioLoaderWav.LoadAudioData(stream);

var buffer = AL.GenBuffer();

ALFormat format;
if (wav.BitsPerSample == 16)
{
Expand Down Expand Up @@ -168,18 +158,24 @@ public AudioStream LoadAudioWav(Stream stream, string? name = null)
throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16");
}

var buffer = new short[wav.Data.Length / sizeof(short)];

unsafe
{
fixed (byte* ptr = wav.Data.Span)
fixed (void* sourcePtr = wav.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate);
fixed (void* destinyPtr = buffer)
{
Buffer.MemoryCopy(sourcePtr,
destinyPtr,
wav.Data.Length,
wav.Data.Length);
}
}
}

_checkAlError();

var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
var handle = new ClydeHandle(_generatedBuffers++);
MakeVorbisCast(handle, format, buffer, wav.Data.Length, wav.SampleRate);
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
return new AudioStream(handle, length, wav.NumChannels, name);
}
Expand All @@ -192,7 +188,8 @@ public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int s
1 => ALFormat.Mono16,
2 => ALFormat.Stereo16,
_ => throw new ArgumentOutOfRangeException(
nameof(channels), "Only stereo and mono is currently supported")
nameof(channels),
"Only stereo and mono is currently supported")
};

var buffer = AL.GenBuffer();
Expand All @@ -208,9 +205,10 @@ public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int s

_checkAlError();

var handle = new ClydeHandle(_audioSampleBuffers.Count);

var handle = new ClydeHandle(_generatedBuffers++);
MakeVorbisCast(handle, fmt, samples.ToArray(), samples.Length * sizeof(short), sampleRate);
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
return new AudioStream(handle, length, channels, name);
}

Expand Down Expand Up @@ -292,8 +290,10 @@ internal void RemoveBufferedAudioSource(int handle)
}

// ReSharper disable once PossibleInvalidOperationException
// TODO: This really shouldn't be indexing based on the ClydeHandle...
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value].BufferHandle);
var audioSample = EnsureAudioSample(stream.ClydeHandle!);

AL.Source(source, ALSourcei.Buffer, audioSample!.BufferHandle);
audioSample.IncreaseUsings();

var audioSource = new AudioSource(this, source, stream);
_audioSources.Add(source, new WeakReference<BaseAudioSource>(audioSource));
Expand Down
82 changes: 82 additions & 0 deletions Robust.Client/Audio/AudioManager.Reloader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using OpenTK.Audio.OpenAL;
using Robust.Shared.Graphics;

namespace Robust.Client.Audio;

internal sealed partial class AudioManager
{
private readonly Dictionary<IClydeHandle, AudioVorbisCast> _audioVorbisCasts = new();
private readonly Dictionary<IClydeHandle, LoadedAudioSample> _loadedAudioSamples = new();

public void NotifySourceDisposed(AudioStream sourceStream)
{
OpenALSawmill.Debug($"Ended a source life with a {sourceStream.ClydeHandle} handle.");
var audioSample = _loadedAudioSamples[sourceStream.ClydeHandle!];
audioSample.DecreaseUsings();

if (!audioSample.IsSafeToDelete())
return;

OpenALSawmill.Debug($"Enqueued {sourceStream.ClydeHandle} handle's buffer to free up.");
_loadedAudioSamples.Remove(sourceStream.ClydeHandle!);
_bufferDisposeQueue.Enqueue(audioSample.BufferHandle);
}

internal AudioVorbisCast MakeVorbisCast(IClydeHandle handle, ALFormat format, short[] buffer, int size, int sampleRate)
{
var vorbisCast = new AudioVorbisCast(format, buffer, size, sampleRate);
_audioVorbisCasts.Add(handle, vorbisCast);
return vorbisCast;
}

internal LoadedAudioSample? EnsureAudioSample(IClydeHandle handle)
{
if (_loadedAudioSamples.TryGetValue(handle, out var value))
{
return value;
}

var buffer = AL.GenBuffer();

_checkAlError();

if (!_audioVorbisCasts.TryGetValue(handle, out var vorbis))
{
OpenALSawmill.Error($"Could not find audio cast for {handle}.");
return null;
}

unsafe
{
fixed (short* prt = vorbis.Buffer)
{
AL.BufferData(buffer, vorbis.Format, (IntPtr) prt, vorbis.Size, vorbis.SampleRate);
}
}

_checkAlError();

var sample = new LoadedAudioSample(buffer);
_loadedAudioSamples.Add(handle, sample);

return sample;
}

internal readonly struct AudioVorbisCast
{
public readonly ALFormat Format;
public readonly short[] Buffer;
public readonly int Size;
public readonly int SampleRate;

public AudioVorbisCast(ALFormat format, short[] buffer, int size, int sampleRate)
{
Format = format;
Buffer = buffer;
Size = size;
SampleRate = sampleRate;
}
}
}
29 changes: 23 additions & 6 deletions Robust.Client/Audio/AudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
using Robust.Shared;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Graphics;
using Robust.Shared.Log;
using Robust.Shared.Utility;

namespace Robust.Client.Audio;

Expand All @@ -23,8 +23,6 @@ internal sealed partial class AudioManager : IAudioInternal
private ALDevice _openALDevice;
private ALContext _openALContext;

private readonly List<LoadedAudioSample> _audioSampleBuffers = new();

private readonly Dictionary<int, WeakReference<BaseAudioSource>> _audioSources =
new();

Expand Down Expand Up @@ -76,7 +74,8 @@ private bool _audioOpenDevice()
if (_openALDevice == IntPtr.Zero)
{
OpenALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
preferredDevice, ALC.GetError(ALDevice.Null));
preferredDevice,
ALC.GetError(ALDevice.Null));

_openALDevice = ALC.OpenDevice(null);
}
Expand Down Expand Up @@ -129,7 +128,8 @@ private static void RemoveEfx((int sourceHandle, int filterHandle) handles)
EFX.DeleteFilter(handles.filterHandle);
}

private void _checkAlcError(ALDevice device,
private void _checkAlcError(
ALDevice device,
[CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
Expand Down Expand Up @@ -160,13 +160,30 @@ public void _checkAlError([CallerMemberName] string callerMember = "", [CallerLi
}
}

private sealed class LoadedAudioSample
internal sealed class LoadedAudioSample
{
public readonly int BufferHandle;
private int UsingsCount { set; get; }

public LoadedAudioSample(int bufferHandle)
{
BufferHandle = bufferHandle;
UsingsCount = 0;
}

public void IncreaseUsings()
{
UsingsCount++;
}

public void DecreaseUsings()
{
UsingsCount--;
}

public bool IsSafeToDelete()
{
return UsingsCount <= 0;
}
}

Expand Down
4 changes: 2 additions & 2 deletions Robust.Client/Audio/Sources/AudioSource.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Numerics;
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Shared.Audio;
using Robust.Shared.Maths;
using Robust.Shared.Utility;

Expand Down Expand Up @@ -84,6 +82,8 @@ protected override void Dispose(bool disposing)
Master._checkAlError();
}

Master.NotifySourceDisposed(_sourceStream);

FilterHandle = 0;
SourceHandle = -1;
}
Expand Down
Loading