Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
405
src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
Normal file
405
src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
Normal file
|
@ -0,0 +1,405 @@
|
|||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Renderer.Dsp;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio renderer manager.
|
||||
/// </summary>
|
||||
public class AudioRendererManager : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Lock used for session allocation.
|
||||
/// </summary>
|
||||
private object _sessionLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Lock used to control the <see cref="AudioProcessor"/> running state.
|
||||
/// </summary>
|
||||
private object _audioProcessorLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The session ids allocation table.
|
||||
/// </summary>
|
||||
private int[] _sessionIds;
|
||||
|
||||
/// <summary>
|
||||
/// The events linked to each session.
|
||||
/// </summary>
|
||||
private IWritableEvent[] _sessionsSystemEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="AudioRenderSystem"/> sessions instances.
|
||||
/// </summary>
|
||||
private AudioRenderSystem[] _sessions;
|
||||
|
||||
/// <summary>
|
||||
/// The count of active sessions.
|
||||
/// </summary>
|
||||
private int _activeSessionCount;
|
||||
|
||||
/// <summary>
|
||||
/// The worker thread used to run <see cref="SendCommands"/>.
|
||||
/// </summary>
|
||||
private Thread _workerThread;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the worker thread and <see cref="AudioProcessor"/> are running.
|
||||
/// </summary>
|
||||
private bool _isRunning;
|
||||
|
||||
/// <summary>
|
||||
/// The audio device driver to create audio outputs.
|
||||
/// </summary>
|
||||
private IHardwareDeviceDriver _deviceDriver;
|
||||
|
||||
/// <summary>
|
||||
/// Tick source used to measure elapsed time.
|
||||
/// </summary>
|
||||
public ITickSource TickSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="AudioProcessor"/> instance associated to this manager.
|
||||
/// </summary>
|
||||
public AudioProcessor Processor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The dispose state.
|
||||
/// </summary>
|
||||
private int _disposeState;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AudioRendererManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="tickSource">Tick source used to measure elapsed time.</param>
|
||||
public AudioRendererManager(ITickSource tickSource)
|
||||
{
|
||||
Processor = new AudioProcessor();
|
||||
TickSource = tickSource;
|
||||
_sessionIds = new int[Constants.AudioRendererSessionCountMax];
|
||||
_sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
|
||||
_activeSessionCount = 0;
|
||||
|
||||
for (int i = 0; i < _sessionIds.Length; i++)
|
||||
{
|
||||
_sessionIds[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the <see cref="AudioRendererManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="sessionSystemEvents">The events associated to each session.</param>
|
||||
/// <param name="deviceDriver">The device driver to use to create audio outputs.</param>
|
||||
public void Initialize(IWritableEvent[] sessionSystemEvents, IHardwareDeviceDriver deviceDriver)
|
||||
{
|
||||
_sessionsSystemEvent = sessionSystemEvents;
|
||||
_deviceDriver = deviceDriver;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the work buffer size required by a session.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user configuration</param>
|
||||
/// <returns>The work buffer size required by a session.</returns>
|
||||
public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter)
|
||||
{
|
||||
return AudioRenderSystem.GetWorkBufferSize(ref parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquire a new session id.
|
||||
/// </summary>
|
||||
/// <returns>A new session id.</returns>
|
||||
private int AcquireSessionId()
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
int index = _activeSessionCount;
|
||||
|
||||
Debug.Assert(index < _sessionIds.Length);
|
||||
|
||||
int sessionId = _sessionIds[index];
|
||||
|
||||
_sessionIds[index] = -1;
|
||||
|
||||
_activeSessionCount++;
|
||||
|
||||
Logger.Info?.Print(LogClass.AudioRenderer, $"Registered new renderer ({sessionId})");
|
||||
|
||||
return sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a given <paramref name="sessionId"/>.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session id to release.</param>
|
||||
private void ReleaseSessionId(int sessionId)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
Debug.Assert(_activeSessionCount > 0);
|
||||
|
||||
int newIndex = --_activeSessionCount;
|
||||
|
||||
_sessionIds[newIndex] = sessionId;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.AudioRenderer, $"Unregistered renderer ({sessionId})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if there is any audio renderer active.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if there is any audio renderer active.</returns>
|
||||
private bool HasAnyActiveRendererLocked()
|
||||
{
|
||||
foreach (AudioRenderSystem renderer in _sessions)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
||||
/// </summary>
|
||||
private void StartLocked(float volume)
|
||||
{
|
||||
_isRunning = true;
|
||||
|
||||
// TODO: virtual device mapping (IAudioDevice)
|
||||
Processor.Start(_deviceDriver, volume);
|
||||
|
||||
_workerThread = new Thread(SendCommands)
|
||||
{
|
||||
Name = "AudioRendererManager.Worker"
|
||||
};
|
||||
|
||||
_workerThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the <see cref="AudioProcessor"/> and worker thread.
|
||||
/// </summary>
|
||||
private void StopLocked()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
_workerThread.Join();
|
||||
Processor.Stop();
|
||||
|
||||
Logger.Info?.Print(LogClass.AudioRenderer, "Stopped audio renderer");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop sending commands to the <see cref="AudioProcessor"/> without stopping the worker thread.
|
||||
/// </summary>
|
||||
public void StopSendingCommands()
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
foreach (AudioRenderSystem renderer in _sessions)
|
||||
{
|
||||
renderer?.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
lock (_audioProcessorLock)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
StopLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker main function. This is used to dispatch audio renderer commands to the <see cref="AudioProcessor"/>.
|
||||
/// </summary>
|
||||
private void SendCommands()
|
||||
{
|
||||
Logger.Info?.Print(LogClass.AudioRenderer, "Starting audio renderer");
|
||||
Processor.Wait();
|
||||
|
||||
while (_isRunning)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
foreach (AudioRenderSystem renderer in _sessions)
|
||||
{
|
||||
renderer?.SendCommands();
|
||||
}
|
||||
}
|
||||
|
||||
Processor.Signal();
|
||||
Processor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new <see cref="AudioRenderSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
||||
private void Register(AudioRenderSystem renderer, float volume)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
_sessions[renderer.GetSessionId()] = renderer;
|
||||
}
|
||||
|
||||
lock (_audioProcessorLock)
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
StartLocked(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister a new <see cref="AudioRenderSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to unregister.</param>
|
||||
internal void Unregister(AudioRenderSystem renderer)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
int sessionId = renderer.GetSessionId();
|
||||
|
||||
_sessions[renderer.GetSessionId()] = null;
|
||||
|
||||
ReleaseSessionId(sessionId);
|
||||
}
|
||||
|
||||
lock (_audioProcessorLock)
|
||||
{
|
||||
if (_isRunning && !HasAnyActiveRendererLocked())
|
||||
{
|
||||
StopLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a new <see cref="AudioRenderSystem"/>
|
||||
/// </summary>
|
||||
/// <param name="renderer">The new <see cref="AudioRenderSystem"/></param>
|
||||
/// <param name="memoryManager">The memory manager that will be used for all guest memory operations.</param>
|
||||
/// <param name="parameter">The user configuration</param>
|
||||
/// <param name="appletResourceUserId">The applet resource user id of the application.</param>
|
||||
/// <param name="workBufferAddress">The guest work buffer address.</param>
|
||||
/// <param name="workBufferSize">The guest work buffer size.</param>
|
||||
/// <param name="processHandle">The process handle of the application.</param>
|
||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
|
||||
public ResultCode OpenAudioRenderer(
|
||||
out AudioRenderSystem renderer,
|
||||
IVirtualMemoryManager memoryManager,
|
||||
ref AudioRendererConfiguration parameter,
|
||||
ulong appletResourceUserId,
|
||||
ulong workBufferAddress,
|
||||
ulong workBufferSize,
|
||||
uint processHandle,
|
||||
float volume)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
AudioRenderSystem audioRenderer = new AudioRenderSystem(this, _sessionsSystemEvent[sessionId]);
|
||||
|
||||
// TODO: Eventually, we should try to use the guest supplied work buffer instead of allocating
|
||||
// our own. However, it was causing problems on some applications that would unmap the memory
|
||||
// before the audio renderer was fully disposed.
|
||||
Memory<byte> workBufferMemory = GC.AllocateArray<byte>((int)workBufferSize, pinned: true);
|
||||
|
||||
ResultCode result = audioRenderer.Initialize(
|
||||
ref parameter,
|
||||
processHandle,
|
||||
workBufferMemory,
|
||||
workBufferAddress,
|
||||
workBufferSize,
|
||||
sessionId,
|
||||
appletResourceUserId,
|
||||
memoryManager);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
renderer = audioRenderer;
|
||||
|
||||
Register(renderer, volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseSessionId(sessionId);
|
||||
|
||||
renderer = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetVolume()
|
||||
{
|
||||
if (Processor != null)
|
||||
{
|
||||
return Processor.GetVolume();
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
Processor?.SetVolume(volume);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Clone the sessions array to dispose them outside the lock.
|
||||
AudioRenderSystem[] sessions;
|
||||
|
||||
lock (_sessionLock)
|
||||
{
|
||||
sessions = _sessions.ToArray();
|
||||
}
|
||||
|
||||
foreach (AudioRenderSystem renderer in sessions)
|
||||
{
|
||||
renderer?.Dispose();
|
||||
}
|
||||
|
||||
lock (_audioProcessorLock)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
StopLocked();
|
||||
}
|
||||
}
|
||||
|
||||
Processor.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue