sdl3 audio

This commit is contained in:
madwind 2025-01-15 17:23:03 +08:00
parent 0bd62888a0
commit fbc5ccfa2c
9 changed files with 43 additions and 133 deletions

View File

@ -1,16 +0,0 @@
namespace Ryujinx.Audio.Backends.SDL3
{
class SDL3AudioBuffer
{
public readonly ulong DriverIdentifier;
public readonly ulong SampleCount;
public ulong SamplePlayed;
public SDL3AudioBuffer(ulong driverIdentifier, ulong sampleCount)
{
DriverIdentifier = driverIdentifier;
SampleCount = sampleCount;
SamplePlayed = 0;
}
}
}

View File

@ -29,7 +29,7 @@ namespace Ryujinx.Audio.Backends.SDL3
SDL3Driver.Instance.Initialize();
if (!SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, out var spec, out int sample_frames))
if (!SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, out var spec, out int _))
{
Logger.Error?.Print(LogClass.Application,
$"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
@ -48,8 +48,7 @@ namespace Ryujinx.Audio.Backends.SDL3
private static bool IsSupportedInternal()
{
var device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax,
Constants.TargetSampleCount, null);
var device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax);
if (device != 0)
{
@ -100,7 +99,7 @@ namespace Ryujinx.Audio.Backends.SDL3
}
private static SDL_AudioSpec GetSDL3Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate,
uint requestedChannelCount, uint sampleCount)
uint requestedChannelCount)
{
return new SDL_AudioSpec
{
@ -123,10 +122,9 @@ namespace Ryujinx.Audio.Backends.SDL3
}
internal static nint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate,
uint requestedChannelCount, uint sampleCount, SDL_AudioStreamCallback callback)
uint requestedChannelCount)
{
SDL_AudioSpec spec = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount,
sampleCount);
SDL_AudioSpec spec = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount);
var stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, ref spec,null,IntPtr.Zero);

View File

@ -1,12 +1,8 @@
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.Runtime.InteropServices;
using System.Threading;
using static SDL3.SDL;
@ -15,14 +11,10 @@ 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 nint _outputStream;
private bool _hasSetupError;
private readonly SDL_AudioStreamCallback _callbackDelegate;
private readonly int _bytesPerFrame;
private uint _sampleCount;
private bool _started;
private float _volume;
@ -34,10 +26,6 @@ namespace Ryujinx.Audio.Backends.SDL3
{
_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;
@ -56,7 +44,7 @@ namespace Ryujinx.Audio.Backends.SDL3
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
var newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate,
RequestedChannelCount, _sampleCount, _callbackDelegate);
RequestedChannelCount);
_hasSetupError = newOutputStream == 0;
@ -75,78 +63,6 @@ namespace Ryujinx.Audio.Backends.SDL3
}
}
private unsafe void Update(nint 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);
// IntPtr unmanagedBuffer = Marshal.AllocHGlobal(samples.Length);
// Marshal.Copy(p, 0, unmanagedBuffer, samples.Length);
// SDL_PutAudioStreamData(_outputStream, unmanagedBuffer,samples.Length);
// }
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);
@ -164,27 +80,21 @@ namespace Ryujinx.Audio.Backends.SDL3
EnsureAudioStreamSetup(buffer);
if (_outputStream != 0)
{
SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
int MinimumAudio = int.MaxValue; // 8000 float samples per second, half a second.
// SDL_ResumeAudioStreamDevice(_outputStream);
Console.WriteLine(SDL_GetAudioStreamAvailable(_outputStream));
if (SDL_GetAudioStreamAvailable(_outputStream) < MinimumAudio)
if (SDL_GetAudioStreamAvailable(_outputStream) < int.MaxValue)
{
unsafe
{
fixed (byte* samplesPtr = buffer.Data)
{
IntPtr buffer2 = (IntPtr)samplesPtr;
SDL_PutAudioStreamData(_outputStream, buffer2, buffer.Data.Length);
var len = buffer.Data.Length;
IntPtr src = (IntPtr)samplesPtr;
byte* dst = stackalloc byte[len];
IntPtr dstPtr = (IntPtr)dst;
SDL_MixAudio(dstPtr, src, _nativeSampleFormat, (uint)len, _driver.Volume);
SDL_PutAudioStreamData(_outputStream, dstPtr, len);
}
}
}
_queuedBuffers.Enqueue(driverBuffer);
}
else
{
@ -229,12 +139,7 @@ namespace Ryujinx.Audio.Backends.SDL3
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
{
if (!_queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
{
return true;
}
return driverBuffer.DriverIdentifier != buffer.DataPointer;
return true;
}
protected virtual void Dispose(bool disposing)

View File

@ -8,7 +8,6 @@ namespace Ryujinx.Common.Configuration.Hid
{
Invalid,
WindowKeyboard,
GamepadSDL2,
GamepadSDL3
}
}

View File

@ -4222,6 +4222,31 @@
"zh_TW": ""
}
},
{
"ID": "SettingsTabSystemAudioBackendSDL3",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "SDL3",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "SettingsTabSystemHacks",
"Translations": {

View File

@ -157,7 +157,7 @@ namespace Ryujinx.Headless
config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Backend = InputBackendType.GamepadSDL3,
Id = null,
ControllerType = ControllerType.JoyconPair,
DeadzoneLeft = 0.1f,

View File

@ -491,7 +491,7 @@ namespace Ryujinx.Ava.UI.Models.Input
var config = new StandardControllerInputConfig
{
Id = Id,
Backend = InputBackendType.GamepadSDL2,
Backend = InputBackendType.GamepadSDL3,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>

View File

@ -579,7 +579,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Backend = InputBackendType.GamepadSDL3,
Id = id,
ControllerType = ControllerType.ProController,
DeadzoneLeft = 0.1f,

View File

@ -48,8 +48,7 @@
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSDL3Enabled}">
<TextBlock>
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
<TextBlock Text="+1" />
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL3}" />
</TextBlock>
</ComboBoxItem>
</ComboBox>