 f556c80d02
			
		
	
	
		f556c80d02
		
			
		
	
	
	
	
		
			
			* 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
		
			
				
	
	
		
			361 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //
 | |
| // 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.Renderer.Common;
 | |
| using Ryujinx.Audio.Renderer.Dsp;
 | |
| using Ryujinx.Common.Memory;
 | |
| using System;
 | |
| using System.Runtime.CompilerServices;
 | |
| using System.Runtime.InteropServices;
 | |
| 
 | |
| namespace Ryujinx.Audio.Renderer.Parameter
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Input information for a voice.
 | |
|     /// </summary>
 | |
|     [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)]
 | |
|     public struct VoiceInParameter
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Id of the voice.
 | |
|         /// </summary>
 | |
|         public int Id;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Node id of the voice.
 | |
|         /// </summary>
 | |
|         public int NodeId;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Set to true if the voice is new.
 | |
|         /// </summary>
 | |
|         [MarshalAs(UnmanagedType.I1)]
 | |
|         public bool IsNew;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Set to true if the voice is used.
 | |
|         /// </summary>
 | |
|         [MarshalAs(UnmanagedType.I1)]
 | |
|         public bool InUse;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The voice <see cref="PlayState"/> wanted by the user.
 | |
|         /// </summary>
 | |
|         public PlayState PlayState;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The <see cref="SampleFormat"/> of the voice.
 | |
|         /// </summary>
 | |
|         public SampleFormat SampleFormat;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The sample rate of the voice.
 | |
|         /// </summary>
 | |
|         public uint SampleRate;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The priority of the voice.
 | |
|         /// </summary>
 | |
|         public uint Priority;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Target sorting position of the voice. (Used to sort voices with the same <see cref="Priority"/>)
 | |
|         /// </summary>
 | |
|         public uint SortingOrder;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The total channel count used.
 | |
|         /// </summary>
 | |
|         public uint ChannelCount;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The pitch used on the voice.
 | |
|         /// </summary>
 | |
|         public float Pitch;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The output volume of the voice.
 | |
|         /// </summary>
 | |
|         public float Volume;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Biquad filters to apply to the output of the voice.
 | |
|         /// </summary>
 | |
|         public Array2<BiquadFilterParameter> BiquadFilters;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Total count of <see cref="WaveBufferInternal"/> of the voice.
 | |
|         /// </summary>
 | |
|         public uint WaveBuffersCount;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Current playing <see cref="WaveBufferInternal"/> of the voice.
 | |
|         /// </summary>
 | |
|         public uint WaveBuffersIndex;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Reserved/unused.
 | |
|         /// </summary>
 | |
|         private uint _reserved1;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// User state address required by the data source.
 | |
|         /// </summary>
 | |
|         /// <remarks>Only used for <see cref="SampleFormat.Adpcm"/> as the address of the GC-ADPCM coefficients.</remarks>
 | |
|         public ulong DataSourceStateAddress;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// User state size required by the data source.
 | |
|         /// </summary>
 | |
|         /// <remarks>Only used for <see cref="SampleFormat.Adpcm"/> as the size of the GC-ADPCM coefficients.</remarks>
 | |
|         public ulong DataSourceStateSize;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The target mix id of the voice.
 | |
|         /// </summary>
 | |
|         public int MixId;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The target splitter id of the voice.
 | |
|         /// </summary>
 | |
|         public uint SplitterId;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The wavebuffer parameters of this voice.
 | |
|         /// </summary>
 | |
|         public Array4<WaveBufferInternal> WaveBuffers;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The channel resource ids associated to the voice.
 | |
|         /// </summary>
 | |
|         public Array6<int> ChannelResourceIds;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Reset the voice drop flag during voice server update.
 | |
|         /// </summary>
 | |
|         [MarshalAs(UnmanagedType.I1)]
 | |
|         public bool ResetVoiceDropFlag;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Flush the amount of wavebuffer specified. This will result in the wavebuffer being skipped and marked played.
 | |
|         /// </summary>
 | |
|         /// <remarks>This was added on REV5.</remarks>
 | |
|         public byte FlushWaveBufferCount;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Reserved/unused.
 | |
|         /// </summary>
 | |
|         private ushort _reserved2;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Change the behaviour of the voice.
 | |
|         /// </summary>
 | |
|         /// <remarks>This was added on REV5.</remarks>
 | |
|         public DecodingBehaviour DecodingBehaviourFlags;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Change the Sample Rate Conversion (SRC) quality of the voice.
 | |
|         /// </summary>
 | |
|         /// <remarks>This was added on REV8.</remarks>
 | |
|         public SampleRateConversionQuality SrcQuality;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
 | |
|         /// </summary>
 | |
|         public uint ExternalContext;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// This was previously used for opus codec support on the Audio Renderer and was removed on REV3.
 | |
|         /// </summary>
 | |
|         public uint ExternalContextSize;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Reserved/unused.
 | |
|         /// </summary>
 | |
|         private unsafe fixed uint _reserved3[2];
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Input information for a voice wavebuffer.
 | |
|         /// </summary>
 | |
|         [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)]
 | |
|         public struct WaveBufferInternal
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Address of the wavebuffer data.
 | |
|             /// </summary>
 | |
|             public ulong Address;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Size of the wavebuffer data.
 | |
|             /// </summary>
 | |
|             public ulong Size;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Offset of the first sample to play.
 | |
|             /// </summary>
 | |
