Haydn: Part 1 (#2007)
* Haydn: Part 1 Based on my reverse of audio 11.0.0. As always, core implementation under LGPLv3 for the same reasons as for Amadeus. This place the bases of a more flexible audio system while making audout & audin accurate. This have the following improvements: - Complete reimplementation of audout and audin. - Audin currently only have a dummy backend. - Dramatically reduce CPU usage by up to 50% in common cases (SoundIO and OpenAL). - Audio Renderer now can output to 5.1 devices when supported. - Audio Renderer init its backend on demand instead of keeping two up all the time. - All backends implementation are now in their own project. - Ryujinx.Audio.Renderer was renamed Ryujinx.Audio and was refactored because of this. As a note, games having issues with OpenAL haven't improved and will not because of OpenAL design (stopping when buffers finish playing causing possible audio "pops" when buffers are very small). * Update for latest hexkyz's edits on Switchbrew * audren: Rollback channel configuration changes * Address gdkchan's comments * Fix typo in OpenAL backend driver * Address last comments * Fix a nit * Address gdkchan's comments
This commit is contained in:
parent
1c49089ff0
commit
f556c80d02
249 changed files with 5614 additions and 2712 deletions
262
Ryujinx.Audio/Input/AudioInputManager.cs
Normal file
262
Ryujinx.Audio/Input/AudioInputManager.cs
Normal file
|
@ -0,0 +1,262 @@
|
|||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Audio.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio input manager.
|
||||
/// </summary>
|
||||
public class AudioInputManager : IDisposable
|
||||
{
|
||||
private object _lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Lock used for session allocation.
|
||||
/// </summary>
|
||||
private object _sessionLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The session ids allocation table.
|
||||
/// </summary>
|
||||
private int[] _sessionIds;
|
||||
|
||||
/// <summary>
|
||||
/// The device driver.
|
||||
/// </summary>
|
||||
private IHardwareDeviceDriver _deviceDriver;
|
||||
|
||||
/// <summary>
|
||||
/// The events linked to each session.
|
||||
/// </summary>
|
||||
private IWritableEvent[] _sessionsBufferEvents;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="AudioInputSystem"/> session instances.
|
||||
/// </summary>
|
||||
private AudioInputSystem[] _sessions;
|
||||
|
||||
/// <summary>
|
||||
/// The count of active sessions.
|
||||
/// </summary>
|
||||
private int _activeSessionCount;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AudioInputManager"/>.
|
||||
/// </summary>
|
||||
public AudioInputManager()
|
||||
{
|
||||
_sessionIds = new int[Constants.AudioInSessionCountMax];
|
||||
_sessions = new AudioInputSystem[Constants.AudioInSessionCountMax];
|
||||
_activeSessionCount = 0;
|
||||
|
||||
for (int i = 0; i < _sessionIds.Length; i++)
|
||||
{
|
||||
_sessionIds[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the <see cref="AudioInputManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="deviceDriver">The device driver.</param>
|
||||
/// <param name="sessionRegisterEvents">The events associated to each session.</param>
|
||||
public void Initialize(IHardwareDeviceDriver deviceDriver, IWritableEvent[] sessionRegisterEvents)
|
||||
{
|
||||
_deviceDriver = deviceDriver;
|
||||
_sessionsBufferEvents = sessionRegisterEvents;
|
||||
}
|
||||
|
||||
/// <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 input ({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 input ({sessionId})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to update audio input system.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
foreach (AudioInputSystem input in _sessions)
|
||||
{
|
||||
input?.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new <see cref="AudioInputSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">The <see cref="AudioInputSystem"/> to register.</param>
|
||||
private void Register(AudioInputSystem input)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
_sessions[input.GetSessionId()] = input;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister a new <see cref="AudioInputSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">The <see cref="AudioInputSystem"/> to unregister.</param>
|
||||
internal void Unregister(AudioInputSystem input)
|
||||
{
|
||||
lock (_sessionLock)
|
||||
{
|
||||
int sessionId = input.GetSessionId();
|
||||
|
||||
_sessions[input.GetSessionId()] = null;
|
||||
|
||||
ReleaseSessionId(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of all audio inputs names.
|
||||
/// </summary>
|
||||
/// <param name="filtered">If true, filter disconnected devices</param>
|
||||
/// <returns>The list of all audio inputs name</returns>
|
||||
public string[] ListAudioIns(bool filtered)
|
||||
{
|
||||
if (filtered)
|
||||
{
|
||||
// TODO: Detect if the driver supports audio input
|
||||
}
|
||||
|
||||
return new string[] { Constants.DefaultDeviceInputName };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a new <see cref="AudioInputSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="outputDeviceName">The output device name selected by the <see cref="AudioInputSystem"/></param>
|
||||
/// <param name="outputConfiguration">The output audio configuration selected by the <see cref="AudioInputSystem"/></param>
|
||||
/// <param name="obj">The new <see cref="AudioInputSystem"/></param>
|
||||
/// <param name="memoryManager">The memory manager that will be used for all guest memory operations</param>
|
||||
/// <param name="inputDeviceName">The input device name wanted by the user</param>
|
||||
/// <param name="sampleFormat">The sample format to use</param>
|
||||
/// <param name="parameter">The user configuration</param>
|
||||
/// <param name="appletResourceUserId">The applet resource user id of the application</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 OpenAudioIn(out string outputDeviceName,
|
||||
out AudioOutputConfiguration outputConfiguration,
|
||||
out AudioInputSystem obj,
|
||||
IVirtualMemoryManager memoryManager,
|
||||
string inputDeviceName,
|
||||
SampleFormat sampleFormat,
|
||||
ref AudioInputConfiguration parameter,
|
||||
ulong appletResourceUserId,
|
||||
uint processHandle)
|
||||
{
|
||||
int sessionId = AcquireSessionId();
|
||||
|
||||
_sessionsBufferEvents[sessionId].Clear();
|
||||
|
||||
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Input, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount);
|
||||
|
||||
AudioInputSystem audioIn = new AudioInputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
||||
|
||||
ResultCode result = audioIn.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
outputDeviceName = audioIn.DeviceName;
|
||||
outputConfiguration = new AudioOutputConfiguration
|
||||
{
|
||||
ChannelCount = audioIn.ChannelCount,
|
||||
SampleFormat = audioIn.SampleFormat,
|
||||
SampleRate = audioIn.SampleRate,
|
||||
AudioOutState = audioIn.GetState(),
|
||||
};
|
||||
|
||||
obj = audioIn;
|
||||
|
||||
Register(audioIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseSessionId(sessionId);
|
||||
|
||||
obj = null;
|
||||
outputDeviceName = null;
|
||||
outputConfiguration = default;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue