aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs9
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs170
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs213
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj15
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/MarshalExtensions.cs (renamed from Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIO.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIO.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOBackend.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelArea.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelAreas.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelId.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODevice.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODeviceAim.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOException.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOException.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOFormat.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOInStream.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIORingBuffer.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs (renamed from Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll (renamed from Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll)bin85504 -> 85504 bytes
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib (renamed from Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib)bin54976 -> 54976 bytes
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so (renamed from Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so)bin88584 -> 88584 bytes
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libsoundio-interop.cs (renamed from Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs)0
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj27
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs9
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs251
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs (renamed from Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs)426
-rw-r--r--Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj32
-rw-r--r--Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj14
-rw-r--r--Ryujinx.Audio/AudioManager.cs137
-rw-r--r--Ryujinx.Audio/Backends/Common/BackendHelper.cs43
-rw-r--r--Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs183
-rw-r--r--Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs89
-rw-r--r--Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs146
-rw-r--r--Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs140
-rw-r--r--Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs (renamed from Ryujinx.Audio/Downmixing.cs)21
-rw-r--r--Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs96
-rw-r--r--Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs84
-rw-r--r--Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs79
-rw-r--r--Ryujinx.Audio/Common/AudioBuffer.cs53
-rw-r--r--Ryujinx.Audio/Common/AudioDeviceSession.cs532
-rw-r--r--Ryujinx.Audio/Common/AudioDeviceState.cs35
-rw-r--r--Ryujinx.Audio/Common/AudioInputConfiguration.cs46
-rw-r--r--Ryujinx.Audio/Common/AudioOutputConfiguration.cs54
-rw-r--r--Ryujinx.Audio/Common/AudioUserBuffer.cs53
-rw-r--r--Ryujinx.Audio/Common/SampleFormat.cs (renamed from Ryujinx.Audio.Renderer/Common/SampleFormat.cs)2
-rw-r--r--Ryujinx.Audio/Constants.cs (renamed from Ryujinx.Audio.Renderer/RendererConstants.cs)45
-rw-r--r--Ryujinx.Audio/IAalOutput.cs56
-rw-r--r--Ryujinx.Audio/Input/AudioInputManager.cs262
-rw-r--r--Ryujinx.Audio/Input/AudioInputSystem.cs400
-rw-r--r--Ryujinx.Audio/Integration/HardwareDeviceImpl.cs82
-rw-r--r--Ryujinx.Audio/Integration/IHardwareDevice.cs (renamed from Ryujinx.Audio.Renderer/Integration/HardwareDevice.cs)10
-rw-r--r--Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs50
-rw-r--r--Ryujinx.Audio/Integration/IHardwareDeviceSession.cs (renamed from Ryujinx.Audio.Renderer/ResultCode.cs)35
-rw-r--r--Ryujinx.Audio/Integration/IWritableEvent.cs (renamed from Ryujinx.Audio.Renderer/Integration/IWritableEvent.cs)2
-rw-r--r--Ryujinx.Audio/LICENSE.txt (renamed from Ryujinx.Audio.Renderer/LICENSE.txt)0
-rw-r--r--Ryujinx.Audio/Output/AudioOutputManager.cs256
-rw-r--r--Ryujinx.Audio/Output/AudioOutputSystem.cs373
-rw-r--r--Ryujinx.Audio/PlaybackState.cs17
-rw-r--r--Ryujinx.Audio/ReleaseCallback.cs4
-rw-r--r--Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs (renamed from Ryujinx.Audio.Renderer/Common/AuxiliaryBufferAddresses.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs (renamed from Ryujinx.Audio.Renderer/Common/BehaviourParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs (renamed from Ryujinx.Audio.Renderer/Common/EdgeMatrix.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Common/EffectType.cs (renamed from Ryujinx.Audio.Renderer/Common/EffectType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs (renamed from Ryujinx.Audio.Renderer/Common/MemoryPoolUserState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs (renamed from Ryujinx.Audio.Renderer/Common/NodeIdHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/NodeIdType.cs (renamed from Ryujinx.Audio.Renderer/Common/NodeIdType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/NodeStates.cs (renamed from Ryujinx.Audio.Renderer/Common/NodeStates.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs (renamed from Ryujinx.Audio.Renderer/Common/PerformanceDetailType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs (renamed from Ryujinx.Audio.Renderer/Common/PerformanceEntryType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/PlayState.cs (renamed from Ryujinx.Audio.Renderer/Common/PlayState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs (renamed from Ryujinx.Audio.Renderer/Common/ReverbEarlyMode.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs (renamed from Ryujinx.Audio.Renderer/Common/ReverbLateMode.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/SinkType.cs (renamed from Ryujinx.Audio.Renderer/Common/SinkType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs (renamed from Ryujinx.Audio.Renderer/Common/UpdateDataHeader.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs (renamed from Ryujinx.Audio.Renderer/Common/VoiceUpdateState.cs)4
-rw-r--r--Ryujinx.Audio/Renderer/Common/WaveBuffer.cs (renamed from Ryujinx.Audio.Renderer/Common/WaveBuffer.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs (renamed from Ryujinx.Audio.Renderer/Common/WorkBufferAllocator.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Device/VirtualDevice.cs (renamed from Ryujinx.Audio.Renderer/Device/VirtualDevice.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs (renamed from Ryujinx.Audio.Renderer/Device/VirtualDeviceSession.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs (renamed from Ryujinx.Audio.Renderer/Device/VirtualDeviceSessionRegistry.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/AdpcmHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs (renamed from Ryujinx.Audio.Renderer/Dsp/AudioProcessor.cs)49
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs)3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/BiquadFilterCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/CircularBufferSinkCommand.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/ClearMixBufferCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs)6
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/CommandType.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/CopyMixBufferCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DataSourceVersion2Command.cs)3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DelayCommand.cs)4
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DepopForMixBuffersCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DepopPrepareCommand.cs)4
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DeviceSinkCommand.cs)8
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs)6
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/ICommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/MixCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/MixRampCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/MixRampGroupedCommand.cs)8
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs)3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs)3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/PerformanceCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/Reverb3dCommand.cs)6
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/ReverbCommand.cs)10
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/UpsampleCommand.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/VolumeCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Command/VolumeRampCommand.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs)3
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Effect/DecayDelay.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Effect/DelayLine.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Effect/DelayLineReverb3d.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs (renamed from Ryujinx.Audio.Renderer/Dsp/Effect/IDelayLine.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/FixedPointHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/FloatingPointHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/PcmHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs (renamed from Ryujinx.Audio.Renderer/Dsp/ResamplerHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/AdpcmLoopContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/BiquadFilterState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/DelayState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/Reverb3dState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs (renamed from Ryujinx.Audio.Renderer/Dsp/State/ReverbState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs (renamed from Ryujinx.Audio.Renderer/Parameter/AudioRendererConfiguration.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/BehaviourErrorInfoOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/BiquadFilterParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/BufferMixerParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/DelayParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/Reverb3dParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Effect/ReverbParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/EffectInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/EffectOutStatus.cs)1
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/MemoryPoolInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/MemoryPoolOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs (renamed from Ryujinx.Audio.Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/MixParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/MixParameter.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/RendererInfoOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Sink/CircularBufferParameter.cs)1
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/Sink/DeviceParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/SinkInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/SinkOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/SplitterDestinationInParameter.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/SplitterInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs (renamed from Ryujinx.Audio.Renderer/Parameter/SplitterInParameterHeader.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/VoiceChannelResourceInParameter.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs (renamed from Ryujinx.Audio.Renderer/Parameter/VoiceInParameter.cs)1
-rw-r--r--Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs (renamed from Ryujinx.Audio.Renderer/Parameter/VoiceOutStatus.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs (renamed from Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs)36
-rw-r--r--Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs (renamed from Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs)25
-rw-r--r--Ryujinx.Audio/Renderer/Server/BehaviourContext.cs (renamed from Ryujinx.Audio.Renderer/Server/BehaviourContext.cs)10
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandBuffer.cs (renamed from Ryujinx.Audio.Renderer/Server/CommandBuffer.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandGenerator.cs (renamed from Ryujinx.Audio.Renderer/Server/CommandGenerator.cs)48
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs (renamed from Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs (renamed from Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs (renamed from Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs)1
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/AuxiliaryBufferEffect.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/BaseEffect.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/BiquadFilterEffect.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/BufferMixEffect.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/DelayEffect.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/EffectContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/Reverb3dEffect.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/ReverbEffect.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs (renamed from Ryujinx.Audio.Renderer/Server/Effect/UsageState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs (renamed from Ryujinx.Audio.Renderer/Server/ICommandProcessingTimeEstimator.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs (renamed from Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs (renamed from Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs (renamed from Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs (renamed from Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs)8
-rw-r--r--Ryujinx.Audio/Renderer/Server/Mix/MixState.cs (renamed from Ryujinx.Audio.Renderer/Server/Mix/MixState.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/IPerformanceDetailEntry.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/IPerformanceEntry.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/IPerformanceHeader.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion1.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion2.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryAddresses.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion1.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion2.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceManager.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs (renamed from Ryujinx.Audio.Renderer/Server/Performance/PerformanceManagerGeneric.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs (renamed from Ryujinx.Audio.Renderer/Server/RendererSystemContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs (renamed from Ryujinx.Audio.Renderer/Server/Sink/BaseSink.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs (renamed from Ryujinx.Audio.Renderer/Server/Sink/CircularBufferSink.cs)2
-rw-r--r--Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs (renamed from Ryujinx.Audio.Renderer/Server/Sink/DeviceSink.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs (renamed from Ryujinx.Audio.Renderer/Server/Sink/SinkContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs (renamed from Ryujinx.Audio.Renderer/Server/Splitter/SplitterContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs (renamed from Ryujinx.Audio.Renderer/Server/Splitter/SplitterDestination.cs)8
-rw-r--r--Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs (renamed from Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/StateUpdater.cs (renamed from Ryujinx.Audio.Renderer/Server/StateUpdater.cs)6
-rw-r--r--Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs (renamed from Ryujinx.Audio.Renderer/Server/Types/AudioRendererExecutionMode.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs (renamed from Ryujinx.Audio.Renderer/Server/Types/AudioRendererRenderingDevice.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Types/PlayState.cs (renamed from Ryujinx.Audio.Renderer/Server/Types/PlayState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs (renamed from Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerManager.cs)4
-rw-r--r--Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs (renamed from Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerState.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs (renamed from Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs (renamed from Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs (renamed from Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs)25
-rw-r--r--Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs (renamed from Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs (renamed from Ryujinx.Audio.Renderer/Utils/AudioProcessorMemoryManager.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/BitArray.cs (renamed from Ryujinx.Audio.Renderer/Utils/BitArray.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs (renamed from Ryujinx.Audio.Renderer/Utils/FileHardwareDevice.cs)6
-rw-r--r--Ryujinx.Audio/Renderer/Utils/Mailbox.cs (renamed from Ryujinx.Audio.Renderer/Utils/Mailbox.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs (renamed from Ryujinx.Audio.Renderer/Utils/SpanIOHelper.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs (renamed from Ryujinx.Audio.Renderer/Utils/SpanMemoryManager.cs)0
-rw-r--r--Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs (renamed from Ryujinx.Audio.Renderer/Utils/SplitterHardwareDevice.cs)10
-rw-r--r--Ryujinx.Audio/Renderers/DummyAudioOut.cs109
-rw-r--r--Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs404
-rw-r--r--Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs183
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs361
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs193
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs29
-rw-r--r--Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs204
-rw-r--r--Ryujinx.Audio/ResultCode.cs38
-rw-r--r--Ryujinx.Audio/Ryujinx.Audio.csproj21
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs54
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs108
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs209
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs34
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs41
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs235
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs108
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs190
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs33
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs41
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs228
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs162
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs98
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs85
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs147
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs13
-rw-r--r--Ryujinx.HLE/Ryujinx.HLE.csproj1
-rw-r--r--Ryujinx.HLE/Switch.cs15
-rw-r--r--Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs2
-rw-r--r--Ryujinx.Tests/Ryujinx.Tests.csproj2
-rw-r--r--Ryujinx.sln20
-rw-r--r--Ryujinx/Ryujinx.csproj7
-rw-r--r--Ryujinx/Ui/MainWindow.cs22
-rw-r--r--Ryujinx/Ui/Windows/SettingsWindow.cs6
249 files changed, 5602 insertions, 2700 deletions
diff --git a/README.md b/README.md
index 5d9f2f2b..18010781 100644
--- a/README.md
+++ b/README.md
@@ -104,7 +104,7 @@ If you'd like to donate, please take a look at our [Patreon](https://www.patreon
## License
This software is licensed under the terms of the MIT license.
-The Ryujinx.Audio.Renderer project is licensed under the terms of the LGPLv3 license.
+The Ryujinx.Audio project is licensed under the terms of the LGPLv3 license.
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](Ryujinx/THIRDPARTY.md) for more details.
diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs
new file mode 100644
index 00000000..050b52a9
--- /dev/null
+++ b/Ryujinx.Audio.Backends.OpenAL/OpenALAudioBuffer.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Audio.Backends.OpenAL
+{
+ class OpenALAudioBuffer
+ {
+ public int BufferId;
+ public ulong DriverIdentifier;
+ public ulong SampleCount;
+ }
+}
diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
new file mode 100644
index 00000000..43f238ef
--- /dev/null
+++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
@@ -0,0 +1,170 @@
+using OpenTK.Audio;
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Memory;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
+
+namespace Ryujinx.Audio.Backends.OpenAL
+{
+ public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
+ {
+ private object _lock = new object();
+
+ private AudioContext _context;
+ private ManualResetEvent _updateRequiredEvent;
+ private List<OpenALHardwareDeviceSession> _sessions;
+ private bool _stillRunning;
+ private Thread _updaterThread;
+
+ public OpenALHardwareDeviceDriver()
+ {
+ _context = new AudioContext();
+ _updateRequiredEvent = new ManualResetEvent(false);
+ _sessions = new List<OpenALHardwareDeviceSession>();
+
+ _stillRunning = true;
+ _updaterThread = new Thread(Update)
+ {
+ Name = "HardwareDeviceDriver.OpenAL"
+ };
+
+ _updaterThread.Start();
+ }
+
+ public static bool IsSupported
+ {
+ get
+ {
+ try
+ {
+ return AudioContext.AvailableDevices.Count > 0;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+
+ public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
+ {
+ if (channelCount == 0)
+ {
+ channelCount = 2;
+ }
+
+ if (sampleRate == 0)
+ {
+ sampleRate = Constants.TargetSampleRate;
+ }
+
+ if (direction != Direction.Output)
+ {
+ throw new ArgumentException($"{direction}");
+ }
+ else if (!SupportsChannelCount(channelCount))
+ {
+ throw new ArgumentException($"{channelCount}");
+ }
+
+ lock (_lock)
+ {
+ OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
+
+ _sessions.Add(session);
+
+ return session;
+ }
+ }
+
+ internal void Unregister(OpenALHardwareDeviceSession session)
+ {
+ lock (_lock)
+ {
+ _sessions.Remove(session);
+ }
+ }
+
+ public ManualResetEvent GetUpdateRequiredEvent()
+ {
+ return _updateRequiredEvent;
+ }
+
+ private void Update()
+ {
+ while (_stillRunning)
+ {
+ bool updateRequired = false;
+
+ lock (_lock)
+ {
+ foreach (OpenALHardwareDeviceSession session in _sessions)
+ {
+ if (session.Update())
+ {
+ updateRequired = true;
+ }
+ }
+ }
+
+ if (updateRequired)
+ {
+ _updateRequiredEvent.Set();
+ }
+
+ // If it's not slept it will waste cycles.
+ Thread.Sleep(10);
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ lock (_lock)
+ {
+ _stillRunning = false;
+ _updaterThread.Join();
+
+ // Loop against all sessions to dispose them (they will unregister themself)
+ while (_sessions.Count > 0)
+ {
+ OpenALHardwareDeviceSession session = _sessions[0];
+
+ session.Dispose();
+ }
+ }
+
+ _context.Dispose();
+ }
+ }
+
+ public bool SupportsSampleRate(uint sampleRate)
+ {
+ return true;
+ }
+
+ public bool SupportsSampleFormat(SampleFormat sampleFormat)
+ {
+ return true;
+ }
+
+ public bool SupportsChannelCount(uint channelCount)
+ {
+ return channelCount == 1 || channelCount == 2 || channelCount == 6;
+ }
+
+ public bool SupportsDirection(Direction direction)
+ {
+ return direction == Direction.Output;
+ }
+ }
+}
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<OpenALAudioBuffer> _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<OpenALAudioBuffer>();
+ _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);
+ }
+ }
+}
diff --git a/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj
new file mode 100644
index 00000000..187fe0fe
--- /dev/null
+++ b/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="OpenTK.NetStandard" Version="1.0.5.32" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/MarshalExtensions.cs
index 5e86263e..5e86263e 100644
--- a/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/MarshalExtensions.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIO.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIO.cs
index c4ce1887..c4ce1887 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIO.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIO.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOBackend.cs
index fd105804..fd105804 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOBackend.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelArea.cs
index c15fb744..c15fb744 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelArea.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelAreas.cs
index e0f375b9..e0f375b9 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelAreas.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelId.cs
index 002669dc..002669dc 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelId.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs
index cff6114f..cff6114f 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODevice.cs
index 6e7c4964..6e7c4964 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODevice.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODeviceAim.cs
index 1e596127..1e596127 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIODeviceAim.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOException.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOException.cs
index 537b6cde..537b6cde 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOException.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOException.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOFormat.cs
index df1b71c6..df1b71c6 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOFormat.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOInStream.cs
index df97d653..df97d653 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOInStream.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs
index 432ca42b..432ca42b 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIORingBuffer.cs
index 7530da72..7530da72 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIORingBuffer.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs
index e01ec2bd..e01ec2bd 100644
--- a/Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs
diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll
index 48804312..48804312 100644
--- a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll
Binary files differ
diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib
index 10171f4f..10171f4f 100644
--- a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib
Binary files differ
diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so
index 87c8b506..87c8b506 100644
--- a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so
Binary files differ
diff --git a/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libsoundio-interop.cs
index 5377582f..5377582f 100644
--- a/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libsoundio-interop.cs
diff --git a/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj
new file mode 100644
index 00000000..a9a2fe75
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.dll</TargetPath>
+ </ContentWithTargetPath>
+ <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.dylib</TargetPath>
+ </ContentWithTargetPath>
+ <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.so</TargetPath>
+ </ContentWithTargetPath>
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs
new file mode 100644
index 00000000..b43143d6
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoAudioBuffer.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Audio.Backends.SoundIo
+{
+ class SoundIoAudioBuffer
+ {
+ public ulong DriverIdentifier;
+ public ulong SampleCount;
+ public ulong SamplePlayed;
+ }
+}
diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs
new file mode 100644
index 00000000..00977fcb
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs
@@ -0,0 +1,251 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Memory;
+using SoundIOSharp;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
+
+namespace Ryujinx.Audio.Backends.SoundIo
+{
+ public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
+ {
+ private object _lock = new object();
+
+ private SoundIO _audioContext;
+ private SoundIODevice _audioDevice;
+ private ManualResetEvent _updateRequiredEvent;
+ private List<SoundIoHardwareDeviceSession> _sessions;
+
+ public SoundIoHardwareDeviceDriver()
+ {
+ _audioContext = new SoundIO();
+ _updateRequiredEvent = new ManualResetEvent(false);
+ _sessions = new List<SoundIoHardwareDeviceSession>();
+
+ _audioContext.Connect();
+ _audioContext.FlushEvents();
+
+ _audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
+ }
+
+ public static bool IsSupported => IsSupportedInternal();
+
+ private static bool IsSupportedInternal()
+ {
+ SoundIO context = null;
+ SoundIODevice device = null;
+ SoundIOOutStream stream = null;
+
+ bool backendDisconnected = false;
+
+ try
+ {
+ context = new SoundIO();
+
+ context.OnBackendDisconnect = (i) =>
+ {
+ backendDisconnected = true;
+ };
+
+ context.Connect();
+ context.FlushEvents();
+
+ if (backendDisconnected)
+ {
+ return false;
+ }
+
+ if (context.OutputDeviceCount == 0)
+ {
+ return false;
+ }
+
+ device = FindNonRawDefaultAudioDevice(context);
+
+ if (device == null || backendDisconnected)
+ {
+ return false;
+ }
+
+ stream = device.CreateOutStream();
+
+ if (stream == null || backendDisconnected)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.Dispose();
+ }
+
+ if (context != null)
+ {
+ context.Dispose();
+ }
+ }
+ }
+
+ private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
+ {
+ SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
+
+ if (!defaultAudioDevice.IsRaw)
+ {
+ return defaultAudioDevice;
+ }
+
+ for (int i = 0; i < audioContext.BackendCount; i++)
+ {
+ SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
+
+ if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
+ {
+ return audioDevice;
+ }
+ }
+
+ return fallback ? defaultAudioDevice : null;
+ }
+
+ public ManualResetEvent GetUpdateRequiredEvent()
+ {
+ return _updateRequiredEvent;
+ }
+
+ public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
+ {
+ if (channelCount == 0)
+ {
+ channelCount = 2;
+ }
+
+ if (sampleRate == 0)
+ {
+ sampleRate = Constants.TargetSampleRate;
+ }
+
+ if (direction != Direction.Output)
+ {
+ throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
+ }
+
+ lock (_lock)
+ {
+ SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
+
+ _sessions.Add(session);
+
+ return session;
+ }
+ }
+
+ internal void Unregister(SoundIoHardwareDeviceSession session)
+ {
+ lock (_lock)
+ {
+ _sessions.Remove(session);
+ }
+ }
+
+ public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
+ {
+ return format switch
+ {
+ SampleFormat.PcmInt8 => SoundIOFormat.S8,
+ SampleFormat.PcmInt16 => SoundIOFormat.S16LE,
+ SampleFormat.PcmInt24 => SoundIOFormat.S24LE,
+ SampleFormat.PcmInt32 => SoundIOFormat.S32LE,
+ SampleFormat.PcmFloat => SoundIOFormat.Float32LE,
+ _ => throw new ArgumentException ($"Unsupported sample format {format}"),
+ };
+ }
+
+ internal SoundIOOutStream OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
+ {
+ SoundIOFormat driverSampleFormat = GetSoundIoFormat(requestedSampleFormat);
+
+ if (!_audioDevice.SupportsSampleRate((int)requestedSampleRate))
+ {
+ throw new ArgumentException($"This sound device does not support a sample rate of {requestedSampleRate}Hz");
+ }
+
+ if (!_audioDevice.SupportsFormat(driverSampleFormat))
+ {
+ throw new ArgumentException($"This sound device does not support {requestedSampleFormat}");
+ }
+
+ if (!_audioDevice.SupportsChannelCount((int)requestedChannelCount))
+ {
+ throw new ArgumentException($"This sound device does not support channel count {requestedChannelCount}");
+ }
+
+ SoundIOOutStream result = _audioDevice.CreateOutStream();
+
+ result.Name = "Ryujinx";
+ result.Layout = SoundIOChannelLayout.GetDefault((int)requestedChannelCount);
+ result.Format = driverSampleFormat;
+ result.SampleRate = (int)requestedSampleRate;
+
+ return result;
+ }
+
+ internal void FlushContextEvents()
+ {
+ _audioContext.FlushEvents();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ while (_sessions.Count > 0)
+ {
+ SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1];
+
+ session.Dispose();
+ }
+
+ _audioContext.Disconnect();
+ _audioContext.Dispose();
+ }
+ }
+
+ public bool SupportsSampleRate(uint sampleRate)
+ {
+ return _audioDevice.SupportsSampleRate((int)sampleRate);
+ }
+
+ public bool SupportsSampleFormat(SampleFormat sampleFormat)
+ {
+ return _audioDevice.SupportsFormat(GetSoundIoFormat(sampleFormat));
+ }
+
+ public bool SupportsChannelCount(uint channelCount)
+ {
+ return _audioDevice.SupportsChannelCount((int)channelCount);
+ }
+
+ public bool SupportsDirection(Direction direction)
+ {
+ // TODO: add direction input when supported.
+ return direction == Direction.Output;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs
index 52c4ebc9..be01ecdc 100644
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs
@@ -1,155 +1,118 @@
-using SoundIOSharp;
+using Ryujinx.Audio.Backends.Common;
+using Ryujinx.Audio.Common;
+using Ryujinx.Memory;
+using SoundIOSharp;
using System;
-using System.Collections.Concurrent;
-using System.Linq;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using System.Threading;
-namespace Ryujinx.Audio.SoundIo
+namespace Ryujinx.Audio.Backends.SoundIo
{
- internal class SoundIoAudioTrack : IDisposable
+ class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
- /// <summary>
- /// The audio track ring buffer
- /// </summary>
- private SoundIoRingBuffer m_Buffer;
-
- /// <summary>
- /// A list of buffers currently pending writeback to the audio backend
- /// </summary>
- private ConcurrentQueue<SoundIoBuffer> m_ReservedBuffers;
-
- /// <summary>
- /// Occurs when a buffer has been released by the audio backend
- /// </summary>
- private event ReleaseCallback BufferReleased;
-
- /// <summary>
- /// The track ID of this <see cref="SoundIoAudioTrack"/>
- /// </summary>
- public int TrackID { get; private set; }
-
- /// <summary>
- /// The current playback state
- /// </summary>
- public PlaybackState State { get; private set; }
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context this track belongs to
- /// </summary>
- public SoundIO AudioContext { get; private set; }
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> this track belongs to
- /// </summary>
- public SoundIODevice AudioDevice { get; private set; }
-
- /// <summary>
- /// The audio output stream of this track
- /// </summary>
- public SoundIOOutStream AudioStream { get; private set; }
-
- /// <summary>
- /// Released buffers the track is no longer holding
- /// </summary>
- public ConcurrentQueue<long> ReleasedBuffers { get; private set; }
-
- /// <summary>
- /// Buffer count of the track
- /// </summary>
- public uint BufferCount => (uint)m_ReservedBuffers.Count;
-
- /// <summary>
- /// Played sample count of the track
- /// </summary>
- public ulong PlayedSampleCount { get; private set; }
-
- private int _hardwareChannels;
- private int _virtualChannels;
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrack"/>
- /// </summary>
- /// <param name="trackId">The track ID</param>
- /// <param name="audioContext">The SoundIO audio context</param>
- /// <param name="audioDevice">The SoundIO audio device</param>
- public SoundIoAudioTrack(int trackId, SoundIO audioContext, SoundIODevice audioDevice)
+ private object _lock = new object();
+
+ private SoundIoHardwareDeviceDriver _driver;
+ private Queue<SoundIoAudioBuffer> _queuedBuffers;
+ private SoundIOOutStream _outputStream;
+ private DynamicRingBuffer _ringBuffer;
+ private ulong _playedSampleCount;
+ private ManualResetEvent _updateRequiredEvent;
+
+ public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{
- TrackID = trackId;
- AudioContext = audioContext;
- AudioDevice = audioDevice;
- State = PlaybackState.Stopped;
- ReleasedBuffers = new ConcurrentQueue<long>();
-
- m_Buffer = new SoundIoRingBuffer();
- m_ReservedBuffers = new ConcurrentQueue<SoundIoBuffer>();
+ _driver = driver;
+ _updateRequiredEvent = _driver.GetUpdateRequiredEvent();
+ _queuedBuffers = new Queue<SoundIoAudioBuffer>();
+ _ringBuffer = new DynamicRingBuffer();
+
+ SetupOutputStream();
}
- /// <summary>
- /// Opens the audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate of the track</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <param name="format">The requested sample format of the track</param>
- public void Open(
- int sampleRate,
- int hardwareChannels,
- int virtualChannels,
- ReleaseCallback callback,
- SoundIOFormat format = SoundIOFormat.S16LE)
+ private void SetupOutputStream()
{
- // Close any existing audio streams
- if (AudioStream != null)
- {
- Close();
- }
+ _outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
+ _outputStream.WriteCallback += Update;
- if (!AudioDevice.SupportsSampleRate(sampleRate))
- {
- throw new InvalidOperationException($"This sound device does not support a sample rate of {sampleRate}Hz");
- }
+ // TODO: Setup other callbacks (errors, ect).
- if (!AudioDevice.SupportsFormat(format))
+ _outputStream.Open();
+ }
+
+ public override ulong GetPlayedSampleCount()
+ {
+ lock (_lock)
{
- throw new InvalidOperationException($"This sound device does not support SoundIOFormat.{Enum.GetName(typeof(SoundIOFormat), format)}");
+ return _playedSampleCount;
}
+ }
+
+ public override float GetVolume()
+ {
+ return _outputStream.Volume;
+ }
- if (!AudioDevice.SupportsChannelCount(hardwareChannels))
+ public override void PrepareToClose() { }
+
+ public override void QueueBuffer(AudioBuffer buffer)
+ {
+ lock (_lock)
{
- throw new InvalidOperationException($"This sound device does not support channel count {hardwareChannels}");
+ SoundIoAudioBuffer driverBuffer = new SoundIoAudioBuffer
+ {
+ DriverIdentifier = buffer.DataPointer,
+ SampleCount = GetSampleCount(buffer),
+ SamplePlayed = 0,
+ };
+
+ _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
+
+ _queuedBuffers.Enqueue(driverBuffer);
}
+ }
- _hardwareChannels = hardwareChannels;
- _virtualChannels = virtualChannels;
+ public override void SetVolume(float volume)
+ {
+ _outputStream.SetVolume(volume);
+ }
+
+ public override void Start()
+ {
+ _outputStream.Start();
+ _outputStream.Pause(false);
- AudioStream = AudioDevice.CreateOutStream();
+ _driver.FlushContextEvents();
+ }
- AudioStream.Name = $"SwitchAudioTrack_{TrackID}";
- AudioStream.Layout = SoundIOChannelLayout.GetDefault(hardwareChannels);
- AudioStream.Format = format;
- AudioStream.SampleRate = sampleRate;
+ public override void Stop()
+ {
+ _outputStream.Pause(true);
- AudioStream.WriteCallback = WriteCallback;
+ _driver.FlushContextEvents();
+ }
- BufferReleased += callback;
+ public override void UnregisterBuffer(AudioBuffer buffer) {}
- AudioStream.Open();
+ public override bool WasBufferFullyConsumed(AudioBuffer buffer)
+ {
+ lock (_lock)
+ {
+ if (!_queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer))
+ {
+ return true;
+ }
+
+ return driverBuffer.DriverIdentifier != buffer.DataPointer;
+ }
}
- /// <summary>
- /// This callback occurs when the sound device is ready to buffer more frames
- /// </summary>
- /// <param name="minFrameCount">The minimum amount of frames expected by the audio backend</param>
- /// <param name="maxFrameCount">The maximum amount of frames that can be written to the audio backend</param>
- private unsafe void WriteCallback(int minFrameCount, int maxFrameCount)
+ private unsafe void Update(int minFrameCount, int maxFrameCount)
{
- int bytesPerFrame = AudioStream.BytesPerFrame;
- uint bytesPerSample = (uint)AudioStream.BytesPerSample;
+ int bytesPerFrame = _outputStream.BytesPerFrame;
+ uint bytesPerSample = (uint)_outputStream.BytesPerSample;
- int bufferedFrames = m_Buffer.Length / bytesPerFrame;
- long bufferedSamples = m_Buffer.Length / bytesPerSample;
+ int bufferedFrames = _ringBuffer.Length / bytesPerFrame;
int frameCount = Math.Min(bufferedFrames, maxFrameCount);
@@ -158,16 +121,18 @@ namespace Ryujinx.Audio.SoundIo
return;
}
- SoundIOChannelAreas areas = AudioStream.BeginWrite(ref frameCount);
+ SoundIOChannelAreas areas = _outputStream.BeginWrite(ref frameCount);
+
int channelCount = areas.ChannelCount;
byte[] samples = new byte[frameCount * bytesPerFrame];
- m_Buffer.Read(samples, 0, samples.Length);
+ _ringBuffer.Read(samples, 0, samples.Length);
// This is a huge ugly block of code, but we save
// a significant amount of time over the generic
// loop that handles other channel counts.
+ // TODO: Is this still right in 2021?
// Mono
if (channelCount == 1)
@@ -438,209 +403,58 @@ namespace Ryujinx.Audio.SoundIo
}
}
- AudioStream.EndWrite();
+ _outputStream.EndWrite();
- PlayedSampleCount += (ulong)samples.Length;
+ ulong sampleCount = (ulong)(samples.Length / bytesPerSample / channelCount);
- UpdateReleasedBuffers(samples.Length);
- }
-
- /// <summary>
- /// Releases any buffers that have been fully written to the output device
- /// </summary>
- /// <param name="bytesRead">The amount of bytes written in the last device write</param>
- private void UpdateReleasedBuffers(int bytesRead)
- {
- bool bufferReleased = false;
-
- while (bytesRead > 0)
- {
- if (m_ReservedBuffers.TryPeek(out SoundIoBuffer buffer))
- {
- if (buffer.Length > bytesRead)
- {
- buffer.Length -= bytesRead;
- bytesRead = 0;
- }
- else
- {
- bufferReleased = true;
- bytesRead -= buffer.Length;
+ ulong availaibleSampleCount = sampleCount;
- m_ReservedBuffers.TryDequeue(out buffer);
- ReleasedBuffers.Enqueue(buffer.Tag);
- }
- }
- }
+ bool needUpdate = false;
- if (bufferReleased)
+ while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SoundIoAudioBuffer driverBuffer))
{
- OnBufferReleased();
- }
- }
+ ulong sampleStillNeeded = driverBuffer.SampleCount - driverBuffer.SamplePlayed;
+ ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
- /// <summary>
- /// Starts audio playback
- /// </summary>
- public void Start()
- {
- if (AudioStream == null)
- {
- return;
- }
+ driverBuffer.SamplePlayed += playedAudioBufferSampleCount;
+ availaibleSampleCount -= playedAudioBufferSampleCount;
- AudioStream.Start();
- AudioStream.Pause(false);
- AudioContext.FlushEvents();
- State = PlaybackState.Playing;
- }
-
- /// <summary>
- /// Stops audio playback
- /// </summary>
- public void Stop()
- {
- if (AudioStream == null)
- {
- return;
- }
-
- AudioStream.Pause(true);
- AudioContext.FlushEvents();
- State = PlaybackState.Stopped;
- }
-
- /// <summary>
- /// Appends an audio buffer to the tracks internal ring buffer
- /// </summary>
- /// <typeparam name="T">The audio sample type</typeparam>
- /// <param name="bufferTag">The unqiue tag of the buffer being appended</param>
- /// <param name="buffer">The buffer to append</param>
- public void AppendBuffer<T>(long bufferTag, T[] buffer) where T: struct
- {
- if (AudioStream == null)
- {
- return;
- }
-
- int sampleSize = Unsafe.SizeOf<T>();
- int targetSize = sampleSize * buffer.Length;
-
- // Do we need to downmix?
- if (_hardwareChannels != _virtualChannels)
- {
- if (sampleSize != sizeof(short))
+ if (driverBuffer.SamplePlayed == driverBuffer.SampleCount)
{
- throw new NotImplementedException("Downmixing formats other than PCM16 is not supported!");
- }
+ _queuedBuffers.TryDequeue(out _);
- short[] downmixedBuffer;
-
- ReadOnlySpan<short> bufferPCM16 = MemoryMarshal.Cast<T, short>(buffer);
-
- if (_virtualChannels == 6)
- {
- downmixedBuffer = Downmixing.DownMixSurroundToStereo(bufferPCM16);
-
- if (_hardwareChannels == 1)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(downmixedBuffer);
- }
- }
- else if (_virtualChannels == 2)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(bufferPCM16);
- }
- else
- {
- throw new NotImplementedException($"Downmixing from {_virtualChannels} to {_hardwareChannels} not implemented!");
+ needUpdate = true;
}
- targetSize = sampleSize * downmixedBuffer.Length;
-
- // Copy the memory to our ring buffer
- m_Buffer.Write(downmixedBuffer, 0, targetSize);
-
- // Keep track of "buffered" buffers
- m_ReservedBuffers.Enqueue(new SoundIoBuffer(bufferTag, targetSize));
+ _playedSampleCount += playedAudioBufferSampleCount;
}
- else
- {
- // Copy the memory to our ring buffer
- m_Buffer.Write(buffer, 0, targetSize);
- // Keep track of "buffered" buffers
- m_ReservedBuffers.Enqueue(new SoundIoBuffer(bufferTag, targetSize));
+ // Notify the output if needed.
+ if (needUpdate)
+ {
+ _updateRequiredEvent.Set();
}
}
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the track
- /// </summary>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(long bufferTag)
+ protected virtual void Dispose(bool disposing)
{
- return m_ReservedBuffers.Any(x => x.Tag == bufferTag);
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- public bool FlushBuffers()
- {
- m_Buffer.Clear();
-
- if (m_ReservedBuffers.Count > 0)
+ if (disposing)
{
- foreach (var buffer in m_ReservedBuffers)
+ lock (_lock)
{
- ReleasedBuffers.Enqueue(buffer.Tag);
- }
-
- OnBufferReleased();
-
- return true;
- }
+ PrepareToClose();
+ Stop();
- return false;
- }
+ _outputStream.Dispose();
- /// <summary>
- /// Closes the <see cref="SoundIoAudioTrack"/>
- /// </summary>
- public void Close()
- {
- if (AudioStream != null)
- {
- AudioStream.Pause(true);
- AudioStream.Dispose();
+ _driver.Unregister(this);
+ }
}
-
- m_Buffer.Clear();
- OnBufferReleased();
- ReleasedBuffers.Clear();
-
- State = PlaybackState.Stopped;
- AudioStream = null;
- BufferReleased = null;
- }
-
- private void OnBufferReleased()
- {
- BufferReleased?.Invoke();
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioTrack" />
- /// </summary>
- public void Dispose()
- {
- Close();
}
- ~SoundIoAudioTrack()
+ public override void Dispose()
{
- Dispose();
+ Dispose(true);
}
}
}
diff --git a/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj b/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj
new file mode 100644
index 00000000..431187ed
--- /dev/null
+++ b/Ryujinx.Audio.Backends/Ryujinx.Audio.Backends.csproj
@@ -0,0 +1,32 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="OpenTK.NetStandard" Version="1.0.5.32" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
+ <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ContentWithTargetPath Include="SoundIo\Native\libsoundio\libs\libsoundio.dll">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.dll</TargetPath>
+ </ContentWithTargetPath>
+ <ContentWithTargetPath Include="SoundIo\Native\libsoundio\libs\libsoundio.dylib">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.dylib</TargetPath>
+ </ContentWithTargetPath>
+ <ContentWithTargetPath Include="SoundIo\Native\libsoundio\libs\libsoundio.so">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <TargetPath>libsoundio.so</TargetPath>
+ </ContentWithTargetPath>
+ </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj b/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj
deleted file mode 100644
index ccdeae3e..00000000
--- a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
- <ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
- <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
- </ItemGroup>
-
-</Project>
diff --git a/Ryujinx.Audio/AudioManager.cs b/Ryujinx.Audio/AudioManager.cs
new file mode 100644
index 00000000..ab25150a
--- /dev/null
+++ b/Ryujinx.Audio/AudioManager.cs
@@ -0,0 +1,137 @@
+//
+// 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 System;
+using System.Threading;
+
+namespace Ryujinx.Audio
+{
+ /// <summary>
+ /// Manage audio input and output system.
+ /// </summary>
+ public class AudioManager : IDisposable
+ {
+ /// <summary>
+ /// Lock used to control the waiters registration.
+ /// </summary>
+ private object _lock = new object();
+
+ /// <summary>
+ /// Events signaled when the driver played audio buffers.
+ /// </summary>
+ private ManualResetEvent[] _updateRequiredEvents;
+
+ /// <summary>
+ /// Action to execute when the driver played audio buffers.
+ /// </summary>
+ private Action[] _actions;
+
+ /// <summary>
+ /// The worker thread in charge of handling sessions update.
+ /// </summary>
+ private Thread _workerThread;
+
+ /// <summary>
+ /// Create a new <see cref="AudioManager"/>.
+ /// </summary>
+ public AudioManager()
+ {
+ _updateRequiredEvents = new ManualResetEvent[2];
+ _actions = new Action[2];
+
+ // Termination event.
+ _updateRequiredEvents[1] = new ManualResetEvent(false);
+
+ _workerThread = new Thread(Update)
+ {
+ Name = "AudioManager.Worker"
+ };
+ }
+
+ /// <summary>
+ /// Start the <see cref="AudioManager"/>.
+ /// </summary>
+ public void Start()
+ {
+ if (_workerThread.IsAlive)
+ {
+ throw new InvalidOperationException();
+ }
+
+ _workerThread.Start();
+ }
+
+ /// <summary>
+ /// Initialize update handlers.
+ /// </summary>
+ /// <param name="updatedRequiredEvent ">The driver event that will get signaled by the device driver when an audio buffer finished playing/being captured</param>
+ /// <param name="outputCallback">The callback to call when an audio buffer finished playing</param>
+ /// <param name="inputCallback">The callback to call when an audio buffer was captured</param>
+ public void Initialize(ManualResetEvent updatedRequiredEvent, Action outputCallback, Action inputCallback)
+ {
+ lock (_lock)
+ {
+ _updateRequiredEvents[0] = updatedRequiredEvent;
+ _actions[0] = outputCallback;
+ _actions[1] = inputCallback;
+ }
+ }
+
+ /// <summary>
+ /// Entrypoint of the <see cref="_workerThread"/> in charge of updating the <see cref="AudioManager"/>.
+ /// </summary>
+ private void Update()
+ {
+ while (true)
+ {
+ int index = WaitHandle.WaitAny(_updateRequiredEvents);
+
+ // Last index is here to indicate thread termination.
+ if (index + 1 == _updateRequiredEvents.Length)
+ {
+ break;
+ }
+
+ lock (_lock)
+ {
+ foreach (Action action in _actions)
+ {
+ action?.Invoke();
+ }
+
+ _updateRequiredEvents[0].Reset();
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _updateRequiredEvents[1].Set();
+ _workerThread.Join();
+
+ _updateRequiredEvents[1].Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Common/BackendHelper.cs b/Ryujinx.Audio/Backends/Common/BackendHelper.cs
new file mode 100644
index 00000000..df81d942
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Common/BackendHelper.cs
@@ -0,0 +1,43 @@
+//
+// 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 System;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ public static class BackendHelper
+ {
+ public static int GetSampleSize(SampleFormat format)
+ {
+ return format switch
+ {
+ SampleFormat.PcmInt8 => sizeof(byte),
+ SampleFormat.PcmInt16 => sizeof(ushort),
+ SampleFormat.PcmInt24 => 3,
+ SampleFormat.PcmInt32 => sizeof(int),
+ SampleFormat.PcmFloat => sizeof(float),
+ _ => throw new ArgumentException($"{format}"),
+ };
+ }
+
+ public static int GetSampleCount(SampleFormat format, int channelCount, int bufferSize)
+ {
+ return bufferSize / GetSampleSize(format) / channelCount;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
new file mode 100644
index 00000000..b4f5f812
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
@@ -0,0 +1,183 @@
+//
+// 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.Common;
+using System;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ /// <summary>
+ /// A ring buffer that grow if data written to it is too big to fit.
+ /// </summary>
+ public class DynamicRingBuffer
+ {
+ private const int RingBufferAlignment = 2048;
+
+ private object _lock = new object();
+
+ private byte[] _buffer;
+ private int _size;
+ private int _headOffset;
+ private int _tailOffset;
+
+ public int Length => _size;
+
+ public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
+ {
+ _buffer = new byte[initialCapacity];
+ }
+
+ public void Clear()
+ {
+ _size = 0;
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+
+ public void Clear(int size)
+ {
+ lock (_lock)
+ {
+ if (size > _size)
+ {
+ size = _size;
+ }
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ _headOffset = (_headOffset + size) % _buffer.Length;
+ _size -= size;
+
+ if (_size == 0)
+ {
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+ }
+ }
+
+ private void SetCapacityLocked(int capacity)
+ {
+ byte[] buffer = new byte[capacity];
+
+ if (_size > 0)
+ {
+ if (_headOffset < _tailOffset)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _size);
+ }
+ else
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _buffer.Length - _headOffset);
+ Buffer.BlockCopy(_buffer, 0, buffer, _buffer.Length - _headOffset, _tailOffset);
+ }
+ }
+
+ _buffer = buffer;
+ _headOffset = 0;
+ _tailOffset = _size;
+ }
+
+
+ public void Write<T>(T[] buffer, int index, int count)
+ {
+ if (count == 0)
+ {
+ return;
+ }
+
+ lock (_lock)
+ {
+ if ((_size + count) > _buffer.Length)
+ {
+ SetCapacityLocked(BitUtils.AlignUp(_size + count, RingBufferAlignment));
+ }
+
+ if (_headOffset < _tailOffset)
+ {
+ int tailLength = _buffer.Length - _tailOffset;
+
+ if (tailLength >= count)
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ }
+ else
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, tailLength);
+ Buffer.BlockCopy(buffer, index + tailLength, _buffer, 0, count - tailLength);
+ }
+ }
+ else
+ {
+ Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ }
+
+ _size += count;
+ _tailOffset = (_tailOffset + count) % _buffer.Length;
+ }
+ }
+
+ public int Read<T>(T[] buffer, int index, int count)
+ {
+ lock (_lock)
+ {
+ if (count > _size)
+ {
+ count = _size;
+ }
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (_headOffset < _tailOffset)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ }
+ else
+ {
+ int tailLength = _buffer.Length - _headOffset;
+
+ if (tailLength >= count)
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ }
+ else
+ {
+ Buffer.BlockCopy(_buffer, _headOffset, buffer, index, tailLength);
+ Buffer.BlockCopy(_buffer, 0, buffer, index + tailLength, count - tailLength);
+ }
+ }
+
+ _size -= count;
+ _headOffset = (_headOffset + count) % _buffer.Length;
+
+ if (_size == 0)
+ {
+ _headOffset = 0;
+ _tailOffset = 0;
+ }
+
+ return count;
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
new file mode 100644
index 00000000..1e000e4c
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs
@@ -0,0 +1,89 @@
+//
+// 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.Memory;
+
+namespace Ryujinx.Audio.Backends.Common
+{
+ public abstract class HardwareDeviceSessionOutputBase : IHardwareDeviceSession
+ {
+ public IVirtualMemoryManager MemoryManager { get; }
+ public SampleFormat RequestedSampleFormat { get; }
+ public uint RequestedSampleRate { get; }
+ public uint RequestedChannelCount { get; }
+
+ public HardwareDeviceSessionOutputBase(IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
+ {
+ MemoryManager = memoryManager;
+ RequestedSampleFormat = requestedSampleFormat;
+ RequestedSampleRate = requestedSampleRate;
+ RequestedChannelCount = requestedChannelCount;
+ }
+
+ private byte[] GetBufferSamples(AudioBuffer buffer)
+ {
+ if (buffer.DataPointer == 0)
+ {
+ return null;
+ }
+
+ byte[] data = new byte[buffer.DataSize];
+
+ MemoryManager.Read(buffer.DataPointer, data);
+
+ return data;
+ }
+
+ protected ulong GetSampleCount(AudioBuffer buffer)
+ {
+ return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, (int)buffer.DataSize);
+ }
+
+ public abstract void Dispose();
+ public abstract void PrepareToClose();
+ public abstract void QueueBuffer(AudioBuffer buffer);
+ public abstract void SetVolume(float volume);
+ public abstract float GetVolume();
+ public abstract void Start();
+ public abstract void Stop();
+ public abstract ulong GetPlayedSampleCount();
+ public abstract bool WasBufferFullyConsumed(AudioBuffer buffer);
+ public virtual bool RegisterBuffer(AudioBuffer buffer)
+ {
+ return RegisterBuffer(buffer, GetBufferSamples(buffer));
+ }
+
+ public virtual bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
+ {
+ if (samples == null)
+ {
+ return false;
+ }
+
+ if (buffer.Data == null)
+ {
+ buffer.Data = samples;
+ }
+
+ return true;
+ }
+
+ public virtual void UnregisterBuffer(AudioBuffer buffer) { }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs
new file mode 100644
index 00000000..c0305f8a
--- /dev/null
+++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs
@@ -0,0 +1,146 @@
+//
+// 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.Backends.Common;
+using Ryujinx.Audio.Backends.Dummy;
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Common.Logging;
+using Ryujinx.Memory;
+using System;
+using System.Threading;
+
+using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
+
+namespace Ryujinx.Audio.Backends.CompatLayer
+{
+ public class CompatLayerHardwareDeviceDriver : IHardwareDeviceDriver
+ {
+ private IHardwareDeviceDriver _realDriver;
+
+ public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice)
+ {
+ _realDriver = realDevice;
+ }
+
+ public void Dispose()
+ {
+ _realDriver.Dispose();
+ }
+
+ public ManualResetEvent GetUpdateRequiredEvent()
+ {
+ return _realDriver.GetUpdateRequiredEvent();
+ }
+
+ private uint SelectHardwareChannelCount(uint targetChannelCount)
+ {
+ if (_realDriver.SupportsChannelCount(targetChannelCount))
+ {
+ return targetChannelCount;
+ }
+
+ return targetChannelCount switch
+ {
+ 6 => SelectHardwareChannelCount(2),
+ 2 => SelectHardwareChannelCount(1),
+ 1 => throw new ArgumentException("No valid channel configuration found!"),
+ _ => throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}")
+ };
+ }
+
+ public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
+ {
+ if (channelCount == 0)
+ {
+ channelCount = 2;
+ }
+
+ if (sampleRate == 0)
+ {
+ sampleRate = Constants.TargetSampleRate;
+ }
+
+ if (!_realDriver.SupportsDirection(direction))
+ {
+ if (direction == Direction.Input)
+ {
+ Logger.Warning?.Print(LogClass.Audio, "The selected audio backend doesn't support audio input, fallback to dummy...");
+
+ return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount);
+ }
+
+ throw new NotImplementedException();
+ }
+
+ uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
+
+ IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount);
+
+ if (hardwareChannelCount == channelCount)
+ {
+ return realSession;
+ }
+
+ if (direction == Direction.Input)
+ {
+ Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy...");
+
+ // TODO: We currently don't support audio input upsampling/downsampling, implement this.
+ realSession.Dispose();
+
+ return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount);
+ }
+
+ // It must be a HardwareDeviceSessionOutputBase.
+ if (realSession is not HardwareDeviceSessionOutputBase realSessionOutputBase)
+ {
+ throw new InvalidOperationException($"Real driver session class type isn't based on {typeof(HardwareDeviceSessionOutputBase).Name}.");
+ }
+
+ // If we need to do post processing before sending to the hardware device, wrap around it.
+ return new CompatLayerHardwareDeviceSession(realSessionOutputBase, channelCount);
+ }
+
+ public bool SupportsChannelCount(uint channelCount)
+ {
+ return channelCount == 1 || channelCount == 2 || channelCount == 6;
+ }
+
+ public bool SupportsSampleFormat(SampleFormat sampleFormat)
+ {
+ // TODO: More formats.
+ return sampleFormat == SampleFormat.PcmInt16;
+ }
+
+ public bool SupportsSampleRate(uint sampleRate)
+ {
+ // TODO: More sample rates.
+ return sampleRate == Constants.TargetSampleRate;
+ }
+
+ public IHardwareDeviceDriver GetRealDeviceDriver()
+ {
+ return _realDriver;
+ }
+
+ public bool SupportsDirection(Direction direction)
+ {
+ return direction == Direction.Input || direction == Direction.Output;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs
new file mode 100644
index 00000000..493d682f
--- /dev/null
+++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceSession.cs
@@ -0,0 +1,140 @@
+//
+// 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.Backends.Common;
+using Ryujinx.Audio.Common;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Backends.CompatLayer
+{
+ class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
+ {
+ private HardwareDeviceSessionOutputBase _realSession;
+ private uint _userChannelCount;
+
+ public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
+ {
+ _realSession = realSession;
+ _userChannelCount = userChannelCount;
+ }
+
+ public override void Dispose()
+ {
+ _realSession.Dispose();
+ }
+
+ public override ulong GetPlayedSampleCount()
+ {
+ return _realSession.GetPlayedSampleCount();
+ }
+
+ public override float GetVolume()
+ {
+ return _realSession.GetVolume();
+ }
+
+ public override void PrepareToClose()
+ {
+ _realSession.PrepareToClose();
+ }
+
+ public override void QueueBuffer(AudioBuffer buffer)
+ {
+ _realSession.QueueBuffer(buffer);
+ }
+
+ public override bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
+ {
+ if (RequestedSampleFormat != SampleFormat.PcmInt16)
+ {
+ throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
+ }
+
+ if (samples == null)
+ {
+ return false;
+ }
+
+ short[] downmixedBufferPCM16;
+
+ ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
+
+ if (_userChannelCount == 6)
+ {
+ downmixedBufferPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
+
+ if (_realSession.RequestedChannelCount == 1)
+ {
+ downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(downmixedBufferPCM16);
+ }
+ }
+ else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
+ {
+ downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
+ }
+ else
+ {
+ throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
+ }
+
+ byte[] downmixedBuffer = MemoryMarshal.Cast<short, byte>(downmixedBufferPCM16).ToArray();
+
+ AudioBuffer fakeBuffer = new AudioBuffer
+ {
+ BufferTag = buffer.BufferTag,
+ DataPointer = buffer.DataPointer,
+ DataSize = (ulong)downmixedBuffer.Length
+ };
+
+ bool result = _realSession.RegisterBuffer(fakeBuffer, downmixedBuffer);
+
+ if (result)
+ {
+ buffer.Data = fakeBuffer.Data;
+ buffer.DataSize = fakeBuffer.DataSize;
+ }
+
+ return result;
+ }
+
+ public override void SetVolume(float volume)
+ {
+ _realSession.SetVolume(volume);
+ }
+
+ public override void Start()
+ {
+ _realSession.Start();
+ }
+
+ public override void Stop()
+ {
+ _realSession.Stop();
+ }
+
+ public override void UnregisterBuffer(AudioBuffer buffer)
+ {
+ _realSession.UnregisterBuffer(buffer);
+ }
+
+ public override bool WasBufferFullyConsumed(AudioBuffer buffer)
+ {
+ return _realSession.WasBufferFullyConsumed(buffer);
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Downmixing.cs b/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs
index 03847737..ff4ba7a1 100644
--- a/Ryujinx.Audio/Downmixing.cs
+++ b/Ryujinx.Audio/Backends/CompatLayer/Downmixing.cs
@@ -1,8 +1,25 @@
-using System;
+//
+// 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 System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-namespace Ryujinx.Audio
+namespace Ryujinx.Audio.Backends.CompatLayer
{
public static class Downmixing
{
diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs
new file mode 100644
index 00000000..f24b359c
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs
@@ -0,0 +1,96 @@
+//
+// 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.Memory;
+using System.Threading;
+
+using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
+
+namespace Ryujinx.Audio.Backends.Dummy
+{
+ public class DummyHardwareDeviceDriver : IHardwareDeviceDriver
+ {
+ private ManualResetEvent _updateRequiredEvent;
+
+ public DummyHardwareDeviceDriver()
+ {
+ _updateRequiredEvent = new ManualResetEvent(false);
+ }
+
+ public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
+ {
+ if (sampleRate == 0)
+ {
+ sampleRate = Constants.TargetSampleRate;
+ }
+
+ if (channelCount == 0)
+ {
+ channelCount = 2;
+ }
+
+ if (direction == Direction.Output)
+ {
+ return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount);
+ }
+ else
+ {
+ return new DummyHardwareDeviceSessionInput(this, memoryManager, sampleFormat, sampleRate, channelCount);
+ }
+ }
+
+ public ManualResetEvent GetUpdateRequiredEvent()
+ {
+ return _updateRequiredEvent;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // NOTE: The _updateRequiredEvent will be disposed somewhere else.
+ }
+ }
+
+ public bool SupportsSampleRate(uint sampleRate)
+ {
+ return true;
+ }
+
+ public bool SupportsSampleFormat(SampleFormat sampleFormat)
+ {
+ return true;
+ }
+
+ public bool SupportsDirection(Direction direction)
+ {
+ return direction == Direction.Output || direction == Direction.Input;
+ }
+
+ public bool SupportsChannelCount(uint channelCount)
+ {
+ return channelCount == 1 || channelCount == 2 || channelCount == 6;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs
new file mode 100644
index 00000000..451a8564
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionInput.cs
@@ -0,0 +1,84 @@
+//
+// 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.Memory;
+using System;
+
+namespace Ryujinx.Audio.Backends.Dummy
+{
+ class DummyHardwareDeviceSessionInput : IHardwareDeviceSession
+ {
+ private float _volume;
+ private IHardwareDeviceDriver _manager;
+ private IVirtualMemoryManager _memoryManager;
+
+ public DummyHardwareDeviceSessionInput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
+ {
+ _volume = 1.0f;
+ _manager = manager;
+ _memoryManager = memoryManager;
+ }
+
+ public void Dispose()
+ {
+ // Nothing to do.
+ }
+
+ public ulong GetPlayedSampleCount()
+ {
+ // Not implemented for input.
+ throw new NotSupportedException();
+ }
+
+ public float GetVolume()
+ {
+ return _volume;
+ }
+
+ public void PrepareToClose() { }
+
+ public void QueueBuffer(AudioBuffer buffer)
+ {
+ _memoryManager.Fill(buffer.DataPointer, buffer.DataSize, 0);
+
+ _manager.GetUpdateRequiredEvent().Set();
+ }
+
+ public bool RegisterBuffer(AudioBuffer buffer)
+ {
+ return buffer.DataPointer != 0;
+ }
+
+ public void SetVolume(float volume)
+ {
+ _volume = volume;
+ }
+
+ public void Start() { }
+
+ public void Stop() { }
+
+ public void UnregisterBuffer(AudioBuffer buffer) { }
+
+ public bool WasBufferFullyConsumed(AudioBuffer buffer)
+ {
+ return true;
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs
new file mode 100644
index 00000000..7cc19997
--- /dev/null
+++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs
@@ -0,0 +1,79 @@
+//
+// 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.Backends.Common;
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Memory;
+using System.Threading;
+
+namespace Ryujinx.Audio.Backends.Dummy
+{
+ internal class DummyHardwareDeviceSessionOutput : HardwareDeviceSessionOutputBase
+ {
+ private float _volume;
+ private IHardwareDeviceDriver _manager;
+
+ private ulong _playedSampleCount;
+
+ public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
+ {
+ _volume = 1.0f;
+ _manager = manager;
+ }
+
+ public override void Dispose()
+ {
+ // Nothing to do.
+ }
+
+ public override ulong GetPlayedSampleCount()
+ {
+ return Interlocked.Read(ref _playedSampleCount);
+ }
+
+ public override float GetVolume()
+ {
+ return _volume;
+ }
+
+ public override void PrepareToClose() { }
+
+ public override void QueueBuffer(AudioBuffer buffer)
+ {
+ Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer));
+
+ _manager.GetUpdateRequiredEvent().Set();
+ }
+
+ public override void SetVolume(float volume)
+ {
+ _volume = volume;
+ }
+
+ public override void Start() { }
+
+ public override void Stop() { }
+
+ public override void UnregisterBuffer(AudioBuffer buffer) { }
+
+ public override bool WasBufferFullyConsumed(AudioBuffer buffer)
+ {
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Audio/Common/AudioBuffer.cs b/Ryujinx.Audio/Common/AudioBuffer.cs
new file mode 100644
index 00000000..853896d5
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioBuffer.cs
@@ -0,0 +1,53 @@
+// 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.Integration;
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// Represent an audio buffer that will be used by an <see cref="IHardwareDeviceSession"/>.
+ /// </summary>
+ public class AudioBuffer
+ {
+ /// <summary>
+ /// Unique tag of this buffer.
+ /// </summary>
+ /// <remarks>Unique per session</remarks>
+ public ulong BufferTag;
+
+ /// <summary>
+ /// Pointer to the user samples.
+ /// </summary>
+ public ulong DataPointer;
+
+ /// <summary>
+ /// Size of the user samples region.
+ /// </summary>
+ public ulong DataSize;
+
+ /// <summary>
+ /// The timestamp at which the buffer was played.
+ /// </summary>
+ /// <remarks>Not used but useful for debugging</remarks>
+ public ulong PlayedTimestamp;
+
+ /// <summary>
+ /// The user samples.
+ /// </summary>
+ public byte[] Data;
+ }
+}
diff --git a/Ryujinx.Audio/Common/AudioDeviceSession.cs b/Ryujinx.Audio/Common/AudioDeviceSession.cs
new file mode 100644
index 00000000..fbdb5a75
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioDeviceSession.cs
@@ -0,0 +1,532 @@
+// 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.Integration;
+using Ryujinx.Common;
+using System;
+using System.Diagnostics;
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// An audio device session.
+ /// </summary>
+ class AudioDeviceSession : IDisposable
+ {
+ /// <summary>
+ /// The volume of the <see cref="AudioDeviceSession"/>.
+ /// </summary>
+ private float _volume;
+
+ /// <summary>
+ /// The state of the <see cref="AudioDeviceSession"/>.
+ /// </summary>
+ private AudioDeviceState _state;
+
+ /// <summary>
+ /// Array of all buffers currently used or released.
+ /// </summary>
+ private AudioBuffer[] _buffers;
+
+ /// <summary>
+ /// The server index inside <see cref="_buffers"/> (appended but not queued to device driver).
+ /// </summary>
+ private uint _serverBufferIndex;
+
+ /// <summary>
+ /// The hardware index inside <see cref="_buffers"/> (queued to device driver).
+ /// </summary>
+ private uint _hardwareBufferIndex;
+
+ /// <summary>
+ /// The released index inside <see cref="_buffers"/> (released by the device driver).
+ /// </summary>
+ private uint _releasedBufferIndex;
+
+ /// <summary>
+ /// The count of buffer appended (server side).
+ /// </summary>
+ private uint _bufferAppendedCount;
+
+ /// <summary>
+ /// The count of buffer registered (driver side).
+ /// </summary>
+ private uint _bufferRegisteredCount;
+
+ /// <summary>
+ /// The count of buffer released (released by the driver side).
+ /// </summary>
+ private uint _bufferReleasedCount;
+
+ /// <summary>
+ /// The released buffer event.
+ /// </summary>
+ private IWritableEvent _bufferEvent;
+
+ /// <summary>
+ /// The session on the device driver.
+ /// </summary>
+ private IHardwareDeviceSession _hardwareDeviceSession;
+
+ /// <summary>
+ /// Max number of buffers that can be registered to the device driver at a time.
+ /// </summary>
+ private uint _bufferRegisteredLimit;
+
+ /// <summary>
+ /// Create a new <see cref="AudioDeviceSession"/>.
+ /// </summary>
+ /// <param name="deviceSession">The device driver session associated</param>
+ /// <param name="bufferEvent">The release buffer event</param>
+ /// <param name="bufferRegisteredLimit">The max number of buffers that can be registered to the device driver at a time</param>
+ public AudioDeviceSession(IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent, uint bufferRegisteredLimit = 4)
+ {
+ _bufferEvent = bufferEvent;
+ _hardwareDeviceSession = deviceSession;
+ _bufferRegisteredLimit = bufferRegisteredLimit;
+
+ _buffers = new AudioBuffer[Constants.AudioDeviceBufferCountMax];
+ _serverBufferIndex = 0;
+ _hardwareBufferIndex = 0;
+ _releasedBufferIndex = 0;
+
+ _bufferAppendedCount = 0;
+ _bufferRegisteredCount = 0;
+ _bufferReleasedCount = 0;
+ _volume = 1.0f;
+ _state = AudioDeviceState.Stopped;
+ }
+
+ /// <summary>
+ /// Get the released buffer event.
+ /// </summary>
+ /// <returns>The released buffer event</returns>
+ public IWritableEvent GetBufferEvent()
+ {
+ return _bufferEvent;
+ }
+
+ /// <summary>
+ /// Get the state of the session.
+ /// </summary>
+ /// <returns>The state of the session</returns>
+ public AudioDeviceState GetState()
+ {
+ Debug.Assert(_state == AudioDeviceState.Started || _state == AudioDeviceState.Stopped);
+
+ return _state;
+ }
+
+ /// <summary>
+ /// Get the total buffer count (server + driver + released).
+ /// </summary>
+ /// <returns>Return the total buffer count</returns>
+ private uint GetTotalBufferCount()
+ {
+ uint bufferCount = _bufferAppendedCount + _bufferRegisteredCount + _bufferReleasedCount;
+
+ Debug.Assert(bufferCount <= Constants.AudioDeviceBufferCountMax);
+
+ return bufferCount;
+ }
+
+ /// <summary>
+ /// Register a new <see cref="AudioBuffer"/> on the server side.
+ /// </summary>
+ /// <param name="buffer">The <see cref="AudioBuffer"/> to register</param>
+ /// <returns>True if the operation succeeded</returns>
+ private bool RegisterBuffer(AudioBuffer buffer)
+ {
+ if (GetTotalBufferCount() == Constants.AudioDeviceBufferCountMax)
+ {
+ return false;
+ }
+
+ _buffers[_serverBufferIndex] = buffer;
+ _serverBufferIndex = (_serverBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
+ _bufferAppendedCount++;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Flush server buffers to hardware.
+ /// </summary>
+ private void FlushToHardware()
+ {
+ uint bufferToFlushCount = Math.Min(Math.Min(_bufferAppendedCount, 4), _bufferRegisteredLimit - _bufferRegisteredCount);
+
+ AudioBuffer[] buffersToFlush = new AudioBuffer[bufferToFlushCount];
+
+ uint hardwareBufferIndex = _hardwareBufferIndex;
+
+ for (int i = 0; i < buffersToFlush.Length; i++)
+ {
+ buffersToFlush[i] = _buffers[_hardwareBufferIndex];
+
+ _bufferAppendedCount--;
+ _bufferRegisteredCount++;
+
+ hardwareBufferIndex = (hardwareBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
+ }
+
+ _hardwareBufferIndex = hardwareBufferIndex;
+
+ for (int i = 0; i < buffersToFlush.Length; i++)
+ {
+ _hardwareDeviceSession.QueueBuffer(buffersToFlush[i]);
+ }
+ }
+
+ /// <summary>
+ /// Get the current index of the <see cref="AudioBuffer"/> playing on the driver side.
+ /// </summary>
+ /// <param name="playingIndex">The output index of the <see cref="AudioBuffer"/> playing on the driver side</param>
+ /// <returns>True if any buffer is playing</returns>
+ private bool TryGetPlayingBufferIndex(out uint playingIndex)
+ {
+ if (_bufferRegisteredCount > 0)
+ {
+ playingIndex = (_hardwareBufferIndex - _bufferRegisteredCount) % Constants.AudioDeviceBufferCountMax;
+
+ return true;
+ }
+
+ playingIndex = 0;
+
+ return false;
+ }
+
+ /// <summary>
+ /// Try to pop the <see cref="AudioBuffer"/> playing on the driver side.
+ /// </summary>
+ /// <param name="buffer">The output <see cref="AudioBuffer"/> playing on the driver side</param>
+ /// <returns>True if any buffer is playing</returns>
+ private bool TryPopPlayingBuffer(out AudioBuffer buffer)
+ {
+ if (_bufferRegisteredCount > 0)
+ {
+ uint bufferIndex = (_hardwareBufferIndex - _bufferRegisteredCount) % Constants.AudioDeviceBufferCountMax;
+
+ buffer = _buffers[bufferIndex];
+
+ _buffers[bufferIndex] = null;
+
+ _bufferRegisteredCount--;
+
+ return true;
+ }
+
+ buffer = null;
+
+ return false;
+ }
+
+ /// <summary>
+ /// Try to pop a <see cref="AudioBuffer"/> released by the driver side.
+ /// </summary>
+ /// <param name="buffer">The output <see cref="AudioBuffer"/> released by the driver side</param>
+ /// <returns>True if any buffer has been released</returns>
+ public bool TryPopReleasedBuffer(out AudioBuffer buffer)
+ {
+ if (_bufferReleasedCount > 0)
+ {
+ uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax;
+
+ buffer = _buffers[bufferIndex];
+
+ _buffers[bufferIndex] = null;
+
+ _bufferReleasedCount--;
+
+ return true;
+ }
+
+ buffer = null;
+
+ return false;
+ }
+
+ /// <summary>
+ /// Release a <see cref="AudioBuffer"/>.
+ /// </summary>
+ /// <param name="buffer">The <see cref="AudioBuffer"/> to release</param>
+ private void ReleaseBuffer(AudioBuffer buffer)
+ {
+ buffer.PlayedTimestamp = (ulong)PerformanceCounter.ElapsedNanoseconds;
+
+ _bufferRegisteredCount--;
+ _bufferReleasedCount++;
+
+ _releasedBufferIndex = (_releasedBufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
+ }
+
+ /// <summary>
+ /// Update the released buffers.
+ /// </summary>
+ /// <param name="updateForStop">True if the session is currently stopping</param>
+ private void UpdateReleaseBuffers(bool updateForStop = false)
+ {
+ bool wasAnyBuffersReleased = false;
+
+ while (TryGetPlayingBufferIndex(out uint playingIndex))
+ {
+ if (!updateForStop && !_hardwareDeviceSession.WasBufferFullyConsumed(_buffers[playingIndex]))
+ {
+ break;
+ }
+
+ if (updateForStop)
+ {
+ _hardwareDeviceSession.UnregisterBuffer(_buffers[playingIndex]);
+ }
+
+ ReleaseBuffer(_buffers[playingIndex]);
+
+ wasAnyBuffersReleased = true;
+ }
+
+ if (wasAnyBuffersReleased)
+ {
+ _bufferEvent.Signal();
+ }
+ }
+
+ /// <summary>
+ /// Append a new <see cref="AudioBuffer"/>.
+ /// </summary>
+ /// <param name="buffer">The <see cref="AudioBuffer"/> to append</param>
+ /// <returns>True if the buffer was appended</returns>
+ public bool AppendBuffer(AudioBuffer buffer)
+ {
+ if (_hardwareDeviceSession.RegisterBuffer(buffer))
+ {
+ if (RegisterBuffer(buffer))
+ {
+ FlushToHardware();
+
+ return true;
+ }
+
+ _hardwareDeviceSession.UnregisterBuffer(buffer);
+ }
+
+ return false;
+ }
+
+ public bool AppendUacBuffer(AudioBuffer buffer, uint handle)
+ {
+ // NOTE: On hardware, there is another RegisterBuffer method taking an handle.
+ // This variant of the call always return false (stubbed?) as a result this logic will never succeed.
+
+ return false;
+ }
+
+ /// <summary>
+ /// Start the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Start()
+ {
+ if (_state == AudioDeviceState.Started)
+ {
+ return ResultCode.OperationFailed;
+ }
+
+ _hardwareDeviceSession.Start();
+
+ _state = AudioDeviceState.Started;
+
+ FlushToHardware();
+
+ _hardwareDeviceSession.SetVolume(_volume);
+
+ return ResultCode.Success;
+ }
+
+ /// <summary>
+ /// Stop the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Stop()
+ {
+ if (_state == AudioDeviceState.Started)
+ {
+ _hardwareDeviceSession.Stop();
+
+ UpdateReleaseBuffers(true);
+
+ _state = AudioDeviceState.Stopped;
+ }
+
+ return ResultCode.Success;
+ }
+
+ /// <summary>
+ /// Get the volume of the session.
+ /// </summary>
+ /// <returns>The volume of the session</returns>
+ public float GetVolume()
+ {
+ return _hardwareDeviceSession.GetVolume();
+ }
+
+ /// <summary>
+ /// Set the volume of the session.
+ /// </summary>
+ /// <param name="volume">The new volume to set</param>
+ public void SetVolume(float volume)
+ {
+ _volume = volume;
+
+ if (_state == AudioDeviceState.Started)
+ {
+ _hardwareDeviceSession.SetVolume(volume);
+ }
+ }
+
+ /// <summary>
+ /// Get the count of buffer currently in use (server + driver side).
+ /// </summary>
+ /// <returns>The count of buffer currently in use</returns>
+ public uint GetBufferCount()
+ {
+ return _bufferAppendedCount + _bufferRegisteredCount;
+ }
+
+ /// <summary>
+ /// Check if a buffer is present.
+ /// </summary>
+ /// <param name="bufferTag">The unique tag of the buffer</param>
+ /// <returns>Return true if a buffer is present</returns>
+ public bool ContainsBuffer(ulong bufferTag)
+ {
+ uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax;
+
+ for (int i = 0; i < GetTotalBufferCount(); i++)
+ {
+ if (_buffers[bufferIndex].BufferTag == bufferTag)
+ {
+ return true;
+ }
+
+ bufferIndex = (bufferIndex + 1) % Constants.AudioDeviceBufferCountMax;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Get the count of sample played in this session.
+ /// </summary>
+ /// <returns>The count of sample played in this session</returns>
+ public ulong GetPlayedSampleCount()
+ {
+ if (_state == AudioDeviceState.Stopped)
+ {
+ return 0;
+ }
+ else
+ {
+ return _hardwareDeviceSession.GetPlayedSampleCount();
+ }
+ }
+
+ /// <summary>
+ /// Flush all buffers to the initial state.
+ /// </summary>
+ /// <returns>True if any buffer was flushed</returns>
+ public bool FlushBuffers()
+ {
+ if (_state == AudioDeviceState.Stopped)
+ {
+ return false;
+ }
+
+ uint bufferCount = GetBufferCount();
+
+ while (TryPopReleasedBuffer(out AudioBuffer buffer))
+ {
+ _hardwareDeviceSession.UnregisterBuffer(buffer);
+ }
+
+ while (TryPopPlayingBuffer(out AudioBuffer buffer))
+ {
+ _hardwareDeviceSession.UnregisterBuffer(buffer);
+ }
+
+ if (_bufferRegisteredCount == 0 || (_bufferReleasedCount + _bufferAppendedCount) > Constants.AudioDeviceBufferCountMax)
+ {
+ return false;
+ }
+
+ _bufferReleasedCount += _bufferAppendedCount;
+ _releasedBufferIndex = (_releasedBufferIndex + _bufferAppendedCount) % Constants.AudioDeviceBufferCountMax;
+ _bufferAppendedCount = 0;
+ _hardwareBufferIndex = _serverBufferIndex;
+
+ if (bufferCount > 0)
+ {
+ _bufferEvent.Signal();
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Update the session.
+ /// </summary>
+ public void Update()
+ {
+ if (_state == AudioDeviceState.Started)
+ {
+ UpdateReleaseBuffers();
+ FlushToHardware();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Tell the hardware session that we are ending.
+ _hardwareDeviceSession.PrepareToClose();
+
+ // Unregister all buffers
+
+ while (TryPopReleasedBuffer(out AudioBuffer buffer))
+ {
+ _hardwareDeviceSession.UnregisterBuffer(buffer);
+ }
+
+ while (TryPopPlayingBuffer(out AudioBuffer buffer))
+ {
+ _hardwareDeviceSession.UnregisterBuffer(buffer);
+ }
+
+ // Finally dispose hardware session.
+ _hardwareDeviceSession.Dispose();
+
+ _bufferEvent.Signal();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Common/AudioDeviceState.cs b/Ryujinx.Audio/Common/AudioDeviceState.cs
new file mode 100644
index 00000000..77604525
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioDeviceState.cs
@@ -0,0 +1,35 @@
+//
+// 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/>.
+//
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// Audio device state.
+ /// </summary>
+ public enum AudioDeviceState : uint
+ {
+ /// <summary>
+ /// The audio device is started.
+ /// </summary>
+ Started,
+
+ /// <summary>
+ /// The audio device is stopped.
+ /// </summary>
+ Stopped
+ }
+}
diff --git a/Ryujinx.Audio/Common/AudioInputConfiguration.cs b/Ryujinx.Audio/Common/AudioInputConfiguration.cs
new file mode 100644
index 00000000..9aa86569
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioInputConfiguration.cs
@@ -0,0 +1,46 @@
+//
+// 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 System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// Audio user input configuration.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct AudioInputConfiguration
+ {
+ /// <summary>
+ /// The target sample rate of the user.
+ /// </summary>
+ /// <remarks>Only 48000Hz is considered valid, other sample rates will be refused.</remarks>
+ public uint SampleRate;
+
+ /// <summary>
+ /// The target channel count of the user.
+ /// </summary>
+ /// <remarks>Only Stereo and Surround are considered valid, other configurations will be refused.</remarks>
+ /// <remarks>Not used in audin.</remarks>
+ public ushort ChannelCount;
+
+ /// <summary>
+ /// Reserved/unused.
+ /// </summary>
+ private ushort _reserved;
+ }
+}
diff --git a/Ryujinx.Audio/Common/AudioOutputConfiguration.cs b/Ryujinx.Audio/Common/AudioOutputConfiguration.cs
new file mode 100644
index 00000000..a493561f
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioOutputConfiguration.cs
@@ -0,0 +1,54 @@
+//
+// 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.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// Audio system output configuration.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct AudioOutputConfiguration
+ {
+ /// <summary>
+ /// The target sample rate of the system.
+ /// </summary>
+ public uint SampleRate;
+
+ /// <summary>
+ /// The target channel count of the system.
+ /// </summary>
+ public uint ChannelCount;
+
+ /// <summary>
+ /// Reserved/unused
+ /// </summary>
+ public SampleFormat SampleFormat;
+
+ /// <summary>
+ /// Reserved/unused.
+ /// </summary>
+ private Array3<byte> _padding;
+
+ /// <summary>
+ /// The initial audio system state.
+ /// </summary>
+ public AudioDeviceState AudioOutState;
+ }
+}
diff --git a/Ryujinx.Audio/Common/AudioUserBuffer.cs b/Ryujinx.Audio/Common/AudioUserBuffer.cs
new file mode 100644
index 00000000..a0614184
--- /dev/null
+++ b/Ryujinx.Audio/Common/AudioUserBuffer.cs
@@ -0,0 +1,53 @@
+//
+// 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 System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Common
+{
+ /// <summary>
+ /// Audio user buffer.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct AudioUserBuffer
+ {
+ /// <summary>
+ /// Pointer to the next buffer (ignored).
+ /// </summary>
+ public ulong NextBuffer;
+
+ /// <summary>
+ /// Pointer to the user samples.
+ /// </summary>
+ public ulong Data;
+
+ /// <summary>
+ /// Capacity of the buffer (unused).
+ /// </summary>
+ public ulong Capacity;
+
+ /// <summary>
+ /// Size of the user samples region.
+ /// </summary>
+ public ulong DataSize;
+
+ /// <summary>
+ /// Offset in the user samples region (unused).
+ /// </summary>
+ public ulong DataOffset;
+ }
+}
diff --git a/Ryujinx.Audio.Renderer/Common/SampleFormat.cs b/Ryujinx.Audio/Common/SampleFormat.cs
index b5268408..bb51bdd3 100644
--- a/Ryujinx.Audio.Renderer/Common/SampleFormat.cs
+++ b/Ryujinx.Audio/Common/SampleFormat.cs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-namespace Ryujinx.Audio.Renderer.Common
+namespace Ryujinx.Audio.Common
{
/// <summary>
/// Sample format definition.
diff --git a/Ryujinx.Audio.Renderer/RendererConstants.cs b/Ryujinx.Audio/Constants.cs
index d2697378..7a407fc6 100644
--- a/Ryujinx.Audio.Renderer/RendererConstants.cs
+++ b/Ryujinx.Audio/Constants.cs
@@ -15,14 +15,24 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-namespace Ryujinx.Audio.Renderer
+namespace Ryujinx.Audio
{
/// <summary>
- /// Define constants used by the Audio Renderer.
+ /// Define constants used by the audio system.
/// </summary>
- public static class RendererConstants
+ public static class Constants
{
/// <summary>
+ /// The default device output name.
+ /// </summary>
+ public const string DefaultDeviceOutputName = "DeviceOut";
+
+ /// <summary>
+ /// The default device input name.
+ /// </summary>
+ public const string DefaultDeviceInputName = "BuiltInHeadset";
+
+ /// <summary>
/// The maximum number of channels supported. (6 channels for 5.1 surround)
/// </summary>
public const int ChannelCountMax = 6;
@@ -33,17 +43,17 @@ namespace Ryujinx.Audio.Renderer
public const int VoiceChannelCountMax = ChannelCountMax;
/// <summary>
- /// The max count of mix buffer supported per operations (volumes, mix effect, ...)
+ /// The maximum count of mix buffer supported per operations (volumes, mix effect, ...)
/// </summary>
public const int MixBufferCountMax = 24;
/// <summary>
- /// The max count of wavebuffer per voice.
+ /// The maximum count of wavebuffer per voice.
/// </summary>
public const int VoiceWaveBufferCount = 4;
/// <summary>
- /// The max count of biquad filter per voice.
+ /// The maximum count of biquad filter per voice.
/// </summary>
public const int VoiceBiquadFilterCount = 2;
@@ -59,7 +69,7 @@ namespace Ryujinx.Audio.Renderer
public const int VoiceHighestPriority = 0;
/// <summary>
- /// Max <see cref="Common.BehaviourParameter.ErrorInfo"/> that can be returned by <see cref="Parameter.BehaviourErrorInfoOutStatus"/>.
+ /// Maximum <see cref="Common.BehaviourParameter.ErrorInfo"/> that can be returned by <see cref="Parameter.BehaviourErrorInfoOutStatus"/>.
/// </summary>
public const int MaxErrorInfos = 10;
@@ -109,11 +119,26 @@ namespace Ryujinx.Audio.Renderer
public const int InvalidProcessingOrder = -1;
/// <summary>
- /// The max number of audio renderer sessions allowed to be created system wide.
+ /// The maximum number of audio renderer sessions allowed to be created system wide.
/// </summary>
public const int AudioRendererSessionCountMax = 2;
/// <summary>
+ /// The maximum number of audio output sessions allowed to be created system wide.
+ /// </summary>
+ public const int AudioOutSessionCountMax = 12;
+
+ /// <summary>
+ /// The maximum number of audio input sessions allowed to be created system wide.
+ /// </summary>
+ public const int AudioInSessionCountMax = 4;
+
+ /// <summary>
+ /// Maximum buffers supported by one audio device session.
+ /// </summary>
+ public const int AudioDeviceBufferCountMax = 32;
+
+ /// <summary>
/// The target sample rate of the audio renderer. (48kHz)
/// </summary>
public const uint TargetSampleRate = 48000;
@@ -139,12 +164,12 @@ namespace Ryujinx.Audio.Renderer
public const int AudioProcessorMaxUpdateTimeTarget = 1000000000 / ((int)TargetSampleRate / TargetSampleCount); // 5.00 ms
/// <summary>
- /// The max update time of the DSP on original hardware.
+ /// The maximum update time of the DSP on original hardware.
/// </summary>
public const int AudioProcessorMaxUpdateTime = 5760000; // 5.76 ms
/// <summary>
- /// The max update time per audio renderer session.
+ /// The maximum update time per audio renderer session.
/// </summary>
public const int AudioProcessorMaxUpdateTimePerSessions = AudioProcessorMaxUpdateTime / AudioRendererSessionCountMax;
diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs
deleted file mode 100644
index ddb1492c..00000000
--- a/Ryujinx.Audio/IAalOutput.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-
-namespace Ryujinx.Audio
-{
- public interface IAalOutput : IDisposable
- {
- bool SupportsChannelCount(int channels);
-
- private int SelectHardwareChannelCount(int targetChannelCount)
- {
- if (SupportsChannelCount(targetChannelCount))
- {
- return targetChannelCount;
- }
-
- return targetChannelCount switch
- {
- 6 => SelectHardwareChannelCount(2),
- 2 => SelectHardwareChannelCount(1),
- 1 => throw new ArgumentException("No valid channel configuration found!"),
- _ => throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}"),
- };
- }
-
- int OpenTrack(int sampleRate, int channels, ReleaseCallback callback)
- {
- return OpenHardwareTrack(sampleRate, SelectHardwareChannelCount(channels), channels, callback);
- }
-
- int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback);
-
- void CloseTrack(int trackId);
-
- bool ContainsBuffer(int trackId, long bufferTag);
-
- long[] GetReleasedBuffers(int trackId, int maxCount);
-
- void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct;
-
- void Start(int trackId);
-
- void Stop(int trackId);
-
- uint GetBufferCount(int trackId);
-
- ulong GetPlayedSampleCount(int trackId);
-
- bool FlushBuffers(int trackId);
-
- float GetVolume(int trackId);
-
- void SetVolume(int trackId, float volume);
-
- PlaybackState GetState(int trackId);
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Input/AudioInputManager.cs b/Ryujinx.Audio/Input/AudioInputManager.cs
new file mode 100644
index 00000000..e098ae9e
--- /dev/null
+++ b/Ryujinx.Audio/Input/AudioInputManager.cs
@@ -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.
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Input/AudioInputSystem.cs b/Ryujinx.Audio/Input/AudioInputSystem.cs
new file mode 100644
index 00000000..8064a947
--- /dev/null
+++ b/Ryujinx.Audio/Input/AudioInputSystem.cs
@@ -0,0 +1,400 @@
+//
+// 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 System;
+
+namespace Ryujinx.Audio.Input
+{
+ /// <summary>
+ /// Audio input system.
+ /// </summary>
+ public class AudioInputSystem : IDisposable
+ {
+ /// <summary>
+ /// The session id associated to the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ private int _sessionId;
+
+ /// <summary>
+ /// The session the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ private AudioDeviceSession _session;
+
+ /// <summary>
+ /// The target device name of the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ public string DeviceName { get; private set; }
+
+ /// <summary>
+ /// The target sample rate of the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ public uint SampleRate { get; private set; }
+
+ /// <summary>
+ /// The target channel count of the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ public uint ChannelCount { get; private set; }
+
+ /// <summary>
+ /// The target sample format of the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ public SampleFormat SampleFormat { get; private set; }
+
+ /// <summary>
+ /// The <see cref="AudioInputManager"/> owning this.
+ /// </summary>
+ private AudioInputManager _manager;
+
+ /// <summary>
+ /// THe lock of the parent.
+ /// </summary>
+ private object _parentLock;
+
+ /// <summary>
+ /// Create a new <see cref="AudioInputSystem"/>.
+ /// </summary>
+ /// <param name="manager">The manager instance</param>
+ /// <param name="parentLock">The lock of the manager</param>
+ /// <param name="deviceSession">The hardware device session</param>
+ /// <param name="bufferEvent">The buffer release event of the audio input</param>
+ public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
+ {
+ _manager = manager;
+ _parentLock = parentLock;
+ _session = new AudioDeviceSession(deviceSession, bufferEvent);
+ }
+
+ /// <summary>
+ /// Get the default device name on the system.
+ /// </summary>
+ /// <returns>The default device name on the system.</returns>
+ private static string GetDeviceDefaultName()
+ {
+ return Constants.DefaultDeviceInputName;
+ }
+
+ /// <summary>
+ /// Check if a given configuration and device name is valid on the system.
+ /// </summary>
+ /// <param name="configuration">The configuration to check.</param>
+ /// <param name="deviceName">The device name to check.</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Get the released buffer event.
+ /// </summary>
+ /// <returns>The released buffer event</returns>
+ public IWritableEvent RegisterBufferEvent()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetBufferEvent();
+ }
+ }
+
+ /// <summary>
+ /// Update the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ public void Update()
+ {
+ lock (_parentLock)
+ {
+ _session.Update();
+ }
+ }
+
+ /// <summary>
+ /// Get the id of this session.
+ /// </summary>
+ /// <returns>The id of this session</returns>
+ public int GetSessionId()
+ {
+ return _sessionId;
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ /// <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="sessionId">The session id associated to this <see cref="AudioInputSystem"/></param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Append a new audio buffer to the audio input.
+ /// </summary>
+ /// <param name="bufferTag">The unique tag of this buffer.</param>
+ /// <param name="userBuffer">The buffer informations.</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Append a new audio buffer to the audio input.
+ /// </summary>
+ /// <remarks>This is broken by design, only added for completness.</remarks>
+ /// <param name="bufferTag">The unique tag of this buffer.</param>
+ /// <param name="userBuffer">The buffer informations.</param>
+ /// <param name="handle">Some unknown handle.</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ public ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer userBuffer, uint handle)
+ {
+ lock (_parentLock)
+ {
+ AudioBuffer buffer = new AudioBuffer
+ {
+ BufferTag = bufferTag,
+ DataPointer = userBuffer.Data,
+ DataSize = userBuffer.DataSize
+ };
+
+ if (_session.AppendUacBuffer(buffer, handle))
+ {
+ return ResultCode.Success;
+ }
+
+ return ResultCode.BufferRingFull;
+ }
+ }
+
+ /// <summary>
+ /// Get the release buffers.
+ /// </summary>
+ /// <param name="releasedBuffers">The buffer to write the release buffers</param>
+ /// <param name="releasedCount">The count of released buffers</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ public ResultCode GetReleasedBuffers(Span<ulong> 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;
+ }
+
+ /// <summary>
+ /// Get the current state of the <see cref="AudioInputSystem"/>.
+ /// </summary>
+ /// <returns>Return the curent sta\te of the <see cref="AudioInputSystem"/></returns>
+ public AudioDeviceState GetState()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetState();
+ }
+ }
+
+ /// <summary>
+ /// Start the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Start()
+ {
+ lock (_parentLock)
+ {
+ return _session.Start();
+ }
+ }
+
+ /// <summary>
+ /// Stop the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Stop()
+ {
+ lock (_parentLock)
+ {
+ return _session.Stop();
+ }
+ }
+
+ /// <summary>
+ /// Get the volume of the session.
+ /// </summary>
+ /// <returns>The volume of the session</returns>
+ public float GetVolume()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetVolume();
+ }
+ }
+
+ /// <summary>
+ /// Set the volume of the session.
+ /// </summary>
+ /// <param name="volume">The new volume to set</param>
+ public void SetVolume(float volume)
+ {
+ lock (_parentLock)
+ {
+ _session.SetVolume(volume);
+ }
+ }
+
+ /// <summary>
+ /// Get the count of buffer currently in use (server + driver side).
+ /// </summary>
+ /// <returns>The count of buffer currently in use</returns>
+ public uint GetBufferCount()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetBufferCount();
+ }
+ }
+
+ /// <summary>
+ /// Check if a buffer is present.
+ /// </summary>
+ /// <param name="bufferTag">The unique tag of the buffer</param>
+ /// <returns>Return true if a buffer is present</returns>
+ public bool ContainsBuffer(ulong bufferTag)
+ {
+ lock (_parentLock)
+ {
+ return _session.ContainsBuffer(bufferTag);
+ }
+ }
+
+ /// <summary>
+ /// Get the count of sample played in this session.
+ /// </summary>
+ /// <returns>The count of sample played in this session</returns>
+ public ulong GetPlayedSampleCount()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetPlayedSampleCount();
+ }
+ }
+
+ /// <summary>
+ /// Flush all buffers to the initial state.
+ /// </summary>
+ /// <returns>True if any buffers was flushed</returns>
+ 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);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs
new file mode 100644
index 00000000..d489b008
--- /dev/null
+++ b/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs
@@ -0,0 +1,82 @@
+//
+// 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 System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Integration
+{
+ public class HardwareDeviceImpl : IHardwareDevice
+ {
+ private IHardwareDeviceSession _session;
+ private uint _channelCount;
+ private uint _sampleRate;
+ private uint _currentBufferTag;
+
+ private byte[] _buffer;
+
+ public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate)
+ {
+ _session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount);
+ _channelCount = channelCount;
+ _sampleRate = sampleRate;
+ _currentBufferTag = 0;
+
+ _buffer = new byte[Constants.TargetSampleCount * channelCount * sizeof(ushort)];
+
+ _session.Start();
+ }
+
+ public void AppendBuffer(ReadOnlySpan<short> data, uint channelCount)
+ {
+ data.CopyTo(MemoryMarshal.Cast<byte, short>(_buffer));
+
+ _session.QueueBuffer(new AudioBuffer
+ {
+ DataPointer = _currentBufferTag++,
+ Data = _buffer,
+ DataSize = (ulong)_buffer.Length,
+ });
+
+ _currentBufferTag = _currentBufferTag % 4;
+ }
+
+ public uint GetChannelCount()
+ {
+ return _channelCount;
+ }
+
+ public uint GetSampleRate()
+ {
+ return _sampleRate;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _session.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio.Renderer/Integration/HardwareDevice.cs b/Ryujinx.Audio/Integration/IHardwareDevice.cs
index 6a82ec85..0f67b2c8 100644
--- a/Ryujinx.Audio.Renderer/Integration/HardwareDevice.cs
+++ b/Ryujinx.Audio/Integration/IHardwareDevice.cs
@@ -18,12 +18,12 @@
using System;
using System.Diagnostics;
-namespace Ryujinx.Audio.Renderer.Integration
+namespace Ryujinx.Audio.Integration
{
/// <summary>
- /// Represent an hardware device used in <see cref="Dsp.Command.DeviceSinkCommand"/>
+ /// Represent an hardware device used in <see cref="Renderer.Dsp.Command.DeviceSinkCommand"/>
/// </summary>
- public interface HardwareDevice : IDisposable
+ public interface IHardwareDevice : IDisposable
{
/// <summary>
/// Get the supported sample rate of this device.
@@ -52,9 +52,9 @@ namespace Ryujinx.Audio.Renderer.Integration
{
uint channelCount = GetChannelCount();
- Debug.Assert(channelCount > 0 && channelCount <= RendererConstants.ChannelCountMax);
+ Debug.Assert(channelCount > 0 && channelCount <= Constants.ChannelCountMax);
- return channelCount != RendererConstants.ChannelCountMax;
+ return channelCount != Constants.ChannelCountMax;
}
}
}
diff --git a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs
new file mode 100644
index 00000000..70738c90
--- /dev/null
+++ b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs
@@ -0,0 +1,50 @@
+//
+// 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.Memory;
+using System;
+using System.Threading;
+
+namespace Ryujinx.Audio.Integration
+{
+ /// <summary>
+ /// Represent an hardware device driver used in <see cref="Output.AudioOutputSystem"/>.
+ /// </summary>
+ public interface IHardwareDeviceDriver : IDisposable
+ {
+ public enum Direction
+ {
+ Input,
+ Output
+ }
+
+ IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount);
+
+ ManualResetEvent GetUpdateRequiredEvent();
+
+ bool SupportsDirection(Direction direction);
+ bool SupportsSampleRate(uint sampleRate);
+ bool SupportsSampleFormat(SampleFormat sampleFormat);
+ bool SupportsChannelCount(uint channelCount);
+
+ IHardwareDeviceDriver GetRealDeviceDriver()
+ {
+ return this;
+ }
+ }
+}
diff --git a/Ryujinx.Audio.Renderer/ResultCode.cs b/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs
index 9b11aefa..0e286768 100644
--- a/Ryujinx.Audio.Renderer/ResultCode.cs
+++ b/Ryujinx.Audio/Integration/IHardwareDeviceSession.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) 2019-2021 Ryujinx
//
// This program is free software: you can redistribute it and/or modify
@@ -15,20 +15,31 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-namespace Ryujinx.Audio.Renderer
+using Ryujinx.Audio.Common;
+using System;
+
+namespace Ryujinx.Audio.Integration
{
- public enum ResultCode
+ public interface IHardwareDeviceSession : IDisposable
{
- ModuleId = 153,
- ErrorCodeShift = 9,
+ bool RegisterBuffer(AudioBuffer buffer);
+
+ void UnregisterBuffer(AudioBuffer buffer);
+
+ void QueueBuffer(AudioBuffer buffer);
+
+ bool WasBufferFullyConsumed(AudioBuffer buffer);
+
+ void SetVolume(float volume);
+
+ float GetVolume();
+
+ ulong GetPlayedSampleCount();
+
+ void Start();
- Success = 0,
+ void Stop();
- OperationFailed = (2 << ErrorCodeShift) | ModuleId,
- WorkBufferTooSmall = (4 << ErrorCodeShift) | ModuleId,
- InvalidUpdateInfo = (41 << ErrorCodeShift) | ModuleId,
- InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
- InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
- UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
+ void PrepareToClose();
}
}
diff --git a/Ryujinx.Audio.Renderer/Integration/IWritableEvent.cs b/Ryujinx.Audio/Integration/IWritableEvent.cs
index 22864b74..ced20dbd 100644
--- a/Ryujinx.Audio.Renderer/Integration/IWritableEvent.cs
+++ b/Ryujinx.Audio/Integration/IWritableEvent.cs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-namespace Ryujinx.Audio.Renderer.Integration
+namespace Ryujinx.Audio.Integration
{
/// <summary>
/// Represent a writable event with manual clear.
diff --git a/Ryujinx.Audio.Renderer/LICENSE.txt b/Ryujinx.Audio/LICENSE.txt
index 0a041280..0a041280 100644
--- a/Ryujinx.Audio.Renderer/LICENSE.txt
+++ b/Ryujinx.Audio/LICENSE.txt
diff --git a/Ryujinx.Audio/Output/AudioOutputManager.cs b/Ryujinx.Audio/Output/AudioOutputManager.cs
new file mode 100644
index 00000000..baa84997
--- /dev/null
+++ b/Ryujinx.Audio/Output/AudioOutputManager.cs
@@ -0,0 +1,256 @@
+//
+// 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.Output
+{
+ /// <summary>
+ /// The audio output manager.
+ /// </summary>
+ public class AudioOutputManager : 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="AudioOutputSystem"/> session instances.
+ /// </summary>
+ private AudioOutputSystem[] _sessions;
+
+ /// <summary>
+ /// The count of active sessions.
+ /// </summary>
+ private int _activeSessionCount;
+
+ /// <summary>
+ /// Create a new <see cref="AudioOutputManager"/>.
+ /// </summary>
+ public AudioOutputManager()
+ {
+ _sessionIds = new int[Constants.AudioOutSessionCountMax];
+ _sessions = new AudioOutputSystem[Constants.AudioOutSessionCountMax];
+ _activeSessionCount = 0;
+
+ for (int i = 0; i < _sessionIds.Length; i++)
+ {
+ _sessionIds[i] = i;
+ }
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="AudioOutputManager"/>.
+ /// </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 output ({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 output ({sessionId})");
+ }
+
+ /// <summary>
+ /// Used to update audio output system.
+ /// </summary>
+ public void Update()
+ {
+ lock (_sessionLock)
+ {
+ foreach (AudioOutputSystem output in _sessions)
+ {
+ output?.Update();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Register a new <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <param name="output">The <see cref="AudioOutputSystem"/> to register.</param>
+ private void Register(AudioOutputSystem output)
+ {
+ lock (_sessionLock)
+ {
+ _sessions[output.GetSessionId()] = output;
+ }
+ }
+
+ /// <summary>
+ /// Unregister a new <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <param name="output">The <see cref="AudioOutputSystem"/> to unregister.</param>
+ internal void Unregister(AudioOutputSystem output)
+ {
+ lock (_sessionLock)
+ {
+ int sessionId = output.GetSessionId();
+
+ _sessions[output.GetSessionId()] = null;
+
+ ReleaseSessionId(sessionId);
+ }
+ }
+
+ /// <summary>
+ /// Get the list of all audio outputs name.
+ /// </summary>
+ /// <returns>The list of all audio outputs name</returns>
+ public string[] ListAudioOuts()
+ {
+ return new string[] { Constants.DefaultDeviceOutputName };
+ }
+
+ /// <summary>
+ /// Open a new <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <param name="outputDeviceName">The output device name selected by the <see cref="AudioOutputSystem"/></param>
+ /// <param name="outputConfiguration">The output audio configuration selected by the <see cref="AudioOutputSystem"/></param>
+ /// <param name="obj">The new <see cref="AudioOutputSystem"/></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 OpenAudioOut(out string outputDeviceName,
+ out AudioOutputConfiguration outputConfiguration,
+ out AudioOutputSystem 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.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount);
+
+ AudioOutputSystem audioOut = new AudioOutputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
+
+ ResultCode result = audioOut.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId);
+
+ if (result == ResultCode.Success)
+ {
+ outputDeviceName = audioOut.DeviceName;
+ outputConfiguration = new AudioOutputConfiguration
+ {
+ ChannelCount = audioOut.ChannelCount,
+ SampleFormat = audioOut.SampleFormat,
+ SampleRate = audioOut.SampleRate,
+ AudioOutState = audioOut.GetState(),
+ };
+
+ obj = audioOut;
+
+ Register(audioOut);
+ }
+ 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.
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/Output/AudioOutputSystem.cs b/Ryujinx.Audio/Output/AudioOutputSystem.cs
new file mode 100644
index 00000000..f5db9d7a
--- /dev/null
+++ b/Ryujinx.Audio/Output/AudioOutputSystem.cs
@@ -0,0 +1,373 @@
+//
+// 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 System;
+
+namespace Ryujinx.Audio.Output
+{
+ /// <summary>
+ /// Audio output system.
+ /// </summary>
+ public class AudioOutputSystem : IDisposable
+ {
+ /// <summary>
+ /// The session id associated to the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ private int _sessionId;
+
+ /// <summary>
+ /// The session the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ private AudioDeviceSession _session;
+
+ /// <summary>
+ /// The target device name of the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ public string DeviceName { get; private set; }
+
+ /// <summary>
+ /// The target sample rate of the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ public uint SampleRate { get; private set; }
+
+ /// <summary>
+ /// The target channel count of the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ public uint ChannelCount { get; private set; }
+
+ /// <summary>
+ /// The target sample format of the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ public SampleFormat SampleFormat { get; private set; }
+
+ /// <summary>
+ /// The <see cref="AudioOutputManager"/> owning this.
+ /// </summary>
+ private AudioOutputManager _manager;
+
+ /// <summary>
+ /// THe lock of the parent.
+ /// </summary>
+ private object _parentLock;
+
+ /// <summary>
+ /// Create a new <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <param name="manager">The manager instance</param>
+ /// <param name="parentLock">The lock of the manager</param>
+ /// <param name="deviceSession">The hardware device session</param>
+ /// <param name="bufferEvent">The buffer release event of the audio output</param>
+ public AudioOutputSystem(AudioOutputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
+ {
+ _manager = manager;
+ _parentLock = parentLock;
+ _session = new AudioDeviceSession(deviceSession, bufferEvent);
+ }
+
+ /// <summary>
+ /// Get the default device name on the system.
+ /// </summary>
+ /// <returns>The default device name on the system.</returns>
+ private static string GetDeviceDefaultName()
+ {
+ return Constants.DefaultDeviceOutputName;
+ }
+
+ /// <summary>
+ /// Check if a given configuration and device name is valid on the system.
+ /// </summary>
+ /// <param name="configuration">The configuration to check.</param>
+ /// <param name="deviceName">The device name to check.</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Get the released buffer event.
+ /// </summary>
+ /// <returns>The released buffer event</returns>
+ public IWritableEvent RegisterBufferEvent()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetBufferEvent();
+ }
+ }
+
+ /// <summary>
+ /// Update the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ public void Update()
+ {
+ lock (_parentLock)
+ {
+ _session.Update();
+ }
+ }
+
+ /// <summary>
+ /// Get the id of this session.
+ /// </summary>
+ /// <returns>The id of this session</returns>
+ public int GetSessionId()
+ {
+ return _sessionId;
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <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="sessionId">The session id associated to this <see cref="AudioOutputSystem"/></param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Append a new audio buffer to the audio output.
+ /// </summary>
+ /// <param name="bufferTag">The unique tag of this buffer.</param>
+ /// <param name="userBuffer">The buffer informations.</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Get the release buffers.
+ /// </summary>
+ /// <param name="releasedBuffers">The buffer to write the release buffers</param>
+ /// <param name="releasedCount">The count of released buffers</param>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
+ public ResultCode GetReleasedBuffer(Span<ulong> 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;
+ }
+
+ /// <summary>
+ /// Get the current state of the <see cref="AudioOutputSystem"/>.
+ /// </summary>
+ /// <returns>Return the curent sta\te of the <see cref="AudioOutputSystem"/></returns>
+ /// <returns></returns>
+ public AudioDeviceState GetState()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetState();
+ }
+ }
+
+ /// <summary>
+ /// Start the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Start()
+ {
+ lock (_parentLock)
+ {
+ return _session.Start();
+ }
+ }
+
+ /// <summary>
+ /// Stop the audio session.
+ /// </summary>
+ /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
+ public ResultCode Stop()
+ {
+ lock (_parentLock)
+ {
+ return _session.Stop();
+ }
+ }
+
+ /// <summary>
+ /// Get the volume of the session.
+ /// </summary>
+ /// <returns>The volume of the session</returns>
+ public float GetVolume()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetVolume();
+ }
+ }
+
+ /// <summary>
+ /// Set the volume of the session.
+ /// </summary>
+ /// <param name="volume">The new volume to set</param>
+ public void SetVolume(float volume)
+ {
+ lock (_parentLock)
+ {
+ _session.SetVolume(volume);
+ }
+ }
+
+ /// <summary>
+ /// Get the count of buffer currently in use (server + driver side).
+ /// </summary>
+ /// <returns>The count of buffer currently in use</returns>
+ public uint GetBufferCount()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetBufferCount();
+ }
+ }
+
+ /// <summary>
+ /// Check if a buffer is present.
+ /// </summary>
+ /// <param name="bufferTag">The unique tag of the buffer</param>
+ /// <returns>Return true if a buffer is present</returns>
+ public bool ContainsBuffer(ulong bufferTag)
+ {
+ lock (_parentLock)
+ {
+ return _session.ContainsBuffer(bufferTag);
+ }
+ }
+
+ /// <summary>
+ /// Get the count of sample played in this session.
+ /// </summary>
+ /// <returns>The count of sample played in this session</returns>
+ public ulong GetPlayedSampleCount()
+ {
+ lock (_parentLock)
+ {
+ return _session.GetPlayedSampleCount();
+ }
+ }
+
+ /// <summary>
+ /// Flush all buffers to the initial state.
+ /// </summary>
+ /// <returns>True if any buffers was flushed</returns>
+ 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);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Audio/PlaybackState.cs b/Ryujinx.Audio/PlaybackState.cs
deleted file mode 100644
index 7d862092..00000000
--- a/Ryujinx.Audio/PlaybackState.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// The playback state of a track
- /// </summary>
- public enum PlaybackState
- {
- /// <summary>
- /// The track is currently playing
- /// </summary>
- Playing = 0,
- /// <summary>
- /// The track is currently stopped
- /// </summary>
- Stopped = 1
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/ReleaseCallback.cs b/Ryujinx.Audio/ReleaseCallback.cs
deleted file mode 100644
index f534e02c..00000000
--- a/Ryujinx.Audio/ReleaseCallback.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace Ryujinx.Audio
-{
- public delegate void ReleaseCallback();
-} \ No newline at end of file
diff --git a/Ryujinx.Audio.Renderer/Common/AuxiliaryBufferAddresses.cs b/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs
index 5d826569..5d826569 100644
--- a/Ryujinx.Audio.Renderer/Common/AuxiliaryBufferAddresses.cs
+++ b/Ryujinx.Audio/Renderer/Common/AuxiliaryBufferAddresses.cs
diff --git a/Ryujinx.Audio.Renderer/Common/BehaviourParameter.cs b/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs
index 0de3d571..0de3d571 100644
--- a/Ryujinx.Audio.Renderer/Common/BehaviourParameter.cs
+++ b/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Common/EdgeMatrix.cs b/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs
index 33b61780..7423eda4 100644
--- a/Ryujinx.Audio.Renderer/Common/EdgeMatrix.cs
+++ b/Ryujinx.Audio/Renderer/Common/EdgeMatrix.cs
@@ -46,7 +46,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// <returns>The size required for the given <paramref name="nodeCount"/>.</returns>
public static int GetWorkBufferSize(int nodeCount)
{
- int size = BitUtils.AlignUp(nodeCount * nodeCount, RendererConstants.BufferAlignment);
+ int size = BitUtils.AlignUp(nodeCount * nodeCount, Constants.BufferAlignment);
return size / Unsafe.SizeOf<byte>();
}
diff --git a/Ryujinx.Audio.Renderer/Common/EffectType.cs b/Ryujinx.Audio/Renderer/Common/EffectType.cs
index 082f94f8..082f94f8 100644
--- a/Ryujinx.Audio.Renderer/Common/EffectType.cs
+++ b/Ryujinx.Audio/Renderer/Common/EffectType.cs
diff --git a/Ryujinx.Audio.Renderer/Common/MemoryPoolUserState.cs b/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs
index 7f9765d9..7f9765d9 100644
--- a/Ryujinx.Audio.Renderer/Common/MemoryPoolUserState.cs
+++ b/Ryujinx.Audio/Renderer/Common/MemoryPoolUserState.cs
diff --git a/Ryujinx.Audio.Renderer/Common/NodeIdHelper.cs b/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs
index 9bf1f2d0..9bf1f2d0 100644
--- a/Ryujinx.Audio.Renderer/Common/NodeIdHelper.cs
+++ b/Ryujinx.Audio/Renderer/Common/NodeIdHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Common/NodeIdType.cs b/Ryujinx.Audio/Renderer/Common/NodeIdType.cs
index 4a4796a9..4a4796a9 100644
--- a/Ryujinx.Audio.Renderer/Common/NodeIdType.cs
+++ b/Ryujinx.Audio/Renderer/Common/NodeIdType.cs
diff --git a/Ryujinx.Audio.Renderer/Common/NodeStates.cs b/Ryujinx.Audio/Renderer/Common/NodeStates.cs
index 5efd7767..5efd7767 100644
--- a/Ryujinx.Audio.Renderer/Common/NodeStates.cs
+++ b/Ryujinx.Audio/Renderer/Common/NodeStates.cs
diff --git a/Ryujinx.Audio.Renderer/Common/PerformanceDetailType.cs b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs
index a92bd0cc..a92bd0cc 100644
--- a/Ryujinx.Audio.Renderer/Common/PerformanceDetailType.cs
+++ b/Ryujinx.Audio/Renderer/Common/PerformanceDetailType.cs
diff --git a/Ryujinx.Audio.Renderer/Common/PerformanceEntryType.cs b/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs
index 0d9a547d..0d9a547d 100644
--- a/Ryujinx.Audio.Renderer/Common/PerformanceEntryType.cs
+++ b/Ryujinx.Audio/Renderer/Common/PerformanceEntryType.cs
diff --git a/Ryujinx.Audio.Renderer/Common/PlayState.cs b/Ryujinx.Audio/Renderer/Common/PlayState.cs
index 7771c154..7771c154 100644
--- a/Ryujinx.Audio.Renderer/Common/PlayState.cs
+++ b/Ryujinx.Audio/Renderer/Common/PlayState.cs
diff --git a/Ryujinx.Audio.Renderer/Common/ReverbEarlyMode.cs b/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs
index a0f65e5e..a0f65e5e 100644
--- a/Ryujinx.Audio.Renderer/Common/ReverbEarlyMode.cs
+++ b/Ryujinx.Audio/Renderer/Common/ReverbEarlyMode.cs
diff --git a/Ryujinx.Audio.Renderer/Common/ReverbLateMode.cs b/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs
index 57760821..57760821 100644
--- a/Ryujinx.Audio.Renderer/Common/ReverbLateMode.cs
+++ b/Ryujinx.Audio/Renderer/Common/ReverbLateMode.cs
diff --git a/Ryujinx.Audio.Renderer/Common/SinkType.cs b/Ryujinx.Audio/Renderer/Common/SinkType.cs
index e1a35508..e1a35508 100644
--- a/Ryujinx.Audio.Renderer/Common/SinkType.cs
+++ b/Ryujinx.Audio/Renderer/Common/SinkType.cs
diff --git a/Ryujinx.Audio.Renderer/Common/UpdateDataHeader.cs b/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs
index ae516662..ae516662 100644
--- a/Ryujinx.Audio.Renderer/Common/UpdateDataHeader.cs
+++ b/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs
diff --git a/Ryujinx.Audio.Renderer/Common/VoiceUpdateState.cs b/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs
index b455ff5c..6cebad5e 100644
--- a/Ryujinx.Audio.Renderer/Common/VoiceUpdateState.cs
+++ b/Ryujinx.Audio/Renderer/Common/VoiceUpdateState.cs
@@ -86,7 +86,7 @@ namespace Ryujinx.Audio.Renderer.Common
/// </summary>
public int LoopCount;
- [StructLayout(LayoutKind.Sequential, Size = 1 * RendererConstants.VoiceWaveBufferCount, Pack = 1)]
+ [StructLayout(LayoutKind.Sequential, Size = 1 * Constants.VoiceWaveBufferCount, Pack = 1)]
private struct WaveBufferValidArray { }
/// <summary>
@@ -107,7 +107,7 @@ namespace Ryujinx.Audio.Renderer.Common
LoopCount = 0;
waveBufferConsumed++;
- if (waveBufferIndex >= RendererConstants.VoiceWaveBufferCount)
+ if (waveBufferIndex >= Constants.VoiceWaveBufferCount)
{
waveBufferIndex = 0;
}
diff --git a/Ryujinx.Audio.Renderer/Common/WaveBuffer.cs b/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs
index 53906c0c..53906c0c 100644
--- a/Ryujinx.Audio.Renderer/Common/WaveBuffer.cs
+++ b/Ryujinx.Audio/Renderer/Common/WaveBuffer.cs
diff --git a/Ryujinx.Audio.Renderer/Common/WorkBufferAllocator.cs b/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs
index 2aff4eb0..2aff4eb0 100644
--- a/Ryujinx.Audio.Renderer/Common/WorkBufferAllocator.cs
+++ b/Ryujinx.Audio/Renderer/Common/WorkBufferAllocator.cs
diff --git a/Ryujinx.Audio.Renderer/Device/VirtualDevice.cs b/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs
index 29395e5c..29395e5c 100644
--- a/Ryujinx.Audio.Renderer/Device/VirtualDevice.cs
+++ b/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs
diff --git a/Ryujinx.Audio.Renderer/Device/VirtualDeviceSession.cs b/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs
index b2669418..b2669418 100644
--- a/Ryujinx.Audio.Renderer/Device/VirtualDeviceSession.cs
+++ b/Ryujinx.Audio/Renderer/Device/VirtualDeviceSession.cs
diff --git a/Ryujinx.Audio.Renderer/Device/VirtualDeviceSessionRegistry.cs b/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs
index ed6806b2..ed6806b2 100644
--- a/Ryujinx.Audio.Renderer/Device/VirtualDeviceSessionRegistry.cs
+++ b/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/AdpcmHelper.cs b/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
index f9f0811d..f9f0811d 100644
--- a/Ryujinx.Audio.Renderer/Dsp/AdpcmHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/AudioProcessor.cs b/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs
index d6cb497a..2c97a0c1 100644
--- a/Ryujinx.Audio.Renderer/Dsp/AudioProcessor.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs
@@ -15,8 +15,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Dsp.Command;
-using Ryujinx.Audio.Renderer.Integration;
using Ryujinx.Audio.Renderer.Utils;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
@@ -48,7 +48,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
private Mailbox<MailboxMessage> _mailbox;
private RendererSession[] _sessionCommandList;
private Thread _workerThread;
- private HardwareDevice[] _outputDevices;
+
+ public IHardwareDevice[] OutputDevices { get; private set; }
private long _lastTime;
private long _playbackEnds;
@@ -59,15 +60,38 @@ namespace Ryujinx.Audio.Renderer.Dsp
_event = new ManualResetEvent(false);
}
- public void SetOutputDevices(HardwareDevice[] outputDevices)
+ private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver)
{
- _outputDevices = outputDevices;
+ // Get the real device driver (In case the compat layer is on top of it).
+ deviceDriver = deviceDriver.GetRealDeviceDriver();
+
+ if (deviceDriver.SupportsChannelCount(6))
+ {
+ return 6;
+ }
+ else
+ {
+ // NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible.
+ return 2;
+ }
}
- public void Start()
+ public void Start(IHardwareDeviceDriver deviceDriver)
{
+ OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
+
+ // TODO: Before enabling this, we need up-mixing from stereo to 5.1.
+ // uint channelCount = GetHardwareChannelCount(deviceDriver);
+ uint channelCount = 2;
+
+ for (int i = 0; i < OutputDevices.Length; i++)
+ {
+ // TODO: Don't hardcode sample rate.
+ OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate);
+ }
+
_mailbox = new Mailbox<MailboxMessage>();
- _sessionCommandList = new RendererSession[RendererConstants.AudioRendererSessionCountMax];
+ _sessionCommandList = new RendererSession[Constants.AudioRendererSessionCountMax];
_event.Reset();
_lastTime = PerformanceCounter.ElapsedNanoseconds;
@@ -89,6 +113,11 @@ namespace Ryujinx.Audio.Renderer.Dsp
{
throw new InvalidOperationException("Audio Processor Stop response was invalid!");
}
+
+ foreach (IHardwareDevice device in OutputDevices)
+ {
+ device.Dispose();
+ }
}
public void Send(int sessionId, CommandList commands, int renderingLimit, ulong appletResourceId)
@@ -113,7 +142,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
throw new InvalidOperationException("Audio Processor Wait response was invalid!");
}
- long increment = RendererConstants.AudioProcessorMaxUpdateTimeTarget;
+ long increment = Constants.AudioProcessorMaxUpdateTimeTarget;
long timeNow = PerformanceCounter.ElapsedNanoseconds;
@@ -188,7 +217,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
{
if (_sessionCommandList[i] != null)
{
- _sessionCommandList[i].CommandList.Process(_outputDevices[i]);
+ _sessionCommandList[i].CommandList.Process(OutputDevices[i]);
_sessionCommandList[i] = null;
}
}
@@ -197,9 +226,9 @@ namespace Ryujinx.Audio.Renderer.Dsp
long elapsedTime = endTicks - startTicks;
- if (RendererConstants.AudioProcessorMaxUpdateTime < elapsedTime)
+ if (Constants.AudioProcessorMaxUpdateTime < elapsedTime)
{
- Logger.Debug?.Print(LogClass.AudioRenderer, $"DSP too slow (exceeded by {elapsedTime - RendererConstants.AudioProcessorMaxUpdateTime}ns)");
+ Logger.Debug?.Print(LogClass.AudioRenderer, $"DSP too slow (exceeded by {elapsedTime - Constants.AudioProcessorMaxUpdateTime}ns)");
}
_mailbox.SendResponse(MailboxMessage.RenderEnd);
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs
index daf75208..bea6215d 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
@@ -54,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch;
- WaveBuffers = new WaveBuffer[RendererConstants.VoiceWaveBufferCount];
+ WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
index 5ff3a6b4..5ff3a6b4 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/BiquadFilterCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs
index 580eb732..580eb732 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/BiquadFilterCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/BiquadFilterCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CircularBufferSinkCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs
index 509aff27..e4c635d5 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/CircularBufferSinkCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs
@@ -44,7 +44,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Enabled = true;
NodeId = nodeId;
- Input = new ushort[RendererConstants.ChannelCountMax];
+ Input = new ushort[Constants.ChannelCountMax];
InputCount = parameter.InputCount;
for (int i = 0; i < InputCount; i++)
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/ClearMixBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs
index ba9a0357..ba9a0357 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/ClearMixBufferCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/ClearMixBufferCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs
index bcc2cd11..fee90192 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
@@ -39,7 +39,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public IVirtualMemoryManager MemoryManager { get; }
- public HardwareDevice OutputDevice { get; private set; }
+ public IHardwareDevice OutputDevice { get; private set; }
public CommandList(AudioRenderSystem renderSystem) : this(renderSystem.MemoryManager,
renderSystem.GetMixBuffer(),
@@ -85,7 +85,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
return (ulong)PerformanceCounter.ElapsedNanoseconds - StartTime;
}
- public void Process(HardwareDevice outputDevice)
+ public void Process(IHardwareDevice outputDevice)
{
OutputDevice = outputDevice;
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CommandType.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
index 8ff1c581..8ff1c581 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/CommandType.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CommandType.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CopyMixBufferCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs
index 2dc6f04b..2dc6f04b 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/CopyMixBufferCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/CopyMixBufferCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DataSourceVersion2Command.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs
index 469f6ca5..cbc48be7 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DataSourceVersion2Command.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
@@ -67,7 +68,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
SampleRate = serverState.SampleRate;
Pitch = serverState.Pitch;
- WaveBuffers = new WaveBuffer[RendererConstants.VoiceWaveBufferCount];
+ WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DelayCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs
index 54234a93..4ceebcf6 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DelayCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs
@@ -54,8 +54,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
IsEffectEnabled = isEnabled;
- InputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
- OutputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < Parameter.ChannelCount; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DepopForMixBuffersCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs
index c6feacfb..c6feacfb 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DepopForMixBuffersCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DepopForMixBuffersCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DepopPrepareCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs
index d09c380e..35275229 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DepopPrepareCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs
@@ -43,9 +43,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
NodeId = nodeId;
MixBufferCount = mixBufferCount;
- OutputBufferIndices = new ushort[RendererConstants.MixBufferCountMax];
+ OutputBufferIndices = new ushort[Constants.MixBufferCountMax];
- for (int i = 0; i < RendererConstants.MixBufferCountMax; i++)
+ for (int i = 0; i < Constants.MixBufferCountMax; i++)
{
OutputBufferIndices[i] = (ushort)(bufferOffset + i);
}
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DeviceSinkCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs
index 621e0269..28b4a5f1 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DeviceSinkCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server.Sink;
using System;
using System.Runtime.CompilerServices;
@@ -75,14 +75,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public void Process(CommandList context)
{
- HardwareDevice device = context.OutputDevice;
+ IHardwareDevice device = context.OutputDevice;
- if (device.GetSampleRate() == RendererConstants.TargetSampleRate)
+ if (device.GetSampleRate() == Constants.TargetSampleRate)
{
int channelCount = (int)device.GetChannelCount();
uint bufferCount = Math.Min(device.GetChannelCount(), InputCount);
- const int sampleCount = RendererConstants.TargetSampleCount;
+ const int sampleCount = Constants.TargetSampleCount;
short[] outputBuffer = new short[bufferCount * sampleCount];
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs
index 96138db2..f81a2849 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/DownMixSurroundToStereoCommand.cs
@@ -40,10 +40,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Enabled = true;
NodeId = nodeId;
- InputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
- OutputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
- for (int i = 0; i < RendererConstants.VoiceChannelCountMax; i++)
+ for (int i = 0; i < Constants.VoiceChannelCountMax; i++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + inputBufferOffset[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + outputBufferOffset[i]);
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/ICommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs
index e2f00ef6..e2f00ef6 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/ICommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/ICommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/MixCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs
index 566fea92..566fea92 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/MixCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/MixRampCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs
index f34e50e4..f34e50e4 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/MixRampCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/MixRampGroupedCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
index d45da1e3..cbd2503b 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/MixRampGroupedCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs
@@ -46,10 +46,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
MixBufferCount = mixBufferCount;
NodeId = nodeId;
- InputBufferIndices = new ushort[RendererConstants.MixBufferCountMax];
- OutputBufferIndices = new ushort[RendererConstants.MixBufferCountMax];
- Volume0 = new float[RendererConstants.MixBufferCountMax];
- Volume1 = new float[RendererConstants.MixBufferCountMax];
+ InputBufferIndices = new ushort[Constants.MixBufferCountMax];
+ OutputBufferIndices = new ushort[Constants.MixBufferCountMax];
+ Volume0 = new float[Constants.MixBufferCountMax];
+ Volume1 = new float[Constants.MixBufferCountMax];
for (int i = 0; i < mixBufferCount; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs
index d50c61fd..c4b9b0ff 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
@@ -55,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
ChannelCount = serverState.ChannelsCount;
Pitch = serverState.Pitch;
- WaveBuffers = new WaveBuffer[RendererConstants.VoiceWaveBufferCount];
+ WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs
index 9decd431..3758dbe0 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using System;
using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
@@ -55,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
ChannelCount = serverState.ChannelsCount;
Pitch = serverState.Pitch;
- WaveBuffers = new WaveBuffer[RendererConstants.VoiceWaveBufferCount];
+ WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
for (int i = 0; i < WaveBuffers.Length; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/PerformanceCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs
index 0cc35712..0cc35712 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/PerformanceCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/PerformanceCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/Reverb3dCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs
index c1527758..c501bc1c 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/Reverb3dCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs
@@ -71,8 +71,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
State = state;
WorkBuffer = workBuffer;
- InputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
- OutputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < Parameter.ChannelCount; i++)
{
@@ -110,7 +110,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
bool isMono = Parameter.ChannelCount == 1;
bool isSurround = Parameter.ChannelCount == 6;
- float[] outputValues = new float[RendererConstants.ChannelCountMax];
+ float[] outputValues = new float[Constants.ChannelCountMax];
float[] channelInput = new float[Parameter.ChannelCount];
float[] feedbackValues = new float[4];
float[] feedbackOutputValues = new float[4];
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/ReverbCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs
index f0303ed1..0ed955de 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/ReverbCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs
@@ -41,8 +41,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private static readonly int[] OutputEarlyIndicesTableSurround = new int[20] { 0, 5, 0, 5, 1, 5, 1, 5, 4, 5, 4, 5, 2, 5, 2, 5, 3, 5, 3, 5 };
private static readonly int[] TargetEarlyDelayLineIndicesTableSurround = new int[20] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9 };
- private static readonly int[] OutputIndicesTableSurround = new int[RendererConstants.ChannelCountMax] { 0, 1, 2, 3, 4, 5 };
- private static readonly int[] TargetOutputFeedbackIndicesTableSurround = new int[RendererConstants.ChannelCountMax] { 0, 1, 2, 3, -1, 3 };
+ private static readonly int[] OutputIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, 4, 5 };
+ private static readonly int[] TargetOutputFeedbackIndicesTableSurround = new int[Constants.ChannelCountMax] { 0, 1, 2, 3, -1, 3 };
public bool Enabled { get; set; }
@@ -74,8 +74,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
State = state;
WorkBuffer = workBuffer;
- InputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
- OutputBufferIndices = new ushort[RendererConstants.VoiceChannelCountMax];
+ InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
+ OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < Parameter.ChannelCount; i++)
{
@@ -141,7 +141,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
- float[] outputValues = new float[RendererConstants.ChannelCountMax];
+ float[] outputValues = new float[Constants.ChannelCountMax];
float[] feedbackValues = new float[4];
float[] feedbackOutputValues = new float[4];
float[] channelInput = new float[Parameter.ChannelCount];
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/UpsampleCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs
index 3bfaa0f6..6c446f05 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/UpsampleCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/UpsampleCommand.cs
@@ -69,7 +69,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public void Process(CommandList context)
{
- float ratio = (float)InputSampleRate / RendererConstants.TargetSampleRate;
+ float ratio = (float)InputSampleRate / Constants.TargetSampleRate;
uint bufferCount = Math.Min(BufferCount, UpsamplerInfo.SourceSampleCount);
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/VolumeCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs
index b58ae1f8..b58ae1f8 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/VolumeCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/VolumeRampCommand.cs b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs
index d07926c8..d07926c8 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/VolumeRampCommand.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Command/VolumeRampCommand.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs b/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs
index bb583dd3..c9514529 100644
--- a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs
@@ -15,6 +15,7 @@
// 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.State;
using Ryujinx.Common.Logging;
@@ -111,7 +112,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
while (y < sampleCountToDecode)
{
- if (waveBufferIndex >= RendererConstants.VoiceWaveBufferCount)
+ if (waveBufferIndex >= Constants.VoiceWaveBufferCount)
{
Logger.Error?.Print(LogClass.AudioRenderer, $"Invalid WaveBuffer index {waveBufferIndex}");
diff --git a/Ryujinx.Audio.Renderer/Dsp/Effect/DecayDelay.cs b/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs
index 8a815c56..8a815c56 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Effect/DecayDelay.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Effect/DecayDelay.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Effect/DelayLine.cs b/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs
index d178e699..d178e699 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Effect/DelayLine.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLine.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Effect/DelayLineReverb3d.cs b/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs
index a30dcc63..a30dcc63 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Effect/DelayLineReverb3d.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Effect/DelayLineReverb3d.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/Effect/IDelayLine.cs b/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs
index ac59445e..ac59445e 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Effect/IDelayLine.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/Effect/IDelayLine.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/FixedPointHelper.cs b/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs
index 0afd8926..0afd8926 100644
--- a/Ryujinx.Audio.Renderer/Dsp/FixedPointHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/FixedPointHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/FloatingPointHelper.cs b/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs
index b143d8f9..b143d8f9 100644
--- a/Ryujinx.Audio.Renderer/Dsp/FloatingPointHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/FloatingPointHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/PcmHelper.cs b/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs
index 12c1d475..12c1d475 100644
--- a/Ryujinx.Audio.Renderer/Dsp/PcmHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/PcmHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/ResamplerHelper.cs b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs
index d40da412..d40da412 100644
--- a/Ryujinx.Audio.Renderer/Dsp/ResamplerHelper.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/ResamplerHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/AdpcmLoopContext.cs b/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs
index 695b32d2..695b32d2 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/AdpcmLoopContext.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/AdpcmLoopContext.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs b/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs
index 3cf24302..3cf24302 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/AuxiliaryBufferHeader.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/BiquadFilterState.cs b/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs
index 9677333d..9677333d 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/BiquadFilterState.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/BiquadFilterState.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/DelayState.cs b/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs
index 7b694fb0..7b694fb0 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/DelayState.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/DelayState.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/Reverb3dState.cs b/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs
index be0f9734..be0f9734 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/Reverb3dState.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/Reverb3dState.cs
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/ReverbState.cs b/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs
index 60d8e284..60d8e284 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/ReverbState.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/State/ReverbState.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/AudioRendererConfiguration.cs b/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs
index 047aa279..047aa279 100644
--- a/Ryujinx.Audio.Renderer/Parameter/AudioRendererConfiguration.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/AudioRendererConfiguration.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/BehaviourErrorInfoOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
index e5312f04..e5312f04 100644
--- a/Ryujinx.Audio.Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/BiquadFilterParameter.cs b/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs
index de6b9577..de6b9577 100644
--- a/Ryujinx.Audio.Renderer/Parameter/BiquadFilterParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/BiquadFilterParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
index 5a747922..5a747922 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/AuxiliaryBufferParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
index 7d58d4be..7d58d4be 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/BiquadFilterEffectParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/BufferMixerParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs
index b0c0c337..b0c0c337 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/BufferMixerParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/BufferMixerParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/DelayParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs
index e0dbeb7c..e0dbeb7c 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/DelayParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/DelayParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/Reverb3dParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs
index 0dbecfb8..0dbecfb8 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/Reverb3dParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/Reverb3dParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Effect/ReverbParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs
index daa81c51..daa81c51 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Effect/ReverbParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Effect/ReverbParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/EffectInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs
index af8edeff..af8edeff 100644
--- a/Ryujinx.Audio.Renderer/Parameter/EffectInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/EffectOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs
index 7ed2c7ba..c6178165 100644
--- a/Ryujinx.Audio.Renderer/Parameter/EffectOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/EffectOutStatus.cs
@@ -44,7 +44,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// <summary>
/// Current effect state.
/// </summary>
- // TODO: enum?
public EffectState State;
/// <summary>
diff --git a/Ryujinx.Audio.Renderer/Parameter/MemoryPoolInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs
index 15498cca..15498cca 100644
--- a/Ryujinx.Audio.Renderer/Parameter/MemoryPoolInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/MemoryPoolInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/MemoryPoolOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs
index c125cb8b..c125cb8b 100644
--- a/Ryujinx.Audio.Renderer/Parameter/MemoryPoolOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/MemoryPoolOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs b/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs
index cdbd5871..cdbd5871 100644
--- a/Ryujinx.Audio.Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/MixInParameterDirtyOnlyUpdate.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/MixParameter.cs b/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs
index 7f45bca6..1d7f27f4 100644
--- a/Ryujinx.Audio.Renderer/Parameter/MixParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/MixParameter.cs
@@ -100,7 +100,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// </summary>
private uint _reserved3;
- [StructLayout(LayoutKind.Sequential, Size = 4 * RendererConstants.MixBufferCountMax * RendererConstants.MixBufferCountMax, Pack = 1)]
+ [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax * Constants.MixBufferCountMax, Pack = 1)]
private struct MixVolumeArray { }
/// <summary>
diff --git a/Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs
index 929f006b..929f006b 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs
index 9263362c..9263362c 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Performance/PerformanceOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Performance/PerformanceOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/RendererInfoOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs
index 315f0968..315f0968 100644
--- a/Ryujinx.Audio.Renderer/Parameter/RendererInfoOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/RendererInfoOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/Sink/CircularBufferParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs
index d688529a..ae06481f 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Sink/CircularBufferParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Sink/CircularBufferParameter.cs
@@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
diff --git a/Ryujinx.Audio.Renderer/Parameter/Sink/DeviceParameter.cs b/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs
index 591b4945..591b4945 100644
--- a/Ryujinx.Audio.Renderer/Parameter/Sink/DeviceParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/Sink/DeviceParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/SinkInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs
index 0859ef31..0859ef31 100644
--- a/Ryujinx.Audio.Renderer/Parameter/SinkInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/SinkInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/SinkOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs
index e0cb4942..e0cb4942 100644
--- a/Ryujinx.Audio.Renderer/Parameter/SinkOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/SinkOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/SplitterDestinationInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs
index a1c18d57..77d5ddc2 100644
--- a/Ryujinx.Audio.Renderer/Parameter/SplitterDestinationInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameter.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
/// </summary>
private unsafe fixed byte _reserved[3];
- [StructLayout(LayoutKind.Sequential, Size = 4 * RendererConstants.MixBufferCountMax, Pack = 1)]
+ [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
private struct MixArray { }
/// <summary>
diff --git a/Ryujinx.Audio.Renderer/Parameter/SplitterInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs
index a829abf9..a829abf9 100644
--- a/Ryujinx.Audio.Renderer/Parameter/SplitterInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/SplitterInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/SplitterInParameterHeader.cs b/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs
index 746d59b7..746d59b7 100644
--- a/Ryujinx.Audio.Renderer/Parameter/SplitterInParameterHeader.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/SplitterInParameterHeader.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/VoiceChannelResourceInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs
index 73ad2bfd..73ad2bfd 100644
--- a/Ryujinx.Audio.Renderer/Parameter/VoiceChannelResourceInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/VoiceChannelResourceInParameter.cs
diff --git a/Ryujinx.Audio.Renderer/Parameter/VoiceInParameter.cs b/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs
index 9a18a95a..bced1538 100644
--- a/Ryujinx.Audio.Renderer/Parameter/VoiceInParameter.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/VoiceInParameter.cs
@@ -15,6 +15,7 @@
// 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;
diff --git a/Ryujinx.Audio.Renderer/Parameter/VoiceOutStatus.cs b/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs
index 62d17a5a..62d17a5a 100644
--- a/Ryujinx.Audio.Renderer/Parameter/VoiceOutStatus.cs
+++ b/Ryujinx.Audio/Renderer/Parameter/VoiceOutStatus.cs
diff --git a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
index 9b87957a..787b8f9f 100644
--- a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs
+++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
@@ -15,9 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.Command;
-using Ryujinx.Audio.Renderer.Integration;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.Effect;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
@@ -134,10 +134,10 @@ namespace Ryujinx.Audio.Renderer.Server
_sampleRate = parameter.SampleRate;
_sampleCount = parameter.SampleCount;
_mixBufferCount = parameter.MixBufferCount;
- _voiceChannelCountMax = RendererConstants.VoiceChannelCountMax;
+ _voiceChannelCountMax = Constants.VoiceChannelCountMax;
_upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
_appletResourceId = appletResourceId;
- _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * RendererConstants.VoiceWaveBufferCount;
+ _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
_executionMode = parameter.ExecutionMode;
_sessionId = sessionId;
MemoryManager = memoryManager;
@@ -160,14 +160,14 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall;
}
- Memory<float> upSamplerWorkBuffer = workBufferAllocator.Allocate<float>(RendererConstants.TargetSampleCount * (_voiceChannelCountMax + _mixBufferCount) * _upsamplerCount, 0x10);
+ Memory<float> upSamplerWorkBuffer = workBufferAllocator.Allocate<float>(Constants.TargetSampleCount * (_voiceChannelCountMax + _mixBufferCount) * _upsamplerCount, 0x10);
if (upSamplerWorkBuffer.IsEmpty)
{
return ResultCode.WorkBufferTooSmall;
}
- _depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, RendererConstants.BufferAlignment), RendererConstants.BufferAlignment);
+ _depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
if (_depopBuffer.IsEmpty)
{
@@ -177,7 +177,7 @@ namespace Ryujinx.Audio.Renderer.Server
// Invalidate DSP cache on what was currently allocated with workBuffer.
AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
- Debug.Assert((workBufferAllocator.Offset % RendererConstants.BufferAlignment) == 0);
+ Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0);
Memory<VoiceState> voices = workBufferAllocator.Allocate<VoiceState>(parameter.VoiceCount, VoiceState.Alignment);
@@ -253,7 +253,7 @@ namespace Ryujinx.Audio.Renderer.Server
}
// Initialize the final mix id
- mixes.Span[0].MixId = RendererConstants.FinalMixId;
+ mixes.Span[0].MixId = Constants.FinalMixId;
Memory<int> sortedMixesState = workBufferAllocator.Allocate<int>(mixesCount, 0x10);
@@ -318,7 +318,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
- _performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, RendererConstants.BufferAlignment);
+ _performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, Constants.BufferAlignment);
if (_performanceBuffer.IsEmpty)
{
@@ -504,7 +504,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
double ticks = ARMeilleure.State.ExecutionContext.ElapsedTicks * ARMeilleure.State.ExecutionContext.TickFrequency;
- return (ulong)(ticks * RendererConstants.TargetTimerFrequency);
+ return (ulong)(ticks * Constants.TargetTimerFrequency);
}
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
@@ -544,7 +544,7 @@ namespace Ryujinx.Audio.Renderer.Server
ref VoiceState voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId));
- if (voice.Priority == RendererConstants.VoiceHighestPriority)
+ if (voice.Priority == Constants.VoiceHighestPriority)
{
break;
}
@@ -641,7 +641,7 @@ namespace Ryujinx.Audio.Renderer.Server
private int GetMaxAllocatedTimeForDsp()
{
- return (int)(RendererConstants.AudioProcessorMaxUpdateTimePerSessions * _behaviourContext.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f));
+ return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourContext.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f));
}
public void SendCommands()
@@ -714,7 +714,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
return new RendererSystemContext
{
- ChannelCount = _manager.OutputDevices[_sessionId].GetChannelCount(),
+ ChannelCount = _manager.Processor.OutputDevices[_sessionId].GetChannelCount(),
BehaviourContext = _behaviourContext,
DepopBuffer = _depopBuffer,
MixBufferCount = GetMixBufferCount(),
@@ -736,18 +736,18 @@ namespace Ryujinx.Audio.Renderer.Server
uint mixesCount = parameter.SubMixBufferCount + 1;
- uint memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * RendererConstants.VoiceWaveBufferCount;
+ uint memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
ulong size = 0;
// Mix Buffers
- size = WorkBufferAllocator.GetTargetSize<float>(size, parameter.SampleCount * (RendererConstants.VoiceChannelCountMax + parameter.MixBufferCount), 0x10);
+ size = WorkBufferAllocator.GetTargetSize<float>(size, parameter.SampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount), 0x10);
// Upsampler workbuffer
- size = WorkBufferAllocator.GetTargetSize<float>(size, RendererConstants.TargetSampleCount * (RendererConstants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
+ size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
// Depop buffer
- size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, RendererConstants.BufferAlignment), RendererConstants.BufferAlignment);
+ size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
// Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
@@ -779,10 +779,10 @@ namespace Ryujinx.Audio.Renderer.Server
{
ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
- size += BitUtils.AlignUp(performanceMetricsPerFramesSize, RendererConstants.PerformanceMetricsPerFramesSizeAlignment);
+ size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
}
- return BitUtils.AlignUp(size, RendererConstants.WorkBufferAlignment);
+ return BitUtils.AlignUp(size, Constants.WorkBufferAlignment);
}
public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)
diff --git a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
index f552935b..ec847948 100644
--- a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs
+++ b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
@@ -15,8 +15,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Dsp;
-using Ryujinx.Audio.Renderer.Integration;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
@@ -72,10 +72,9 @@ namespace Ryujinx.Audio.Renderer.Server
private bool _isRunning;
/// <summary>
- /// The output devices associated to each session.
+ /// The audio device driver to create audio outputs.
/// </summary>
- // TODO: get ride of this with the audout rewrite.
- public HardwareDevice[] OutputDevices { get; private set; }
+ private IHardwareDeviceDriver _deviceDriver;
/// <summary>
/// The <see cref="AudioProcessor"/> instance associated to this manager.
@@ -88,8 +87,8 @@ namespace Ryujinx.Audio.Renderer.Server
public AudioRendererManager()
{
Processor = new AudioProcessor();
- _sessionIds = new int[RendererConstants.AudioRendererSessionCountMax];
- _sessions = new AudioRenderSystem[RendererConstants.AudioRendererSessionCountMax];
+ _sessionIds = new int[Constants.AudioRendererSessionCountMax];
+ _sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
_activeSessionCount = 0;
for (int i = 0; i < _sessionIds.Length; i++)
@@ -102,11 +101,11 @@ namespace Ryujinx.Audio.Renderer.Server
/// Initialize the <see cref="AudioRendererManager"/>.
/// </summary>
/// <param name="sessionSystemEvents">The events associated to each session.</param>
- /// <param name="devices">The output devices associated to each session.</param>
- public void Initialize(IWritableEvent[] sessionSystemEvents, HardwareDevice[] devices)
+ /// <param name="deviceDriver">The device driver to use to create audio outputs.</param>
+ public void Initialize(IWritableEvent[] sessionSystemEvents, IHardwareDeviceDriver deviceDriver)
{
_sessionsSystemEvent = sessionSystemEvents;
- OutputDevices = devices;
+ _deviceDriver = deviceDriver;
}
/// <summary>
@@ -186,8 +185,7 @@ namespace Ryujinx.Audio.Renderer.Server
_isRunning = true;
// TODO: virtual device mapping (IAudioDevice)
- Processor.SetOutputDevices(OutputDevices);
- Processor.Start();
+ Processor.Start(_deviceDriver);
_workerThread = new Thread(SendCommands)
{
@@ -330,11 +328,6 @@ namespace Ryujinx.Audio.Renderer.Server
}
Processor.Dispose();
-
- foreach (HardwareDevice device in OutputDevices)
- {
- device.Dispose();
- }
}
}
}
diff --git a/Ryujinx.Audio.Renderer/Server/BehaviourContext.cs b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
index 28bcdac5..b31f9e9f 100644
--- a/Ryujinx.Audio.Renderer/Server/BehaviourContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
@@ -132,7 +132,7 @@ namespace Ryujinx.Audio.Renderer.Server
public BehaviourContext()
{
UserRevision = 0;
- _errorInfos = new ErrorInfo[RendererConstants.MaxErrorInfos];
+ _errorInfos = new ErrorInfo[Constants.MaxErrorInfos];
_errorIndex = 0;
}
@@ -357,7 +357,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
Debug.Assert(errorInfo.ErrorCode == ResultCode.Success);
- if (_errorIndex <= RendererConstants.MaxErrorInfos - 1)
+ if (_errorIndex <= Constants.MaxErrorInfos - 1)
{
_errorInfos[_errorIndex++] = errorInfo;
}
@@ -370,14 +370,14 @@ namespace Ryujinx.Audio.Renderer.Server
/// <param name="errorCount">The output error count containing the count of <see cref="ErrorInfo"/> copied.</param>
public void CopyErrorInfo(Span<ErrorInfo> errorInfos, out uint errorCount)
{
- if (errorInfos.Length != RendererConstants.MaxErrorInfos)
+ if (errorInfos.Length != Constants.MaxErrorInfos)
{
throw new ArgumentException("Invalid size of errorInfos span!");
}
- errorCount = Math.Min(_errorIndex, RendererConstants.MaxErrorInfos);
+ errorCount = Math.Min(_errorIndex, Constants.MaxErrorInfos);
- for (int i = 0; i < RendererConstants.MaxErrorInfos; i++)
+ for (int i = 0; i < Constants.MaxErrorInfos; i++)
{
if (i < errorCount)
{
diff --git a/Ryujinx.Audio.Renderer/Server/CommandBuffer.cs b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
index ca37e090..ca37e090 100644
--- a/Ryujinx.Audio.Renderer/Server/CommandBuffer.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs
diff --git a/Ryujinx.Audio.Renderer/Server/CommandGenerator.cs b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
index 36c438fe..d2499c1d 100644
--- a/Ryujinx.Audio.Renderer/Server/CommandGenerator.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs
@@ -15,6 +15,7 @@
// 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.Command;
using Ryujinx.Audio.Renderer.Dsp.State;
@@ -53,12 +54,12 @@ namespace Ryujinx.Audio.Renderer.Server
_splitterContext = splitterContext;
_performanceManager = performanceManager;
- _commandBuffer.GenerateClearMixBuffer(RendererConstants.InvalidNodeId);
+ _commandBuffer.GenerateClearMixBuffer(Constants.InvalidNodeId);
}
private void GenerateDataSource(ref VoiceState voiceState, Memory<VoiceUpdateState> dspState, int channelIndex)
{
- if (voiceState.MixId != RendererConstants.UnusedMixId)
+ if (voiceState.MixId != Constants.UnusedMixId)
{
ref MixState mix = ref _mixContext.GetState(voiceState.MixId);
@@ -69,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Server
voiceState.NodeId,
voiceState.WasPlaying);
}
- else if (voiceState.SplitterId != RendererConstants.UnusedSplitterId)
+ else if (voiceState.SplitterId != Constants.UnusedSplitterId)
{
int destinationId = 0;
@@ -88,7 +89,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != RendererConstants.UnusedSplitterIdInt)
+ if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{
ref MixState mix = ref _mixContext.GetState(mixId);
@@ -156,7 +157,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (filter.Enable)
{
- Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * RendererConstants.VoiceBiquadFilterCount);
+ Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state).Slice(VoiceUpdateState.BiquadStateOffset, VoiceUpdateState.BiquadStateSize * Constants.VoiceBiquadFilterCount);
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
@@ -173,7 +174,7 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateVoiceMix(Span<float> mixVolumes, Span<float> previousMixVolumes, Memory<VoiceUpdateState> state, uint bufferOffset, uint bufferCount, uint bufferIndex, int nodeId)
{
- if (bufferCount > RendererConstants.VoiceChannelCountMax)
+ if (bufferCount > Constants.VoiceChannelCountMax)
{
_commandBuffer.GenerateMixRampGrouped(bufferCount,
bufferIndex,
@@ -287,9 +288,9 @@ namespace Ryujinx.Audio.Renderer.Server
voiceState.PreviousVolume = voiceState.Volume;
- if (voiceState.MixId == RendererConstants.UnusedMixId)
+ if (voiceState.MixId == Constants.UnusedMixId)
{
- if (voiceState.SplitterId != RendererConstants.UnusedSplitterId)
+ if (voiceState.SplitterId != Constants.UnusedSplitterId)
{
int destinationId = channelIndex;
@@ -310,7 +311,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != RendererConstants.UnusedSplitterIdInt)
+ if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{
ref MixState mix = ref _mixContext.GetState(mixId);
@@ -541,7 +542,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
int nodeId = mix.NodeId;
- bool isFinalMix = mix.MixId == RendererConstants.FinalMixId;
+ bool isFinalMix = mix.MixId == Constants.FinalMixId;
PerformanceEntryAddresses performanceEntry = new PerformanceEntryAddresses();
@@ -597,7 +598,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
int effectOrder = effectProcessingOrderArray[i];
- if (effectOrder == RendererConstants.InvalidProcessingOrder)
+ if (effectOrder == Constants.InvalidProcessingOrder)
{
break;
}
@@ -619,11 +620,11 @@ namespace Ryujinx.Audio.Renderer.Server
{
if (mix.HasAnyDestination())
{
- Debug.Assert(mix.DestinationMixId != RendererConstants.UnusedMixId || mix.DestinationSplitterId != RendererConstants.UnusedSplitterId);
+ Debug.Assert(mix.DestinationMixId != Constants.UnusedMixId || mix.DestinationSplitterId != Constants.UnusedSplitterId);
- if (mix.DestinationMixId == RendererConstants.UnusedMixId)
+ if (mix.DestinationMixId == Constants.UnusedMixId)
{
- if (mix.DestinationSplitterId != RendererConstants.UnusedSplitterId)
+ if (mix.DestinationSplitterId != Constants.UnusedSplitterId)
{
int destinationId = 0;
@@ -644,7 +645,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
int mixId = destination.DestinationId;
- if (mixId < _mixContext.GetCount() && mixId != RendererConstants.UnusedSplitterIdInt)
+ if (mixId < _mixContext.GetCount() && mixId != Constants.UnusedSplitterIdInt)
{
ref MixState destinationMix = ref _mixContext.GetState(mixId);
@@ -727,7 +728,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
ref MixState sortedState = ref _mixContext.GetSortedState(id);
- if (sortedState.IsUsed && sortedState.MixId != RendererConstants.FinalMixId)
+ if (sortedState.IsUsed && sortedState.MixId != Constants.FinalMixId)
{
int nodeId = sortedState.NodeId;
@@ -833,7 +834,7 @@ namespace Ryujinx.Audio.Renderer.Server
private void GenerateCircularBuffer(CircularBufferSink sink, ref MixState finalMix)
{
- _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, RendererConstants.InvalidNodeId);
+ _commandBuffer.GenerateCircularBuffer(finalMix.BufferOffset, sink, Constants.InvalidNodeId);
}
private void GenerateDevice(DeviceSink sink, ref MixState finalMix)
@@ -851,17 +852,16 @@ namespace Ryujinx.Audio.Renderer.Server
sink.Parameter.Input.ToSpan(),
sink.Parameter.Input.ToSpan(),
sink.DownMixCoefficients,
- RendererConstants.InvalidNodeId);
+ Constants.InvalidNodeId);
}
- // NOTE: we do the downmixing at the DSP level as right now the renderer interface doesn't use audout.
- // TODO: Remove this when audout is rewritten.
+ // NOTE: We do the downmixing at the DSP level as it's easier that way.
else if (_rendererContext.ChannelCount == 2 && sink.Parameter.InputCount == 6)
{
_commandBuffer.GenerateDownMixSurroundToStereo(finalMix.BufferOffset,
sink.Parameter.Input.ToSpan(),
sink.Parameter.Input.ToSpan(),
- RendererConstants.DefaultSurroundToStereoCoefficients,
- RendererConstants.InvalidNodeId);
+ Constants.DefaultSurroundToStereoCoefficients,
+ Constants.InvalidNodeId);
}
CommandList commandList = _commandBuffer.CommandList;
@@ -875,14 +875,14 @@ namespace Ryujinx.Audio.Renderer.Server
commandList.BufferCount,
commandList.SampleCount,
commandList.SampleRate,
- RendererConstants.InvalidNodeId);
+ Constants.InvalidNodeId);
}
_commandBuffer.GenerateDeviceSink(finalMix.BufferOffset,
sink,
_rendererContext.SessionId,
commandList.Buffers,
- RendererConstants.InvalidNodeId);
+ Constants.InvalidNodeId);
}
private void GenerateSink(BaseSink sink, ref MixState finalMix)
diff --git a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
index 81d3b57b..81d3b57b 100644
--- a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs
diff --git a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
index 0f4fe3c2..0f4fe3c2 100644
--- a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs
diff --git a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
index 8e15d642..b48ff8b5 100644
--- a/Ryujinx.Audio.Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
+++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs
@@ -15,6 +15,7 @@
// 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.Command;
using System;
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
index df82945b..df82945b 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/AuxiliaryBufferEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/BaseEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
index 7c491fd1..7529f894 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/BaseEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
@@ -77,7 +77,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
IsEnabled = false;
BufferUnmapped = false;
- MixId = RendererConstants.UnusedMixId;
+ MixId = Constants.UnusedMixId;
ProcessingOrder = uint.MaxValue;
WorkBuffers = new AddressInfo[2];
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/BiquadFilterEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
index aadf2844..35ba8a0d 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/BiquadFilterEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
@@ -47,7 +47,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
public BiquadFilterEffect()
{
Parameter = new BiquadFilterEffectParameter();
- State = new BiquadFilterState[RendererConstants.ChannelCountMax];
+ State = new BiquadFilterState[Constants.ChannelCountMax];
}
public override EffectType TargetEffectType => EffectType.BiquadFilter;
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/BufferMixEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
index 14ed3950..14ed3950 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/BufferMixEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/DelayEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
index df3e5ee7..df3e5ee7 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/DelayEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/EffectContext.cs b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs
index ff6051ae..ff6051ae 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/EffectContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/Reverb3dEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
index d9f23799..d9f23799 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/Reverb3dEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/ReverbEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
index 4c81f729..4c81f729 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/ReverbEffect.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Effect/UsageState.cs b/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs
index a519acd4..a519acd4 100644
--- a/Ryujinx.Audio.Renderer/Server/Effect/UsageState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Effect/UsageState.cs
diff --git a/Ryujinx.Audio.Renderer/Server/ICommandProcessingTimeEstimator.cs b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
index 119ec907..119ec907 100644
--- a/Ryujinx.Audio.Renderer/Server/ICommandProcessingTimeEstimator.cs
+++ b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs
diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs b/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs
index 2b625d06..2b625d06 100644
--- a/Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs
+++ b/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs
diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs b/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs
index ebde1403..ebde1403 100644
--- a/Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs
+++ b/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs
diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs b/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
index b255960c..b255960c 100644
--- a/Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs
+++ b/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs b/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs
index 5a3c3a63..0e094665 100644
--- a/Ryujinx.Audio.Renderer/Server/Mix/MixContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Mix/MixContext.cs
@@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// <returns>A reference to the final <see cref="MixState"/>.</returns>
public ref MixState GetFinalState()
{
- return ref GetState(RendererConstants.FinalMixId);
+ return ref GetState(Constants.FinalMixId);
}
/// <summary>
@@ -158,13 +158,13 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
{
uint distance;
- if (mix.MixId != RendererConstants.FinalMixId)
+ if (mix.MixId != Constants.FinalMixId)
{
int mixId = mix.MixId;
for (distance = 0; distance < GetCount(); distance++)
{
- if (mixId == RendererConstants.UnusedMixId)
+ if (mixId == Constants.UnusedMixId)
{
distance = MixState.InvalidDistanceFromFinalMix;
break;
@@ -180,7 +180,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
mixId = distanceMix.DestinationMixId;
- if (mixId == RendererConstants.FinalMixId)
+ if (mixId == Constants.FinalMixId)
{
break;
}
diff --git a/Ryujinx.Audio.Renderer/Server/Mix/MixState.cs b/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs
index d697488a..028d0885 100644
--- a/Ryujinx.Audio.Renderer/Server/Mix/MixState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs
@@ -25,7 +25,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using static Ryujinx.Audio.Renderer.RendererConstants;
+using static Ryujinx.Audio.Constants;
namespace Ryujinx.Audio.Renderer.Server.Mix
{
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceDetailEntry.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs
index 893cc008..893cc008 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceDetailEntry.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceEntry.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs
index 2a2fa9cc..2a2fa9cc 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceEntry.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceHeader.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs
index 5a26754d..5a26754d 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/IPerformanceHeader.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs
index b7df7f28..b7df7f28 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs
index da898b29..da898b29 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceDetailVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryAddresses.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs
index ad99867f..ad99867f 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryAddresses.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs
index ac91ca19..ac91ca19 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs
index 84ee5bc9..84ee5bc9 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceEntryVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs
index b0757b37..b0757b37 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs
index ba873239..ba873239 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceManager.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs
index 3a336af3..3a336af3 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceManager.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceManagerGeneric.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs
index b17e6f3f..b17e6f3f 100644
--- a/Ryujinx.Audio.Renderer/Server/Performance/PerformanceManagerGeneric.cs
+++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs
diff --git a/Ryujinx.Audio.Renderer/Server/RendererSystemContext.cs b/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs
index a89a8a60..a89a8a60 100644
--- a/Ryujinx.Audio.Renderer/Server/RendererSystemContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/RendererSystemContext.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Sink/BaseSink.cs b/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs
index 949e3971..949e3971 100644
--- a/Ryujinx.Audio.Renderer/Server/Sink/BaseSink.cs
+++ b/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Sink/CircularBufferSink.cs b/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs
index 16d25a65..ceb955e5 100644
--- a/Ryujinx.Audio.Renderer/Server/Sink/CircularBufferSink.cs
+++ b/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs
@@ -99,7 +99,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
if (IsUsed)
{
- uint frameSize = RendererConstants.TargetSampleSize * Parameter.SampleCount * Parameter.InputCount;
+ uint frameSize = Constants.TargetSampleSize * Parameter.SampleCount * Parameter.InputCount;
_lastWrittenOffset = _oldWrittenOffset;
diff --git a/Ryujinx.Audio.Renderer/Server/Sink/DeviceSink.cs b/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs
index 79af05f7..79af05f7 100644
--- a/Ryujinx.Audio.Renderer/Server/Sink/DeviceSink.cs
+++ b/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Sink/SinkContext.cs b/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs
index b57dbde9..b57dbde9 100644
--- a/Ryujinx.Audio.Renderer/Server/Sink/SinkContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Sink/SinkContext.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterContext.cs b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs
index 0e107a53..0e107a53 100644
--- a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterDestination.cs b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs
index 6d273e45..077bf8ae 100644
--- a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterDestination.cs
+++ b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs
@@ -64,7 +64,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
[MarshalAs(UnmanagedType.I1)]
public bool NeedToUpdateInternalState;
- [StructLayout(LayoutKind.Sequential, Size = 4 * RendererConstants.MixBufferCountMax, Pack = 1)]
+ [StructLayout(LayoutKind.Sequential, Size = 4 * Constants.MixBufferCountMax, Pack = 1)]
private struct MixArray { }
/// <summary>
@@ -100,7 +100,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
public SplitterDestination(int id) : this()
{
Id = id;
- DestinationId = RendererConstants.UnusedMixId;
+ DestinationId = Constants.UnusedMixId;
ClearVolumes();
}
@@ -157,7 +157,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>True if the <see cref="SplitterDestination"/> is used and has a destination.</returns>
public bool IsConfigured()
{
- return IsUsed && DestinationId != RendererConstants.UnusedMixId;
+ return IsUsed && DestinationId != Constants.UnusedMixId;
}
/// <summary>
@@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// <returns>The volume for the given destination.</returns>
public float GetMixVolume(int destinationIndex)
{
- Debug.Assert(destinationIndex >= 0 && destinationIndex < RendererConstants.MixBufferCountMax);
+ Debug.Assert(destinationIndex >= 0 && destinationIndex < Constants.MixBufferCountMax);
return MixBufferVolume[destinationIndex];
}
diff --git a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
index c928ec53..c928ec53 100644
--- a/Ryujinx.Audio.Renderer/Server/Splitter/SplitterState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
diff --git a/Ryujinx.Audio.Renderer/Server/StateUpdater.cs b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
index b26e2721..77935b75 100644
--- a/Ryujinx.Audio.Renderer/Server/StateUpdater.cs
+++ b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
@@ -171,7 +171,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
VoiceInParameter parameter = parameters[i];
- Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[RendererConstants.VoiceChannelCountMax];
+ Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
@@ -327,9 +327,9 @@ namespace Ryujinx.Audio.Renderer.Server
{
if (parameters[i].IsUsed)
{
- if (parameters[i].DestinationMixId != RendererConstants.UnusedMixId &&
+ if (parameters[i].DestinationMixId != Constants.UnusedMixId &&
parameters[i].DestinationMixId > maxMixStateCount &&
- parameters[i].MixId != RendererConstants.FinalMixId)
+ parameters[i].MixId != Constants.FinalMixId)
{
return true;
}
diff --git a/Ryujinx.Audio.Renderer/Server/Types/AudioRendererExecutionMode.cs b/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs
index 7bb0507c..7bb0507c 100644
--- a/Ryujinx.Audio.Renderer/Server/Types/AudioRendererExecutionMode.cs
+++ b/Ryujinx.Audio/Renderer/Server/Types/AudioRendererExecutionMode.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Types/AudioRendererRenderingDevice.cs b/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs
index f89086f6..f89086f6 100644
--- a/Ryujinx.Audio.Renderer/Server/Types/AudioRendererRenderingDevice.cs
+++ b/Ryujinx.Audio/Renderer/Server/Types/AudioRendererRenderingDevice.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Types/PlayState.cs b/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs
index 7f8895c9..7f8895c9 100644
--- a/Ryujinx.Audio.Renderer/Server/Types/PlayState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Types/PlayState.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerManager.cs b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs
index fd396d9f..6878123b 100644
--- a/Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerManager.cs
+++ b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs
@@ -72,12 +72,12 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
{
if (_upsamplers[i] == null)
{
- _upsamplers[i] = new UpsamplerState(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, RendererConstants.UpSampleEntrySize), RendererConstants.TargetSampleCount);
+ _upsamplers[i] = new UpsamplerState(this, i, _upSamplerWorkBuffer.Slice(workBufferOffset, Constants.UpSampleEntrySize), Constants.TargetSampleCount);
return _upsamplers[i];
}
- workBufferOffset += RendererConstants.UpSampleEntrySize;
+ workBufferOffset += Constants.UpSampleEntrySize;
}
}
diff --git a/Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerState.cs b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs
index 8ea55384..8ea55384 100644
--- a/Ryujinx.Audio.Renderer/Server/Upsampler/UpsamplerState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerState.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs b/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs
index 00520cfc..00520cfc 100644
--- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceChannelResource.cs
+++ b/Ryujinx.Audio/Renderer/Server/Voice/VoiceChannelResource.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs b/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs
index 0dfaa4d5..0dfaa4d5 100644
--- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceContext.cs
+++ b/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs
diff --git a/Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs b/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs
index f30ee2f7..89abe9aa 100644
--- a/Ryujinx.Audio.Renderer/Server/Voice/VoiceState.cs
+++ b/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs
@@ -15,6 +15,7 @@
// 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.Parameter;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
@@ -190,7 +191,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <remarks>This was added on REV5.</remarks>
public byte FlushWaveBufferCount;
- [StructLayout(LayoutKind.Sequential, Size = RendererConstants.VoiceBiquadFilterCount)]
+ [StructLayout(LayoutKind.Sequential, Size = Constants.VoiceBiquadFilterCount)]
private struct BiquadFilterNeedInitializationArrayStruct { }
/// <summary>
@@ -209,7 +210,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
BufferInfoUnmapped = false;
FlushWaveBufferCount = 0;
PlayState = Types.PlayState.Stopped;
- Priority = RendererConstants.VoiceLowestPriority;
+ Priority = Constants.VoiceLowestPriority;
Id = 0;
NodeId = 0;
SampleRate = 0;
@@ -221,8 +222,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
BiquadFilters.ToSpan().Fill(new BiquadFilterParameter());
WaveBuffersCount = 0;
WaveBuffersIndex = 0;
- MixId = RendererConstants.UnusedMixId;
- SplitterId = RendererConstants.UnusedSplitterId;
+ MixId = Constants.UnusedMixId;
+ SplitterId = Constants.UnusedSplitterId;
DataSourceStateAddressInfo.Setup(0, 0);
InitializeWaveBuffers();
@@ -260,7 +261,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <returns>True if the mix has any destinations.</returns>
public bool HasAnyDestination()
{
- return MixId != RendererConstants.UnusedMixId || SplitterId != RendererConstants.UnusedSplitterId;
+ return MixId != Constants.UnusedMixId || SplitterId != Constants.UnusedSplitterId;
}
/// <summary>
@@ -321,7 +322,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
}
else
{
- SplitterId = RendererConstants.UnusedSplitterId;
+ SplitterId = Constants.UnusedSplitterId;
}
parameter.ChannelResourceIds.ToSpan().CopyTo(ChannelResourceIds.ToSpan());
@@ -442,7 +443,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <param name="behaviourContext">The behaviour context.</param>
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
{
- errorInfos = new ErrorInfo[RendererConstants.VoiceWaveBufferCount * 2];
+ errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
if (parameter.IsNew)
{
@@ -456,7 +457,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0];
- for (int i = 0; i < RendererConstants.VoiceWaveBufferCount; i++)
+ for (int i = 0; i < Constants.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);
}
@@ -570,12 +571,12 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
{
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[j].Span[0];
- voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount;
+ voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
voiceUpdateState.WaveBufferConsumed++;
voiceUpdateState.IsWaveBufferValid[(int)waveBufferIndex] = false;
}
- waveBufferIndex = (waveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount;
+ waveBufferIndex = (waveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
}
}
@@ -640,7 +641,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
if (voiceUpdateState.IsWaveBufferValid[i])
{
- voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % RendererConstants.VoiceWaveBufferCount;
+ voiceUpdateState.WaveBufferIndex = (voiceUpdateState.WaveBufferIndex + 1) % Constants.VoiceWaveBufferCount;
voiceUpdateState.WaveBufferConsumed++;
}
@@ -702,7 +703,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
IsNew = false;
}
- Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[RendererConstants.VoiceChannelCountMax];
+ Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
for (int i = 0; i < ChannelsCount; i++)
{
diff --git a/Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs b/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs
index a03aa757..a03aa757 100644
--- a/Ryujinx.Audio.Renderer/Server/Voice/WaveBuffer.cs
+++ b/Ryujinx.Audio/Renderer/Server/Voice/WaveBuffer.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/AudioProcessorMemoryManager.cs b/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs
index 99cbfcdc..99cbfcdc 100644
--- a/Ryujinx.Audio.Renderer/Utils/AudioProcessorMemoryManager.cs
+++ b/Ryujinx.Audio/Renderer/Utils/AudioProcessorMemoryManager.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/BitArray.cs b/Ryujinx.Audio/Renderer/Utils/BitArray.cs
index 6109dd01..6109dd01 100644
--- a/Ryujinx.Audio.Renderer/Utils/BitArray.cs
+++ b/Ryujinx.Audio/Renderer/Utils/BitArray.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/FileHardwareDevice.cs b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs
index d5cbb8df..2008bafc 100644
--- a/Ryujinx.Audio.Renderer/Utils/FileHardwareDevice.cs
+++ b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using System;
using System.IO;
using System.Runtime.InteropServices;
@@ -24,9 +24,9 @@ using System.Text;
namespace Ryujinx.Audio.Renderer.Utils
{
/// <summary>
- /// A <see cref="HardwareDevice"/> that outputs to a wav file.
+ /// A <see cref="IHardwareDevice"/> that outputs to a wav file.
/// </summary>
- public class FileHardwareDevice : HardwareDevice
+ public class FileHardwareDevice : IHardwareDevice
{
private FileStream _stream;
private uint _channelCount;
diff --git a/Ryujinx.Audio.Renderer/Utils/Mailbox.cs b/Ryujinx.Audio/Renderer/Utils/Mailbox.cs
index a32de870..a32de870 100644
--- a/Ryujinx.Audio.Renderer/Utils/Mailbox.cs
+++ b/Ryujinx.Audio/Renderer/Utils/Mailbox.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/SpanIOHelper.cs b/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs
index 70a2fce6..70a2fce6 100644
--- a/Ryujinx.Audio.Renderer/Utils/SpanIOHelper.cs
+++ b/Ryujinx.Audio/Renderer/Utils/SpanIOHelper.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/SpanMemoryManager.cs b/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs
index 102a9f95..102a9f95 100644
--- a/Ryujinx.Audio.Renderer/Utils/SpanMemoryManager.cs
+++ b/Ryujinx.Audio/Renderer/Utils/SpanMemoryManager.cs
diff --git a/Ryujinx.Audio.Renderer/Utils/SplitterHardwareDevice.cs b/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs
index f23bde8a..c5411ac0 100644
--- a/Ryujinx.Audio.Renderer/Utils/SplitterHardwareDevice.cs
+++ b/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs
@@ -15,17 +15,17 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using System;
namespace Ryujinx.Audio.Renderer.Utils
{
- public class SplitterHardwareDevice : HardwareDevice
+ public class SplitterHardwareDevice : IHardwareDevice
{
- private HardwareDevice _baseDevice;
- private HardwareDevice _secondaryDevice;
+ private IHardwareDevice _baseDevice;
+ private IHardwareDevice _secondaryDevice;
- public SplitterHardwareDevice(HardwareDevice baseDevice, HardwareDevice secondaryDevice)
+ public SplitterHardwareDevice(IHardwareDevice baseDevice, IHardwareDevice secondaryDevice)
{
_baseDevice = baseDevice;
_secondaryDevice = secondaryDevice;
diff --git a/Ryujinx.Audio/Renderers/DummyAudioOut.cs b/Ryujinx.Audio/Renderers/DummyAudioOut.cs
deleted file mode 100644
index cd197592..00000000
--- a/Ryujinx.Audio/Renderers/DummyAudioOut.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// A Dummy audio renderer that does not output any audio
- /// </summary>
- public class DummyAudioOut : IAalOutput
- {
- private int _lastTrackId = 1;
- private float _volume = 1.0f;
-
- private ConcurrentQueue<int> _trackIds;
- private ConcurrentQueue<long> _buffers;
- private ConcurrentDictionary<int, ReleaseCallback> _releaseCallbacks;
- private ulong _playedSampleCount;
-
- public DummyAudioOut()
- {
- _buffers = new ConcurrentQueue<long>();
- _trackIds = new ConcurrentQueue<int>();
- _releaseCallbacks = new ConcurrentDictionary<int, ReleaseCallback>();
- }
-
- /// <summary>
- /// Dummy audio output is always available, Baka!
- /// </summary>
- public static bool IsSupported => true;
-
- public PlaybackState GetState(int trackId) => PlaybackState.Stopped;
-
- public bool SupportsChannelCount(int channels)
- {
- return true;
- }
-
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- if (!_trackIds.TryDequeue(out int trackId))
- {
- trackId = ++_lastTrackId;
- }
-
- _releaseCallbacks[trackId] = callback;
-
- return trackId;
- }
-
- public void CloseTrack(int trackId)
- {
- _trackIds.Enqueue(trackId);
- _releaseCallbacks.Remove(trackId, out _);
- }
-
- public bool ContainsBuffer(int trackID, long bufferTag) => false;
-
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- List<long> bufferTags = new List<long>();
-
- for (int i = 0; i < maxCount; i++)
- {
- if (!_buffers.TryDequeue(out long tag))
- {
- break;
- }
-
- bufferTags.Add(tag);
- }
-
- return bufferTags.ToArray();
- }
-
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- _buffers.Enqueue(bufferTag);
-
- _playedSampleCount += (ulong)buffer.Length;
-
- if (_releaseCallbacks.TryGetValue(trackId, out var callback))
- {
- callback?.Invoke();
- }
- }
-
- public void Start(int trackId) { }
-
- public void Stop(int trackId) { }
-
- public uint GetBufferCount(int trackId) => (uint)_buffers.Count;
-
- public ulong GetPlayedSampleCount(int trackId) => _playedSampleCount;
-
- public bool FlushBuffers(int trackId) => false;
-
- public float GetVolume(int trackId) => _volume;
-
- public void SetVolume(int trackId, float volume)
- {
- _volume = volume;
- }
-
- public void Dispose()
- {
- _buffers.Clear();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs
deleted file mode 100644
index 1ad82319..00000000
--- a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs
+++ /dev/null
@@ -1,404 +0,0 @@
-using OpenTK.Audio;
-using OpenTK.Audio.OpenAL;
-using System;
-using System.Collections.Concurrent;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// An audio renderer that uses OpenAL as the audio backend
- /// </summary>
- public class OpenALAudioOut : IAalOutput, IDisposable
- {
- /// <summary>
- /// The maximum amount of tracks we can issue simultaneously
- /// </summary>
- private const int MaxTracks = 256;
-
- /// <summary>
- /// The <see cref="OpenTK.Audio"/> audio context
- /// </summary>
- private AudioContext _context;
-
- /// <summary>
- /// An object pool containing <see cref="OpenALAudioTrack"/> objects
- /// </summary>
- private ConcurrentDictionary<int, OpenALAudioTrack> _tracks;
-
- /// <summary>
- /// True if the thread need to keep polling
- /// </summary>
- private bool _keepPolling;
-
- /// <summary>
- /// The poller thread audio context
- /// </summary>
- private Thread _audioPollerThread;
-
- /// <summary>
- /// True if OpenAL is supported on the device
- /// </summary>
- public static bool IsSupported
- {
- get
- {
- try
- {
- return AudioContext.AvailableDevices.Count > 0;
- }
- catch
- {
- return false;
- }
- }
- }
-
- public OpenALAudioOut()
- {
- _context = new AudioContext();
- _tracks = new ConcurrentDictionary<int, OpenALAudioTrack>();
- _keepPolling = true;
- _audioPollerThread = new Thread(AudioPollerWork)
- {
- Name = "Audio.PollerThread"
- };
-
- _audioPollerThread.Start();
- }
-
- private void AudioPollerWork()
- {
- do
- {
- foreach (OpenALAudioTrack track in _tracks.Values)
- {
- lock (track)
- {
- track.CallReleaseCallbackIfNeeded();
- }
- }
-
- // If it's not slept it will waste cycles.
- Thread.Sleep(10);
- }
- while (_keepPolling);
-
- foreach (OpenALAudioTrack track in _tracks.Values)
- {
- track.Dispose();
- }
-
- _tracks.Clear();
- _context.Dispose();
- }
-
- public bool SupportsChannelCount(int channels)
- {
- // NOTE: OpenAL doesn't give us a way to know if the 5.1 setup is supported by hardware or actually emulated.
- // TODO: find a way to determine hardware support.
- return channels == 1 || channels == 2;
- }
-
- /// <summary>
- /// Creates a new audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <returns>The created track's Track ID</returns>
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- OpenALAudioTrack track = new OpenALAudioTrack(sampleRate, GetALFormat(hardwareChannels), hardwareChannels, virtualChannels, callback);
-
- for (int id = 0; id < MaxTracks; id++)
- {
- if (_tracks.TryAdd(id, track))
- {
- return id;
- }
- }
-
- return -1;
- }
-
- private ALFormat GetALFormat(int channels)
- {
- switch (channels)
- {
- case 1: return ALFormat.Mono16;
- case 2: return ALFormat.Stereo16;
- case 6: return ALFormat.Multi51Chn16Ext;
- }
-
- throw new ArgumentOutOfRangeException(nameof(channels));
- }
-
- /// <summary>
- /// Stops playback and closes the track specified by <paramref name="trackId"/>
- /// </summary>
- /// <param name="trackId">The ID of the track to close</param>
- public void CloseTrack(int trackId)
- {
- if (_tracks.TryRemove(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.Dispose();
- }
- }
- }
-
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the specified track
- /// </summary>
- /// <param name="trackId">The track to check</param>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(int trackId, long bufferTag)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.ContainsBuffer(bufferTag);
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets a list of buffer tags the specified track is no longer reserving
- /// </summary>
- /// <param name="trackId">The track to retrieve buffer tags from</param>
- /// <param name="maxCount">The maximum amount of buffer tags to retrieve</param>
- /// <returns>Buffers released by the specified track</returns>
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.GetReleasedBuffers(maxCount);
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Appends an audio buffer to the specified track
- /// </summary>
- /// <typeparam name="T">The sample type of the buffer</typeparam>
- /// <param name="trackId">The track to append the buffer to</param>
- /// <param name="bufferTag">The internal tag of the buffer</param>
- /// <param name="buffer">The buffer to append to the track</param>
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- int bufferId = track.AppendBuffer(bufferTag);
-
- // Do we need to downmix?
- if (track.HardwareChannels != track.VirtualChannels)
- {
- short[] downmixedBuffer;
-
- ReadOnlySpan<short> bufferPCM16 = MemoryMarshal.Cast<T, short>(buffer);
-
- if (track.VirtualChannels == 6)
- {
- downmixedBuffer = Downmixing.DownMixSurroundToStereo(bufferPCM16);
-
- if (track.HardwareChannels == 1)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(downmixedBuffer);
- }
- }
- else if (track.VirtualChannels == 2)
- {
- downmixedBuffer = Downmixing.DownMixStereoToMono(bufferPCM16);
- }
- else
- {
- throw new NotImplementedException($"Downmixing from {track.VirtualChannels} to {track.HardwareChannels} not implemented!");
- }
-
- AL.BufferData(bufferId, track.Format, downmixedBuffer, downmixedBuffer.Length * sizeof(ushort), track.SampleRate);
- }
- else
- {
- AL.BufferData(bufferId, track.Format, buffer, buffer.Length * sizeof(ushort), track.SampleRate);
- }
-
- AL.SourceQueueBuffer(track.SourceId, bufferId);
-
- StartPlaybackIfNeeded(track);
-
- track.PlayedSampleCount += (ulong)buffer.Length;
- }
- }
- }
-
- /// <summary>
- /// Starts playback
- /// </summary>
- /// <param name="trackId">The ID of the track to start playback on</param>
- public void Start(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.State = PlaybackState.Playing;
-
- StartPlaybackIfNeeded(track);
- }
- }
- }
-
- private void StartPlaybackIfNeeded(OpenALAudioTrack track)
- {
- AL.GetSource(track.SourceId, ALGetSourcei.SourceState, out int stateInt);
-
- ALSourceState State = (ALSourceState)stateInt;
-
- if (State != ALSourceState.Playing && track.State == PlaybackState.Playing)
- {
- AL.SourcePlay(track.SourceId);
- }
- }
-
- /// <summary>
- /// Stops playback
- /// </summary>
- /// <param name="trackId">The ID of the track to stop playback on</param>
- public void Stop(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.State = PlaybackState.Stopped;
-
- AL.SourceStop(track.SourceId);
- }
- }
- }
-
- /// <summary>
- /// Get track buffer count
- /// </summary>
- /// <param name="trackId">The ID of the track to get buffer count</param>
- public uint GetBufferCount(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.BufferCount;
- }
- }
-
- return 0;
- }
-
- /// <summary>
- /// Get track played sample count
- /// </summary>
- /// <param name="trackId">The ID of the track to get played sample count</param>
- public ulong GetPlayedSampleCount(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.PlayedSampleCount;
- }
- }
-
- return 0;
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- /// <param name="trackId">The ID of the track to flush</param>
- public bool FlushBuffers(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.FlushBuffers();
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Set track volume
- /// </summary>
- /// <param name="trackId">The ID of the track to set volume</param>
- /// <param name="volume">The volume of the track</param>
- public void SetVolume(int trackId, float volume)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- track.SetVolume(volume);
- }
- }
- }
-
- /// <summary>
- /// Get track volume
- /// </summary>
- /// <param name="trackId">The ID of the track to get volume</param>
- public float GetVolume(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- lock (track)
- {
- return track.GetVolume();
- }
- }
-
- return 1.0f;
- }
-
- /// <summary>
- /// Gets the current playback state of the specified track
- /// </summary>
- /// <param name="trackId">The track to retrieve the playback state for</param>
- public PlaybackState GetState(int trackId)
- {
- if (_tracks.TryGetValue(trackId, out OpenALAudioTrack track))
- {
- return track.State;
- }
-
- return PlaybackState.Stopped;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _keepPolling = false;
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs
deleted file mode 100644
index 690129eb..00000000
--- a/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioTrack.cs
+++ /dev/null
@@ -1,183 +0,0 @@
-using OpenTK.Audio.OpenAL;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- internal class OpenALAudioTrack : IDisposable
- {
- public int SourceId { get; private set; }
- public int SampleRate { get; private set; }
- public ALFormat Format { get; private set; }
- public PlaybackState State { get; set; }
-
- public int HardwareChannels { get; }
- public int VirtualChannels { get; }
- public uint BufferCount => (uint)_buffers.Count;
- public ulong PlayedSampleCount { get; set; }
-
- private ReleaseCallback _callback;
-
- private ConcurrentDictionary<long, int> _buffers;
-
- private Queue<long> _queuedTagsQueue;
- private Queue<long> _releasedTagsQueue;
-
- private bool _disposed;
-
- public OpenALAudioTrack(int sampleRate, ALFormat format, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- SampleRate = sampleRate;
- Format = format;
- State = PlaybackState.Stopped;
- SourceId = AL.GenSource();
-
- HardwareChannels = hardwareChannels;
- VirtualChannels = virtualChannels;
-
- _callback = callback;
-
- _buffers = new ConcurrentDictionary<long, int>();
-
- _queuedTagsQueue = new Queue<long>();
- _releasedTagsQueue = new Queue<long>();
- }
-
- public bool ContainsBuffer(long tag)
- {
- foreach (long queuedTag in _queuedTagsQueue)
- {
- if (queuedTag == tag)
- {
- return true;
- }
- }
-
- return false;
- }
-
- public long[] GetReleasedBuffers(int count)
- {
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
-
- releasedCount += _releasedTagsQueue.Count;
-
- if (count > releasedCount)
- {
- count = releasedCount;
- }
-
- List<long> tags = new List<long>();
-
- while (count-- > 0 && _releasedTagsQueue.TryDequeue(out long tag))
- {
- tags.Add(tag);
- }
-
- while (count-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
- {
- AL.SourceUnqueueBuffers(SourceId, 1);
-
- tags.Add(tag);
- }
-
- return tags.ToArray();
- }
-
- public int AppendBuffer(long tag)
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- int id = AL.GenBuffer();
-
- _buffers.AddOrUpdate(tag, id, (key, oldId) =>
- {
- AL.DeleteBuffer(oldId);
-
- return id;
- });
-
- _queuedTagsQueue.Enqueue(tag);
-
- return id;
- }
-
- public void CallReleaseCallbackIfNeeded()
- {
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
-
- if (releasedCount > 0)
- {
- // If we signal, then we also need to have released buffers available
- // to return when GetReleasedBuffers is called.
- // If playback needs to be re-started due to all buffers being processed,
- // then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue.
- while (releasedCount-- > 0 && _queuedTagsQueue.TryDequeue(out long tag))
- {
- AL.SourceUnqueueBuffers(SourceId, 1);
-
- _releasedTagsQueue.Enqueue(tag);
- }
-
- _callback();
- }
- }
-
- public bool FlushBuffers()
- {
- while (_queuedTagsQueue.TryDequeue(out long tag))
- {
- _releasedTagsQueue.Enqueue(tag);
- }
-
- _callback();
-
- foreach (var buffer in _buffers)
- {
- AL.DeleteBuffer(buffer.Value);
- }
-
- bool heldBuffers = _buffers.Count > 0;
-
- _buffers.Clear();
-
- return heldBuffers;
- }
-
- public void SetVolume(float volume)
- {
- AL.Source(SourceId, ALSourcef.Gain, volume);
- }
-
- public float GetVolume()
- {
- AL.GetSource(SourceId, ALSourcef.Gain, out float volume);
-
- return volume;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing && !_disposed)
- {
- _disposed = true;
-
- AL.DeleteSource(SourceId);
-
- foreach (int id in _buffers.Values)
- {
- AL.DeleteBuffer(id);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs
deleted file mode 100644
index 92bf42c4..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs
+++ /dev/null
@@ -1,361 +0,0 @@
-using Ryujinx.Audio.SoundIo;
-using SoundIOSharp;
-using System.Collections.Generic;
-
-namespace Ryujinx.Audio
-{
- /// <summary>
- /// An audio renderer that uses libsoundio as the audio backend
- /// </summary>
- public class SoundIoAudioOut : IAalOutput
- {
- /// <summary>
- /// The maximum amount of tracks we can issue simultaneously
- /// </summary>
- private const int MaximumTracks = 256;
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context
- /// </summary>
- private SoundIO _audioContext;
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> audio device
- /// </summary>
- private SoundIODevice _audioDevice;
-
- /// <summary>
- /// An object pool containing <see cref="SoundIoAudioTrack"/> objects
- /// </summary>
- private SoundIoAudioTrackPool _trackPool;
-
- /// <summary>
- /// True if SoundIO is supported on the device
- /// </summary>
- public static bool IsSupported
- {
- get
- {
- return IsSupportedInternal();
- }
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioOut"/>
- /// </summary>
- public SoundIoAudioOut()
- {
- _audioContext = new SoundIO();
-
- _audioContext.Connect();
- _audioContext.FlushEvents();
-
- _audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
- _trackPool = new SoundIoAudioTrackPool(_audioContext, _audioDevice, MaximumTracks);
- }
-
- public bool SupportsChannelCount(int channels)
- {
- return _audioDevice.SupportsChannelCount(channels);
- }
-
- /// <summary>
- /// Creates a new audio track with the specified parameters
- /// </summary>
- /// <param name="sampleRate">The requested sample rate</param>
- /// <param name="hardwareChannels">The requested hardware channels</param>
- /// <param name="virtualChannels">The requested virtual channels</param>
- /// <param name="callback">A <see cref="ReleaseCallback" /> that represents the delegate to invoke when a buffer has been released by the audio track</param>
- /// <returns>The created track's Track ID</returns>
- public int OpenHardwareTrack(int sampleRate, int hardwareChannels, int virtualChannels, ReleaseCallback callback)
- {
- if (!_trackPool.TryGet(out SoundIoAudioTrack track))
- {
- return -1;
- }
-
- // Open the output. We currently only support 16-bit signed LE
- track.Open(sampleRate, hardwareChannels, virtualChannels, callback, SoundIOFormat.S16LE);
-
- return track.TrackID;
- }
-
- /// <summary>
- /// Stops playback and closes the track specified by <paramref name="trackId"/>
- /// </summary>
- /// <param name="trackId">The ID of the track to close</param>
- public void CloseTrack(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- // Close and dispose of the track
- track.Close();
-
- // Recycle the track back into the pool
- _trackPool.Put(track);
- }
- }
-
- /// <summary>
- /// Returns a value indicating whether the specified buffer is currently reserved by the specified track
- /// </summary>
- /// <param name="trackId">The track to check</param>
- /// <param name="bufferTag">The buffer tag to check</param>
- public bool ContainsBuffer(int trackId, long bufferTag)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.ContainsBuffer(bufferTag);
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets a list of buffer tags the specified track is no longer reserving
- /// </summary>
- /// <param name="trackId">The track to retrieve buffer tags from</param>
- /// <param name="maxCount">The maximum amount of buffer tags to retrieve</param>
- /// <returns>Buffers released by the specified track</returns>
- public long[] GetReleasedBuffers(int trackId, int maxCount)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- List<long> bufferTags = new List<long>();
-
- while(maxCount-- > 0 && track.ReleasedBuffers.TryDequeue(out long tag))
- {
- bufferTags.Add(tag);
- }
-
- return bufferTags.ToArray();
- }
-
- return new long[0];
- }
-
- /// <summary>
- /// Appends an audio buffer to the specified track
- /// </summary>
- /// <typeparam name="T">The sample type of the buffer</typeparam>
- /// <param name="trackId">The track to append the buffer to</param>
- /// <param name="bufferTag">The internal tag of the buffer</param>
- /// <param name="buffer">The buffer to append to the track</param>
- public void AppendBuffer<T>(int trackId, long bufferTag, T[] buffer) where T : struct
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.AppendBuffer(bufferTag, buffer);
- }
- }
-
- /// <summary>
- /// Starts playback
- /// </summary>
- /// <param name="trackId">The ID of the track to start playback on</param>
- public void Start(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.Start();
- }
- }
-
- /// <summary>
- /// Stops playback
- /// </summary>
- /// <param name="trackId">The ID of the track to stop playback on</param>
- public void Stop(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.Stop();
- }
- }
-
- /// <summary>
- /// Get track buffer count
- /// </summary>
- /// <param name="trackId">The ID of the track to get buffer count</param>
- public uint GetBufferCount(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.BufferCount;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Get track played sample count
- /// </summary>
- /// <param name="trackId">The ID of the track to get played sample</param>
- public ulong GetPlayedSampleCount(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.PlayedSampleCount;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Flush all track buffers
- /// </summary>
- /// <param name="trackId">The ID of the track to flush</param>
- public bool FlushBuffers(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.FlushBuffers();
- }
-
- return false;
- }
-
- /// <summary>
- /// Set track volume
- /// </summary>
- /// <param name="volume">The volume of the playback</param>
- public void SetVolume(int trackId, float volume)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- track.AudioStream.SetVolume(volume);
- }
- }
-
- /// <summary>
- /// Get track volume
- /// </summary>
- public float GetVolume(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.AudioStream.Volume;
- }
-
- return 1.0f;
- }
-
- /// <summary>
- /// Gets the current playback state of the specified track
- /// </summary>
- /// <param name="trackId">The track to retrieve the playback state for</param>
- public PlaybackState GetState(int trackId)
- {
- if (_trackPool.TryGet(trackId, out SoundIoAudioTrack track))
- {
- return track.State;
- }
-
- return PlaybackState.Stopped;
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioOut" />
- /// </summary>
- public void Dispose()
- {
- _trackPool.Dispose();
- _audioContext.Disconnect();
- _audioContext.Dispose();
- }
-
- /// <summary>
- /// Searches for a shared version of the default audio device
- /// </summary>
- /// <param name="audioContext">The <see cref="SoundIO"/> audio context</param>
- /// <param name="fallback">Whether to fallback to the raw default audio device if a non-raw device cannot be found</param>
- private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
- {
- SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
-
- if (!defaultAudioDevice.IsRaw)
- {
- return defaultAudioDevice;
- }
-
- for (int i = 0; i < audioContext.BackendCount; i++)
- {
- SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
-
- if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
- {
- return audioDevice;
- }
- }
-
- return fallback ? defaultAudioDevice : null;
- }
-
- /// <summary>
- /// Determines if SoundIO can connect to a supported backend
- /// </summary>
- /// <returns></returns>
- private static bool IsSupportedInternal()
- {
- SoundIO context = null;
- SoundIODevice device = null;
- SoundIOOutStream stream = null;
-
- bool backendDisconnected = false;
-
- try
- {
- context = new SoundIO();
-
- context.OnBackendDisconnect = (i) => {
- backendDisconnected = true;
- };
-
- context.Connect();
- context.FlushEvents();
-
- if (backendDisconnected)
- {
- return false;
- }
-
- if (context.OutputDeviceCount == 0)
- {
- return false;
- }
-
- device = FindNonRawDefaultAudioDevice(context);
-
- if (device == null || backendDisconnected)
- {
- return false;
- }
-
- stream = device.CreateOutStream();
-
- if (stream == null || backendDisconnected)
- {
- return false;
- }
-
- return true;
- }
- catch
- {
- return false;
- }
- finally
- {
- if (stream != null)
- {
- stream.Dispose();
- }
-
- if (context != null)
- {
- context.Dispose();
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs
deleted file mode 100644
index 95f181dc..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-using SoundIOSharp;
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// An object pool containing a set of audio tracks
- /// </summary>
- internal class SoundIoAudioTrackPool : IDisposable
- {
- /// <summary>
- /// The current size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- private int m_Size;
-
- /// <summary>
- /// The maximum size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- private int m_MaxSize;
-
- /// <summary>
- /// The <see cref="SoundIO"/> audio context this track pool belongs to
- /// </summary>
- private SoundIO m_Context;
-
- /// <summary>
- /// The <see cref="SoundIODevice"/> audio device this track pool belongs to
- /// </summary>
- private SoundIODevice m_Device;
-
- /// <summary>
- /// The queue that keeps track of the available <see cref="SoundIoAudioTrack"/> in the pool.
- /// </summary>
- private ConcurrentQueue<SoundIoAudioTrack> m_Queue;
-
- /// <summary>
- /// The dictionary providing mapping between a TrackID and <see cref="SoundIoAudioTrack"/>
- /// </summary>
- private ConcurrentDictionary<int, SoundIoAudioTrack> m_TrackList;
-
- /// <summary>
- /// Gets the current size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- public int Size { get => m_Size; }
-
- /// <summary>
- /// Gets the maximum size of the <see cref="SoundIoAudioTrackPool"/>
- /// </summary>
- public int MaxSize { get => m_MaxSize; }
-
- /// <summary>
- /// Gets a value that indicates whether the <see cref="SoundIoAudioTrackPool"/> is empty
- /// </summary>
- public bool IsEmpty { get => m_Queue.IsEmpty; }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that is empty
- /// </summary>
- /// <param name="maxSize">The maximum amount of tracks that can be created</param>
- public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize)
- {
- m_Size = 0;
- m_Context = context;
- m_Device = device;
- m_MaxSize = maxSize;
-
- m_Queue = new ConcurrentQueue<SoundIoAudioTrack>();
- m_TrackList = new ConcurrentDictionary<int, SoundIoAudioTrack>();
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that contains
- /// the specified amount of <see cref="SoundIoAudioTrack"/>
- /// </summary>
- /// <param name="maxSize">The maximum amount of tracks that can be created</param>
- /// <param name="initialCapacity">The initial number of tracks that the pool contains</param>
- public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize, int initialCapacity)
- : this(context, device, maxSize)
- {
- var trackCollection = Enumerable.Range(0, initialCapacity)
- .Select(TrackFactory);
-
- m_Size = initialCapacity;
- m_Queue = new ConcurrentQueue<SoundIoAudioTrack>(trackCollection);
- }
-
- /// <summary>
- /// Creates a new <see cref="SoundIoAudioTrack"/> with the proper AudioContext and AudioDevice
- /// and the specified <paramref name="trackId" />
- /// </summary>
- /// <param name="trackId">The ID of the track to be created</param>
- /// <returns>A new AudioTrack with the specified ID</returns>
- private SoundIoAudioTrack TrackFactory(int trackId)
- {
- // Create a new AudioTrack
- SoundIoAudioTrack track = new SoundIoAudioTrack(trackId, m_Context, m_Device);
-
- // Keep track of issued tracks
- m_TrackList[trackId] = track;
-
- return track;
- }
-
- /// <summary>
- /// Retrieves a <see cref="SoundIoAudioTrack"/> from the pool
- /// </summary>
- /// <returns>An AudioTrack from the pool</returns>
- public SoundIoAudioTrack Get()
- {
- // If we have a track available, reuse it
- if (m_Queue.TryDequeue(out SoundIoAudioTrack track))
- {
- return track;
- }
-
- // Have we reached the maximum size of our pool?
- if (m_Size >= m_MaxSize)
- {
- return null;
- }
-
- // We don't have any pooled tracks, so create a new one
- return TrackFactory(m_Size++);
- }
-
- /// <summary>
- /// Retrieves the <see cref="SoundIoAudioTrack"/> associated with the specified <paramref name="trackId"/> from the pool
- /// </summary>
- /// <param name="trackId">The ID of the track to retrieve</param>
- public SoundIoAudioTrack Get(int trackId)
- {
- if (m_TrackList.TryGetValue(trackId, out SoundIoAudioTrack track))
- {
- return track;
- }
-
- return null;
- }
-
- /// <summary>
- /// Attempts to get a <see cref="SoundIoAudioTrack"/> from the pool
- /// </summary>
- /// <param name="track">The track retrieved from the pool</param>
- /// <returns>True if retrieve was successful</returns>
- public bool TryGet(out SoundIoAudioTrack track)
- {
- track = Get();
-
- return track != null;
- }
-
- /// <summary>
- /// Attempts to get the <see cref="SoundIoAudioTrack" /> associated with the specified <paramref name="trackId"/> from the pool
- /// </summary>
- /// <param name="trackId">The ID of the track to retrieve</param>
- /// <param name="track">The track retrieved from the pool</param>
- public bool TryGet(int trackId, out SoundIoAudioTrack track)
- {
- return m_TrackList.TryGetValue(trackId, out track);
- }
-
- /// <summary>
- /// Returns an <see cref="SoundIoAudioTrack"/> back to the pool for reuse
- /// </summary>
- /// <param name="track">The track to be returned to the pool</param>
- public void Put(SoundIoAudioTrack track)
- {
- // Ensure the track is disposed and not playing audio
- track.Close();
-
- // Requeue the track for reuse later
- m_Queue.Enqueue(track);
- }
-
- /// <summary>
- /// Releases the unmanaged resources used by the <see cref="SoundIoAudioTrackPool" />
- /// </summary>
- public void Dispose()
- {
- foreach (var track in m_TrackList)
- {
- track.Value.Close();
- track.Value.Dispose();
- }
-
- m_Size = 0;
- m_Queue.Clear();
- m_TrackList.Clear();
- }
- }
-}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs
deleted file mode 100644
index 2a6190b5..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// Represents the remaining bytes left buffered for a specific buffer tag
- /// </summary>
- internal class SoundIoBuffer
- {
- /// <summary>
- /// The buffer tag this <see cref="SoundIoBuffer"/> represents
- /// </summary>
- public long Tag { get; private set; }
-
- /// <summary>
- /// The remaining bytes still to be released
- /// </summary>
- public int Length { get; set; }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoBuffer"/>
- /// </summary>
- /// <param name="tag">The buffer tag</param>
- /// <param name="length">The size of the buffer</param>
- public SoundIoBuffer(long tag, int length)
- {
- Tag = tag;
- Length = length;
- }
- }
-}
diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs
deleted file mode 100644
index b2885021..00000000
--- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using System;
-
-namespace Ryujinx.Audio.SoundIo
-{
- /// <summary>
- /// A thread-safe variable-size circular buffer
- /// </summary>
- internal class SoundIoRingBuffer
- {
- private byte[] m_Buffer;
- private int m_Size;
- private int m_HeadOffset;
- private int m_TailOffset;
-
- /// <summary>
- /// Gets the available bytes in the ring buffer
- /// </summary>
- public int Length
- {
- get { return m_Size; }
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoRingBuffer"/>
- /// </summary>
- public SoundIoRingBuffer()
- {
- m_Buffer = new byte[2048];
- }
-
- /// <summary>
- /// Constructs a new instance of a <see cref="SoundIoRingBuffer"/> with the specified capacity
- /// </summary>
- /// <param name="capacity">The number of entries that the <see cref="SoundIoRingBuffer"/> can initially contain</param>
- public SoundIoRingBuffer(int capacity)
- {
- m_Buffer = new byte[capacity];
- }
-
- /// <summary>
- /// Clears the ring buffer
- /// </summary>
- public void Clear()
- {
- m_Size = 0;
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- /// <summary>
- /// Clears the specified amount of bytes from the ring buffer
- /// </summary>
- /// <param name="size">The amount of bytes to clear from the ring buffer</param>
- public void Clear(int size)
- {
- lock (this)
- {
- if (size > m_Size)
- {
- size = m_Size;
- }
-
- if (size == 0)
- {
- return;
- }
-
- m_HeadOffset = (m_HeadOffset + size) % m_Buffer.Length;
- m_Size -= size;
-
- if (m_Size == 0)
- {
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- return;
- }
- }
-
- /// <summary>
- /// Extends the capacity of the ring buffer
- /// </summary>
- private void SetCapacity(int capacity)
- {
- byte[] buffer = new byte[capacity];
-
- if (m_Size > 0)
- {
- if (m_HeadOffset < m_TailOffset)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Size);
- }
- else
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Buffer.Length - m_HeadOffset);
- Buffer.BlockCopy(m_Buffer, 0, buffer, m_Buffer.Length - m_HeadOffset, m_TailOffset);
- }
- }
-
- m_Buffer = buffer;
- m_HeadOffset = 0;
- m_TailOffset = m_Size;
- }
-
-
- /// <summary>
- /// Writes a sequence of bytes to the ring buffer
- /// </summary>
- /// <param name="buffer">A byte array containing the data to write</param>
- /// <param name="index">The zero-based byte offset in <paramref name="buffer" /> from which to begin copying bytes to the ring buffer</param>
- /// <param name="count">The number of bytes to write</param>
- public void Write<T>(T[] buffer, int index, int count)
- {
- if (count == 0)
- {
- return;
- }
-
- lock (this)
- {
- if ((m_Size + count) > m_Buffer.Length)
- {
- SetCapacity((m_Size + count + 2047) & ~2047);
- }
-
- if (m_HeadOffset < m_TailOffset)
- {
- int tailLength = m_Buffer.Length - m_TailOffset;
-
- if (tailLength >= count)
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count);
- }
- else
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, tailLength);
- Buffer.BlockCopy(buffer, index + tailLength, m_Buffer, 0, count - tailLength);
- }
- }
- else
- {
- Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count);
- }
-
- m_Size += count;
- m_TailOffset = (m_TailOffset + count) % m_Buffer.Length;
- }
- }
-
- /// <summary>
- /// Reads a sequence of bytes from the ring buffer and advances the position within the ring buffer by the number of bytes read
- /// </summary>
- /// <param name="buffer">The buffer to write the data into</param>
- /// <param name="index">The zero-based byte offset in <paramref name="buffer" /> at which the read bytes will be placed</param>
- /// <param name="count">The maximum number of bytes to read</param>
- /// <returns>The total number of bytes read into the buffer. This might be less than the number of bytes requested if that number of bytes are not currently available, or zero if the ring buffer is empty</returns>
- public int Read<T>(T[] buffer, int index, int count)
- {
- lock (this)
- {
- if (count > m_Size)
- {
- count = m_Size;
- }
-
- if (count == 0)
- {
- return 0;
- }
-
- if (m_HeadOffset < m_TailOffset)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count);
- }
- else
- {
- int tailLength = m_Buffer.Length - m_HeadOffset;
-
- if (tailLength >= count)
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count);
- }
- else
- {
- Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, tailLength);
- Buffer.BlockCopy(m_Buffer, 0, buffer, index + tailLength, count - tailLength);
- }
- }
-
- m_Size -= count;
- m_HeadOffset = (m_HeadOffset + count) % m_Buffer.Length;
-
- if (m_Size == 0)
- {
- m_HeadOffset = 0;
- m_TailOffset = 0;
- }
-
- return count;
- }
- }
- }
-}
diff --git a/Ryujinx.Audio/ResultCode.cs b/Ryujinx.Audio/ResultCode.cs
new file mode 100644
index 00000000..6708cdc1
--- /dev/null
+++ b/Ryujinx.Audio/ResultCode.cs
@@ -0,0 +1,38 @@
+//
+// 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/>.
+//
+
+namespace Ryujinx.Audio
+{
+ public enum ResultCode
+ {
+ ModuleId = 153,
+ ErrorCodeShift = 9,
+
+ Success = 0,
+
+ DeviceNotFound = (1 << ErrorCodeShift) | ModuleId,
+ OperationFailed = (2 << ErrorCodeShift) | ModuleId,
+ UnsupportedSampleRate = (3 << ErrorCodeShift) | ModuleId,
+ WorkBufferTooSmall = (4 << ErrorCodeShift) | ModuleId,
+ BufferRingFull = (8 << ErrorCodeShift) | ModuleId,
+ UnsupportedChannelConfiguration = (10 << ErrorCodeShift) | ModuleId,
+ InvalidUpdateInfo = (41 << ErrorCodeShift) | ModuleId,
+ InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
+ InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
+ UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
+ }
+}
diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj
index 6e0b668a..ccdeae3e 100644
--- a/Ryujinx.Audio/Ryujinx.Audio.csproj
+++ b/Ryujinx.Audio/Ryujinx.Audio.csproj
@@ -6,26 +6,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="OpenTK.NetStandard" Version="1.0.5.32" />
- </ItemGroup>
-
- <ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- <TargetPath>libsoundio.dll</TargetPath>
- </ContentWithTargetPath>
- <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- <TargetPath>libsoundio.dylib</TargetPath>
- </ContentWithTargetPath>
- <ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- <TargetPath>libsoundio.so</TargetPath>
- </ContentWithTargetPath>
+ <ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
+ <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
</ItemGroup>
</Project>
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 9309ae41..16b4c376 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -2,9 +2,11 @@ using LibHac;
using LibHac.Bcat;
using LibHac.Fs;
using LibHac.FsSystem;
-using Ryujinx.Audio.Renderer;
+using Ryujinx.Audio;
+using Ryujinx.Audio.Input;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Audio.Output;
using Ryujinx.Audio.Renderer.Device;
-using Ryujinx.Audio.Renderer.Integration;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Common;
using Ryujinx.Configuration;
@@ -51,6 +53,9 @@ namespace Ryujinx.HLE.HOS
internal Switch Device { get; private set; }
internal SurfaceFlinger SurfaceFlinger { get; private set; }
+ internal AudioManager AudioManager { get; private set; }
+ internal AudioOutputManager AudioOutputManager { get; private set; }
+ internal AudioInputManager AudioInputManager { get; private set; }
internal AudioRendererManager AudioRendererManager { get; private set; }
internal VirtualDeviceSessionRegistry AudioDeviceSessionRegistry { get; private set; }
@@ -206,29 +211,48 @@ namespace Ryujinx.HLE.HOS
private void InitializeAudioRenderer()
{
+ AudioManager = new AudioManager();
+ AudioOutputManager = new AudioOutputManager();
+ AudioInputManager = new AudioInputManager();
AudioRendererManager = new AudioRendererManager();
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
- IWritableEvent[] writableEvents = new IWritableEvent[RendererConstants.AudioRendererSessionCountMax];
+ IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
- for (int i = 0; i < writableEvents.Length; i++)
+ for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++)
{
- KEvent systemEvent = new KEvent(KernelContext);
+ KEvent registerBufferEvent = new KEvent(KernelContext);
- writableEvents[i] = new AudioKernelEvent(systemEvent);
+ audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
}
- HardwareDevice[] devices = new HardwareDevice[RendererConstants.AudioRendererSessionCountMax];
+ AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
+
+ IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
- // TODO: don't hardcode those values.
- // TODO: keep the device somewhere and dispose it when exiting.
- // TODO: This is kind of wrong, we should have an high level API for that and mix all buffers between them.
- for (int i = 0; i < devices.Length; i++)
+ for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++)
{
- devices[i] = new AalHardwareDevice(i, Device.AudioOut, 2, RendererConstants.TargetSampleRate);
+ KEvent registerBufferEvent = new KEvent(KernelContext);
+
+ audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
}
- AudioRendererManager.Initialize(writableEvents, devices);
+ AudioInputManager.Initialize(Device.AudioDeviceDriver, audioInputRegisterBufferEvents);
+
+ IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax];
+
+ for (int i = 0; i < systemEvents.Length; i++)
+ {
+ KEvent systemEvent = new KEvent(KernelContext);
+
+ systemEvents[i] = new AudioKernelEvent(systemEvent);
+ }
+
+ AudioManager.Initialize(Device.AudioDeviceDriver.GetUpdateRequiredEvent(), AudioOutputManager.Update, AudioInputManager.Update);
+
+ AudioRendererManager.Initialize(systemEvents, Device.AudioDeviceDriver);
+
+ AudioManager.Start();
}
public void InitializeServices()
@@ -363,6 +387,10 @@ namespace Ryujinx.HLE.HOS
// This is safe as KThread that are likely to call ioctls are going to be terminated by the post handler hook on the SVC facade.
INvDrvServices.Destroy();
+ AudioManager.Dispose();
+ AudioOutputManager.Dispose();
+ AudioInputManager.Dispose();
+
AudioRendererManager.Dispose();
KernelContext.Dispose();
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs
new file mode 100644
index 00000000..ee85ded9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs
@@ -0,0 +1,108 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Input;
+using Ryujinx.Audio.Integration;
+using Ryujinx.HLE.HOS.Kernel;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
+{
+ class AudioIn : IAudioIn
+ {
+ private AudioInputSystem _system;
+ private uint _processHandle;
+ private KernelContext _kernelContext;
+
+ public AudioIn(AudioInputSystem system, KernelContext kernelContext, uint processHandle)
+ {
+ _system = system;
+ _kernelContext = kernelContext;
+ _processHandle = processHandle;
+ }
+
+ public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer)
+ {
+ return (ResultCode)_system.AppendBuffer(bufferTag, ref buffer);
+ }
+
+ public ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer buffer, uint handle)
+ {
+ return (ResultCode)_system.AppendUacBuffer(bufferTag, ref buffer, handle);
+ }
+
+ public bool ContainsBuffer(ulong bufferTag)
+ {
+ return _system.ContainsBuffer(bufferTag);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _system.Dispose();
+
+ _kernelContext.Syscall.CloseHandle((int)_processHandle);
+ }
+ }
+
+ public bool FlushBuffers()
+ {
+ return _system.FlushBuffers();
+ }
+
+ public uint GetBufferCount()
+ {
+ return _system.GetBufferCount();
+ }
+
+ public ResultCode GetReleasedBuffers(Span<ulong> releasedBuffers, out uint releasedCount)
+ {
+ return (ResultCode)_system.GetReleasedBuffers(releasedBuffers, out releasedCount);
+ }
+
+ public AudioDeviceState GetState()
+ {
+ return _system.GetState();
+ }
+
+ public float GetVolume()
+ {
+ return _system.GetVolume();
+ }
+
+ public KEvent RegisterBufferEvent()
+ {
+ IWritableEvent outEvent = _system.RegisterBufferEvent();
+
+ if (outEvent is AudioKernelEvent)
+ {
+ return ((AudioKernelEvent)outEvent).Event;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void SetVolume(float volume)
+ {
+ _system.SetVolume(volume);
+ }
+
+ public ResultCode Start()
+ {
+ return (ResultCode)_system.Start();
+ }
+
+ public ResultCode Stop()
+ {
+ return (ResultCode)_system.Stop();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
new file mode 100644
index 00000000..1a1a3b6e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
@@ -0,0 +1,209 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
+{
+ class AudioInServer : IpcService, IDisposable
+ {
+ private IAudioIn _impl;
+
+ public AudioInServer(IAudioIn impl)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // GetAudioInState() -> u32 state
+ public ResultCode GetAudioInState(ServiceCtx context)
+ {
+ context.ResponseData.Write((uint)_impl.GetState());
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // Start()
+ public ResultCode Start(ServiceCtx context)
+ {
+ return _impl.Start();
+ }
+
+ [Command(2)]
+ // Stop()
+ public ResultCode StopAudioIn(ServiceCtx context)
+ {
+ return _impl.Stop();
+ }
+
+ [Command(3)]
+ // AppendAudioInBuffer(u64 tag, buffer<nn::audio::AudioInBuffer, 5>)
+ public ResultCode AppendAudioInBuffer(ServiceCtx context)
+ {
+ long position = context.Request.SendBuff[0].Position;
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(4)]
+ // RegisterBufferEvent() -> handle<copy>
+ public ResultCode RegisterBufferEvent(ServiceCtx context)
+ {
+ KEvent bufferEvent = _impl.RegisterBufferEvent();
+
+ if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+
+ return ResultCode.Success;
+ }
+
+ [Command(5)]
+ // GetReleasedAudioInBuffers() -> (u32 count, buffer<u64, 6> tags)
+ public ResultCode GetReleasedAudioInBuffers(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ {
+ ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
+
+ context.ResponseData.Write(releasedCount);
+
+ return result;
+ }
+ }
+
+ [Command(6)]
+ // ContainsAudioInBuffer(u64 tag) -> b8
+ public ResultCode ContainsAudioInBuffer(ServiceCtx context)
+ {
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ context.ResponseData.Write(_impl.ContainsBuffer(bufferTag));
+
+ return ResultCode.Success;
+ }
+
+ [Command(7)] // 3.0.0+
+ // AppendUacInBuffer(u64 tag, handle<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>)
+ public ResultCode AppendUacInBuffer(ServiceCtx context)
+ {
+ long position = context.Request.SendBuff[0].Position;
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+ uint handle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendUacBuffer(bufferTag, ref data, handle);
+ }
+
+ [Command(8)] // 3.0.0+
+ // AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>)
+ public ResultCode AppendAudioInBufferAuto(ServiceCtx context)
+ {
+ (long position, _) = context.Request.GetBufferType0x21();
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(9)] // 3.0.0+
+ // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
+ public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context)
+ {
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ {
+ ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
+
+ context.ResponseData.Write(releasedCount);
+
+ return result;
+ }
+ }
+
+ [Command(10)] // 3.0.0+
+ // AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>)
+ public ResultCode AppendUacInBufferAuto(ServiceCtx context)
+ {
+ (long position, _) = context.Request.GetBufferType0x21();
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+ uint handle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendUacBuffer(bufferTag, ref data, handle);
+ }
+
+ [Command(11)] // 4.0.0+
+ // GetAudioInBufferCount() -> u32
+ public ResultCode GetAudioInBufferCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetBufferCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(12)] // 4.0.0+
+ // SetAudioInVolume(s32)
+ public ResultCode SetAudioInVolume(ServiceCtx context)
+ {
+ float volume = context.RequestData.ReadSingle();
+
+ _impl.SetVolume(volume);
+
+ return ResultCode.Success;
+ }
+
+ [Command(13)] // 4.0.0+
+ // GetAudioInVolume() -> s32
+ public ResultCode GetAudioInVolume(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetVolume());
+
+ return ResultCode.Success;
+ }
+
+ [Command(14)] // 6.0.0+
+ // FlushAudioInBuffers() -> b8
+ public ResultCode FlushAudioInBuffers(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.FlushBuffers());
+
+ return ResultCode.Success;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _impl.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs
new file mode 100644
index 00000000..b5073fce
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs
@@ -0,0 +1,34 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
+{
+ interface IAudioIn : IDisposable
+ {
+ AudioDeviceState GetState();
+
+ ResultCode Start();
+
+ ResultCode Stop();
+
+ ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer);
+
+ // NOTE: This is broken by design... not quite sure what it's used for (if anything in production).
+ ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer buffer, uint handle);
+
+ KEvent RegisterBufferEvent();
+
+ ResultCode GetReleasedBuffers(Span<ulong> releasedBuffers, out uint releasedCount);
+
+ bool ContainsBuffer(ulong bufferTag);
+
+ uint GetBufferCount();
+
+ bool FlushBuffers();
+
+ void SetVolume(float volume);
+
+ float GetVolume();
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs
new file mode 100644
index 00000000..2d342206
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs
@@ -0,0 +1,41 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Input;
+using Ryujinx.HLE.HOS.Services.Audio.AudioIn;
+
+using AudioInManagerImpl = Ryujinx.Audio.Input.AudioInputManager;
+
+namespace Ryujinx.HLE.HOS.Services.Audio
+{
+ class AudioInManager : IAudioInManager
+ {
+ private AudioInManagerImpl _impl;
+
+ public AudioInManager(AudioInManagerImpl impl)
+ {
+ _impl = impl;
+ }
+
+ public string[] ListAudioIns(bool filtered)
+ {
+ return _impl.ListAudioIns(filtered);
+ }
+
+ public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle)
+ {
+ var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
+
+ ResultCode result = (ResultCode)_impl.OpenAudioIn(out outputDeviceName, out outputConfiguration, out AudioInputSystem inSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle);
+
+ if (result == ResultCode.Success)
+ {
+ obj = new AudioIn.AudioIn(inSystem, context.Device.System.KernelContext, processHandle);
+ }
+ else
+ {
+ obj = null;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
new file mode 100644
index 00000000..079b91ca
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
@@ -0,0 +1,235 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Services.Audio.AudioIn;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Audio
+{
+ [Service("audin:u")]
+ class AudioInManagerServer : IpcService
+ {
+ private const int AudioInNameSize = 0x100;
+
+ private IAudioInManager _impl;
+
+ public AudioInManagerServer(ServiceCtx context) : this(context, new AudioInManager(context.Device.System.AudioInputManager)) { }
+
+ public AudioInManagerServer(ServiceCtx context, IAudioInManager impl) : base(context.Device.System.AudOutServer)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // ListAudioIns() -> (u32, buffer<bytes, 6>)
+ public ResultCode ListAudioIns(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(false);
+
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // OpenAudioIn(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name)
+ // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> name)
+ public ResultCode OpenAudioIn(ServiceCtx context)
+ {
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(2)] // 3.0.0+
+ // ListAudioInsAuto() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioInsAuto(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(false);
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(3)] // 3.0.0+
+ // OpenAudioInAuto(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 0x21>)
+ // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 0x22> name)
+ public ResultCode OpenAudioInAuto(ServiceCtx context)
+ {
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(4)] // 3.0.0+
+ // ListAudioInsAutoFiltered() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioInsAutoFiltered(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(true);
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(5)] // 5.0.0+
+ // OpenAudioInProtocolSpecified(b64 protocol_specified_related, AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name)
+ // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> name)
+ public ResultCode OpenAudioInProtocolSpecified(ServiceCtx context)
+ {
+ // NOTE: We always assume that only the default device will be plugged (we never report any USB Audio Class type devices).
+ bool protocolSpecifiedRelated = context.RequestData.ReadUInt64() == 1;
+
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs
new file mode 100644
index 00000000..f2588452
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs
@@ -0,0 +1,108 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Audio.Output;
+using Ryujinx.HLE.HOS.Kernel;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
+{
+ class AudioOut : IAudioOut
+ {
+ private AudioOutputSystem _system;
+ private uint _processHandle;
+ private KernelContext _kernelContext;
+
+ public AudioOut(AudioOutputSystem system, KernelContext kernelContext, uint processHandle)
+ {
+ _system = system;
+ _kernelContext = kernelContext;
+ _processHandle = processHandle;
+ }
+
+ public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer)
+ {
+ return (ResultCode)_system.AppendBuffer(bufferTag, ref buffer);
+ }
+
+ public bool ContainsBuffer(ulong bufferTag)
+ {
+ return _system.ContainsBuffer(bufferTag);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _system.Dispose();
+
+ _kernelContext.Syscall.CloseHandle((int)_processHandle);
+ }
+ }
+
+ public bool FlushBuffers()
+ {
+ return _system.FlushBuffers();
+ }
+
+ public uint GetBufferCount()
+ {
+ return _system.GetBufferCount();
+ }
+
+ public ulong GetPlayedSampleCount()
+ {
+ return _system.GetPlayedSampleCount();
+ }
+
+ public ResultCode GetReleasedBuffers(Span<ulong> releasedBuffers, out uint releasedCount)
+ {
+ return (ResultCode)_system.GetReleasedBuffer(releasedBuffers, out releasedCount);
+ }
+
+ public AudioDeviceState GetState()
+ {
+ return _system.GetState();
+ }
+
+ public float GetVolume()
+ {
+ return _system.GetVolume();
+ }
+
+ public KEvent RegisterBufferEvent()
+ {
+ IWritableEvent outEvent = _system.RegisterBufferEvent();
+
+ if (outEvent is AudioKernelEvent)
+ {
+ return ((AudioKernelEvent)outEvent).Event;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void SetVolume(float volume)
+ {
+ _system.SetVolume(volume);
+ }
+
+ public ResultCode Start()
+ {
+ return (ResultCode)_system.Start();
+ }
+
+ public ResultCode Stop()
+ {
+ return (ResultCode)_system.Stop();
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
new file mode 100644
index 00000000..4242eb7e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
@@ -0,0 +1,190 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
+{
+ class AudioOutServer : IpcService, IDisposable
+ {
+ private IAudioOut _impl;
+
+ public AudioOutServer(IAudioOut impl)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // GetAudioOutState() -> u32 state
+ public ResultCode GetAudioOutState(ServiceCtx context)
+ {
+ context.ResponseData.Write((uint)_impl.GetState());
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // Start()
+ public ResultCode Start(ServiceCtx context)
+ {
+ return _impl.Start();
+ }
+
+ [Command(2)]
+ // Stop()
+ public ResultCode Stop(ServiceCtx context)
+ {
+ return _impl.Stop();
+ }
+
+ [Command(3)]
+ // AppendAudioOutBuffer(u64 bufferTag, buffer<nn::audio::AudioOutBuffer, 5> buffer)
+ public ResultCode AppendAudioOutBuffer(ServiceCtx context)
+ {
+ long position = context.Request.SendBuff[0].Position;
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(4)]
+ // RegisterBufferEvent() -> handle<copy>
+ public ResultCode RegisterBufferEvent(ServiceCtx context)
+ {
+ KEvent bufferEvent = _impl.RegisterBufferEvent();
+
+ if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+
+ return ResultCode.Success;
+ }
+
+ [Command(5)]
+ // GetReleasedAudioOutBuffers() -> (u32 count, buffer<u64, 6> tags)
+ public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ {
+ ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
+
+ context.ResponseData.Write(releasedCount);
+
+ return result;
+ }
+ }
+
+ [Command(6)]
+ // ContainsAudioOutBuffer(u64 tag) -> b8
+ public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
+ {
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ context.ResponseData.Write(_impl.ContainsBuffer(bufferTag));
+
+ return ResultCode.Success;
+ }
+
+ [Command(7)] // 3.0.0+
+ // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
+ public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
+ {
+ (long position, _) = context.Request.GetBufferType0x21();
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(8)] // 3.0.0+
+ // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
+ public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context)
+ {
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size))
+ {
+ ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
+
+ context.ResponseData.Write(releasedCount);
+
+ return result;
+ }
+ }
+
+ [Command(9)] // 4.0.0+
+ // GetAudioOutBufferCount() -> u32
+ public ResultCode GetAudioOutBufferCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetBufferCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(10)] // 4.0.0+
+ // GetAudioOutPlayedSampleCount() -> u64
+ public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetPlayedSampleCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(11)] // 4.0.0+
+ // FlushAudioOutBuffers() -> b8
+ public ResultCode FlushAudioOutBuffers(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.FlushBuffers());
+
+ return ResultCode.Success;
+ }
+
+ [Command(12)] // 6.0.0+
+ // SetAudioOutVolume(s32)
+ public ResultCode SetAudioOutVolume(ServiceCtx context)
+ {
+ float volume = context.RequestData.ReadSingle();
+
+ _impl.SetVolume(volume);
+
+ return ResultCode.Success;
+ }
+
+ [Command(13)] // 6.0.0+
+ // GetAudioOutVolume() -> s32
+ public ResultCode GetAudioOutVolume(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetVolume());
+
+ return ResultCode.Success;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _impl.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs
new file mode 100644
index 00000000..8533d3c5
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
+{
+ interface IAudioOut : IDisposable
+ {
+ AudioDeviceState GetState();
+
+ ResultCode Start();
+
+ ResultCode Stop();
+
+ ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer);
+
+ KEvent RegisterBufferEvent();
+
+ ResultCode GetReleasedBuffers(Span<ulong> releasedBuffers, out uint releasedCount);
+
+ bool ContainsBuffer(ulong bufferTag);
+
+ uint GetBufferCount();
+
+ ulong GetPlayedSampleCount();
+
+ bool FlushBuffers();
+
+ void SetVolume(float volume);
+
+ float GetVolume();
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
new file mode 100644
index 00000000..29490553
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
@@ -0,0 +1,41 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Audio.Output;
+using Ryujinx.HLE.HOS.Services.Audio.AudioOut;
+
+using AudioOutManagerImpl = Ryujinx.Audio.Output.AudioOutputManager;
+
+namespace Ryujinx.HLE.HOS.Services.Audio
+{
+ class AudioOutManager : IAudioOutManager
+ {
+ private AudioOutManagerImpl _impl;
+
+ public AudioOutManager(AudioOutManagerImpl impl)
+ {
+ _impl = impl;
+ }
+
+ public string[] ListAudioOuts()
+ {
+ return _impl.ListAudioOuts();
+ }
+
+ public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle)
+ {
+ var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
+
+ ResultCode result = (ResultCode)_impl.OpenAudioOut(out outputDeviceName, out outputConfiguration, out AudioOutputSystem outSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle);
+
+ if (result == ResultCode.Success)
+ {
+ obj = new AudioOut.AudioOut(outSystem, context.Device.System.KernelContext, processHandle);
+ }
+ else
+ {
+ obj = null;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
deleted file mode 100644
index c941cf4f..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
+++ /dev/null
@@ -1,228 +0,0 @@
-using Ryujinx.Audio;
-using Ryujinx.Cpu;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.HOS.Kernel.Common;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
-{
- class IAudioOut : IpcService, IDisposable
- {
- private readonly KernelContext _kernelContext;
- private readonly IAalOutput _audioOut;
- private readonly KEvent _releaseEvent;
- private int _releaseEventHandle;
- private readonly int _track;
- private readonly int _clientHandle;
-
- public IAudioOut(KernelContext kernelContext, IAalOutput audioOut, KEvent releaseEvent, int track, int clientHandle)
- {
- _kernelContext = kernelContext;
- _audioOut = audioOut;
- _releaseEvent = releaseEvent;
- _track = track;
- _clientHandle = clientHandle;
- }
-
- [Command(0)]
- // GetAudioOutState() -> u32 state
- public ResultCode GetAudioOutState(ServiceCtx context)
- {
- context.ResponseData.Write((int)_audioOut.GetState(_track));
-
- return ResultCode.Success;
- }
-
- [Command(1)]
- // StartAudioOut()
- public ResultCode StartAudioOut(ServiceCtx context)
- {
- _audioOut.Start(_track);
-
- return ResultCode.Success;
- }
-
- [Command(2)]
- // StopAudioOut()
- public ResultCode StopAudioOut(ServiceCtx context)
- {
- _audioOut.Stop(_track);
-
- return ResultCode.Success;
- }
-
- [Command(3)]
- // AppendAudioOutBuffer(u64 tag, buffer<nn::audio::AudioOutBuffer, 5>)
- public ResultCode AppendAudioOutBuffer(ServiceCtx context)
- {
- return AppendAudioOutBufferImpl(context, context.Request.SendBuff[0].Position);
- }
-
- [Command(4)]
- // RegisterBufferEvent() -> handle<copy>
- public ResultCode RegisterBufferEvent(ServiceCtx context)
- {
- if (_releaseEventHandle == 0)
- {
- if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out _releaseEventHandle) != KernelResult.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_releaseEventHandle);
-
- return ResultCode.Success;
- }
-
- [Command(5)]
- // GetReleasedAudioOutBuffer() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 6>)
- public ResultCode GetReleasedAudioOutBuffer(ServiceCtx context)
- {
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
-
- return GetReleasedAudioOutBufferImpl(context, position, size);
- }
-
- [Command(6)]
- // ContainsAudioOutBuffer(u64 tag) -> b8
- public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
- {
- long tag = context.RequestData.ReadInt64();
-
- context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag));
-
- return ResultCode.Success;
- }
-
- [Command(7)] // 3.0.0+
- // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
- public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
- {
- (long position, _) = context.Request.GetBufferType0x21();
-
- return AppendAudioOutBufferImpl(context, position);
- }
-
- public ResultCode AppendAudioOutBufferImpl(ServiceCtx context, long position)
- {
- long tag = context.RequestData.ReadInt64();
-
- AudioOutData data = MemoryHelper.Read<AudioOutData>(context.Memory, position);
-
- // NOTE: Assume PCM16 all the time, change if new format are found.
- short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
-
- context.Process.HandleTable.GetKProcess(_clientHandle).CpuMemory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast<short, byte>(buffer));
-
- _audioOut.AppendBuffer(_track, tag, buffer);
-
- return ResultCode.Success;
- }
-
- [Command(8)] // 3.0.0+
- // GetReleasedAudioOutBufferAuto() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 0x22>)
- public ResultCode GetReleasedAudioOutBufferAuto(ServiceCtx context)
- {
- (long position, long size) = context.Request.GetBufferType0x22();
-
- return GetReleasedAudioOutBufferImpl(context, position, size);
- }
-
- public ResultCode GetReleasedAudioOutBufferImpl(ServiceCtx context, long position, long size)
- {
- uint count = (uint)((ulong)size >> 3);
-
- long[] releasedBuffers = _audioOut.GetReleasedBuffers(_track, (int)count);
-
- for (uint index = 0; index < count; index++)
- {
- long tag = 0;
-
- if (index < releasedBuffers.Length)
- {
- tag = releasedBuffers[index];
- }
-
- context.Memory.Write((ulong)(position + index * 8), tag);
- }
-
- context.ResponseData.Write(releasedBuffers.Length);
-
- return ResultCode.Success;
- }
-
- [Command(9)] // 4.0.0+
- // GetAudioOutBufferCount() -> u32
- public ResultCode GetAudioOutBufferCount(ServiceCtx context)
- {
- uint bufferCount = _audioOut.GetBufferCount(_track);
-
- context.ResponseData.Write(bufferCount);
-
- return ResultCode.Success;
- }
-
- [Command(10)] // 4.0.0+
- // GetAudioOutPlayedSampleCount() -> u64
- public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context)
- {
- ulong playedSampleCount = _audioOut.GetPlayedSampleCount(_track);
-
- context.ResponseData.Write(playedSampleCount);
-
- return ResultCode.Success;
- }
-
- [Command(11)] // 4.0.0+
- // FlushAudioOutBuffers() -> b8
- public ResultCode FlushAudioOutBuffers(ServiceCtx context)
- {
- bool heldBuffers = _audioOut.FlushBuffers(_track);
-
- context.ResponseData.Write(heldBuffers);
-
- return ResultCode.Success;
- }
-
- [Command(12)] // 6.0.0+
- // SetAudioOutVolume(s32)
- public ResultCode SetAudioOutVolume(ServiceCtx context)
- {
- float volume = context.RequestData.ReadSingle();
-
- _audioOut.SetVolume(_track, volume);
-
- return ResultCode.Success;
- }
-
- [Command(13)] // 6.0.0+
- // GetAudioOutVolume() -> s32
- public ResultCode GetAudioOutVolume(ServiceCtx context)
- {
- float volume = _audioOut.GetVolume(_track);
-
- context.ResponseData.Write(volume);
-
- return ResultCode.Success;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _kernelContext.Syscall.CloseHandle(_clientHandle);
- _audioOut.CloseTrack(_track);
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs
deleted file mode 100644
index 2598d0f8..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
-{
- [StructLayout(LayoutKind.Sequential)]
- struct AudioOutData
- {
- public long NextBufferPtr;
- public long SampleBufferPtr;
- public long SampleBufferCapacity;
- public long SampleBufferSize;
- public long SampleBufferInnerOffset;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
new file mode 100644
index 00000000..13930d31
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
@@ -0,0 +1,162 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Services.Audio.AudioOut;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Audio
+{
+ [Service("audout:u")]
+ class AudioOutManagerServer : IpcService
+ {
+ private const int AudioOutNameSize = 0x100;
+
+ private IAudioOutManager _impl;
+
+ public AudioOutManagerServer(ServiceCtx context) : this(context, new AudioOutManager(context.Device.System.AudioOutputManager)) { }
+
+ public AudioOutManagerServer(ServiceCtx context, IAudioOutManager impl) : base(context.Device.System.AudOutServer)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // ListAudioOuts() -> (u32, buffer<bytes, 6>)
+ public ResultCode ListAudioOuts(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioOuts();
+
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+
+ position += AudioOutNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process> process_handle, buffer<bytes, 5> name_in)
+ // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
+ public ResultCode OpenAudioOut(ServiceCtx context)
+ {
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioOutServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(2)] // 3.0.0+
+ // ListAudioOutsAuto() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioOutsAuto(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioOuts();
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+
+ position += AudioOutNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(3)] // 3.0.0+
+ // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle<copy, process> process_handle, buffer<bytes, 0x21> name_in)
+ // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out)
+ public ResultCode OpenAudioOutAuto(ServiceCtx context)
+ {
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioOutServer(obj));
+ }
+
+ return resultCode;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs
deleted file mode 100644
index fdc23604..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using Ryujinx.Audio;
-using Ryujinx.Audio.Renderer;
-using Ryujinx.Audio.Renderer.Integration;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
-{
- public class AalHardwareDevice : HardwareDevice
- {
- private IAalOutput _output;
- private int _trackId;
- private int _bufferTag;
- private int _nextTag;
- private AutoResetEvent _releaseEvent;
-
- private uint _channelCount;
- private uint _sampleRate;
-
- private short[] _buffer;
-
- private Queue<long> _releasedTags;
-
- public AalHardwareDevice(int bufferTag, IAalOutput output, uint channelCount, uint sampleRate)
- {
- _bufferTag = bufferTag;
- _channelCount = channelCount;
- _sampleRate = sampleRate;
- _output = output;
- _releaseEvent = new AutoResetEvent(true);
- _trackId = _output.OpenTrack((int)sampleRate, (int)channelCount, AudioCallback);
- _releasedTags = new Queue<long>();
-
- _buffer = new short[RendererConstants.TargetSampleCount * channelCount];
-
- _output.Start(_trackId);
- }
-
- private void AudioCallback()
- {
- long[] released = _output.GetReleasedBuffers(_trackId, int.MaxValue);
-
- lock (_releasedTags)
- {
- foreach (long tag in released)
- {
- _releasedTags.Enqueue(tag);
- }
- }
- }
-
- private long GetReleasedTag()
- {
- lock (_releasedTags)
- {
- if (_releasedTags.Count > 0)
- {
- return _releasedTags.Dequeue();
- }
-
- return (_bufferTag << 16) | (_nextTag++);
- }
- }
-
- public void AppendBuffer(ReadOnlySpan<short> data, uint channelCount)
- {
- data.CopyTo(_buffer.AsSpan());
-
- _output.AppendBuffer(_trackId, GetReleasedTag(), _buffer);
- }
-
- public uint GetChannelCount()
- {
- return _channelCount;
- }
-
- public uint GetSampleRate()
- {
- return _sampleRate;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _output.Stop(_trackId);
- _output.CloseTrack(_trackId);
- _releaseEvent.Dispose();
- }
- }
- }
-}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
index 1a132b91..55bf29ae 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
index 702648dd..d69bde03 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
index b3f7f5e0..9bbe5b0e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
@@ -1,87 +1,12 @@
-using Ryujinx.Cpu;
-using Ryujinx.Memory;
-using System;
-using System.Text;
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Services.Audio.AudioIn;
namespace Ryujinx.HLE.HOS.Services.Audio
{
- [Service("audin:u")]
- class IAudioInManager : IpcService
+ interface IAudioInManager
{
- private const string DefaultAudioInsName = "BuiltInHeadset";
+ public string[] ListAudioIns(bool filtered);
- public IAudioInManager(ServiceCtx context) { }
-
- [Command(0)]
- // ListAudioIns() -> (u32 count, buffer<bytes, 6> names)
- public ResultCode ListAudioIns(ServiceCtx context)
- {
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [Command(2)] // 3.0.0+
- // ListAudioInsAuto() -> (u32 count, buffer<bytes, 0x22> names)
- public ResultCode ListAudioInsAuto(ServiceCtx context)
- {
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [Command(4)] // 3.0.0+
- // ListAudioInsAutoFiltered() -> (u32 count, buffer<bytes, 0x22> names)
- public ResultCode ListAudioInsAutoFiltered(ServiceCtx context)
- {
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, true);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- private uint ListAudioInsImpl(IVirtualMemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false)
- {
- uint count = 0;
-
- MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize);
-
- if (bufferSize > 0)
- {
- // NOTE: The service also check that the input target is enabled when in filtering mode, as audctl and most of the audin logic isn't supported, we don't support it.
- if (!filtered)
- {
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioInsName + "\0");
-
- memory.Write((ulong)bufferPosition, deviceNameBuffer);
-
- count++;
- }
-
- // NOTE: The service adds other input devices names available in the buffer,
- // every name is aligned to 0x100 bytes.
- // Since we don't support it for now, it's fine to do nothing here.
- }
-
- return count;
- }
+ public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle);
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
index 6204a7be..0b164019 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
@@ -1,147 +1,12 @@
-using Ryujinx.Audio;
-using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Audio.AudioOutManager;
-using System.Text;
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Services.Audio.AudioOut;
namespace Ryujinx.HLE.HOS.Services.Audio
{
- [Service("audout:u")]
- class IAudioOutManager : IpcService
+ interface IAudioOutManager
{
- private const string DefaultAudioOutput = "DeviceOut";
- private const int DefaultSampleRate = 48000;
- private const int DefaultChannelsCount = 2;
+ public string[] ListAudioOuts();
- public IAudioOutManager(ServiceCtx context) : base(context.Device.System.AudOutServer) { }
-
- [Command(0)]
- // ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
- public ResultCode ListAudioOuts(ServiceCtx context)
- {
- return ListAudioOutsImpl(context, context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
- }
-
- [Command(1)]
- // OpenAudioOut(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name_in)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
- public ResultCode OpenAudioOut(ServiceCtx context)
- {
- return OpenAudioOutImpl(context, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size,
- context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
- }
-
- [Command(2)] // 3.0.0+
- // ListAudioOutsAuto() -> (u32 count, buffer<bytes, 0x22>)
- public ResultCode ListAudioOutsAuto(ServiceCtx context)
- {
- (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
-
- return ListAudioOutsImpl(context, recvPosition, recvSize);
- }
-
- [Command(3)] // 3.0.0+
- // OpenAudioOutAuto(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 0x21>)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out)
- public ResultCode OpenAudioOutAuto(ServiceCtx context)
- {
- (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
- (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
-
- return OpenAudioOutImpl(context, sendPosition, sendSize, recvPosition, recvSize);
- }
-
- private ResultCode ListAudioOutsImpl(ServiceCtx context, long position, long size)
- {
- int nameCount = 0;
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioOutput + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= (ulong)size)
- {
- context.Memory.Write((ulong)position, deviceNameBuffer);
-
- nameCount++;
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
- }
-
- context.ResponseData.Write(nameCount);
-
- return ResultCode.Success;
- }
-
- private ResultCode OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
- {
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, sendPosition, sendSize);
-
- if (deviceName == string.Empty)
- {
- deviceName = DefaultAudioOutput;
- }
-
- if (deviceName != DefaultAudioOutput)
- {
- Logger.Warning?.Print(LogClass.Audio, "Invalid device name!");
-
- return ResultCode.DeviceNotFound;
- }
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(deviceName + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= (ulong)receiveSize)
- {
- context.Memory.Write((ulong)receivePosition, deviceNameBuffer);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {receiveSize} too small!");
- }
-
- int sampleRate = context.RequestData.ReadInt32();
- int channels = context.RequestData.ReadInt32();
-
- if (sampleRate == 0)
- {
- sampleRate = DefaultSampleRate;
- }
-
- if (sampleRate != DefaultSampleRate)
- {
- Logger.Warning?.Print(LogClass.Audio, "Invalid sample rate!");
-
- return ResultCode.UnsupportedSampleRate;
- }
-
- channels = (ushort)channels;
-
- if (channels == 0)
- {
- channels = DefaultChannelsCount;
- }
-
- KEvent releaseEvent = new KEvent(context.Device.System.KernelContext);
-
- ReleaseCallback callback = () =>
- {
- releaseEvent.ReadableEvent.Signal();
- };
-
- IAalOutput audioOut = context.Device.AudioOut;
-
- int track = audioOut.OpenTrack(sampleRate, channels, callback);
-
- MakeObject(context, new IAudioOut(context.Device.System.KernelContext, audioOut, releaseEvent, track, context.Request.HandleDesc.ToCopy[0]));
-
- context.ResponseData.Write(sampleRate);
- context.ResponseData.Write(channels);
- context.ResponseData.Write((int)SampleFormat.PcmInt16);
- context.ResponseData.Write((int)PlaybackState.Stopped);
-
- return ResultCode.Success;
- }
+ public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle);
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs b/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs
deleted file mode 100644
index 654436e4..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Audio
-{
- enum SampleFormat : byte
- {
- Invalid = 0,
- PcmInt8 = 1,
- PcmInt16 = 2,
- PcmInt24 = 3,
- PcmInt32 = 4,
- PcmFloat = 5,
- Adpcm = 6
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index c3b5ac7a..3d48d893 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -6,7 +6,6 @@
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\Ryujinx.Audio.Renderer\Ryujinx.Audio.Renderer.csproj" />
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 2306e5d3..865a86d7 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,5 +1,6 @@
using LibHac.FsSystem;
-using Ryujinx.Audio;
+using Ryujinx.Audio.Backends.CompatLayer;
+using Ryujinx.Audio.Integration;
using Ryujinx.Common;
using Ryujinx.Configuration;
using Ryujinx.Graphics.GAL;
@@ -22,7 +23,7 @@ namespace Ryujinx.HLE
{
public class Switch : IDisposable
{
- public IAalOutput AudioOut { get; private set; }
+ public IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
internal MemoryBlock Memory { get; private set; }
@@ -48,16 +49,16 @@ namespace Ryujinx.HLE
public bool EnableDeviceVsync { get; set; } = true;
- public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, UserChannelPersistence userChannelPersistence, IRenderer renderer, IAalOutput audioOut)
+ public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, UserChannelPersistence userChannelPersistence, IRenderer renderer, IHardwareDeviceDriver audioDeviceDriver)
{
if (renderer == null)
{
throw new ArgumentNullException(nameof(renderer));
}
- if (audioOut == null)
+ if (audioDeviceDriver == null)
{
- throw new ArgumentNullException(nameof(audioOut));
+ throw new ArgumentNullException(nameof(audioDeviceDriver));
}
if (userChannelPersistence == null)
@@ -67,7 +68,7 @@ namespace Ryujinx.HLE
UserChannelPersistence = userChannelPersistence;
- AudioOut = audioOut;
+ AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(audioDeviceDriver);
Memory = new MemoryBlock(1UL << 32);
@@ -210,7 +211,7 @@ namespace Ryujinx.HLE
System.Dispose();
Host1x.Dispose();
- AudioOut.Dispose();
+ AudioDeviceDriver.Dispose();
FileSystem.Unload();
Memory.Dispose();
}
diff --git a/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs b/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs
index e743252d..d2c2e6cb 100644
--- a/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs
+++ b/Ryujinx.Tests/Audio/Renderer/Server/PoolMapperTests.cs
@@ -1,5 +1,5 @@
using NUnit.Framework;
-using Ryujinx.Audio.Renderer;
+using Ryujinx.Audio;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System;
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj
index e6d7208c..3dfc7405 100644
--- a/Ryujinx.Tests/Ryujinx.Tests.csproj
+++ b/Ryujinx.Tests/Ryujinx.Tests.csproj
@@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\Ryujinx.Audio.Renderer\Ryujinx.Audio.Renderer.csproj" />
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
diff --git a/Ryujinx.sln b/Ryujinx.sln
index 3e557dea..ee1d57bd 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "Ry
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
@@ -31,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Renderer", "Ryujinx.Audio.Renderer\Ryujinx.Audio.Renderer.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
@@ -57,6 +55,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.H264
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -79,10 +81,6 @@ Global
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.Build.0 = Release|Any CPU
- {5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.Build.0 = Release|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -159,6 +157,14 @@ Global
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index 7f0d23e7..c048eb26 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -23,7 +23,8 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
+ <ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
+ <ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
@@ -38,9 +39,9 @@
<None Update="THIRDPARTY.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
- <ContentWithTargetPath Include="..\Ryujinx.Audio.Renderer\LICENSE.txt">
+ <ContentWithTargetPath Include="..\Ryujinx.Audio\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
- <TargetPath>LICENSE-Ryujinx.Audio.Renderer.txt</TargetPath>
+ <TargetPath>LICENSE-Ryujinx.Audio.txt</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 828fe853..809b693b 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -4,6 +4,10 @@ using Gtk;
using LibHac.Common;
using LibHac.Ns;
using Ryujinx.Audio;
+using Ryujinx.Audio.Backends.Dummy;
+using Ryujinx.Audio.Backends.OpenAL;
+using Ryujinx.Audio.Backends.SoundIo;
+using Ryujinx.Audio.Integration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
@@ -281,14 +285,14 @@ namespace Ryujinx.Ui
{
_virtualFileSystem.Reload();
- IRenderer renderer = new Renderer();
- IAalOutput audioEngine = new DummyAudioOut();
+ IRenderer renderer = new Renderer();
+ IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver();
if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
{
- if (SoundIoAudioOut.IsSupported)
+ if (SoundIoHardwareDeviceDriver.IsSupported)
{
- audioEngine = new SoundIoAudioOut();
+ deviceDriver = new SoundIoHardwareDeviceDriver();
}
else
{
@@ -297,22 +301,22 @@ namespace Ryujinx.Ui
}
else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl)
{
- if (OpenALAudioOut.IsSupported)
+ if (OpenALHardwareDeviceDriver.IsSupported)
{
- audioEngine = new OpenALAudioOut();
+ deviceDriver = new OpenALHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO.");
- if (SoundIoAudioOut.IsSupported)
+ if (SoundIoHardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
SaveConfig();
- audioEngine = new SoundIoAudioOut();
+ deviceDriver = new SoundIoHardwareDeviceDriver();
}
else
{
@@ -321,7 +325,7 @@ namespace Ryujinx.Ui
}
}
- _emulationContext = new HLE.Switch(_virtualFileSystem, _contentManager, _userChannelPersistence, renderer, audioEngine)
+ _emulationContext = new HLE.Switch(_virtualFileSystem, _contentManager, _userChannelPersistence, renderer, deviceDriver)
{
UiHandler = _uiHandler
};
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index 0938783f..3bd828a4 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -1,5 +1,7 @@
using Gtk;
using Ryujinx.Audio;
+using Ryujinx.Audio.Backends.OpenAL;
+using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Configuration;
@@ -324,8 +326,8 @@ namespace Ryujinx.Ui.Windows
Task.Run(() =>
{
- openAlIsSupported = OpenALAudioOut.IsSupported;
- soundIoIsSupported = SoundIoAudioOut.IsSupported;
+ openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported;
+ soundIoIsSupported = SoundIoHardwareDeviceDriver.IsSupported;
});
// This function runs whenever the dropdown is opened