using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using System;
using System.Threading;
namespace Ryujinx.Audio.Input
{
///
/// Audio input system.
///
public class AudioInputSystem : IDisposable
{
///
/// The session id associated to the .
///
private int _sessionId;
///
/// The session the .
///
private readonly AudioDeviceSession _session;
///
/// The target device name of the .
///
public string DeviceName { get; private set; }
///
/// The target sample rate of the .
///
public uint SampleRate { get; private set; }
///
/// The target channel count of the .
///
public uint ChannelCount { get; private set; }
///
/// The target sample format of the .
///
public SampleFormat SampleFormat { get; private set; }
///
/// The owning this.
///
private readonly AudioInputManager _manager;
///
/// The lock of the parent.
///
private readonly object _parentLock;
///
/// The dispose state.
///
private int _disposeState;
///
/// Create a new .
///
/// The manager instance
/// The lock of the manager
/// The hardware device session
/// The buffer release event of the audio input
public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
{
_manager = manager;
_parentLock = parentLock;
_session = new AudioDeviceSession(deviceSession, bufferEvent);
}
///
/// Get the default device name on the system.
///
/// The default device name on the system.
private static string GetDeviceDefaultName()
{
return Constants.DefaultDeviceInputName;
}
///
/// Check if a given configuration and device name is valid on the system.
///
/// The configuration to check.
/// The device name to check.
/// A reporting an error or a success.
private static ResultCode IsConfigurationValid(ref AudioInputConfiguration configuration, string deviceName)
{
if (deviceName.Length != 0 && !deviceName.Equals(GetDeviceDefaultName()))
{
return ResultCode.DeviceNotFound;
}
if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate)
{
return ResultCode.UnsupportedSampleRate;
}
if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6)
{
return ResultCode.UnsupportedChannelConfiguration;
}
return ResultCode.Success;
}
///
/// Get the released buffer event.
///
/// The released buffer event
public IWritableEvent RegisterBufferEvent()
{
lock (_parentLock)
{
return _session.GetBufferEvent();
}
}
///
/// Update the .
///
public void Update()
{
lock (_parentLock)
{
_session.Update();
}
}
///
/// Get the id of this session.
///
/// The id of this session
public int GetSessionId()
{
return _sessionId;
}
///
/// Initialize the .
///
/// The input device name wanted by the user
/// The sample format to use
/// The user configuration
/// The session id associated to this
/// A reporting an error or a success.
public ResultCode Initialize(string inputDeviceName, SampleFormat sampleFormat, ref AudioInputConfiguration parameter, int sessionId)
{
_sessionId = sessionId;
ResultCode result = IsConfigurationValid(ref parameter, inputDeviceName);
if (result == ResultCode.Success)
{
if (inputDeviceName.Length == 0)
{
DeviceName = GetDeviceDefaultName();
}
else
{
DeviceName = inputDeviceName;
}
if (parameter.ChannelCount == 6)
{
ChannelCount = 6;
}
else
{
ChannelCount = 2;
}
SampleFormat = sampleFormat;
SampleRate = Constants.TargetSampleRate;
}
return result;
}
///
/// Append a new audio buffer to the audio input.
///
/// The unique tag of this buffer.
/// The buffer informations.
/// A reporting an error or a success.
public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer userBuffer)
{
lock (_parentLock)
{
AudioBuffer buffer = new()
{
BufferTag = bufferTag,
DataPointer = userBuffer.Data,
DataSize = userBuffer.DataSize,
};
if (_session.AppendBuffer(buffer))
{
return ResultCode.Success;
}
return ResultCode.BufferRingFull;
}
}
///
/// Append a new audio buffer to the audio input.
///
/// This is broken by design, only added for completness.
/// The unique tag of this buffer.
/// The buffer informations.
/// Some unknown handle.
/// A reporting an error or a success.
public ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer userBuffer, uint handle)
{
lock (_parentLock)
{
AudioBuffer buffer = new()
{
BufferTag = bufferTag,
DataPointer = userBuffer.Data,
DataSize = userBuffer.DataSize,
};
if (AudioDeviceSession.AppendUacBuffer(buffer, handle))
{
return ResultCode.Success;
}
return ResultCode.BufferRingFull;
}
}
///
/// Get the release buffers.
///
/// The buffer to write the release buffers
/// The count of released buffers
/// A reporting an error or a success.
public ResultCode GetReleasedBuffers(Span releasedBuffers, out uint releasedCount)
{
releasedCount = 0;
// Ensure that the first entry is set to zero if no entries are returned.
if (releasedBuffers.Length > 0)
{
releasedBuffers[0] = 0;
}
lock (_parentLock)
{
for (int i = 0; i < releasedBuffers.Length; i++)
{
if (!_session.TryPopReleasedBuffer(out AudioBuffer buffer))
{
break;
}
releasedBuffers[i] = buffer.BufferTag;
releasedCount++;
}
}
return ResultCode.Success;
}
///
/// Get the current state of the .
///
/// Return the curent sta\te of the
public AudioDeviceState GetState()
{
lock (_parentLock)
{
return _session.GetState();
}
}
///
/// Start the audio session.
///
/// A reporting an error or a success
public ResultCode Start()
{
lock (_parentLock)
{
return _session.Start();
}
}
///
/// Stop the audio session.
///
/// A reporting an error or a success
public ResultCode Stop()
{
lock (_parentLock)
{
return _session.Stop();
}
}
///
/// Get the volume of the session.
///
/// The volume of the session
public float GetVolume()
{
lock (_parentLock)
{
return _session.GetVolume();
}
}
///
/// Set the volume of the session.
///
/// The new volume to set
public void SetVolume(float volume)
{
lock (_parentLock)
{
_session.SetVolume(volume);
}
}
///
/// Get the count of buffer currently in use (server + driver side).
///
/// The count of buffer currently in use
public uint GetBufferCount()
{
lock (_parentLock)
{
return _session.GetBufferCount();
}
}
///
/// Check if a buffer is present.
///
/// The unique tag of the buffer
/// Return true if a buffer is present
public bool ContainsBuffer(ulong bufferTag)
{
lock (_parentLock)
{
return _session.ContainsBuffer(bufferTag);
}
}
///
/// Get the count of sample played in this session.
///
/// The count of sample played in this session
public ulong GetPlayedSampleCount()
{
lock (_parentLock)
{
return _session.GetPlayedSampleCount();
}
}
///
/// Flush all buffers to the initial state.
///
/// True if any buffers was flushed
public bool FlushBuffers()
{
lock (_parentLock)
{
return _session.FlushBuffers();
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
{
Dispose(true);
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_session.Dispose();
_manager.Unregister(this);
}
}
}
}