mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2026-01-31 21:32:51 +00:00
Merge branch 'master' into more-launch-checks
This commit is contained in:
commit
ed69f28113
@ -47,6 +47,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Apple", "src\Ryujinx.Audio.Backends.Apple\Ryujinx.Audio.Backends.Apple.csproj", "{AC26EFF0-8593-4184-9A09-98E37EFFB32E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
|
||||
@ -569,6 +571,8 @@ Global
|
||||
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "RenderDoc 프레임 캡처 시작",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@ -37,7 +37,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "RenderDoc 프레임 캡처 종료",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@ -62,7 +62,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "RenderDoc 프레임 캡처 폐기",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@ -87,7 +87,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "현재 활성화된 RenderDoc 프레임 캡처를 종료하고 결과를 즉시 폐기합니다.",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
|
||||
@ -4850,6 +4850,31 @@
|
||||
"zh_TW": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemAudioBackendAudioToolbox",
|
||||
"Translations": {
|
||||
"ar_SA": null,
|
||||
"de_DE": null,
|
||||
"el_GR": null,
|
||||
"en_US": "Apple Audio (macOS only)",
|
||||
"es_ES": null,
|
||||
"fr_FR": null,
|
||||
"he_IL": null,
|
||||
"it_IT": null,
|
||||
"ja_JP": null,
|
||||
"ko_KR": null,
|
||||
"no_NO": null,
|
||||
"pl_PL": null,
|
||||
"pt_BR": null,
|
||||
"ru_RU": null,
|
||||
"sv_SE": null,
|
||||
"th_TH": null,
|
||||
"tr_TR": null,
|
||||
"uk_UA": null,
|
||||
"zh_CN": null,
|
||||
"zh_TW": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemHacks",
|
||||
"Translations": {
|
||||
@ -24776,4 +24801,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
_allocated.Dispose();
|
||||
|
||||
foreach (IntPtr page in _pages.Values)
|
||||
foreach (nint page in _pages.Values)
|
||||
{
|
||||
NativeAllocator.Instance.Free((void*)page);
|
||||
}
|
||||
|
||||
16
src/Ryujinx.Audio.Backends.Apple/AppleAudioBuffer.cs
Normal file
16
src/Ryujinx.Audio.Backends.Apple/AppleAudioBuffer.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Ryujinx.Audio.Backends.Apple
|
||||
{
|
||||
class AppleAudioBuffer
|
||||
{
|
||||
public readonly ulong DriverIdentifier;
|
||||
public readonly ulong SampleCount;
|
||||
public ulong SamplePlayed;
|
||||
|
||||
public AppleAudioBuffer(ulong driverIdentifier, ulong sampleCount)
|
||||
{
|
||||
DriverIdentifier = driverIdentifier;
|
||||
SampleCount = sampleCount;
|
||||
SamplePlayed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
228
src/Ryujinx.Audio.Backends.Apple/AppleHardwareDeviceDriver.cs
Normal file
228
src/Ryujinx.Audio.Backends.Apple/AppleHardwareDeviceDriver.cs
Normal file
@ -0,0 +1,228 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Runtime.Versioning;
|
||||
using Ryujinx.Audio.Backends.Apple.Native;
|
||||
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
|
||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Apple
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
[SupportedOSPlatform("ios")]
|
||||
public sealed class AppleHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private readonly ManualResetEvent _pauseEvent;
|
||||
private readonly ConcurrentDictionary<AppleHardwareDeviceSession, byte> _sessions;
|
||||
private readonly bool _supportSurroundConfiguration;
|
||||
|
||||
public float Volume { get; set; }
|
||||
|
||||
public AppleHardwareDeviceDriver()
|
||||
{
|
||||
_updateRequiredEvent = new ManualResetEvent(false);
|
||||
_pauseEvent = new ManualResetEvent(true);
|
||||
_sessions = new ConcurrentDictionary<AppleHardwareDeviceSession, byte>();
|
||||
|
||||
_supportSurroundConfiguration = TestSurroundSupport();
|
||||
|
||||
Volume = 1f;
|
||||
}
|
||||
|
||||
private bool TestSurroundSupport()
|
||||
{
|
||||
try
|
||||
{
|
||||
AudioStreamBasicDescription format =
|
||||
GetAudioFormat(SampleFormat.PcmFloat, Constants.TargetSampleRate, 6);
|
||||
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out nint testQueue);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
AudioChannelLayout layout = new AudioChannelLayout
|
||||
{
|
||||
AudioChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A,
|
||||
AudioChannelBitmap = 0,
|
||||
NumberChannelDescriptions = 0
|
||||
};
|
||||
|
||||
int layoutResult = AudioQueueSetProperty(
|
||||
testQueue,
|
||||
kAudioQueueProperty_ChannelLayout,
|
||||
ref layout,
|
||||
(uint)Marshal.SizeOf<AudioChannelLayout>());
|
||||
|
||||
if (layoutResult == 0)
|
||||
{
|
||||
AudioQueueDispose(testQueue, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioQueueDispose(testQueue, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSupported => IsSupportedInternal();
|
||||
|
||||
private static bool IsSupportedInternal()
|
||||
{
|
||||
if (!OperatingSystem.IsMacOS()) return false;
|
||||
|
||||
try
|
||||
{
|
||||
AudioStreamBasicDescription format =
|
||||
GetAudioFormat(SampleFormat.PcmInt16, Constants.TargetSampleRate, 2);
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out nint testQueue);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
AudioQueueDispose(testQueue, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Audio, $"Failed to check if AudioToolbox is supported: {e.Message}\n{e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public ManualResetEvent GetUpdateRequiredEvent()
|
||||
=> _updateRequiredEvent;
|
||||
|
||||
public ManualResetEvent GetPauseEvent()
|
||||
=> _pauseEvent;
|
||||
|
||||
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 Apple backend!");
|
||||
}
|
||||
|
||||
AppleHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
internal bool Unregister(AppleHardwareDeviceSession session)
|
||||
=> _sessions.TryRemove(session, out _);
|
||||
|
||||
internal static AudioStreamBasicDescription GetAudioFormat(SampleFormat sampleFormat, uint sampleRate,
|
||||
uint channelCount)
|
||||
{
|
||||
uint formatFlags;
|
||||
uint bitsPerChannel;
|
||||
|
||||
switch (sampleFormat)
|
||||
{
|
||||
case SampleFormat.PcmInt8:
|
||||
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
bitsPerChannel = 8;
|
||||
break;
|
||||
case SampleFormat.PcmInt16:
|
||||
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
bitsPerChannel = 16;
|
||||
break;
|
||||
case SampleFormat.PcmInt32:
|
||||
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
bitsPerChannel = 32;
|
||||
break;
|
||||
case SampleFormat.PcmFloat:
|
||||
formatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
|
||||
bitsPerChannel = 32;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported sample format {sampleFormat}");
|
||||
}
|
||||
|
||||
uint bytesPerFrame = (bitsPerChannel / 8) * channelCount;
|
||||
|
||||
return new AudioStreamBasicDescription
|
||||
{
|
||||
SampleRate = sampleRate,
|
||||
FormatID = kAudioFormatLinearPCM,
|
||||
FormatFlags = formatFlags,
|
||||
BytesPerPacket = bytesPerFrame,
|
||||
FramesPerPacket = 1,
|
||||
BytesPerFrame = bytesPerFrame,
|
||||
ChannelsPerFrame = channelCount,
|
||||
BitsPerChannel = bitsPerChannel,
|
||||
Reserved = 0
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (AppleHardwareDeviceSession session in _sessions.Keys)
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
_pauseEvent.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsDirection(Direction direction)
|
||||
=> direction != Direction.Input;
|
||||
|
||||
public bool SupportsSampleRate(uint sampleRate) => true;
|
||||
|
||||
public bool SupportsSampleFormat(SampleFormat sampleFormat)
|
||||
=> sampleFormat != SampleFormat.PcmInt24;
|
||||
|
||||
public bool SupportsChannelCount(uint channelCount)
|
||||
=> channelCount != 6 || _supportSurroundConfiguration;
|
||||
}
|
||||
}
|
||||
285
src/Ryujinx.Audio.Backends.Apple/AppleHardwareDeviceSession.cs
Normal file
285
src/Ryujinx.Audio.Backends.Apple/AppleHardwareDeviceSession.cs
Normal file
@ -0,0 +1,285 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Runtime.Versioning;
|
||||
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Apple
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
[SupportedOSPlatform("ios")]
|
||||
class AppleHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private const int NumBuffers = 3;
|
||||
|
||||
private readonly AppleHardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<AppleAudioBuffer> _queuedBuffers = new();
|
||||
private readonly DynamicRingBuffer _ringBuffer = new();
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
|
||||
private readonly AudioQueueOutputCallback _callbackDelegate;
|
||||
private readonly GCHandle _gcHandle;
|
||||
|
||||
private nint _audioQueue;
|
||||
private readonly nint[] _audioQueueBuffers = new nint[NumBuffers];
|
||||
private readonly int[] _bufferBytesFilled = new int[NumBuffers];
|
||||
|
||||
private readonly int _bytesPerFrame;
|
||||
|
||||
private ulong _playedSampleCount;
|
||||
private bool _started;
|
||||
private float _volume = 1f;
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void AudioQueueOutputCallback(
|
||||
nint userData,
|
||||
nint audioQueue,
|
||||
nint buffer);
|
||||
|
||||
public AppleHardwareDeviceSession(
|
||||
AppleHardwareDeviceDriver driver,
|
||||
IVirtualMemoryManager memoryManager,
|
||||
SampleFormat requestedSampleFormat,
|
||||
uint requestedSampleRate,
|
||||
uint requestedChannelCount)
|
||||
: base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = driver.GetUpdateRequiredEvent();
|
||||
_callbackDelegate = OutputCallback;
|
||||
_bytesPerFrame = BackendHelper.GetSampleSize(requestedSampleFormat) * (int)requestedChannelCount;
|
||||
|
||||
_gcHandle = GCHandle.Alloc(this, GCHandleType.Normal);
|
||||
|
||||
SetupAudioQueue();
|
||||
}
|
||||
|
||||
private void SetupAudioQueue()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
AudioStreamBasicDescription format = AppleHardwareDeviceDriver.GetAudioFormat(
|
||||
RequestedSampleFormat,
|
||||
RequestedSampleRate,
|
||||
RequestedChannelCount);
|
||||
|
||||
nint callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate);
|
||||
nint userData = GCHandle.ToIntPtr(_gcHandle);
|
||||
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
callbackPtr,
|
||||
userData,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out _audioQueue);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"AudioQueueNewOutput failed: {result}");
|
||||
}
|
||||
|
||||
uint framesPerBuffer = RequestedSampleRate / 100;
|
||||
uint bufferSize = framesPerBuffer * (uint)_bytesPerFrame;
|
||||
|
||||
for (int i = 0; i < NumBuffers; i++)
|
||||
{
|
||||
AudioQueueAllocateBuffer(_audioQueue, bufferSize, out _audioQueueBuffers[i]);
|
||||
_bufferBytesFilled[i] = 0;
|
||||
|
||||
PrimeBuffer(_audioQueueBuffers[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void PrimeBuffer(nint bufferPtr, int bufferIndex)
|
||||
{
|
||||
AudioQueueBuffer* buffer = (AudioQueueBuffer*)bufferPtr;
|
||||
|
||||
int capacityBytes = (int)buffer->AudioDataBytesCapacity;
|
||||
int framesPerBuffer = capacityBytes / _bytesPerFrame;
|
||||
|
||||
int availableFrames = _ringBuffer.Length / _bytesPerFrame;
|
||||
int framesToRead = Math.Min(availableFrames, framesPerBuffer);
|
||||
int bytesToRead = framesToRead * _bytesPerFrame;
|
||||
|
||||
Span<byte> dst = new((void*)buffer->AudioData, capacityBytes);
|
||||
dst.Clear();
|
||||
|
||||
if (bytesToRead > 0)
|
||||
{
|
||||
Span<byte> audio = dst.Slice(0, bytesToRead);
|
||||
_ringBuffer.Read(audio, 0, bytesToRead);
|
||||
ApplyVolume(buffer->AudioData, bytesToRead);
|
||||
}
|
||||
|
||||
buffer->AudioDataByteSize = (uint)capacityBytes;
|
||||
_bufferBytesFilled[bufferIndex] = bytesToRead;
|
||||
|
||||
AudioQueueEnqueueBuffer(_audioQueue, bufferPtr, 0, nint.Zero);
|
||||
}
|
||||
|
||||
private void OutputCallback(nint userData, nint audioQueue, nint bufferPtr)
|
||||
{
|
||||
if (!_started || bufferPtr == nint.Zero)
|
||||
return;
|
||||
|
||||
int bufferIndex = Array.IndexOf(_audioQueueBuffers, bufferPtr);
|
||||
if (bufferIndex < 0)
|
||||
return;
|
||||
|
||||
int bytesPlayed = _bufferBytesFilled[bufferIndex];
|
||||
if (bytesPlayed > 0)
|
||||
{
|
||||
ProcessPlayedSamples(bytesPlayed);
|
||||
}
|
||||
|
||||
PrimeBuffer(bufferPtr, bufferIndex);
|
||||
}
|
||||
|
||||
private void ProcessPlayedSamples(int bytesPlayed)
|
||||
{
|
||||
ulong samplesPlayed = GetSampleCount(bytesPlayed);
|
||||
ulong remaining = samplesPlayed;
|
||||
bool needUpdate = false;
|
||||
|
||||
while (remaining > 0 && _queuedBuffers.TryPeek(out AppleAudioBuffer buffer))
|
||||
{
|
||||
ulong needed = buffer.SampleCount - Interlocked.Read(ref buffer.SamplePlayed);
|
||||
ulong take = Math.Min(needed, remaining);
|
||||
|
||||
ulong played = Interlocked.Add(ref buffer.SamplePlayed, take);
|
||||
remaining -= take;
|
||||
|
||||
if (played == buffer.SampleCount)
|
||||
{
|
||||
_queuedBuffers.TryDequeue(out _);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _playedSampleCount, take);
|
||||
}
|
||||
|
||||
if (needUpdate)
|
||||
{
|
||||
_updateRequiredEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void ApplyVolume(nint dataPtr, int byteSize)
|
||||
{
|
||||
float volume = Math.Clamp(_volume * _driver.Volume, 0f, 1f);
|
||||
if (volume >= 0.999f)
|
||||
return;
|
||||
|
||||
int sampleCount = byteSize / BackendHelper.GetSampleSize(RequestedSampleFormat);
|
||||
|
||||
switch (RequestedSampleFormat)
|
||||
{
|
||||
case SampleFormat.PcmInt16:
|
||||
short* s16 = (short*)dataPtr;
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
s16[i] = (short)(s16[i] * volume);
|
||||
break;
|
||||
|
||||
case SampleFormat.PcmFloat:
|
||||
float* f32 = (float*)dataPtr;
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
f32[i] *= volume;
|
||||
break;
|
||||
|
||||
case SampleFormat.PcmInt32:
|
||||
int* s32 = (int*)dataPtr;
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
s32[i] = (int)(s32[i] * volume);
|
||||
break;
|
||||
|
||||
case SampleFormat.PcmInt8:
|
||||
sbyte* s8 = (sbyte*)dataPtr;
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
s8[i] = (sbyte)(s8[i] * volume);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void QueueBuffer(AudioBuffer buffer)
|
||||
{
|
||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||
_queuedBuffers.Enqueue(new AppleAudioBuffer(buffer.DataPointer, GetSampleCount(buffer)));
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_started)
|
||||
return;
|
||||
|
||||
_started = true;
|
||||
AudioQueueStart(_audioQueue, nint.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_started)
|
||||
return;
|
||||
|
||||
_started = false;
|
||||
AudioQueuePause(_audioQueue);
|
||||
}
|
||||
}
|
||||
|
||||
public override ulong GetPlayedSampleCount()
|
||||
=> Interlocked.Read(ref _playedSampleCount);
|
||||
|
||||
public override float GetVolume() => _volume;
|
||||
public override void SetVolume(float volume) => _volume = volume;
|
||||
|
||||
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
|
||||
{
|
||||
if (!_queuedBuffers.TryPeek(out AppleAudioBuffer driverBuffer))
|
||||
return true;
|
||||
|
||||
return driverBuffer.DriverIdentifier != buffer.DataPointer;
|
||||
}
|
||||
|
||||
public override void PrepareToClose() { }
|
||||
public override void UnregisterBuffer(AudioBuffer buffer) { }
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (_audioQueue != nint.Zero)
|
||||
{
|
||||
AudioQueueStop(_audioQueue, true);
|
||||
AudioQueueDispose(_audioQueue, true);
|
||||
_audioQueue = nint.Zero;
|
||||
}
|
||||
|
||||
if (_gcHandle.IsAllocated)
|
||||
{
|
||||
_gcHandle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/Ryujinx.Audio.Backends.Apple/Native/AudioToolbox.cs
Normal file
102
src/Ryujinx.Audio.Backends.Apple/Native/AudioToolbox.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System.Runtime.InteropServices;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Apple.Native
|
||||
{
|
||||
public static partial class AudioToolbox
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct AudioStreamBasicDescription
|
||||
{
|
||||
public double SampleRate;
|
||||
public uint FormatID;
|
||||
public uint FormatFlags;
|
||||
public uint BytesPerPacket;
|
||||
public uint FramesPerPacket;
|
||||
public uint BytesPerFrame;
|
||||
public uint ChannelsPerFrame;
|
||||
public uint BitsPerChannel;
|
||||
public uint Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct AudioChannelLayout
|
||||
{
|
||||
public uint AudioChannelLayoutTag;
|
||||
public uint AudioChannelBitmap;
|
||||
public uint NumberChannelDescriptions;
|
||||
}
|
||||
|
||||
internal const uint kAudioFormatLinearPCM = 0x6C70636D;
|
||||
internal const uint kAudioQueueProperty_ChannelLayout = 0x6171636c;
|
||||
internal const uint kAudioChannelLayoutTag_MPEG_5_1_A = 0x650006;
|
||||
internal const uint kAudioFormatFlagIsFloat = (1 << 0);
|
||||
internal const uint kAudioFormatFlagIsSignedInteger = (1 << 2);
|
||||
internal const uint kAudioFormatFlagIsPacked = (1 << 3);
|
||||
internal const uint kAudioFormatFlagIsBigEndian = (1 << 1);
|
||||
internal const uint kAudioFormatFlagIsAlignedHigh = (1 << 4);
|
||||
internal const uint kAudioFormatFlagIsNonInterleaved = (1 << 5);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueNewOutput(
|
||||
ref AudioStreamBasicDescription format,
|
||||
nint callback,
|
||||
nint userData,
|
||||
nint callbackRunLoop,
|
||||
nint callbackRunLoopMode,
|
||||
uint flags,
|
||||
out nint audioQueue);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueSetProperty(
|
||||
nint audioQueue,
|
||||
uint propertyID,
|
||||
ref AudioChannelLayout layout,
|
||||
uint layoutSize);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueDispose(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueAllocateBuffer(
|
||||
nint audioQueue,
|
||||
uint bufferByteSize,
|
||||
out nint buffer);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueStart(nint audioQueue, nint startTime);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueuePause(nint audioQueue);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueStop(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueSetParameter(
|
||||
nint audioQueue,
|
||||
uint parameterID,
|
||||
float value);
|
||||
|
||||
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
|
||||
internal static partial int AudioQueueEnqueueBuffer(
|
||||
nint audioQueue,
|
||||
nint buffer,
|
||||
uint numPacketDescs,
|
||||
nint packetDescs);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct AudioQueueBuffer
|
||||
{
|
||||
public uint AudioDataBytesCapacity;
|
||||
public nint AudioData;
|
||||
public uint AudioDataByteSize;
|
||||
public nint UserData;
|
||||
public uint PacketDescriptionCapacity;
|
||||
public nint PacketDescriptions;
|
||||
public uint PacketDescriptionCount;
|
||||
}
|
||||
|
||||
internal const uint kAudioQueueParam_Volume = 1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -10,7 +10,8 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.OpenAL
|
||||
{
|
||||
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public sealed class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly ALDevice _device;
|
||||
private readonly ALContext _context;
|
||||
@ -148,7 +149,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
|
||||
@ -9,7 +9,8 @@ using System.Threading;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.OpenAL
|
||||
{
|
||||
class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
// ReSharper disable once InconsistentNaming
|
||||
sealed class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private readonly OpenALHardwareDeviceDriver _driver;
|
||||
private readonly int _sourceId;
|
||||
@ -190,7 +191,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _driver.Unregister(this))
|
||||
{
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Backends.SDL3
|
||||
|
||||
using unsafe SDL_AudioStreamCallbackPointer = delegate* unmanaged[Cdecl]<nint, SDL_AudioStream*, int, int, void>;
|
||||
|
||||
public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
public sealed class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private readonly ManualResetEvent _pauseEvent;
|
||||
@ -162,7 +162,7 @@ namespace Ryujinx.Audio.Backends.SDL3
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
|
||||
@ -12,10 +12,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
|
||||
|
||||
|
||||
unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
sealed unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private readonly SDL3HardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
|
||||
@ -226,7 +223,7 @@ namespace Ryujinx.Audio.Backends.SDL3
|
||||
return driverBuffer.DriverIdentifier != buffer.DataPointer;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _driver.Unregister(this))
|
||||
{
|
||||
|
||||
@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
unsafe
|
||||
{
|
||||
int* frameCountPtr = &nativeFrameCount;
|
||||
IntPtr* arenasPtr = &arenas;
|
||||
nint* arenasPtr = &arenas;
|
||||
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
|
||||
|
||||
frameCount = *frameCountPtr;
|
||||
|
||||
@ -10,7 +10,7 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo
|
||||
{
|
||||
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
public sealed class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly SoundIoContext _audioContext;
|
||||
private readonly SoundIoDeviceContext _audioDevice;
|
||||
@ -227,7 +227,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
|
||||
@ -11,7 +11,7 @@ using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo
|
||||
{
|
||||
class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
sealed class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private readonly SoundIoHardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
|
||||
@ -428,7 +428,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _driver.Unregister(this))
|
||||
{
|
||||
|
||||
@ -30,9 +30,9 @@ namespace ARMeilleure.Common
|
||||
/// <summary>
|
||||
/// Base address for the page.
|
||||
/// </summary>
|
||||
public readonly IntPtr Address;
|
||||
public readonly nint Address;
|
||||
|
||||
public AddressTablePage(bool isSparse, IntPtr address)
|
||||
public AddressTablePage(bool isSparse, nint address)
|
||||
{
|
||||
IsSparse = isSparse;
|
||||
Address = address;
|
||||
@ -47,20 +47,20 @@ namespace ARMeilleure.Common
|
||||
public readonly SparseMemoryBlock Block;
|
||||
private readonly TrackingEventDelegate _trackingEvent;
|
||||
|
||||
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit)
|
||||
{
|
||||
SparseMemoryBlock block = new(size, pageInit, null);
|
||||
|
||||
_trackingEvent = (address, size, write) =>
|
||||
{
|
||||
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||
ensureMapped((IntPtr)pointer);
|
||||
ensureMapped((nint)pointer);
|
||||
return pointer;
|
||||
};
|
||||
|
||||
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||
(nuint)block.Block.Pointer,
|
||||
(nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
|
||||
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
|
||||
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||
|
||||
if (!added)
|
||||
@ -116,7 +116,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr Base
|
||||
public nint Base
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -124,7 +124,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
return (IntPtr)GetRootPage();
|
||||
return (nint)GetRootPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,7 +240,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
long index = Levels[^1].GetValue(address);
|
||||
|
||||
EnsureMapped((IntPtr)(page + index));
|
||||
EnsureMapped((nint)(page + index));
|
||||
|
||||
return ref page[index];
|
||||
}
|
||||
@ -284,7 +284,7 @@ namespace ARMeilleure.Common
|
||||
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||
/// </summary>
|
||||
/// <param name="ptr">Pointer to be mapped</param>
|
||||
private void EnsureMapped(IntPtr ptr)
|
||||
private void EnsureMapped(nint ptr)
|
||||
{
|
||||
if (Sparse)
|
||||
{
|
||||
@ -299,7 +299,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
SparseMemoryBlock sparse = reserved.Block;
|
||||
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
|
||||
{
|
||||
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||
|
||||
@ -319,15 +319,15 @@ namespace ARMeilleure.Common
|
||||
/// </summary>
|
||||
/// <param name="level">Level to get the fill value for</param>
|
||||
/// <returns>The fill value</returns>
|
||||
private IntPtr GetFillValue(int level)
|
||||
private nint GetFillValue(int level)
|
||||
{
|
||||
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||
{
|
||||
return (IntPtr)_fillBottomLevelPtr;
|
||||
return (nint)_fillBottomLevelPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
return nint.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@ namespace ARMeilleure.Common
|
||||
/// <param name="fill">Fill value</param>
|
||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||
/// <returns>Allocated block</returns>
|
||||
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
{
|
||||
int size = sizeof(T) * length;
|
||||
|
||||
@ -405,7 +405,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
}
|
||||
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset);
|
||||
|
||||
_sparseReservedOffset += (ulong)size;
|
||||
|
||||
@ -413,7 +413,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
IntPtr address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
|
||||
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
||||
page = new AddressTablePage(false, address);
|
||||
|
||||
Span<T> span = new((void*)page.Address, length);
|
||||
|
||||
@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
|
||||
|
||||
IntPtr hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
|
||||
nint hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
|
||||
|
||||
if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
|
||||
{
|
||||
|
||||
@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
IntPtr configSize = (nint)Marshal.SizeOf<MVKConfiguration>();
|
||||
nint configSize = (nint)Marshal.SizeOf<MVKConfiguration>();
|
||||
|
||||
vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize);
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
||||
}
|
||||
|
||||
IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
nint appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
|
||||
ApplicationInfo applicationInfo = new()
|
||||
{
|
||||
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api)
|
||||
{
|
||||
IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
nint appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
|
||||
ApplicationInfo applicationInfo = new()
|
||||
{
|
||||
|
||||
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
|
||||
public bool Blocking { get => Socket.Blocking; set => Socket.Blocking = value; }
|
||||
|
||||
public nint Handle => IntPtr.Zero;
|
||||
public nint Handle => nint.Zero;
|
||||
|
||||
public IPEndPoint RemoteEndPoint => Socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
|
||||
@ -159,7 +159,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
SplitForMap((ulong)location, (ulong)size, srcOffset);
|
||||
|
||||
IntPtr ptr = WindowsApi.MapViewOfFile3(
|
||||
nint ptr = WindowsApi.MapViewOfFile3(
|
||||
sharedMemory,
|
||||
WindowsApi.CurrentProcessHandle,
|
||||
location,
|
||||
|
||||
@ -227,7 +227,7 @@ namespace Ryujinx.Tests.Memory
|
||||
|
||||
// Create some info to be used for managing the native writing loop.
|
||||
int stateSize = Unsafe.SizeOf<NativeWriteLoopState>();
|
||||
IntPtr statePtr = Marshal.AllocHGlobal(stateSize);
|
||||
nint statePtr = Marshal.AllocHGlobal(stateSize);
|
||||
Unsafe.InitBlockUnaligned((void*)statePtr, 0, (uint)stateSize);
|
||||
|
||||
ref NativeWriteLoopState writeLoopState = ref Unsafe.AsRef<NativeWriteLoopState>((void*)statePtr);
|
||||
|
||||
@ -77,12 +77,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.RenderDocApi\Ryujinx.Graphics.RenderDocApi.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.Apple\Ryujinx.Audio.Backends.Apple.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.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" />
|
||||
|
||||
@ -6,6 +6,7 @@ using Avalonia.Threading;
|
||||
using DiscordRPC;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Audio.Backends.Apple;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
@ -949,6 +950,9 @@ namespace Ryujinx.Ava.Systems
|
||||
AudioBackend.Dummy
|
||||
];
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
availableBackends.Insert(0, AudioBackend.AudioToolbox);
|
||||
|
||||
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
|
||||
|
||||
if (preferredBackend is AudioBackend.SDL2)
|
||||
@ -985,6 +989,9 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
deviceDriver = currentBackend switch
|
||||
{
|
||||
#pragma warning disable CA1416 // Platform compatibility is enforced in AppleHardwareDeviceDriver.IsSupported, before any potentially platform-sensitive code can run.
|
||||
AudioBackend.AudioToolbox => InitializeAudioBackend<AppleHardwareDeviceDriver>(AudioBackend.AudioToolbox, nextBackend),
|
||||
#pragma warning restore CA1416
|
||||
AudioBackend.SDL3 => InitializeAudioBackend<SDL3HardwareDeviceDriver>(AudioBackend.SDL3, nextBackend),
|
||||
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
|
||||
@ -9,6 +9,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
OpenAl,
|
||||
SoundIo,
|
||||
SDL3,
|
||||
AudioToolbox,
|
||||
SDL2 = SDL3
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Systems.Update.Client;
|
||||
using Ryujinx.Systems.Update.Common;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -46,6 +47,12 @@ namespace Ryujinx.Ava.Systems
|
||||
return Return<VersionResponse>.Failure(
|
||||
new MessageError("DNS resolution error occurred. Is your internet down?"));
|
||||
}
|
||||
catch (HttpRequestException hre)
|
||||
when (hre.StatusCode is HttpStatusCode.BadGateway)
|
||||
{
|
||||
return Return<VersionResponse>.Failure(
|
||||
new MessageError("Could not connect to the update server, but it appears like you have internet. It seems like the update server is offline, try again later."));
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
|
||||
|
||||
@ -5,6 +5,7 @@ using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Apple;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
@ -277,6 +278,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool IsOpenAlEnabled { get; set; }
|
||||
public bool IsSoundIoEnabled { get; set; }
|
||||
public bool IsSDL3Enabled { get; set; }
|
||||
public bool IsAudioToolboxEnabled { get; set; }
|
||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||
|
||||
@ -524,12 +526,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
||||
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
|
||||
IsSDL3Enabled = SDL3HardwareDeviceDriver.IsSupported;
|
||||
IsAudioToolboxEnabled = OperatingSystem.IsMacOS() && AppleHardwareDeviceDriver.IsSupported;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(IsOpenAlEnabled));
|
||||
OnPropertyChanged(nameof(IsSoundIoEnabled));
|
||||
OnPropertyChanged(nameof(IsSDL3Enabled));
|
||||
OnPropertyChanged(nameof(IsAudioToolboxEnabled));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,9 @@
|
||||
<ComboBoxItem
|
||||
IsEnabled="{Binding IsSDL3Enabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSDL3}" />
|
||||
<ComboBoxItem
|
||||
IsEnabled="{Binding IsAudioToolboxEnabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendAudioToolbox}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user