aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Audio/Backends/Common
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Audio/Backends/Common')
-rw-r--r--src/Ryujinx.Audio/Backends/Common/BackendHelper.cs26
-rw-r--r--src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs166
-rw-r--r--src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs79
3 files changed, 271 insertions, 0 deletions
diff --git a/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs b/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs
new file mode 100644
index 00000000..30db340f
--- /dev/null
+++ b/src/Ryujinx.Audio/Backends/Common/BackendHelper.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Audio.Common;
+using System;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ public static class BackendHelper
+ {
+ public static int GetSampleSize(SampleFormat format)
+ {
+ return format switch
+ {
+ SampleFormat.PcmInt8 => sizeof(byte),
+ SampleFormat.PcmInt16 => sizeof(ushort),
+ SampleFormat.PcmInt24 => 3,
+ SampleFormat.PcmInt32 => sizeof(int),
+ SampleFormat.PcmFloat => sizeof(float),
+ _ => throw new ArgumentException($"{format}"),
+ };
+ }
+
+ public static int GetSampleCount(SampleFormat format, int channelCount, int bufferSize)
+ {
+ return bufferSize / GetSampleSize(format) / channelCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
new file mode 100644
index 00000000..9bf20d4b
--- /dev/null
+++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
@@ -0,0 +1,166 @@
+using Ryujinx.Common;
+using System;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ /// <summary>
+ /// A ring buffer that grow if data written to it is too big to fit.
+ /// </summary>
+ public class DynamicRingBuffer
+ {
+ private const int RingBufferAlignment = 2048;
+
+ private object _lock = new object();
+
+ private byte[] _buffer;
+ private int _size;
+ private int _headOffset;
+ private int _tailOffset;
+
+ public int Length => _size;
+
+ public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
+ {
+ _buffer = new byte[initialCapacity];
+ }
+
+ public void Clear()
+ {
+ _size = 0;
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+
+ public void Clear(int size)
+ {
+ lock (_lock)
+ {
+ if (size > _size)
+ {
+ size = _size;
+ }
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ _headOffset = (_headOffset + size) % _buffer.Length;
+ _size -= size;
+
+ if (_size == 0)
+ {
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+ }
+ }
+
+ private void SetCapacityLocked(int capacity)
+ {
+ byte[] buffer = new byte[capacity];
+
+ if (_size > 0)
+ {
+ if (_headOffset < _tailOffset)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _size);
+ }
+ else
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _buffer.Length - _headOffset);
+ Buffer.BlockCopy(_buffer, 0, buffer, _buffer.Length - _headOffset, _tailOffset);
+ }
+ }
+
+ _buffer = buffer;
+ _headOffset = 0;
+ _tailOffset = _size;
+ }
+
+
+ public void Write<T>(T[] buffer, int index, int count)
+ {
+ if (count == 0)
+ {
+ return;
+ }
+
+ lock (_lock)
+ {
+ if ((_size + count) > _buffer.Length)
+ {
+ SetCapacityLocked(BitUtils.AlignUp(_size + count, RingBufferAlignment));
+ }
+
+ if (_headOffset < _tailOffset)
+ {
+ int tailLength = _buffer.Length - _tailOffset;
+
+ if (tailLength >= count)
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ }
+ else
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, tailLength);
+ Buffer.BlockCopy(buffer, index + tailLength, _buffer, 0, count - tailLength);
+ }
+ }
+ else
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ }
+
+ _size += count;
+ _tailOffset = (_tailOffset + count) % _buffer.Length;
+ }
+ }
+
+ public int Read<T>(T[] buffer, int index, int count)
+ {
+ lock (_lock)
+ {
+ if (count > _size)
+ {
+ count = _size;
+ }
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (_headOffset < _tailOffset)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ }
+ else
+ {
+ int tailLength = _buffer.Length - _headOffset;
+
+ if (tailLength >= count)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ }
+ else
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, tailLength);
+ Buffer.BlockCopy(_buffer, 0, buffer, index + tailLength, count - tailLength);
+ }
+ }
+
+ _size -= count;
+ _headOffset = (_headOffset + count) % _buffer.Length;
+
+ if (_size == 0)
+ {
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+
+ return count;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
new file mode 100644
index 00000000..6fb3bee0
--- /dev/null
+++ b/src/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
@@ -0,0 +1,79 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Memory;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ public abstract class HardwareDeviceSessionOutputBase : IHardwareDeviceSession
+ {
+ public IVirtualMemoryManager MemoryManager { get; }
+ public SampleFormat RequestedSampleFormat { get; }
+ public uint RequestedSampleRate { get; }
+ public uint RequestedChannelCount { get; }
+
+ public HardwareDeviceSessionOutputBase(IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
+ {
+ MemoryManager = memoryManager;
+ RequestedSampleFormat = requestedSampleFormat;
+ RequestedSampleRate = requestedSampleRate;
+ RequestedChannelCount = requestedChannelCount;
+ }
+
+ private byte[] GetBufferSamples(AudioBuffer buffer)
+ {
+ if (buffer.DataPointer == 0)
+ {
+ return null;
+ }
+
+ byte[] data = new byte[buffer.DataSize];
+
+ MemoryManager.Read(buffer.DataPointer, data);
+
+ return data;
+ }
+
+ protected ulong GetSampleCount(AudioBuffer buffer)
+ {
+ return GetSampleCount((int)buffer.DataSize);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected ulong GetSampleCount(int dataSize)
+ {
+ return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, dataSize);
+ }
+
+ public abstract void Dispose();
+ public abstract void PrepareToClose();
+ public abstract void QueueBuffer(AudioBuffer buffer);
+ public abstract void SetVolume(float volume);
+ public abstract float GetVolume();
+ public abstract void Start();
+ public abstract void Stop();
+ public abstract ulong GetPlayedSampleCount();
+ public abstract bool WasBufferFullyConsumed(AudioBuffer buffer);
+ public virtual bool RegisterBuffer(AudioBuffer buffer)
+ {
+ return RegisterBuffer(buffer, GetBufferSamples(buffer));
+ }
+
+ public virtual bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
+ {
+ if (samples == null)
+ {
+ return false;
+ }
+
+ if (buffer.Data == null)
+ {
+ buffer.Data = samples;
+ }
+
+ return true;
+ }
+
+ public virtual void UnregisterBuffer(AudioBuffer buffer) { }
+ }
+} \ No newline at end of file