From f556c80d0230056335632b60c71f1567e177239e Mon Sep 17 00:00:00 2001 From: Mary Date: Fri, 26 Feb 2021 01:11:56 +0100 Subject: 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 --- .../OpenALHardwareDeviceSession.cs | 213 +++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs (limited to 'Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs') diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs new file mode 100644 index 00000000..7bca4427 --- /dev/null +++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -0,0 +1,213 @@ +using OpenTK.Audio.OpenAL; +using Ryujinx.Audio.Backends.Common; +using Ryujinx.Audio.Common; +using Ryujinx.Memory; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Ryujinx.Audio.Backends.OpenAL +{ + class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase + { + private OpenALHardwareDeviceDriver _driver; + private int _sourceId; + private ALFormat _targetFormat; + private bool _isActive; + private Queue _queuedBuffers; + private ulong _playedSampleCount; + + private object _lock = new object(); + + public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + { + _driver = driver; + _queuedBuffers = new Queue(); + _sourceId = AL.GenSource(); + _targetFormat = GetALFormat(); + _isActive = false; + _playedSampleCount = 0; + } + + private ALFormat GetALFormat() + { + switch (RequestedSampleFormat) + { + case SampleFormat.PcmInt16: + switch (RequestedChannelCount) + { + case 1: + return ALFormat.Mono16; + case 2: + return ALFormat.Stereo16; + case 6: + return ALFormat.Multi51Chn16Ext; + default: + throw new NotImplementedException($"Unsupported channel config {RequestedChannelCount}"); + } + default: + throw new NotImplementedException($"Unsupported sample format {RequestedSampleFormat}"); + } + } + + public override void PrepareToClose() { } + + private void StartIfNotPlaying() + { + AL.GetSource(_sourceId, ALGetSourcei.SourceState, out int stateInt); + + ALSourceState State = (ALSourceState)stateInt; + + if (State != ALSourceState.Playing) + { + AL.SourcePlay(_sourceId); + } + } + + public override void QueueBuffer(AudioBuffer buffer) + { + lock (_lock) + { + OpenALAudioBuffer driverBuffer = new OpenALAudioBuffer + { + DriverIdentifier = buffer.DataPointer, + BufferId = AL.GenBuffer(), + SampleCount = GetSampleCount(buffer) + }; + + AL.BufferData(driverBuffer.BufferId, _targetFormat, buffer.Data, (int)buffer.DataSize, (int)RequestedSampleRate); + + _queuedBuffers.Enqueue(driverBuffer); + + AL.SourceQueueBuffer(_sourceId, driverBuffer.BufferId); + + if (_isActive) + { + StartIfNotPlaying(); + } + } + } + + public override void SetVolume(float volume) + { + lock (_lock) + { + AL.Source(_sourceId, ALSourcef.Gain, volume); + } + } + + public override float GetVolume() + { + AL.GetSource(_sourceId, ALSourcef.Gain, out float volume); + + return volume; + } + + public override void Start() + { + lock (_lock) + { + _isActive = true; + + StartIfNotPlaying(); + } + } + + public override void Stop() + { + lock (_lock) + { + SetVolume(0.0f); + + AL.SourceStop(_sourceId); + + _isActive = false; + } + } + + public override void UnregisterBuffer(AudioBuffer buffer) {} + + public override bool WasBufferFullyConsumed(AudioBuffer buffer) + { + lock (_lock) + { + if (!_queuedBuffers.TryPeek(out OpenALAudioBuffer driverBuffer)) + { + return true; + } + + return driverBuffer.DriverIdentifier != buffer.DataPointer; + } + } + + public override ulong GetPlayedSampleCount() + { + lock (_lock) + { + return _playedSampleCount; + } + } + + public bool Update() + { + lock (_lock) + { + if (_isActive) + { + AL.GetSource(_sourceId, ALGetSourcei.BuffersProcessed, out int releasedCount); + + if (releasedCount > 0) + { + int[] bufferIds = new int[releasedCount]; + + AL.SourceUnqueueBuffers(_sourceId, releasedCount, bufferIds); + + int i = 0; + + while (_queuedBuffers.TryPeek(out OpenALAudioBuffer buffer) && i < bufferIds.Length) + { + if (buffer.BufferId == bufferIds[i]) + { + _playedSampleCount += buffer.SampleCount; + + _queuedBuffers.TryDequeue(out _); + + i++; + } + } + + Debug.Assert(i < bufferIds.Length, "Unknown buffer id"); + + AL.DeleteBuffers(bufferIds); + } + + return releasedCount > 0; + } + + return false; + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (_lock) + { + PrepareToClose(); + Stop(); + + AL.DeleteSource(_sourceId); + + _driver.Unregister(this); + } + } + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} -- cgit v1.2.3