//
// Copyright (c) 2019-2021 Ryujinx
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using System;
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;
///
/// 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()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_session.Dispose();
_manager.Unregister(this);
}
}
}
}