using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using System; using System.Threading; namespace Ryujinx.Audio.Output { /// /// Audio output system. /// public class AudioOutputSystem : IDisposable { /// /// The session id associated to the . /// private int _sessionId; /// /// The session the . /// private 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 AudioOutputManager _manager; /// /// THe lock of the parent. /// private 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 output public AudioOutputSystem(AudioOutputManager 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.DefaultDeviceOutputName; } /// /// 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; } else if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) { return ResultCode.UnsupportedSampleRate; } else 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 output. /// /// 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 AudioBuffer { BufferTag = bufferTag, DataPointer = userBuffer.Data, DataSize = userBuffer.DataSize }; if (_session.AppendBuffer(buffer)) { 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 GetReleasedBuffer(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() { if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); } } protected virtual void Dispose(bool disposing) { if (disposing) { _session.Dispose(); _manager.Unregister(this); } } } }