|             public uint StartSampleOffset;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Offset of the last sample to play.
 | |
|             /// </summary>
 | |
|             public uint EndSampleOffset;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// If set to true, the wavebuffer will loop when reaching <see cref="EndSampleOffset"/>.
 | |
|             /// </summary>
 | |
|             /// <remarks>
 | |
|             /// Starting with REV8, you can specify how many times to loop the wavebuffer (<see cref="LoopCount"/>) and where it should start and end when looping (<see cref="LoopFirstSampleOffset"/> and <see cref="LoopLastSampleOffset"/>)
 | |
|             /// </remarks>
 | |
|             [MarshalAs(UnmanagedType.I1)]
 | |
|             public bool ShouldLoop;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Indicates that this is the last wavebuffer to play of the voice.
 | |
|             /// </summary>
 | |
|             [MarshalAs(UnmanagedType.I1)]
 | |
|             public bool IsEndOfStream;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Indicates if the server should update its internal state.
 | |
|             /// </summary>
 | |
|             [MarshalAs(UnmanagedType.I1)]
 | |
|             public bool SentToServer;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Reserved/unused.
 | |
|             /// </summary>
 | |
|             private byte _reserved;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// If set to anything other than 0, specifies how many times to loop the wavebuffer.
 | |
|             /// </summary>
 | |
|             /// <remarks>This was added in REV8.</remarks>
 | |
|             public int LoopCount;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Address of the context used by the sample decoder.
 | |
|             /// </summary>
 | |
|             /// <remarks>This is only currently used by <see cref="SampleFormat.Adpcm"/>.</remarks>
 | |
|             public ulong ContextAddress;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Size of the context used by the sample decoder.
 | |
|             /// </summary>
 | |
|             /// <remarks>This is only currently used by <see cref="SampleFormat.Adpcm"/>.</remarks>
 | |
|             public ulong ContextSize;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// If set to anything other than 0, specifies the offset of the first sample to play when looping.
 | |
|             /// </summary>
 | |
|             /// <remarks>This was added in REV8.</remarks>
 | |
|             public uint LoopFirstSampleOffset;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// If set to anything other than 0, specifies the offset of the last sample to play when looping.
 | |
|             /// </summary>
 | |
|             /// <remarks>This was added in REV8.</remarks>
 | |
|             public uint LoopLastSampleOffset;
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Check if the sample offsets are in a valid range for generic PCM.
 | |
|             /// </summary>
 | |
|             /// <typeparam name="T">The PCM sample type</typeparam>
 | |
|             /// <returns>Returns true if the sample offset are in range of the size.</returns>
 | |
|             [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|             private bool IsSampleOffsetInRangeForPcm<T>() where T : unmanaged
 | |
|             {
 | |
|                 uint dataTypeSize = (uint)Unsafe.SizeOf<T>();
 | |
| 
 | |
|                 return StartSampleOffset * dataTypeSize <= Size &&
 | |
|                        EndSampleOffset * dataTypeSize <= Size;
 | |
|             }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Check if the sample offsets are in a valid range for the given <see cref="SampleFormat"/>.
 | |
|             /// </summary>
 | |
|             /// <param name="format">The target <see cref="SampleFormat"/></param>
 | |
|             /// <returns>Returns true if the sample offset are in range of the size.</returns>
 | |
|             public bool IsSampleOffsetValid(SampleFormat format)
 | |
|             {
 | |
|                 bool result;
 | |
| 
 | |
|                 switch (format)
 | |
|                 {
 | |
|                     case SampleFormat.PcmInt16:
 | |
|                         result = IsSampleOffsetInRangeForPcm<ushort>();
 | |
|                         break;
 | |
|                     case SampleFormat.PcmFloat:
 | |
|                         result = IsSampleOffsetInRangeForPcm<float>();
 | |
|                         break;
 | |
|                     case SampleFormat.Adpcm:
 | |
|                         result = AdpcmHelper.GetAdpcmDataSize((int)StartSampleOffset) <= Size &&
 | |
|                                  AdpcmHelper.GetAdpcmDataSize((int)EndSampleOffset) <= Size;
 | |
|                         break;
 | |
|                     default:
 | |
|                         throw new NotImplementedException($"{format} not implemented!");
 | |
|                 }
 | |
| 
 | |
|                 return result;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Flag altering the behaviour of wavebuffer decoding.
 | |
|         /// </summary>
 | |
|         [Flags]
 | |
|         public enum DecodingBehaviour : ushort
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Default decoding behaviour.
 | |
|             /// </summary>
 | |
|             Default = 0,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Reset the played samples accumulator when looping.
 | |
|             /// </summary>
 | |
|             PlayedSampleCountResetWhenLooping = 1,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Skip pitch and Sample Rate Conversion (SRC).
 | |
|             /// </summary>
 | |
|             SkipPitchAndSampleRateConversion = 2
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Specify the quality to use during Sample Rate Conversion (SRC) and pitch handling.
 | |
|         /// </summary>
 | |
|         /// <remarks>This was added in REV8.</remarks>
 | |
|         public enum SampleRateConversionQuality : byte
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Resample interpolating 4 samples per output sample.
 | |
|             /// </summary>
 | |
|             Default,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Resample interpolating 8 samples per output sample.
 | |
|             /// </summary>
 | |
|             High,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Resample interpolating 1 samples per output sample.
 | |
|             /// </summary>
 | |
|             Low
 | |
|         }
 | |
|     }
 | |
| }
 |