aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio/Renderers
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Audio/Renderers')
-rw-r--r--Ryujinx.Audio/Renderers/DummyAudioOut.cs109
-rw-r--r--Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs404
-rw-r--r--Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs183
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs361
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs646
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs193
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs29
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs204
8 files changed, 0 insertions, 2129 deletions
diff --git a/Ryujinx.Audio/Renderers/DummyAudioOut.cs b/Ryujinx.Audio/Renderers/DummyAudioOut.cs
deleted file mode 100644
index cd197592..00000000
--- a/Ryujinx.Audio/Renderers/DummyAudioOut.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// A Dummy audio renderer that does not output any audio
- /// </summary>
- public class DummyAudioOut : IAalOutput
- {
- private int _lastTrackId = 1;
- private float _volume = 1.0f;
-
- private ConcurrentQueue<int> _trackIds;
- private ConcurrentQueue<long> _buffers;
- private ConcurrentDictionary<int, ReleaseCallback> _releaseCallbacks;
- private ulong _playedSampleCount;
-
- public DummyAudioOut()
- {
- _buffers = new ConcurrentQueue<long>();
- _trackIds = new ConcurrentQueue<int>();
- _releaseCallbacks = new ConcurrentDictionary<int, ReleaseCallback>();
- }
-
- /// <summary>
- /// Dummy audio output is always available, Baka!
- /// </summary>
- public static bool IsSupported => true;
-
- public PlaybackState GetState(int trackId) => PlaybackState.Stopped;
-
- public bool SupportsChannelCount(int channels)
- {
- return true;
- }
-
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- if (!_trackIds.TryDequeue(out int trackId))
- {
- trackId = ++_lastTrackId;
- }
-
- _releaseCallbacks[trackId] = callback;
-
- return trackId;
- }
-
- public void CloseTrack(int trackId)
- {
- _trackIds.Enqueue(trackId);
- _releaseCallbacks.Remove(trackId, out _);
- }
-
- public bool ContainsBuffer(int trackID, long bufferTag) => false;
-
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- List<long> bufferTags = new List<long>();
-
- for (int i = 0; i < maxCount; i++)
- {
- if (!_buffers.TryDequeue(out long tag))
- {
- break;
- }
-
- bufferTags.Add(tag);
- }
-
- return bufferTags.ToArray();
- }
-
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- _buffers.Enqueue(bufferTag);
-
- _playedSampleCount += (ulong)buffer.Length;
-
- if (_releaseCallbacks.TryGetValue(trackId, out var callback))
- {
- callback?.Invoke();
- }
- }
-
- public void Start(int trackId) { }
-
- public void Stop(int trackId) { }
-
- public uint GetBufferCount(int trackId) => (uint)_buffers.Count;
-
- public ulong GetPlayedSampleCount(int trackId) => _playedSampleCount;
-
- public bool FlushBuffers(int trackId) => false;
-
- public float GetVolume(int trackId) => _volume;
-
- public void SetVolume(int trackId, float volume)
- {
- _volume = volume;
- }
-
- public void Dispose()
- {
- _buffers.Clear();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs
deleted file mode 100644
index 1ad82319..00000000
--- a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs
+++ /dev/null
@@ -1,404 +0,0 @@
-using OpenTK.Audio;
-using OpenTK.Audio.OpenAL;
-using System;
-using System.Collections.Concurrent;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// An audio renderer that uses OpenAL as the audio backend
- /// </summary>
- public class OpenALAudioOut : IAalOutput, IDisposable
- {
- /// <summary>
- /// The maximum amount of tracks we can issue simultaneously
- /// </summary>
- private const int MaxTracks = 256;
-
- /// <summary>
- /// The <see cref="OpenTK.Audio"/> audio context
- /// </summary>
- private AudioContext _context;
-
- /// <summary>
- /// An object pool containing <see cref="OpenALAudioTrack"/> objects
- /// </summary>
- private ConcurrentDictionary<int, OpenALAudioTrack> _tracks;
-
- /// <summary>
- /// True if the thread need to keep polling
- /// </summary>
- private bool _keepPolling;
-
- /// <summary>
- /// The poller thread audio context
- /// </summary>
- private Thread _audioPollerThread;
-
- /// <summary>
- /// True if OpenAL is supported on the device
- /// </summary>
- public static bool IsSupported
- {
- get
- {
- try
- {
- return AudioContext.AvailableDevices.Count > 0;
- }
- catch
- {
- return false;
- }
- }
- }
-
- public OpenALAudioOut()
- {
- _context = new AudioContext();
- _tracks = new ConcurrentDictionary<int, OpenALAudioTrack>();
- _keepPolling = true;
- _audioPollerThread = new Thread(AudioPollerWork)
- {
- Name = "Audio.PollerThread"
- };
-
- _audioPollerThread.Start();
- }
-
- private void AudioPollerWork()
- {
- do
- {
- foreach (OpenALAudioTrack track in _tracks.Values)
- {
- lock (track)
- {
- track.CallReleaseCallbackIfNeeded();
- }
- }
-
- // If it's not slept it will waste cycles.
- Thread.Sleep(10);
- }
- while (_keepPolling);
-
- foreach (OpenALAudioTrack track in _tracks.Values)
- {
- track.Dispose();
- }
-
- _tracks.Clear();
- _context.Dispose();
- }
-
- public bool SupportsChannelCount(int channels)
- {
- // NOTE: OpenAL doesn't give us a way to know if the 5.1 setup is supported by hardware or actually emulated.
- // TODO: find a way to determine hardware support.
- return channels == 1 || channels == 2;
- }
-
- /// <summary>
- /// Creates a new audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <returns>The created track's Track ID</returns>
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- OpenALAudioTrack track = new OpenALAudioTrack(sampleRate, GetALFormat(hardwareChannels), hardwareChannels, virtualChannels, callback);
-
- for (int id = 0; id < MaxTracks; id++)
- {
- if (_tracks.TryAdd(id, track))
- {
- return id;
- }
- }
-
- return -1;
- }
-
- private ALFormat GetALFormat(int channels)
- {
- switch (channels)
- {
- case 1: return ALFormat.Mono16;
- case 2: return ALFormat.Stereo16;
- case 6: return ALFormat.Multi51Chn16Ext;
- }
-
- throw new ArgumentOutOfRangeException(nameof(channels));
- }
-
- /// <summary>
- /// Stops playback and closes the track specified by <paramref name="trackId"/>
- /// </summary>
- /// <param name="trackId">The ID of the track to close</param>
- public void CloseTrack(int trackId)
- {
- if (_tracks.TryRemove(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.Dispose();
- }
- }
- }
-
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the specified track
- /// </summary>
- /// <param name="trackId">The track to check</param>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(int trackId, long bufferTag)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.ContainsBuffer(bufferTag);
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets a list of buffer tags the specified track is no longer reserving
- /// </summary>
- /// <param name="trackId">The track to retrieve buffer tags from</param>
- /// <param name="maxCount">The maximum amount of buffer tags to retrieve</param>
- /// <returns>Buffers released by the specified track</returns>
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.GetReleasedBuffers(maxCount);
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Appends an audio buffer to the specified track
- /// </summary>
- /// <typeparam name="T">The sample type of the buffer</typeparam>
- /// <param name="trackId">The track to append the buffer to</param>
- /// <param name="bufferTag">The internal tag of the buffer</param>
- /// <param name="buffer">The buffer to append to the track</param>
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- int bufferId = track.AppendBuffer(bufferTag);
-
- // Do we need to downmix?
- if (track.HardwareChannels != track.VirtualChannels)
- {
- short[] downmixedBuffer;
-
- ReadOnlySpan<short> bufferPCM16 = MemoryMarshal.Cast<T, short>(buffer);
-
- if (track.VirtualChannels == 6)
- {
- downmixedBuffer = Downmixing.DownMixSurroundToStereo(bufferPCM16);
-
- if (track.HardwareChannels == 1)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(downmixedBuffer);
- }
- }
- else if (track.VirtualChannels == 2)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(bufferPCM16);
- }
- else
- {
- throw new NotImplementedException($"Downmixing from {track.VirtualChannels} to {track.HardwareChannels} not implemented!");
- }
-
- AL.BufferData(bufferId, track.Format, downmixedBuffer, downmixedBuffer.Length * sizeof(ushort), track.SampleRate);
- }
- else
- {
- AL.BufferData(bufferId, track.Format, buffer, buffer.Length * sizeof(ushort), track.SampleRate);
- }
-
- AL.SourceQueueBuffer(track.SourceId, bufferId);
-
- StartPlaybackIfNeeded(track);
-
- track.PlayedSampleCount += (ulong)buffer.Length;
- }
- }
- }
-
- /// <summary>
- /// Starts playback
- /// </summary>
- /// <param name="trackId">The ID of the track to start playback on</param>
- public void Start(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.State = PlaybackState.Playing;
-
- StartPlaybackIfNeeded(track);
- }
- }
- }
-
- private void StartPlaybackIfNeeded(OpenALAudioTrack track)
- {
- AL.GetSource(track.SourceId, ALGetSourcei.SourceState, out int stateInt);
-
- ALSourceState State = (ALSourceState)stateInt;
-
- if (State != ALSourceState.Playing && track.State == PlaybackState.Playing)
- {
- AL.SourcePlay(track.SourceId);
- }
- }
-
- /// <summary>
- /// Stops playback
- /// </summary>
- /// <param name="trackId">The ID of the track to stop playback on</param>
- public void Stop(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.State = PlaybackState.Stopped;
-
- AL.SourceStop(track.SourceId);
- }
- }
- }
-
- /// <summary>
- /// Get track buffer count
- /// </summary>
- /// <param name="trackId">The ID of the track to get buffer count</param>
- public uint GetBufferCount(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.BufferCount;
- }
- }
-
- return 0;
- }
-
- /// <summary>
- /// Get track played sample count
- /// </summary>
- /// <param name="trackId">The ID of the track to get played sample count</param>
- public ulong GetPlayedSampleCount(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.PlayedSampleCount;
- }
- }
-
- return 0;
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- /// <param name="trackId">The ID of the track to flush</param>
- public bool FlushBuffers(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.FlushBuffers();
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Set track volume
- /// </summary>
- /// <param name="trackId">The ID of the track to set volume</param>
- /// <param name="volume">The volume of the track</param>
- public void SetVolume(int trackId, float volume)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.SetVolume(volume);
- }
- }
- }
-
- /// <summary>
- /// Get track volume
- /// </summary>
- /// <param name="trackId">The ID of the track to get volume</param>
- public float GetVolume(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.GetVolume();
- }
- }
-
- return 1.0f;
- }
-
- /// <summary>
- /// Gets the current playback state of the specified track
- /// </summary>
- /// <param name="trackId">The track to retrieve the playback state for</param>
- public PlaybackState GetState(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- return track.State;
- }
-
- return PlaybackState.Stopped;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _keepPolling = false;
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs
deleted file mode 100644
index 690129eb..00000000
--- a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs
+++ /dev/null
@@ -1,183 +0,0 @@
-using OpenTK.Audio.OpenAL;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- internal class OpenALAudioTrack : IDisposable
- {
- public int SourceId { get; private set; }
- public int SampleRate { get; private set; }
- public ALFormat Format { get; private set; }
- public PlaybackState State { get; set; }
-
- public int HardwareChannels { get; }
- public int VirtualChannels { get; }
- public uint BufferCount => (uint)_buffers.Count;
- public ulong PlayedSampleCount { get; set; }
-
- private ReleaseCallback _callback;
-
- private ConcurrentDictionary<long, int> _buffers;
-
- private Queue<long> _queuedTagsQueue;
- private Queue<long> _releasedTagsQueue;
-
- private bool _disposed;
-
- public OpenALAudioTrack(int sampleRate, ALFormat format, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- SampleRate = sampleRate;
- Format = format;
- State = PlaybackState.Stopped;
- SourceId = AL.GenSource();
-
- HardwareChannels = hardwareChannels;
- VirtualChannels = virtualChannels;
-
- _callback = callback;
-
- _buffers = new ConcurrentDictionary<long, int>();
-
- _queuedTagsQueue = new Queue<long>();
- _releasedTagsQueue = new Queue<long>();
- }
-
- public bool ContainsBuffer(long tag)
- {
- foreach (long queuedTag in _queuedTagsQueue)
- {
- if (queuedTag == tag)
- {
- return true;
- }
- }
-
- return false;
- }
-
- public long[] GetReleasedBuffers(int count)
- {
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
-
- releasedCount += _releasedTagsQueue.Count;
-
- if (count > releasedCount)
- {
- count = releasedCount;
- }
-
- List<long> tags = new List<long>();
-
- while (count-- > 0 && _releasedTagsQueue.TryDequeue(out long tag))
- {
- tags.Add(tag);
- }
-
- while (count-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
- {
- AL.SourceUnqueueBuffers(SourceId, 1);
-
- tags.Add(tag);
- }
-
- return tags.ToArray();
- }
-
- public int AppendBuffer(long tag)
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- int id = AL.GenBuffer();
-
- _buffers.AddOrUpdate(tag, id, (key, oldId) =>
- {
- AL.DeleteBuffer(oldId);
-
- return id;
- });
-
- _queuedTagsQueue.Enqueue(tag);
-
- return id;
- }
-
- public void CallReleaseCallbackIfNeeded()
- {
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
-
- if (releasedCount > 0)
- {
- // If we signal, then we also need to have released buffers available
- // to return when GetReleasedBuffers is called.
- // If playback needs to be re-started due to all buffers being processed,
- // then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue.
- while (releasedCount-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
- {
- AL.SourceUnqueueBuffers(SourceId, 1);
-
- _releasedTagsQueue.Enqueue(tag);
- }
-
- _callback();
- }
- }
-
- public bool FlushBuffers()
- {
- while (_queuedTagsQueue.TryDequeue(out long tag))
- {
- _releasedTagsQueue.Enqueue(tag);
- }
-
- _callback();
-
- foreach (var buffer in _buffers)
- {
- AL.DeleteBuffer(buffer.Value);
- }
-
- bool heldBuffers = _buffers.Count > 0;
-
- _buffers.Clear();
-
- return heldBuffers;
- }
-
- public void SetVolume(float volume)
- {
- AL.Source(SourceId, ALSourcef.Gain, volume);
- }
-
- public float GetVolume()
- {
- AL.GetSource(SourceId, ALSourcef.Gain, out float volume);
-
- return volume;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing && !_disposed)
- {
- _disposed = true;
-
- AL.DeleteSource(SourceId);
-
- foreach (int id in _buffers.Values)
- {
- AL.DeleteBuffer(id);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs
deleted file mode 100644
index 92bf42c4..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs
+++ /dev/null
@@ -1,361 +0,0 @@
-using Ryujinx.Audio.SoundIo;
-using SoundIOSharp;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// An audio renderer that uses libsoundio as the audio backend
- /// </summary>
- public class SoundIoAudioOut : IAalOutput
- {
- /// <summary>
- /// The maximum amount of tracks we can issue simultaneously
- /// </summary>
- private const int MaximumTracks = 256;
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context
- /// </summary>
- private SoundIO _audioContext;
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> audio device
- /// </summary>
- private SoundIODevice _audioDevice;
-
- /// <summary>
- /// An object pool containing <see cref="SoundIoAudioTrack"/> objects
- /// </summary>
- private SoundIoAudioTrackPool _trackPool;
-
- /// <summary>
- /// True if SoundIO is supported on the device
- /// </summary>
- public static bool IsSupported
- {
- get
- {
- return IsSupportedInternal();
- }
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioOut"/>
- /// </summary>
- public SoundIoAudioOut()
- {
- _audioContext = new SoundIO();
-
- _audioContext.Connect();
- _audioContext.FlushEvents();
-
- _audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
- _trackPool = new SoundIoAudioTrackPool(_audioContext, _audioDevice, MaximumTracks);
- }
-
- public bool SupportsChannelCount(int channels)
- {
- return _audioDevice.SupportsChannelCount(channels);
- }
-
- /// <summary>
- /// Creates a new audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <returns>The created track's Track ID</returns>
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- if (!_trackPool.TryGet(out SoundIoAudioTrack track))
- {
- return -1;
- }
-
- // Open the output. We currently only support 16-bit signed LE
- track.Open(sampleRate, hardwareChannels, virtualChannels, callback, SoundIOFormat.S16LE);
-
- return track.TrackID;
- }
-
- /// <summary>
- /// Stops playback and closes the track specified by <paramref name="trackId"/>
- /// </summary>
- /// <param name="trackId">The ID of the track to close</param>
- public void CloseTrack(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- // Close and dispose of the track
- track.Close();
-
- // Recycle the track back into the pool
- _trackPool.Put(track);
- }
- }
-
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the specified track
- /// </summary>
- /// <param name="trackId">The track to check</param>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(int trackId, long bufferTag)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.ContainsBuffer(bufferTag);
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets a list of buffer tags the specified track is no longer reserving
- /// </summary>
- /// <param name="trackId">The track to retrieve buffer tags from</param>
- /// <param name="maxCount">The maximum amount of buffer tags to retrieve</param>
- /// <returns>Buffers released by the specified track</returns>
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- List<long> bufferTags = new List<long>();
-
- while(maxCount-- > 0 && track.ReleasedBuffers.TryDequeue(out long tag))
- {
- bufferTags.Add(tag);
- }
-
- return bufferTags.ToArray();
- }
-
- return new long[0];
- }
-
- /// <summary>
- /// Appends an audio buffer to the specified track
- /// </summary>
- /// <typeparam name="T">The sample type of the buffer</typeparam>
- /// <param name="trackId">The track to append the buffer to</param>
- /// <param name="bufferTag">The internal tag of the buffer</param>
- /// <param name="buffer">The buffer to append to the track</param>
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.AppendBuffer(bufferTag, buffer);
- }
- }
-
- /// <summary>
- /// Starts playback
- /// </summary>
- /// <param name="trackId">The ID of the track to start playback on</param>
- public void Start(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.Start();
- }
- }
-
- /// <summary>
- /// Stops playback
- /// </summary>
- /// <param name="trackId">The ID of the track to stop playback on</param>
- public void Stop(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.Stop();
- }
- }
-
- /// <summary>
- /// Get track buffer count
- /// </summary>
- /// <param name="trackId">The ID of the track to get buffer count</param>
- public uint GetBufferCount(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.BufferCount;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Get track played sample count
- /// </summary>
- /// <param name="trackId">The ID of the track to get played sample</param>
- public ulong GetPlayedSampleCount(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.PlayedSampleCount;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- /// <param name="trackId">The ID of the track to flush</param>
- public bool FlushBuffers(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.FlushBuffers();
- }
-
- return false;
- }
-
- /// <summary>
- /// Set track volume
- /// </summary>
- /// <param name="volume">The volume of the playback</param>
- public void SetVolume(int trackId, float volume)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.AudioStream.SetVolume(volume);
- }
- }
-
- /// <summary>
- /// Get track volume
- /// </summary>
- public float GetVolume(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.AudioStream.Volume;
- }
-
- return 1.0f;
- }
-
- /// <summary>
- /// Gets the current playback state of the specified track
- /// </summary>
- /// <param name="trackId">The track to retrieve the playback state for</param>
- public PlaybackState GetState(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.State;
- }
-
- return PlaybackState.Stopped;
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioOut" />
- /// </summary>
- public void Dispose()
- {
- _trackPool.Dispose();
- _audioContext.Disconnect();
- _audioContext.Dispose();
- }
-
- /// <summary>
- /// Searches for a shared version of the default audio device
- /// </summary>
- /// <param name="audioContext">The <see cref="SoundIO"/> audio context</param>
- /// <param name="fallback">Whether to fallback to the raw default audio device if a non-raw device cannot be found</param>
- private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
- {
- SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
-
- if (!defaultAudioDevice.IsRaw)
- {
- return defaultAudioDevice;
- }
-
- for (int i = 0; i < audioContext.BackendCount; i++)
- {
- SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
-
- if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
- {
- return audioDevice;
- }
- }
-
- return fallback ? defaultAudioDevice : null;
- }
-
- /// <summary>
- /// Determines if SoundIO can connect to a supported backend
- /// </summary>
- /// <returns></returns>
- private static bool IsSupportedInternal()
- {
- SoundIO context = null;
- SoundIODevice device = null;
- SoundIOOutStream stream = null;
-
- bool backendDisconnected = false;
-
- try
- {
- context = new SoundIO();
-
- context.OnBackendDisconnect = (i) => {
- backendDisconnected = true;
- };
-
- context.Connect();
- context.FlushEvents();
-
- if (backendDisconnected)
- {
- return false;
- }
-
- if (context.OutputDeviceCount == 0)
- {
- return false;
- }
-
- device = FindNonRawDefaultAudioDevice(context);
-
- if (device == null || backendDisconnected)
- {
- return false;
- }
-
- stream = device.CreateOutStream();
-
- if (stream == null || backendDisconnected)
- {
- return false;
- }
-
- return true;
- }
- catch
- {
- return false;
- }
- finally
- {
- if (stream != null)
- {
- stream.Dispose();
- }
-
- if (context != null)
- {
- context.Dispose();
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs
deleted file mode 100644
index 52c4ebc9..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs
+++ /dev/null
@@ -1,646 +0,0 @@
-using SoundIOSharp;
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Audio.SoundIo
-{
- internal class SoundIoAudioTrack : IDisposable
- {
- /// <summary>
- /// The audio track ring buffer
- /// </summary>
- private SoundIoRingBuffer m_Buffer;
-
- /// <summary>
- /// A list of buffers currently pending writeback to the audio backend
- /// </summary>
- private ConcurrentQueue<SoundIoBuffer> m_ReservedBuffers;
-
- /// <summary>
- /// Occurs when a buffer has been released by the audio backend
- /// </summary>
- private event ReleaseCallback BufferReleased;
-
- /// <summary>
- /// The track ID of this <see cref="SoundIoAudioTrack"/>
- /// </summary>
- public int TrackID { get; private set; }
-
- /// <summary>
- /// The current playback state
- /// </summary>
- public PlaybackState State { get; private set; }
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context this track belongs to
- /// </summary>
- public SoundIO AudioContext { get; private set; }
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> this track belongs to
- /// </summary>
- public SoundIODevice AudioDevice { get; private set; }
-
- /// <summary>
- /// The audio output stream of this track
- /// </summary>
- public SoundIOOutStream AudioStream { get; private set; }
-
- /// <summary>
- /// Released buffers the track is no longer holding
- /// </summary>
- public ConcurrentQueue<long> ReleasedBuffers { get; private set; }
-
- /// <summary>
- /// Buffer count of the track
- /// </summary>
- public uint BufferCount => (uint)m_ReservedBuffers.Count;
-
- /// <summary>
- /// Played sample count of the track
- /// </summary>
- public ulong PlayedSampleCount { get; private set; }
-
- private int _hardwareChannels;
- private int _virtualChannels;
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrack"/>
- /// </summary>
- /// <param name="trackId">The track ID</param>
- /// <param name="audioContext">The SoundIO audio context</param>
- /// <param name="audioDevice">The SoundIO audio device</param>
- public SoundIoAudioTrack(int trackId, SoundIO audioContext, SoundIODevice audioDevice)
- {
- TrackID = trackId;
- AudioContext = audioContext;
- AudioDevice = audioDevice;
- State = PlaybackState.Stopped;
- ReleasedBuffers = new ConcurrentQueue<long>();
-
- m_Buffer = new SoundIoRingBuffer();
- m_ReservedBuffers = new ConcurrentQueue<SoundIoBuffer>();
- }
-
- /// <summary>
- /// Opens the audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate of the track</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <param name="format">The requested sample format of the track</param>
- public void Open(
- int sampleRate,
- int hardwareChannels,
- int virtualChannels,
- ReleaseCallback callback,
- SoundIOFormat format = SoundIOFormat.S16LE)
- {
- // Close any existing audio streams
- if (AudioStream != null)
- {
- Close();
- }
-
- if (!AudioDevice.SupportsSampleRate(sampleRate))
- {
- throw new InvalidOperationException($"This sound device does not support a sample rate of {sampleRate}Hz");
- }
-
- if (!AudioDevice.SupportsFormat(format))
- {
- throw new InvalidOperationException($"This sound device does not support SoundIOFormat.{Enum.GetName(typeof(SoundIOFormat), format)}");
- }
-
- if (!AudioDevice.SupportsChannelCount(hardwareChannels))
- {
- throw new InvalidOperationException($"This sound device does not support channel count {hardwareChannels}");
- }
-
- _hardwareChannels = hardwareChannels;
- _virtualChannels = virtualChannels;
-
- AudioStream = AudioDevice.CreateOutStream();
-
- AudioStream.Name = $"SwitchAudioTrack_{TrackID}";
- AudioStream.Layout = SoundIOChannelLayout.GetDefault(hardwareChannels);
- AudioStream.Format = format;
- AudioStream.SampleRate = sampleRate;
-
- AudioStream.WriteCallback = WriteCallback;
-
- BufferReleased += callback;
-
- AudioStream.Open();
- }
-
- /// <summary>
- /// This callback occurs when the sound device is ready to buffer more frames
- /// </summary>
- /// <param name="minFrameCount">The minimum amount of frames expected by the audio backend</param>
- /// <param name="maxFrameCount">The maximum amount of frames that can be written to the audio backend</param>
- private unsafe void WriteCallback(int minFrameCount, int maxFrameCount)
- {
- int bytesPerFrame = AudioStream.BytesPerFrame;
- uint bytesPerSample = (uint)AudioStream.BytesPerSample;
-
- int bufferedFrames = m_Buffer.Length / bytesPerFrame;
- long bufferedSamples = m_Buffer.Length / bytesPerSample;
-
- int frameCount = Math.Min(bufferedFrames, maxFrameCount);
-
- if (frameCount == 0)
- {
- return;
- }
-
- SoundIOChannelAreas areas = AudioStream.BeginWrite(ref frameCount);
- int channelCount = areas.ChannelCount;
-
- byte[] samples = new byte[frameCount * bytesPerFrame];
-
- m_Buffer.Read(samples, 0, samples.Length);
-
- // This is a huge ugly block of code, but we save
- // a significant amount of time over the generic
- // loop that handles other channel counts.
-
- // Mono
- if (channelCount == 1)
- {
- SoundIOChannelArea area = areas.GetArea(0);
-
- fixed (byte* srcptr = samples)
- {
- if (bytesPerSample == 1)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- ((byte*)area.Pointer)[0] = srcptr[frame * bytesPerFrame];
-
- area.Pointer += area.Step;
- }
- }
- else if (bytesPerSample == 2)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- ((short*)area.Pointer)[0] = ((short*)srcptr)[frame * bytesPerFrame >> 1];
-
- area.Pointer += area.Step;
- }
- }
- else if (bytesPerSample == 4)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- ((int*)area.Pointer)[0] = ((int*)srcptr)[frame * bytesPerFrame >> 2];
-
- area.Pointer += area.Step;
- }
- }
- else
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- Unsafe.CopyBlockUnaligned((byte*)area.Pointer, srcptr + (frame * bytesPerFrame), bytesPerSample);
-
- area.Pointer += area.Step;
- }
- }
- }
- }
- // Stereo
- else if (channelCount == 2)
- {
- SoundIOChannelArea area1 = areas.GetArea(0);
- SoundIOChannelArea area2 = areas.GetArea(1);
-
- fixed (byte* srcptr = samples)
- {
- if (bytesPerSample == 1)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((byte*)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0];
-
- // Channel 2
- ((byte*)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- }
- }
- else if (bytesPerSample == 2)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((short*)area1.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 0];
-
- // Channel 2
- ((short*)area2.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 1];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- }
- }
- else if (bytesPerSample == 4)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((int*)area1.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 0];
-
- // Channel 2
- ((int*)area2.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 1];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- }
- }
- else
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- Unsafe.CopyBlockUnaligned((byte*)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample);
-
- // Channel 2
- Unsafe.CopyBlockUnaligned((byte*)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample);
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- }
- }
- }
- }
- // Surround
- else if (channelCount == 6)
- {
- SoundIOChannelArea area1 = areas.GetArea(0);
- SoundIOChannelArea area2 = areas.GetArea(1);
- SoundIOChannelArea area3 = areas.GetArea(2);
- SoundIOChannelArea area4 = areas.GetArea(3);
- SoundIOChannelArea area5 = areas.GetArea(4);
- SoundIOChannelArea area6 = areas.GetArea(5);
-
- fixed (byte* srcptr = samples)
- {
- if (bytesPerSample == 1)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((byte*)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0];
-
- // Channel 2
- ((byte*)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1];
-
- // Channel 3
- ((byte*)area3.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 2];
-
- // Channel 4
- ((byte*)area4.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 3];
-
- // Channel 5
- ((byte*)area5.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 4];
-
- // Channel 6
- ((byte*)area6.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 5];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- area3.Pointer += area3.Step;
- area4.Pointer += area4.Step;
- area5.Pointer += area5.Step;
- area6.Pointer += area6.Step;
- }
- }
- else if (bytesPerSample == 2)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((short*)area1.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 0];
-
- // Channel 2
- ((short*)area2.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 1];
-
- // Channel 3
- ((short*)area3.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 2];
-
- // Channel 4
- ((short*)area4.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 3];
-
- // Channel 5
- ((short*)area5.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 4];
-
- // Channel 6
- ((short*)area6.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 5];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- area3.Pointer += area3.Step;
- area4.Pointer += area4.Step;
- area5.Pointer += area5.Step;
- area6.Pointer += area6.Step;
- }
- }
- else if (bytesPerSample == 4)
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- ((int*)area1.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 0];
-
- // Channel 2
- ((int*)area2.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 1];
-
- // Channel 3
- ((int*)area3.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 2];
-
- // Channel 4
- ((int*)area4.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 3];
-
- // Channel 5
- ((int*)area5.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 4];
-
- // Channel 6
- ((int*)area6.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 5];
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- area3.Pointer += area3.Step;
- area4.Pointer += area4.Step;
- area5.Pointer += area5.Step;
- area6.Pointer += area6.Step;
- }
- }
- else
- {
- for (int frame = 0; frame < frameCount; frame++)
- {
- // Channel 1
- Unsafe.CopyBlockUnaligned((byte*)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample);
-
- // Channel 2
- Unsafe.CopyBlockUnaligned((byte*)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample);
-
- // Channel 3
- Unsafe.CopyBlockUnaligned((byte*)area3.Pointer, srcptr + (frame * bytesPerFrame) + (2 * bytesPerSample), bytesPerSample);
-
- // Channel 4
- Unsafe.CopyBlockUnaligned((byte*)area4.Pointer, srcptr + (frame * bytesPerFrame) + (3 * bytesPerSample), bytesPerSample);
-
- // Channel 5
- Unsafe.CopyBlockUnaligned((byte*)area5.Pointer, srcptr + (frame * bytesPerFrame) + (4 * bytesPerSample), bytesPerSample);
-
- // Channel 6
- Unsafe.CopyBlockUnaligned((byte*)area6.Pointer, srcptr + (frame * bytesPerFrame) + (5 * bytesPerSample), bytesPerSample);
-
- area1.Pointer += area1.Step;
- area2.Pointer += area2.Step;
- area3.Pointer += area3.Step;
- area4.Pointer += area4.Step;
- area5.Pointer += area5.Step;
- area6.Pointer += area6.Step;
- }
- }
- }
- }
- // Every other channel count
- else
- {
- SoundIOChannelArea[] channels = new SoundIOChannelArea[channelCount];
-
- // Obtain the channel area for each channel
- for (int i = 0; i < channelCount; i++)
- {
- channels[i] = areas.GetArea(i);
- }
-
- fixed (byte* srcptr = samples)
- {
- for (int frame = 0; frame < frameCount; frame++)
- for (int channel = 0; channel < areas.ChannelCount; channel++)
- {
- // Copy channel by channel, frame by frame. This is slow!
- Unsafe.CopyBlockUnaligned((byte*)channels[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample);
-
- channels[channel].Pointer += channels[channel].Step;
- }
- }
- }
-
- AudioStream.EndWrite();
-
- PlayedSampleCount += (ulong)samples.Length;
-
- UpdateReleasedBuffers(samples.Length);
- }
-
- /// <summary>
- /// Releases any buffers that have been fully written to the output device
- /// </summary>
- /// <param name="bytesRead">The amount of bytes written in the last device write</param>
- private void UpdateReleasedBuffers(int bytesRead)
- {
- bool bufferReleased = false;
-
- while (bytesRead > 0)
- {
- if (m_ReservedBuffers.TryPeek(out SoundIoBuffer buffer))
- {
- if (buffer.Length > bytesRead)
- {
- buffer.Length -= bytesRead;
- bytesRead = 0;
- }
- else
- {
- bufferReleased = true;
- bytesRead -= buffer.Length;
-
- m_ReservedBuffers.TryDequeue(out buffer);
- ReleasedBuffers.Enqueue(buffer.Tag);
- }
- }
- }
-
- if (bufferReleased)
- {
- OnBufferReleased();
- }
- }
-
- /// <summary>
- /// Starts audio playback
- /// </summary>
- public void Start()
- {
- if (AudioStream == null)
- {
- return;
- }
-
- AudioStream.Start();
- AudioStream.Pause(false);
- AudioContext.FlushEvents();
- State = PlaybackState.Playing;
- }
-
- /// <summary>
- /// Stops audio playback
- /// </summary>
- public void Stop()
- {
- if (AudioStream == null)
- {
- return;
- }
-
- AudioStream.Pause(true);
- AudioContext.FlushEvents();
- State = PlaybackState.Stopped;
- }
-
- /// <summary>
- /// Appends an audio buffer to the tracks internal ring buffer
- /// </summary>
- /// <typeparam name="T">The audio sample type</typeparam>
- /// <param name="bufferTag">The unqiue tag of the buffer being appended</param>
- /// <param name="buffer">The buffer to append</param>
- public void AppendBuffer<T>(long bufferTag, T[] buffer) where T: struct
- {
- if (AudioStream == null)
- {
- return;
- }
-
- int sampleSize = Unsafe.SizeOf<T>();
- int targetSize = sampleSize * buffer.Length;
-
- // Do we need to downmix?
- if (_hardwareChannels != _virtualChannels)
- {
- if (sampleSize != sizeof(short))
- {
- throw new NotImplementedException("Downmixing formats other than PCM16 is not supported!");
- }
-
- short[] downmixedBuffer;
-
- ReadOnlySpan<short> bufferPCM16 = MemoryMarshal.Cast<T, short>(buffer);
-
- if (_virtualChannels == 6)
- {
- downmixedBuffer = Downmixing.DownMixSurroundToStereo(bufferPCM16);
-
- if (_hardwareChannels == 1)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(downmixedBuffer);
- }
- }
- else if (_virtualChannels == 2)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(bufferPCM16);
- }
- else
- {
- throw new NotImplementedException($"Downmixing from {_virtualChannels} to {_hardwareChannels} not implemented!");
- }
-
- targetSize = sampleSize * downmixedBuffer.Length;
-
- // Copy the memory to our ring buffer
- m_Buffer.Write(downmixedBuffer, 0, targetSize);
-
- // Keep track of "buffered" buffers
- m_ReservedBuffers.Enqueue(new SoundIoBuffer(bufferTag, targetSize));
- }
- else
- {
- // Copy the memory to our ring buffer
- m_Buffer.Write(buffer, 0, targetSize);
-
- // Keep track of "buffered" buffers
- m_ReservedBuffers.Enqueue(new SoundIoBuffer(bufferTag, targetSize));
- }
- }
-
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the track
- /// </summary>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(long bufferTag)
- {
- return m_ReservedBuffers.Any(x => x.Tag == bufferTag);
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- public bool FlushBuffers()
- {
- m_Buffer.Clear();
-
- if (m_ReservedBuffers.Count > 0)
- {
- foreach (var buffer in m_ReservedBuffers)
- {
- ReleasedBuffers.Enqueue(buffer.Tag);
- }
-
- OnBufferReleased();
-
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Closes the <see cref="SoundIoAudioTrack"/>
- /// </summary>
- public void Close()
- {
- if (AudioStream != null)
- {
- AudioStream.Pause(true);
- AudioStream.Dispose();
- }
-
- m_Buffer.Clear();
- OnBufferReleased();
- ReleasedBuffers.Clear();
-
- State = PlaybackState.Stopped;
- AudioStream = null;
- BufferReleased = null;
- }
-
- private void OnBufferReleased()
- {
- BufferReleased?.Invoke();
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioTrack" />
- /// </summary>
- public void Dispose()
- {
- Close();
- }
-
- ~SoundIoAudioTrack()
- {
- Dispose();
- }
- }
-}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs
deleted file mode 100644
index 95f181dc..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-using SoundIOSharp;
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// An object pool containing a set of audio tracks
- /// </summary>
- internal class SoundIoAudioTrackPool : IDisposable
- {
- /// <summary>
- /// The current size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- private int m_Size;
-
- /// <summary>
- /// The maximum size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- private int m_MaxSize;
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context this track pool belongs to
- /// </summary>
- private SoundIO m_Context;
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> audio device this track pool belongs to
- /// </summary>
- private SoundIODevice m_Device;
-
- /// <summary>
- /// The queue that keeps track of the available <see cref="SoundIoAudioTrack"/> in the pool.
- /// </summary>
- private ConcurrentQueue<SoundIoAudioTrack> m_Queue;
-
- /// <summary>
- /// The dictionary providing mapping between a TrackID and <see cref="SoundIoAudioTrack"/>
- /// </summary>
- private ConcurrentDictionary<int, SoundIoAudioTrack> m_TrackList;
-
- /// <summary>
- /// Gets the current size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- public int Size { get => m_Size; }
-
- /// <summary>
- /// Gets the maximum size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- public int MaxSize { get => m_MaxSize; }
-
- /// <summary>
- /// Gets a value that indicates whether the <see cref="SoundIoAudioTrackPool"/> is empty
- /// </summary>
- public bool IsEmpty { get => m_Queue.IsEmpty; }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that is empty
- /// </summary>
- /// <param name="maxSize">The maximum amount of tracks that can be created</param>
- public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize)
- {
- m_Size = 0;
- m_Context = context;
- m_Device = device;
- m_MaxSize = maxSize;
-
- m_Queue = new ConcurrentQueue<SoundIoAudioTrack>();
- m_TrackList = new ConcurrentDictionary<int, SoundIoAudioTrack>();
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that contains
- /// the specified amount of <see cref="SoundIoAudioTrack"/>
- /// </summary>
- /// <param name="maxSize">The maximum amount of tracks that can be created</param>
- /// <param name="initialCapacity">The initial number of tracks that the pool contains</param>
- public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize, int initialCapacity)
- : this(context, device, maxSize)
- {
- var trackCollection = Enumerable.Range(0, initialCapacity)
- .Select(TrackFactory);
-
- m_Size = initialCapacity;
- m_Queue = new ConcurrentQueue<SoundIoAudioTrack>(trackCollection);
- }
-
- /// <summary>
- /// Creates a new <see cref="SoundIoAudioTrack"/> with the proper AudioContext and AudioDevice
- /// and the specified <paramref name="trackId" />
- /// </summary>
- /// <param name="trackId">The ID of the track to be created</param>
- /// <returns>A new AudioTrack with the specified ID</returns>
- private SoundIoAudioTrack TrackFactory(int trackId)
- {
- // Create a new AudioTrack
- SoundIoAudioTrack track = new SoundIoAudioTrack(trackId, m_Context, m_Device);
-
- // Keep track of issued tracks
- m_TrackList[trackId] = track;
-
- return track;
- }
-
- /// <summary>
- /// Retrieves a <see cref="SoundIoAudioTrack"/> from the pool
- /// </summary>
- /// <returns>An AudioTrack from the pool</returns>
- public SoundIoAudioTrack Get()
- {
- // If we have a track available, reuse it
- if (m_Queue.TryDequeue(out SoundIoAudioTrack track))
- {
- return track;
- }
-
- // Have we reached the maximum size of our pool?
- if (m_Size >= m_MaxSize)
- {
- return null;
- }
-
- // We don't have any pooled tracks, so create a new one
- return TrackFactory(m_Size++);
- }
-
- /// <summary>
- /// Retrieves the <see cref="SoundIoAudioTrack"/> associated with the specified <paramref name="trackId"/> from the pool
- /// </summary>
- /// <param name="trackId">The ID of the track to retrieve</param>
- public SoundIoAudioTrack Get(int trackId)
- {
- if (m_TrackList.TryGetValue(trackId, out SoundIoAudioTrack track))
- {
- return track;
- }
-
- return null;
- }
-
- /// <summary>
- /// Attempts to get a <see cref="SoundIoAudioTrack"/> from the pool
- /// </summary>
- /// <param name="track">The track retrieved from the pool</param>
- /// <returns>True if retrieve was successful</returns>
- public bool TryGet(out SoundIoAudioTrack track)
- {
- track = Get();
-
- return track != null;
- }
-
- /// <summary>
- /// Attempts to get the <see cref="SoundIoAudioTrack" /> associated with the specified <paramref name="trackId"/> from the pool
- /// </summary>
- /// <param name="trackId">The ID of the track to retrieve</param>
- /// <param name="track">The track retrieved from the pool</param>
- public bool TryGet(int trackId, out SoundIoAudioTrack track)
- {
- return m_TrackList.TryGetValue(trackId, out track);
- }
-
- /// <summary>
- /// Returns an <see cref="SoundIoAudioTrack"/> back to the pool for reuse
- /// </summary>
- /// <param name="track">The track to be returned to the pool</param>
- public void Put(SoundIoAudioTrack track)
- {
- // Ensure the track is disposed and not playing audio
- track.Close();
-
- // Requeue the track for reuse later
- m_Queue.Enqueue(track);
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioTrackPool" />
- /// </summary>
- public void Dispose()
- {
- foreach (var track in m_TrackList)
- {
- track.Value.Close();
- track.Value.Dispose();
- }
-
- m_Size = 0;
- m_Queue.Clear();
- m_TrackList.Clear();
- }
- }
-}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs
deleted file mode 100644
index 2a6190b5..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// Represents the remaining bytes left buffered for a specific buffer tag
- /// </summary>
- internal class SoundIoBuffer
- {
- /// <summary>
- /// The buffer tag this <see cref="SoundIoBuffer"/> represents
- /// </summary>
- public long Tag { get; private set; }
-
- /// <summary>
- /// The remaining bytes still to be released
- /// </summary>
- public int Length { get; set; }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoBuffer"/>
- /// </summary>
- /// <param name="tag">The buffer tag</param>
- /// <param name="length">The size of the buffer</param>
- public SoundIoBuffer(long tag, int length)
- {
- Tag = tag;
- Length = length;
- }
- }
-}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs
deleted file mode 100644
index b2885021..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using System;
-
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// A thread-safe variable-size circular buffer
- /// </summary>
- internal class SoundIoRingBuffer
- {
- private byte[] m_Buffer;
- private int m_Size;
- private int m_HeadOffset;
- private int m_TailOffset;
-
- /// <summary>
- /// Gets the available bytes in the ring buffer
- /// </summary>
- public int Length
- {
- get { return m_Size; }
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoRingBuffer"/>
- /// </summary>
- public SoundIoRingBuffer()
- {
- m_Buffer = new byte[2048];
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoRingBuffer"/> with the specified capacity
- /// </summary>
- /// <param name="capacity">The number of entries that the <see cref="SoundIoRingBuffer"/> can initially contain</param>
- public SoundIoRingBuffer(int capacity)
- {
- m_Buffer = new byte[capacity];
- }
-
- /// <summary>
- /// Clears the ring buffer
- /// </summary>
- public void Clear()
- {
- m_Size = 0;
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- /// <summary>
- /// Clears the specified amount of bytes from the ring buffer
- /// </summary>
- /// <param name="size">The amount of bytes to clear from the ring buffer</param>
- public void Clear(int size)
- {
- lock (this)
- {
- if (size > m_Size)
- {
- size = m_Size;
- }
-
- if (size == 0)
- {
- return;
- }
-
- m_HeadOffset = (m_HeadOffset + size) % m_Buffer.Length;
- m_Size -= size;
-
- if (m_Size == 0)
- {
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- return;
- }
- }
-
- /// <summary>
- /// Extends the capacity of the ring buffer
- /// </summary>
- private void SetCapacity(int capacity)
- {
- byte[] buffer = new byte[capacity];
-
- if (m_Size > 0)
- {
- if (m_HeadOffset < m_TailOffset)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Size);
- }
- else
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Buffer.Length - m_HeadOffset);
- Buffer.BlockCopy(m_Buffer, 0, buffer, m_Buffer.Length - m_HeadOffset, m_TailOffset);
- }
- }
-
- m_Buffer = buffer;
- m_HeadOffset = 0;
- m_TailOffset = m_Size;
- }
-
-
- /// <summary>
- /// Writes a sequence of bytes to the ring buffer
- /// </summary>
- /// <param name="buffer">A byte array containing the data to write</param>
- /// <param name="index">The zero-based byte offset in <paramref name="buffer" /> from which to begin copying bytes to the ring buffer</param>
- /// <param name="count">The number of bytes to write</param>
- public void Write<T>(T[] buffer, int index, int count)
- {
- if (count == 0)
- {
- return;
- }
-
- lock (this)
- {
- if ((m_Size + count) > m_Buffer.Length)
- {
- SetCapacity((m_Size + count + 2047) & ~2047);
- }
-
- if (m_HeadOffset < m_TailOffset)
- {
- int tailLength = m_Buffer.Length - m_TailOffset;
-
- if (tailLength >= count)
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count);
- }
- else
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, tailLength);
- Buffer.BlockCopy(buffer, index + tailLength, m_Buffer, 0, count - tailLength);
- }
- }
- else
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count);
- }
-
- m_Size += count;
- m_TailOffset = (m_TailOffset + count) % m_Buffer.Length;
- }
- }
-
- /// <summary>
- /// Reads a sequence of bytes from the ring buffer and advances the position within the ring buffer by the number of bytes read
- /// </summary>
- /// <param name="buffer">The buffer to write the data into</param>
- /// <param name="index">The zero-based byte offset in <paramref name="buffer" /> at which the read bytes will be placed</param>
- /// <param name="count">The maximum number of bytes to read</param>
- /// <returns>The total number of bytes read into the buffer. This might be less than the number of bytes requested if that number of bytes are not currently available, or zero if the ring buffer is empty</returns>
- public int Read<T>(T[] buffer, int index, int count)
- {
- lock (this)
- {
- if (count > m_Size)
- {
- count = m_Size;
- }
-
- if (count == 0)
- {
- return 0;
- }
-
- if (m_HeadOffset < m_TailOffset)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count);
- }
- else
- {
- int tailLength = m_Buffer.Length - m_HeadOffset;
-
- if (tailLength >= count)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count);
- }
- else
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, tailLength);
- Buffer.BlockCopy(m_Buffer, 0, buffer, index + tailLength, count - tailLength);
- }
- }
-
- m_Size -= count;
- m_HeadOffset = (m_HeadOffset + count) % m_Buffer.Length;
-
- if (m_Size == 0)
- {
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- return count;
- }
- }
- }
-}