This commit is contained in:
madwind 2025-01-11 00:24:53 +08:00
parent 91f70584ec
commit 241b0152ad
14 changed files with 480 additions and 453 deletions

View File

@ -1,208 +0,0 @@
// using Ryujinx.Audio.Common;
// using Ryujinx.Audio.Integration;
// using Ryujinx.Common.Logging;
// using Ryujinx.Memory;
// using Ryujinx.SDL3.Common;
// using System;
// using System.Collections.Concurrent;
// using System.Runtime.InteropServices;
// using System.Threading;
// using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
// using static SDL3.SDL;
//
// namespace Ryujinx.Audio.Backends.SDL3
// {
// public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
// {
// private readonly ManualResetEvent _updateRequiredEvent;
// private readonly ManualResetEvent _pauseEvent;
// private readonly ConcurrentDictionary<SDL3HardwareDeviceSession, byte> _sessions;
//
// private readonly bool _supportSurroundConfiguration;
//
// public float Volume { get; set; }
//
// // TODO: Add this to SDL3-CS
// // NOTE: We use a DllImport here because of marshaling issue for spec.
// #pragma warning disable SYSLIB1054
// [DllImport("SDL3")]
// private static extern int SDL_GetDefaultAudioInfo(nint name, out SDL_AudioSpec spec, int isCapture);
// #pragma warning restore SYSLIB1054
//
// public SDL3HardwareDeviceDriver()
// {
// _updateRequiredEvent = new ManualResetEvent(false);
// _pauseEvent = new ManualResetEvent(true);
// _sessions = new ConcurrentDictionary<SDL3HardwareDeviceSession, byte>();
//
// SDL3Driver.Instance.Initialize();
//
// int res = SDL_GetDefaultAudioInfo(nint.Zero, out var spec, 0);
//
// if (res != 0)
// {
// Logger.Error?.Print(LogClass.Application,
// $"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
//
// _supportSurroundConfiguration = true;
// }
// else
// {
// _supportSurroundConfiguration = spec.channels >= 6;
// }
//
// Volume = 1f;
// }
//
// public static bool IsSupported => IsSupportedInternal();
//
// private static bool IsSupportedInternal()
// {
// uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
//
// if (device != 0)
// {
// SDL_CloseAudioDevice(device);
// }
//
// return device != 0;
// }
//
// public ManualResetEvent GetUpdateRequiredEvent()
// {
// return _updateRequiredEvent;
// }
//
// public ManualResetEvent GetPauseEvent()
// {
// return _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 SDL3 backend!");
// }
//
// SDL3HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
//
// _sessions.TryAdd(session, 0);
//
// return session;
// }
//
// internal bool Unregister(SDL3HardwareDeviceSession session)
// {
// return _sessions.TryRemove(session, out _);
// }
//
// private static SDL_AudioSpec GetSDL3Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
// {
// return new SDL_AudioSpec
// {
// channels = (byte)requestedChannelCount,
// format = GetSDL3Format(requestedSampleFormat),
// freq = (int)requestedSampleRate,
// samples = (ushort)sampleCount,
// };
// }
//
// internal static ushort GetSDL3Format(SampleFormat format)
// {
// return format switch
// {
// SampleFormat.PcmInt8 => AUDIO_S8,
// SampleFormat.PcmInt16 => AUDIO_S16,
// SampleFormat.PcmInt32 => AUDIO_S32,
// SampleFormat.PcmFloat => AUDIO_F32,
// _ => throw new ArgumentException($"Unsupported sample format {format}"),
// };
// }
//
// internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback)
// {
// SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount);
//
// desired.callback = callback;
//
// uint device = SDL_OpenAudioDevice(nint.Zero, 0, ref desired, out SDL_AudioSpec got, 0);
//
// if (device == 0)
// {
// Logger.Error?.Print(LogClass.Application, $"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\"");
//
// return 0;
// }
//
// bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
//
// if (!isValid)
// {
// Logger.Error?.Print(LogClass.Application, "SDL3 open audio device is not valid");
// SDL_CloseAudioDevice(device);
//
// return 0;
// }
//
// return device;
// }
//
// public void Dispose()
// {
// GC.SuppressFinalize(this);
// Dispose(true);
// }
//
// protected virtual void Dispose(bool disposing)
// {
// if (disposing)
// {
// foreach (SDL3HardwareDeviceSession session in _sessions.Keys)
// {
// session.Dispose();
// }
//
// SDL3Driver.Instance.Dispose();
//
// _pauseEvent.Dispose();
// }
// }
//
// public bool SupportsSampleRate(uint sampleRate)
// {
// return true;
// }
//
// public bool SupportsSampleFormat(SampleFormat sampleFormat)
// {
// return sampleFormat != SampleFormat.PcmInt24;
// }
//
// public bool SupportsChannelCount(uint channelCount)
// {
// if (channelCount == 6)
// {
// return _supportSurroundConfiguration;
// }
//
// return true;
// }
//
// public bool SupportsDirection(Direction direction)
// {
// // TODO: add direction input when supported.
// return direction == Direction.Output;
// }
// }
// }

