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 --- .../Server/Voice/VoiceChannelResource.cs | 57 -- .../Server/Voice/VoiceContext.cs | 166 ----- Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs | 715 --------------------- Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs | 121 ---- 4 files changed, 1059 deletions(-) delete mode 100644 Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs delete mode 100644 Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs delete mode 100644 Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs delete mode 100644 Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs (limited to 'Ryujinx.Audio.Renderer/Server/Voice') diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs b/Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs deleted file mode 100644 index 00520cfc..00000000 --- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// 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.Common.Memory; -using System.Runtime.InteropServices; - -namespace Ryujinx.Audio.Renderer.Server.Voice -{ - /// - /// Server state for a voice channel resource. - /// - [StructLayout(LayoutKind.Sequential, Size = 0xD0, Pack = Alignment)] - public struct VoiceChannelResource - { - public const int Alignment = 0x10; - - /// - /// Mix volumes for the resource. - /// - public Array24 Mix; - - /// - /// Previous mix volumes for resource. - /// - public Array24 PreviousMix; - - /// - /// The id of the resource. - /// - public uint Id; - - /// - /// Indicate if the resource is used. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsUsed; - - public void UpdateState() - { - Mix.ToSpan().CopyTo(PreviousMix.ToSpan()); - } - } -} diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs b/Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs deleted file mode 100644 index 0dfaa4d5..00000000 --- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs +++ /dev/null @@ -1,166 +0,0 @@ -// -// 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.Renderer.Common; -using Ryujinx.Audio.Renderer.Utils; -using System; -using System.Diagnostics; - -namespace Ryujinx.Audio.Renderer.Server.Voice -{ - /// - /// Voice context. - /// - public class VoiceContext - { - /// - /// Storage of the sorted indices to . - /// - private Memory _sortedVoices; - - /// - /// Storage for . - /// - private Memory _voices; - - /// - /// Storage for . - /// - private Memory _voiceChannelResources; - - /// - /// Storage for that are used during audio renderer server updates. - /// - private Memory _voiceUpdateStatesCpu; - - /// - /// Storage for for the . - /// - private Memory _voiceUpdateStatesDsp; - - /// - /// The total voice count. - /// - private uint _voiceCount; - - public void Initialize(Memory sortedVoices, Memory voices, Memory voiceChannelResources, Memory voiceUpdateStatesCpu, Memory voiceUpdateStatesDsp, uint voiceCount) - { - _sortedVoices = sortedVoices; - _voices = voices; - _voiceChannelResources = voiceChannelResources; - _voiceUpdateStatesCpu = voiceUpdateStatesCpu; - _voiceUpdateStatesDsp = voiceUpdateStatesDsp; - _voiceCount = voiceCount; - } - - /// - /// Get the total voice count. - /// - /// The total voice count. - public uint GetCount() - { - return _voiceCount; - } - - /// - /// Get a reference to a at the given . - /// - /// The index to use. - /// A reference to a at the given . - public ref VoiceChannelResource GetChannelResource(int id) - { - return ref SpanIOHelper.GetFromMemory(_voiceChannelResources, id, _voiceCount); - } - - /// - /// Get a at the given . - /// - /// The index to use. - /// A at the given . - /// The returned should only be used when updating the server state. - public Memory GetUpdateStateForCpu(int id) - { - return SpanIOHelper.GetMemory(_voiceUpdateStatesCpu, id, _voiceCount); - } - - /// - /// Get a at the given . - /// - /// The index to use. - /// A at the given . - /// The returned should only be used in the context of processing on the . - public Memory GetUpdateStateForDsp(int id) - { - return SpanIOHelper.GetMemory(_voiceUpdateStatesDsp, id, _voiceCount); - } - - /// - /// Get a reference to a at the given . - /// - /// The index to use. - /// A reference to a at the given . - public ref VoiceState GetState(int id) - { - return ref SpanIOHelper.GetFromMemory(_voices, id, _voiceCount); - } - - public ref VoiceState GetSortedState(int id) - { - Debug.Assert(id >= 0 && id < _voiceCount); - - return ref GetState(_sortedVoices.Span[id]); - } - - /// - /// Update internal state during command generation. - /// - public void UpdateForCommandGeneration() - { - _voiceUpdateStatesDsp.CopyTo(_voiceUpdateStatesCpu); - } - - /// - /// Sort the internal voices by priority and sorting order (if the priorities match). - /// - public void Sort() - { - for (int i = 0; i < _voiceCount; i++) - { - _sortedVoices.Span[i] = i; - } - - int[] sortedVoicesTemp = _sortedVoices.Slice(0, (int)GetCount()).ToArray(); - - Array.Sort(sortedVoicesTemp, (a, b) => - { - ref VoiceState aState = ref GetState(a); - ref VoiceState bState = ref GetState(b); - - int result = aState.Priority.CompareTo(bState.Priority); - - if (result == 0) - { - return aState.SortingOrder.CompareTo(bState.SortingOrder); - } - - return result; - }); - - sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span); - } - } -} diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs b/Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs deleted file mode 100644 index f30ee2f7..00000000 --- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs +++ /dev/null @@ -1,715 +0,0 @@ -// -// 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.Renderer.Common; -using Ryujinx.Audio.Renderer.Parameter; -using Ryujinx.Audio.Renderer.Server.MemoryPool; -using Ryujinx.Common.Memory; -using Ryujinx.Common.Utilities; -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using static Ryujinx.Audio.Renderer.Common.BehaviourParameter; -using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; - -namespace Ryujinx.Audio.Renderer.Server.Voice -{ - [StructLayout(LayoutKind.Sequential, Pack = Alignment)] - public struct VoiceState - { - public const int Alignment = 0x10; - - /// - /// Set to true if the voice is used. - /// - [MarshalAs(UnmanagedType.I1)] - public bool InUse; - - /// - /// Set to true if the voice is new. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsNew; - - [MarshalAs(UnmanagedType.I1)] - public bool WasPlaying; - - /// - /// The of the voice. - /// - public SampleFormat SampleFormat; - - /// - /// The sample rate of the voice. - /// - public uint SampleRate; - - /// - /// The total channel count used. - /// - public uint ChannelsCount; - - /// - /// Id of the voice. - /// - public int Id; - - /// - /// Node id of the voice. - /// - public int NodeId; - - /// - /// The target mix id of the voice. - /// - public int MixId; - - /// - /// The current voice . - /// - public Types.PlayState PlayState; - - /// - /// The previous voice . - /// - public Types.PlayState PreviousPlayState; - - /// - /// The priority of the voice. - /// - public uint Priority; - - /// - /// Target sorting position of the voice. (used to sort voice with the same ) - /// - public uint SortingOrder; - - /// - /// The pitch used on the voice. - /// - public float Pitch; - - /// - /// The output volume of the voice. - /// - public float Volume; - - /// - /// The previous output volume of the voice. - /// - public float PreviousVolume; - - /// - /// Biquad filters to apply to the output of the voice. - /// - public Array2 BiquadFilters; - - /// - /// Total count of of the voice. - /// - public uint WaveBuffersCount; - - /// - /// Current playing of the voice. - /// - public uint WaveBuffersIndex; - - /// - /// Change the behaviour of the voice. - /// - /// This was added on REV5. - public DecodingBehaviour DecodingBehaviour; - - /// - /// User state required by the data source. - /// - /// Only used for as the GC-ADPCM coefficients. - public AddressInfo DataSourceStateAddressInfo; - - /// - /// The wavebuffers of this voice. - /// - public Array4 WaveBuffers; - - /// - /// The channel resource ids associated to the voice. - /// - public Array6 ChannelResourceIds; - - /// - /// The target splitter id of the voice. - /// - public uint SplitterId; - - /// - /// Change the Sample Rate Conversion (SRC) quality of the voice. - /// - /// This was added on REV8. - public SampleRateConversionQuality SrcQuality; - - /// - /// If set to true, the voice was dropped. - /// - [MarshalAs(UnmanagedType.I1)] - public bool VoiceDropFlag; - - /// - /// Set to true if the data source state work buffer wasn't mapped. - /// - [MarshalAs(UnmanagedType.I1)] - public bool DataSourceStateUnmapped; - - /// - /// Set to true if any of the work buffer wasn't mapped. - /// - [MarshalAs(UnmanagedType.I1)] - public bool BufferInfoUnmapped; - - /// - /// The biquad filter initialization state storage. - /// - private BiquadFilterNeedInitializationArrayStruct _biquadFilterNeedInitialization; - - /// - /// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played. - /// - /// This was added on REV5. - public byte FlushWaveBufferCount; - - [StructLayout(LayoutKind.Sequential, Size = RendererConstants.VoiceBiquadFilterCount)] - private struct BiquadFilterNeedInitializationArrayStruct { } - - /// - /// The biquad filter initialization state array. - /// - public Span BiquadFilterNeedInitialization => SpanHelpers.AsSpan(ref _biquadFilterNeedInitialization); - - /// - /// Initialize the . - /// - public void Initialize() - { - IsNew = false; - VoiceDropFlag = false; - DataSourceStateUnmapped = false; - BufferInfoUnmapped = false; - FlushWaveBufferCount = 0; - PlayState = Types.PlayState.Stopped; - Priority = RendererConstants.VoiceLowestPriority; - Id = 0; - NodeId = 0; - SampleRate = 0; - SampleFormat = SampleFormat.Invalid; - ChannelsCount = 0; - Pitch = 0.0f; - Volume= 0.0f; - PreviousVolume = 0.0f; - BiquadFilters.ToSpan().Fill(new BiquadFilterParameter()); - WaveBuffersCount = 0; - WaveBuffersIndex = 0; - MixId = RendererConstants.UnusedMixId; - SplitterId = RendererConstants.UnusedSplitterId; - DataSourceStateAddressInfo.Setup(0, 0); - - InitializeWaveBuffers(); - } - - /// - /// Initialize the in this . - /// - private void InitializeWaveBuffers() - { - for (int i = 0; i < WaveBuffers.Length; i++) - { - WaveBuffers[i].StartSampleOffset = 0; - WaveBuffers[i].EndSampleOffset = 0; - WaveBuffers[i].ShouldLoop = false; - WaveBuffers[i].IsEndOfStream = false; - WaveBuffers[i].BufferAddressInfo.Setup(0, 0); - WaveBuffers[i].ContextAddressInfo.Setup(0, 0); - WaveBuffers[i].IsSendToAudioProcessor = true; - } - } - - /// - /// Check if the voice needs to be skipped. - /// - /// Returns true if the voice needs to be skipped. - public bool ShouldSkip() - { - return !InUse || WaveBuffersCount == 0 || DataSourceStateUnmapped || BufferInfoUnmapped || VoiceDropFlag; - } - - /// - /// Return true if the mix has any destinations. - /// - /// True if the mix has any destinations. - public bool HasAnyDestination() - { - return MixId != RendererConstants.UnusedMixId || SplitterId != RendererConstants.UnusedSplitterId; - } - - /// - /// Indicate if the server voice information needs to be updated. - /// - /// The user parameter. - /// Return true, if the server voice information needs to be updated. - private bool ShouldUpdateParameters(ref VoiceInParameter parameter) - { - if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress) - { - return DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize; - } - - return DataSourceStateAddressInfo.CpuAddress != parameter.DataSourceStateAddress || - DataSourceStateAddressInfo.Size != parameter.DataSourceStateSize || - DataSourceStateUnmapped; - } - - /// - /// Update the internal state from a user parameter. - /// - /// The possible that was generated. - /// The user parameter. - /// The mapper to use. - /// The behaviour context. - public void UpdateParameters(out ErrorInfo outErrorInfo, ref VoiceInParameter parameter, ref PoolMapper poolMapper, ref BehaviourContext behaviourContext) - { - InUse = parameter.InUse; - Id = parameter.Id; - NodeId = parameter.NodeId; - - UpdatePlayState(parameter.PlayState); - - SrcQuality = parameter.SrcQuality; - - Priority = parameter.Priority; - SortingOrder = parameter.SortingOrder; - SampleRate = parameter.SampleRate; - SampleFormat = parameter.SampleFormat; - ChannelsCount = parameter.ChannelCount; - Pitch = parameter.Pitch; - Volume = parameter.Volume; - parameter.BiquadFilters.ToSpan().CopyTo(BiquadFilters.ToSpan()); - WaveBuffersCount = parameter.WaveBuffersCount; - WaveBuffersIndex = parameter.WaveBuffersIndex; - - if (behaviourContext.IsFlushVoiceWaveBuffersSupported()) - { - FlushWaveBufferCount += parameter.FlushWaveBufferCount; - } - - MixId = parameter.MixId; - - if (behaviourContext.IsSplitterSupported()) - { - SplitterId = parameter.SplitterId; - } - else - { - SplitterId = RendererConstants.UnusedSplitterId; - } - - parameter.ChannelResourceIds.ToSpan().CopyTo(ChannelResourceIds.ToSpan()); - - DecodingBehaviour behaviour = DecodingBehaviour.Default; - - if (behaviourContext.IsDecodingBehaviourFlagSupported()) - { - behaviour = parameter.DecodingBehaviourFlags; - } - - DecodingBehaviour = behaviour; - - if (parameter.ResetVoiceDropFlag) - { - VoiceDropFlag = false; - } - - if (ShouldUpdateParameters(ref parameter)) - { - DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize); - } - else - { - outErrorInfo = new ErrorInfo(); - } - } - - /// - /// Update the internal play state from user play state. - /// - /// The target user play state. - public void UpdatePlayState(PlayState userPlayState) - { - Types.PlayState oldServerPlayState = PlayState; - - PreviousPlayState = oldServerPlayState; - - Types.PlayState newServerPlayState; - - switch (userPlayState) - { - case Common.PlayState.Start: - newServerPlayState = Types.PlayState.Started; - break; - - case Common.PlayState.Stop: - if (oldServerPlayState == Types.PlayState.Stopped) - { - return; - } - - newServerPlayState = Types.PlayState.Stopping; - break; - - case Common.PlayState.Pause: - newServerPlayState = Types.PlayState.Paused; - break; - - default: - throw new NotImplementedException($"Unhandled PlayState.{userPlayState}"); - } - - PlayState = newServerPlayState; - } - - /// - /// Write the status of the voice to the given user output. - /// - /// The given user output. - /// The user parameter. - /// The voice states associated to the . - public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory[] voiceUpdateStates) - { -#if DEBUG - // Sanity check in debug mode of the internal state - if (!parameter.IsNew && !IsNew) - { - for (int i = 1; i < ChannelsCount; i++) - { - ref VoiceUpdateState stateA = ref voiceUpdateStates[i - 1].Span[0]; - ref VoiceUpdateState stateB = ref voiceUpdateStates[i].Span[0]; - - Debug.Assert(stateA.WaveBufferConsumed == stateB.WaveBufferConsumed); - Debug.Assert(stateA.PlayedSampleCount == stateB.PlayedSampleCount); - Debug.Assert(stateA.Offset == stateB.Offset); - Debug.Assert(stateA.WaveBufferIndex == stateB.WaveBufferIndex); - Debug.Assert(stateA.Fraction == stateB.Fraction); - Debug.Assert(stateA.IsWaveBufferValid.SequenceEqual(stateB.IsWaveBufferValid)); - } - } -#endif - if (parameter.IsNew || IsNew) - { - IsNew = true; - - outStatus.VoiceDropFlag = false; - outStatus.PlayedWaveBuffersCount = 0; - outStatus.PlayedSampleCount = 0; - } - else - { - ref VoiceUpdateState state = ref voiceUpdateStates[0].Span[0]; - - outStatus.VoiceDropFlag = VoiceDropFlag; - outStatus.PlayedWaveBuffersCount = state.WaveBufferConsumed; - outStatus.PlayedSampleCount = state.PlayedSampleCount; - } - } - - /// - /// Update the internal state of all the of the . - /// - /// An array of used to report errors when mapping any of the . - /// The user parameter. - /// The voice states associated to the . - /// The mapper to use. - /// The behaviour context. - public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext) - { - errorInfos = new ErrorInfo[RendererConstants.VoiceWaveBufferCount * 2]; - - if (parameter.IsNew) - { - InitializeWaveBuffers(); - - for (int i = 0; i < parameter.ChannelCount; i++) - { - voiceUpdateStates[i].Span[0].IsWaveBufferValid.Fill(false); - } - } - - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0]; - - for (int i = 0; i < RendererConstants.VoiceWaveBufferCount; i++) - { - UpdateWaveBuffer(errorInfos.AsSpan().Slice(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], ref mapper, ref behaviourContext); - } - } - - /// - /// Update the internal state of one of the of the . - /// - /// A used to report errors when mapping the . - /// The to update. - /// The from the user input. - /// The from the user input. - /// If set to true, the server side wavebuffer is considered valid. - /// The mapper to use. - /// The behaviour context. - private void UpdateWaveBuffer(Span errorInfos, ref WaveBuffer waveBuffer, ref WaveBufferInternal inputWaveBuffer, SampleFormat sampleFormat, bool isValid, ref PoolMapper mapper, ref BehaviourContext behaviourContext) - { - if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0) - { - mapper.ForceUnmap(ref waveBuffer.BufferAddressInfo); - waveBuffer.BufferAddressInfo.Setup(0, 0); - } - - if (!inputWaveBuffer.SentToServer || BufferInfoUnmapped) - { - if (inputWaveBuffer.IsSampleOffsetValid(sampleFormat)) - { - Debug.Assert(waveBuffer.IsSendToAudioProcessor); - - waveBuffer.IsSendToAudioProcessor = false; - waveBuffer.StartSampleOffset = inputWaveBuffer.StartSampleOffset; - waveBuffer.EndSampleOffset = inputWaveBuffer.EndSampleOffset; - waveBuffer.ShouldLoop = inputWaveBuffer.ShouldLoop; - waveBuffer.IsEndOfStream = inputWaveBuffer.IsEndOfStream; - waveBuffer.LoopStartSampleOffset = inputWaveBuffer.LoopFirstSampleOffset; - waveBuffer.LoopEndSampleOffset = inputWaveBuffer.LoopLastSampleOffset; - waveBuffer.LoopCount = inputWaveBuffer.LoopCount; - - BufferInfoUnmapped = !mapper.TryAttachBuffer(out ErrorInfo bufferInfoError, ref waveBuffer.BufferAddressInfo, inputWaveBuffer.Address, inputWaveBuffer.Size); - - errorInfos[0] = bufferInfoError; - - if (sampleFormat == SampleFormat.Adpcm && behaviourContext.IsAdpcmLoopContextBugFixed() && inputWaveBuffer.ContextAddress != 0) - { - bool adpcmLoopContextMapped = mapper.TryAttachBuffer(out ErrorInfo adpcmLoopContextInfoError, - ref waveBuffer.ContextAddressInfo, - inputWaveBuffer.ContextAddress, - inputWaveBuffer.ContextSize); - - errorInfos[1] = adpcmLoopContextInfoError; - - if (adpcmLoopContextMapped) - { - BufferInfoUnmapped = DataSourceStateUnmapped; - } - else - { - BufferInfoUnmapped = true; - } - } - else - { - waveBuffer.ContextAddressInfo.Setup(0, 0); - } - } - else - { - errorInfos[0].ErrorCode = ResultCode.InvalidAddressInfo; - errorInfos[0].ExtraErrorInfo = inputWaveBuffer.Address; - } - } - } - - /// - /// Reset the resources associated to this . - /// - /// The voice context. - private void ResetResources(VoiceContext context) - { - for (int i = 0; i < ChannelsCount; i++) - { - int channelResourceId = ChannelResourceIds[i]; - - ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId); - - Debug.Assert(voiceChannelResource.IsUsed); - - Memory dspSharedState = context.GetUpdateStateForDsp(channelResourceId); - - MemoryMarshal.Cast(dspSharedState.Span).Fill(0); - - voiceChannelResource.UpdateState(); - } - } - - /// - /// Flush a certain amount of . - /// - /// The amount of wavebuffer to flush. - /// The voice states associated to the . - /// The channel count from user input. - private void FlushWaveBuffers(uint waveBufferCount, Memory[] voiceUpdateStates, uint channelCount) - { - uint waveBufferIndex = WaveBuffersIndex; - - for (int i = 0; i < waveBufferCount; i++) - { - WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true; - - for (int j = 0; j < channelCount; j++) - { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; - - voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount; - voiceUpdateState.WaveBufferConsumed++; - voiceUpdateState.IsWaveBufferValid[(int)waveBufferIndex] = false; - } - - waveBufferIndex = (waveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount; - } - } - - /// - /// Update the internal parameters for command generation. - /// - /// The voice states associated to the . - /// Return true if this voice should be played. - public bool UpdateParametersForCommandGeneration(Memory[] voiceUpdateStates) - { - if (FlushWaveBufferCount != 0) - { - FlushWaveBuffers(FlushWaveBufferCount, voiceUpdateStates, ChannelsCount); - - FlushWaveBufferCount = 0; - } - - switch (PlayState) - { - case Types.PlayState.Started: - for (int i = 0; i < WaveBuffers.Length; i++) - { - ref WaveBuffer wavebuffer = ref WaveBuffers[i]; - - if (!wavebuffer.IsSendToAudioProcessor) - { - for (int y = 0; y < ChannelsCount; y++) - { - Debug.Assert(!voiceUpdateStates[y].Span[0].IsWaveBufferValid[i]); - - voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true; - } - - wavebuffer.IsSendToAudioProcessor = true; - } - } - - WasPlaying = false; - - ref VoiceUpdateState primaryVoiceUpdateState = ref voiceUpdateStates[0].Span[0]; - - for (int i = 0; i < primaryVoiceUpdateState.IsWaveBufferValid.Length; i++) - { - if (primaryVoiceUpdateState.IsWaveBufferValid[i]) - { - return true; - } - } - - return false; - - case Types.PlayState.Stopping: - for (int i = 0; i < WaveBuffers.Length; i++) - { - ref WaveBuffer wavebuffer = ref WaveBuffers[i]; - - wavebuffer.IsSendToAudioProcessor = true; - - for (int j = 0; j < ChannelsCount; j++) - { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0]; - - if (voiceUpdateState.IsWaveBufferValid[i]) - { - voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount; - voiceUpdateState.WaveBufferConsumed++; - } - - voiceUpdateState.IsWaveBufferValid[i] = false; - } - } - - for (int i = 0; i < ChannelsCount; i++) - { - ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[i].Span[0]; - - voiceUpdateState.Offset = 0; - voiceUpdateState.PlayedSampleCount = 0; - voiceUpdateState.Pitch.ToSpan().Fill(0); - voiceUpdateState.Fraction = 0; - voiceUpdateState.LoopContext = new Dsp.State.AdpcmLoopContext(); - } - - PlayState = Types.PlayState.Stopped; - WasPlaying = PreviousPlayState == Types.PlayState.Started; - - return WasPlaying; - - case Types.PlayState.Stopped: - case Types.PlayState.Paused: - foreach (ref WaveBuffer wavebuffer in WaveBuffers.ToSpan()) - { - wavebuffer.BufferAddressInfo.GetReference(true); - wavebuffer.ContextAddressInfo.GetReference(true); - } - - if (SampleFormat == SampleFormat.Adpcm) - { - if (DataSourceStateAddressInfo.CpuAddress != 0) - { - DataSourceStateAddressInfo.GetReference(true); - } - } - - WasPlaying = PreviousPlayState == Types.PlayState.Started; - - return WasPlaying; - default: - throw new NotImplementedException($"{PlayState}"); - } - } - - /// - /// Update the internal state for command generation. - /// - /// The voice context. - /// Return true if this voice should be played. - public bool UpdateForCommandGeneration(VoiceContext context) - { - if (IsNew) - { - ResetResources(context); - PreviousVolume = Volume; - IsNew = false; - } - - Memory[] voiceUpdateStates = new Memory[RendererConstants.VoiceChannelCountMax]; - - for (int i = 0; i < ChannelsCount; i++) - { - voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]); - } - - return UpdateParametersForCommandGeneration(voiceUpdateStates); - } - } -} diff --git a/Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs b/Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs deleted file mode 100644 index a03aa757..00000000 --- a/Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -// 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.Renderer.Server.MemoryPool; -using System.Runtime.InteropServices; - -namespace Ryujinx.Audio.Renderer.Server.Voice -{ - /// - /// A wavebuffer used for server update. - /// - [StructLayout(LayoutKind.Sequential, Size = 0x58, Pack = 1)] - public struct WaveBuffer - { - /// - /// The of the sample data of the wavebuffer. - /// - public AddressInfo BufferAddressInfo; - - /// - /// The of the context of the wavebuffer. - /// - /// Only used by . - public AddressInfo ContextAddressInfo; - - - /// - /// First sample to play of the wavebuffer. - /// - public uint StartSampleOffset; - - /// - /// Last sample to play of the wavebuffer. - /// - public uint EndSampleOffset; - - /// - /// Set to true if the wavebuffer is looping. - /// - [MarshalAs(UnmanagedType.I1)] - public bool ShouldLoop; - - /// - /// Set to true if the wavebuffer is the end of stream. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsEndOfStream; - - /// - /// Set to true if the wavebuffer wasn't sent to the . - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsSendToAudioProcessor; - - /// - /// First sample to play when looping the wavebuffer. - /// - public uint LoopStartSampleOffset; - - /// - /// Last sample to play when looping the wavebuffer. - /// - public uint LoopEndSampleOffset; - - /// - /// The max loop count. - /// - public int LoopCount; - - /// - /// Create a new for use by the . - /// - /// The target version of the wavebuffer. - /// A new for use by the . - public Common.WaveBuffer ToCommon(int version) - { - Common.WaveBuffer waveBuffer = new Common.WaveBuffer(); - - waveBuffer.Buffer = BufferAddressInfo.GetReference(true); - waveBuffer.BufferSize = (uint)BufferAddressInfo.Size; - - if (ContextAddressInfo.CpuAddress != 0) - { - waveBuffer.Context = ContextAddressInfo.GetReference(true); - waveBuffer.ContextSize = (uint)ContextAddressInfo.Size; - } - - waveBuffer.StartSampleOffset = StartSampleOffset; - waveBuffer.EndSampleOffset = EndSampleOffset; - waveBuffer.Looping = ShouldLoop; - waveBuffer.IsEndOfStream = IsEndOfStream; - - if (version == 2) - { - waveBuffer.LoopCount = LoopCount; - waveBuffer.LoopStartSampleOffset = LoopStartSampleOffset; - waveBuffer.LoopEndSampleOffset = LoopEndSampleOffset; - } - else - { - waveBuffer.LoopCount = -1; - } - - return waveBuffer; - } - } -} -- cgit v1.2.3