View File

@ -1,234 +0,0 @@
// using Ryujinx.Audio.Backends.Common;
// using Ryujinx.Audio.Common;
// using Ryujinx.Common.Logging;
// using Ryujinx.Common.Memory;
// using Ryujinx.Memory;
// using System;
// using System.Buffers;
// using System.Collections.Concurrent;
// using System.Threading;
//
// using static SDL3.SDL;
//
// namespace Ryujinx.Audio.Backends.SDL3
// {
// class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
// {
// private readonly SDL3HardwareDeviceDriver _driver;
// private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
// private readonly DynamicRingBuffer _ringBuffer;
// private ulong _playedSampleCount;
// private readonly ManualResetEvent _updateRequiredEvent;
// private uint _outputStream;
// private bool _hasSetupError;
// private readonly SDL_AudioCallback _callbackDelegate;
// private readonly int _bytesPerFrame;
// private uint _sampleCount;
// private bool _started;
// private float _volume;
// private readonly ushort _nativeSampleFormat;
//
// public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
// {
// _driver = driver;
// _updateRequiredEvent = _driver.GetUpdateRequiredEvent();
// _queuedBuffers = new ConcurrentQueue<SDL3AudioBuffer>();
// _ringBuffer = new DynamicRingBuffer();
// _callbackDelegate = Update;
// _bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
// _nativeSampleFormat = SDL3HardwareDeviceDriver.GetSDL3Format(RequestedSampleFormat);
// _sampleCount = uint.MaxValue;
// _started = false;
// _volume = 1f;
// }
//
// private void EnsureAudioStreamSetup(AudioBuffer buffer)
// {
// uint bufferSampleCount = (uint)GetSampleCount(buffer);
// bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
// (bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
//
// if (needAudioSetup)
// {
// _sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
//
// uint newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
//
// _hasSetupError = newOutputStream == 0;
//
// if (!_hasSetupError)
// {
// if (_outputStream != 0)
// {
// SDL_CloseAudioDevice(_outputStream);
// }
//
// _outputStream = newOutputStream;
//
// SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1);
//
// Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
// }
// }
// }
//
// private unsafe void Update(nint userdata, nint stream, int streamLength)
// {
// Span<byte> streamSpan = new((void*)stream, streamLength);
//
// int maxFrameCount = (int)GetSampleCount(streamLength);
// int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
//
// int frameCount = Math.Min(bufferedFrames, maxFrameCount);
//
// if (frameCount == 0)
// {
// // SDL3 left the responsibility to the user to clear the buffer.
// streamSpan.Clear();
//
// return;
// }
//
// using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
//
// Span<byte> samples = samplesOwner.Span;
//
// _ringBuffer.Read(samples, 0, samples.Length);
//
// fixed (byte* p = samples)
// {
// nint pStreamSrc = (nint)p;
//
// // Zero the dest buffer
// streamSpan.Clear();
//
// // Apply volume to written data
// SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME));
// }
//
// ulong sampleCount = GetSampleCount(samples.Length);
//
// ulong availaibleSampleCount = sampleCount;
//
// bool needUpdate = false;
//
// while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
// {
// ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
// ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
//
// ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
// availaibleSampleCount -= playedAudioBufferSampleCount;
//
// if (currentSamplePlayed == driverBuffer.SampleCount)
// {
// _queuedBuffers.TryDequeue(out _);
//
// needUpdate = true;
// }
//
// Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount);
// }
//
// // Notify the output if needed.
// if (needUpdate)
// {
// _updateRequiredEvent.Set();
// }
// }
//
// 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)
// {
// EnsureAudioStreamSetup(buffer);
//
// if (_outputStream != 0)
// {
// SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
//
// _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
//
// _queuedBuffers.Enqueue(driverBuffer);
// }
// else
// {
// Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer));
//
// _updateRequiredEvent.Set();
// }
// }
//
// public override void SetVolume(float volume)
// {
// _volume = volume;
// }
//
// public override void Start()
// {
// if (!_started)
// {
// if (_outputStream != 0)
// {
// SDL_PauseAudioDevice(_outputStream, 0);
// }
//
// _started = true;
// }
// }
//
// public override void Stop()
// {
// if (_started)
// {
// if (_outputStream != 0)
// {
// SDL_PauseAudioDevice(_outputStream, 1);
// }
//
// _started = false;
// }
// }
//
// public override void UnregisterBuffer(AudioBuffer buffer) { }
//
// public override bool WasBufferFullyConsumed(AudioBuffer buffer)
// {
// if (!_queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
// {
// return true;
// }
//
// return driverBuffer.DriverIdentifier != buffer.DataPointer;
// }
//
// protected virtual void Dispose(bool disposing)
// {
// if (disposing && _driver.Unregister(this))
// {
// PrepareToClose();
// Stop();
//
// if (_outputStream != 0)
// {
// SDL_CloseAudioDevice(_outputStream);
// }
// }
// }
//
// public override void Dispose()
// {
// Dispose(true);
// }
// }
// }

View File

@ -0,0 +1,217 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using Ryujinx.SDL3.Common;
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
using static SDL3.SDL;
namespace Ryujinx.Audio.Backends.SDL3
{
public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<SDL3HardwareDeviceSession, byte> _sessions;
private readonly bool _supportSurroundConfiguration;
public float Volume { get; set; }
public SDL3HardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
_sessions = new ConcurrentDictionary<SDL3HardwareDeviceSession, byte>();
SDL3Driver.Instance.Initialize();
if (!SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, out var spec, out int sample_frames))
{
Logger.Error?.Print(LogClass.Application,
$"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
_supportSurroundConfiguration = true;
}
else
{
_supportSurroundConfiguration = spec.channels >= 6;
}
Volume = 1f;
}
public static bool IsSupported => IsSupportedInternal();
private static bool IsSupportedInternal()
{
var devices = SDL_GetAudioPlaybackDevices(out int count);
var joystickIDs = new int[count];
Marshal.Copy(devices, joystickIDs, 0, count);
for (int i = 0; i < count; ++i)
{
Console.WriteLine(joystickIDs[i]);
}
var device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax,
Constants.TargetSampleCount, null);
if (device != 0)
{
SDL_CloseAudioDevice(device);
}
return device != 0;
}
public ManualResetEvent GetUpdateRequiredEvent()
{
return _updateRequiredEvent;
}
public ManualResetEvent GetPauseEvent()
{
return _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 SDL3 backend!");
}
SDL3HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
_sessions.TryAdd(session, 0);
return session;
}
internal bool Unregister(SDL3HardwareDeviceSession session)
{
return _sessions.TryRemove(session, out _);
}
private static SDL_AudioSpec GetSDL3Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate,
uint requestedChannelCount, uint sampleCount)
{
return new SDL_AudioSpec
{
channels = (byte)requestedChannelCount,
format = GetSDL3Format(requestedSampleFormat),
freq = (int)requestedSampleRate,
};
}
internal static SDL_AudioFormat GetSDL3Format(SampleFormat format)
{
return format switch
{
SampleFormat.PcmInt8 => SDL_AudioFormat.SDL_AUDIO_S8,
SampleFormat.PcmInt16 => SDL_AudioFormat.SDL_AUDIO_S16,
SampleFormat.PcmInt32 => SDL_AudioFormat.SDL_AUDIO_S32,
SampleFormat.PcmFloat => SDL_AudioFormat.SDL_AUDIO_F32,
_ => throw new ArgumentException($"Unsupported sample format {format}"),
};
}
internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate,
uint requestedChannelCount, uint sampleCount, SDL_AudioStreamCallback callback)
{
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount,
sampleCount);
SDL_AudioSpec desired2 = default;
var device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, ref desired);
if (device == 0)
{
Logger.Error?.Print(LogClass.Application,
$"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\"");
return 0;
}
Console.WriteLine(SDL_GetAudioDeviceName(device));
bool isValid = false;
if (SDL_GetAudioDeviceFormat(device, out SDL_AudioSpec got, out int i))
{
Console.WriteLine(got.freq);
isValid = got.format == desired.format && got.freq == desired.freq &&
got.channels == desired.channels;
}
// if (!isValid)
// {
// Logger.Error?.Print(LogClass.Application, "SDL3 open audio device is not valid");
// SDL_CloseAudioDevice(device);
//
// return 0;
// }
return device;
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (SDL3HardwareDeviceSession session in _sessions.Keys)
{
session.Dispose();
}
SDL3Driver.Instance.Dispose();
_pauseEvent.Dispose();
}
}
public bool SupportsSampleRate(uint sampleRate)
{
return true;
}
public bool SupportsSampleFormat(SampleFormat sampleFormat)
{
return sampleFormat != SampleFormat.PcmInt24;
}
public bool SupportsChannelCount(uint channelCount)
{
if (channelCount == 6)
{
return _supportSurroundConfiguration;
}
return true;
}
public bool SupportsDirection(Direction direction)
{
// TODO: add direction input when supported.
return direction == Direction.Output;
}
}
}

View File

@ -0,0 +1,235 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Threading;
using static SDL3.SDL;
namespace Ryujinx.Audio.Backends.SDL3
{
class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private readonly SDL3HardwareDeviceDriver _driver;
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
private readonly DynamicRingBuffer _ringBuffer;
private ulong _playedSampleCount;
private readonly ManualResetEvent _updateRequiredEvent;
private uint _outputStream;
private bool _hasSetupError;
private readonly SDL_AudioStreamCallback _callbackDelegate;
private readonly int _bytesPerFrame;
private uint _sampleCount;
private bool _started;
private float _volume;
private readonly SDL_AudioFormat _nativeSampleFormat;
public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{
_driver = driver;
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
_queuedBuffers = new ConcurrentQueue<SDL3AudioBuffer>();
_ringBuffer = new DynamicRingBuffer();
_callbackDelegate = Update;
_bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
_nativeSampleFormat = SDL3HardwareDeviceDriver.GetSDL3Format(RequestedSampleFormat);
_sampleCount = uint.MaxValue;
_started = false;
_volume = 1f;
}
private void EnsureAudioStreamSetup(AudioBuffer buffer)
{
uint bufferSampleCount = (uint)GetSampleCount(buffer);
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
if (needAudioSetup)
{
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
var newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
_hasSetupError = newOutputStream == 0;
if (!_hasSetupError)
{
if (_outputStream != 0)
{
SDL_CloseAudioDevice(_outputStream);
}
_outputStream = newOutputStream;
SDL_PauseAudioDevice(_outputStream);
Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
}
}
}
private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength, int total_amount)
{
Console.WriteLine("call");
Console.WriteLine(SDL_GetAudioDeviceName(SDL_GetAudioStreamDevice(stream)));
Span<byte> streamSpan = new((void*)stream, streamLength);
int maxFrameCount = (int)GetSampleCount(streamLength);
int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
int frameCount = Math.Min(bufferedFrames, maxFrameCount);
if (frameCount == 0)
{
// SDL3 left the responsibility to the user to clear the buffer.
streamSpan.Clear();
return;
}
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
Span<byte> samples = samplesOwner.Span;
_ringBuffer.Read(samples, 0, samples.Length);
fixed (byte* p = samples)
{
nint pStreamSrc = (nint)p;
// Zero the dest buffer
streamSpan.Clear();
// Apply volume to written data
// SDL_MixAudio(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, _driver.Volume * _volume);
}
ulong sampleCount = GetSampleCount(samples.Length);
ulong availaibleSampleCount = sampleCount;
bool needUpdate = false;
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
{
ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
availaibleSampleCount -= playedAudioBufferSampleCount;
if (currentSamplePlayed == driverBuffer.SampleCount)
{
_queuedBuffers.TryDequeue(out _);
needUpdate = true;
}
Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount);
}
// Notify the output if needed.
if (needUpdate)
{
_updateRequiredEvent.Set();
}
}
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)
{
Console.WriteLine(buffer);
EnsureAudioStreamSetup(buffer);
if (_outputStream != 0)
{
SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
_queuedBuffers.Enqueue(driverBuffer);
}
else
{
Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer));
_updateRequiredEvent.Set();
}
}
public override void SetVolume(float volume)
{
_volume = volume;
}
public override void Start()
{
if (!_started)
{
if (_outputStream != 0)
{
SDL_ResumeAudioDevice(_outputStream);
}
_started = true;
}
}
public override void Stop()
{
if (_started)
{
if (_outputStream != 0)
{
SDL_PauseAudioDevice(_outputStream);
}
_started = false;
}
}
public override void UnregisterBuffer(AudioBuffer buffer) { }
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
{
if (!_queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
{
return true;
}
return driverBuffer.DriverIdentifier != buffer.DataPointer;
}
protected virtual void Dispose(bool disposing)
{
if (disposing && _driver.Unregister(this))
{
PrepareToClose();
Stop();
if (_outputStream != 0)
{
SDL_CloseAudioDevice(_outputStream);
}
}
}
public override void Dispose()
{
Dispose(true);
}
}
}

View File

@ -193,7 +193,7 @@ namespace Ryujinx.Input.SDL3
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ToSDL2Scancode(Key key) private static int ToSDL3Scancode(Key key)
{ {
if (key >= Key.Unknown && key <= Key.Menu) if (key >= Key.Unknown && key <= Key.Menu)
{ {
@ -235,7 +235,7 @@ namespace Ryujinx.Input.SDL3
for (Key key = 0; key < Key.Count; key++) for (Key key = 0; key < Key.Count; key++)
{ {
int index = ToSDL2Scancode(key); int index = ToSDL3Scancode(key);
if (index == -1) if (index == -1)
{ {
SDL_Keymod modifierMask = GetKeyboardModifierMask(key); SDL_Keymod modifierMask = GetKeyboardModifierMask(key);

View File

@ -8048,5 +8048,6 @@ public static unsafe partial class SDL
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static partial int SDL_EnterAppMainCallbacks(int argc, IntPtr argv, SDL_AppInit_func appinit, SDL_AppIterate_func appiter, SDL_AppEvent_func appevent, SDL_AppQuit_func appquit); public static partial int SDL_EnterAppMainCallbacks(int argc, IntPtr argv, SDL_AppInit_func appinit, SDL_AppIterate_func appiter, SDL_AppEvent_func appevent, SDL_AppQuit_func appquit);
public const uint SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = 0xFFFFFFFFu;
} }

View File

@ -25,7 +25,7 @@ namespace Ryujinx.SDL3.Common
public static Action<Action> MainThreadDispatcher { get; set; } public static Action<Action> MainThreadDispatcher { get; set; }
private const SDL_InitFlags SdlInitFlags = SDL_InitFlags.SDL_INIT_GAMEPAD; private const SDL_InitFlags SdlInitFlags = SDL_InitFlags.SDL_INIT_GAMEPAD | SDL_InitFlags.SDL_INIT_AUDIO;
private bool _isRunning; private bool _isRunning;
private uint _refereceCount; private uint _refereceCount;
@ -52,13 +52,13 @@ namespace Ryujinx.SDL3.Common
return; return;
} }
// SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx"); SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx");
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
// SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
// SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
// //
// //
// // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them. // // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them.
@ -67,7 +67,7 @@ namespace Ryujinx.SDL3.Common
if (!SDL_Init(SdlInitFlags)) if (!SDL_Init(SdlInitFlags))
{ {
string errorMessage = $"SDL2 initialization failed with error \"{SDL_GetError()}\""; string errorMessage = $"SDL3 initialization failed with error \"{SDL_GetError()}\"";
Logger.Error?.Print(LogClass.Application, errorMessage); Logger.Error?.Print(LogClass.Application, errorMessage);

View File

@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SDL3;
using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
@ -957,6 +958,7 @@ namespace Ryujinx.Ava
{ {
var availableBackends = new List<AudioBackend> var availableBackends = new List<AudioBackend>
{ {
AudioBackend.SDL3,
AudioBackend.SDL2, AudioBackend.SDL2,
AudioBackend.SoundIo, AudioBackend.SoundIo,
AudioBackend.OpenAl, AudioBackend.OpenAl,
@ -996,6 +998,7 @@ namespace Ryujinx.Ava
deviceDriver = currentBackend switch deviceDriver = currentBackend switch
{ {
AudioBackend.SDL3 => InitializeAudioBackend<SDL3HardwareDeviceDriver>(AudioBackend.SDL3, nextBackend),
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend), AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend), AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend), AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),

View File

@ -15,6 +15,7 @@ using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2; using Ryujinx.Input.SDL2;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using Ryujinx.SDL3.Common;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -116,6 +117,7 @@ namespace Ryujinx.Headless
HostUITheme = new HeadlessHostUiTheme(); HostUITheme = new HeadlessHostUiTheme();
SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.Initialize();
SDL3Driver.Instance.Initialize();
} }
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse) public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse)

View File

@ -71,6 +71,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" /> <ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />

View File

@ -6,6 +6,7 @@ using Gommon;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SDL3;
using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
@ -212,6 +213,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsOpenAlEnabled { get; set; } public bool IsOpenAlEnabled { get; set; }
public bool IsSoundIoEnabled { get; set; } public bool IsSoundIoEnabled { get; set; }
public bool IsSDL2Enabled { get; set; } public bool IsSDL2Enabled { get; set; }
public bool IsSDL3Enabled { get; set; }
public bool IsCustomResolutionScaleActive => _resolutionScale == 4; public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr; public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
@ -373,6 +375,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported; IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported; IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported; IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
IsSDL3Enabled = SDL3HardwareDeviceDriver.IsSupported;
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {

View File

@ -46,6 +46,12 @@
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}"> <ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL2}" /> <TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSDL3Enabled}">
<TextBlock>
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
<TextBlock Text="*" />
</TextBlock>
</ComboBoxItem>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">

View File

@ -10,5 +10,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
OpenAl, OpenAl,
SoundIo, SoundIo,
SDL2, SDL2,
SDL3,
} }
} }