sdl3 audio

This commit is contained in:
madwind 2025-01-15 12:08:08 +08:00
parent b01dcd1963
commit 0bd62888a0
5 changed files with 61 additions and 51 deletions

View File

@ -5,7 +5,6 @@ using Ryujinx.Memory;
using Ryujinx.SDL3.Common; using Ryujinx.SDL3.Common;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
using static SDL3.SDL; using static SDL3.SDL;
@ -54,7 +53,7 @@ namespace Ryujinx.Audio.Backends.SDL3
if (device != 0) if (device != 0)
{ {
SDL_CloseAudioDevice(device); SDL_DestroyAudioStream(device);
} }
return device != 0; return device != 0;
@ -123,15 +122,15 @@ namespace Ryujinx.Audio.Backends.SDL3
}; };
} }
internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, internal static nint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate,
uint requestedChannelCount, uint sampleCount, SDL_AudioStreamCallback callback) uint requestedChannelCount, uint sampleCount, SDL_AudioStreamCallback callback)
{ {
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, SDL_AudioSpec spec = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount,
sampleCount); sampleCount);
SDL_AudioSpec desired2 = default;
var device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, ref desired);
if (device == 0) var stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, ref spec,null,IntPtr.Zero);
if (stream == 0)
{ {
Logger.Error?.Print(LogClass.Application, Logger.Error?.Print(LogClass.Application,
$"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\""); $"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\"");
@ -139,22 +138,8 @@ namespace Ryujinx.Audio.Backends.SDL3
return 0; return 0;
} }
bool isValid = false;
if (SDL_GetAudioDeviceFormat(device, out SDL_AudioSpec got, out int i))
{
isValid = got.format == desired.format && got.freq == desired.freq &&
got.channels == desired.channels;
}
// if (!isValid) return stream;
// {
// Logger.Error?.Print(LogClass.Application, "SDL3 open audio device is not valid");
// SDL_CloseAudioDevice(device);
//
// return 0;
// }
return device;
} }
public void Dispose() public void Dispose()

View File

@ -6,8 +6,8 @@ using Ryujinx.Memory;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using static SDL3.SDL; using static SDL3.SDL;
namespace Ryujinx.Audio.Backends.SDL3 namespace Ryujinx.Audio.Backends.SDL3
@ -19,7 +19,7 @@ namespace Ryujinx.Audio.Backends.SDL3
private readonly DynamicRingBuffer _ringBuffer; private readonly DynamicRingBuffer _ringBuffer;
private ulong _playedSampleCount; private ulong _playedSampleCount;
private readonly ManualResetEvent _updateRequiredEvent; private readonly ManualResetEvent _updateRequiredEvent;
private uint _outputStream; private nint _outputStream;
private bool _hasSetupError; private bool _hasSetupError;
private readonly SDL_AudioStreamCallback _callbackDelegate; private readonly SDL_AudioStreamCallback _callbackDelegate;
private readonly int _bytesPerFrame; private readonly int _bytesPerFrame;
@ -28,7 +28,9 @@ namespace Ryujinx.Audio.Backends.SDL3
private float _volume; private float _volume;
private readonly SDL_AudioFormat _nativeSampleFormat; private readonly SDL_AudioFormat _nativeSampleFormat;
public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager,
SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(
memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{ {
_driver = driver; _driver = driver;
_updateRequiredEvent = _driver.GetUpdateRequiredEvent(); _updateRequiredEvent = _driver.GetUpdateRequiredEvent();
@ -46,13 +48,15 @@ namespace Ryujinx.Audio.Backends.SDL3
{ {
uint bufferSampleCount = (uint)GetSampleCount(buffer); uint bufferSampleCount = (uint)GetSampleCount(buffer);
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) || bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount); (bufferSampleCount >= Constants.TargetSampleCount &&
bufferSampleCount < _sampleCount);
if (needAudioSetup) if (needAudioSetup)
{ {
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount); _sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
var newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate); var newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate,
RequestedChannelCount, _sampleCount, _callbackDelegate);
_hasSetupError = newOutputStream == 0; _hasSetupError = newOutputStream == 0;
@ -60,17 +64,18 @@ namespace Ryujinx.Audio.Backends.SDL3
{ {
if (_outputStream != 0) if (_outputStream != 0)
{ {
SDL_CloseAudioDevice(_outputStream); SDL_DestroyAudioStream(_outputStream);
} }
_outputStream = newOutputStream; _outputStream = newOutputStream;
SDL_PauseAudioDevice(_outputStream); SDL_ResumeAudioStreamDevice(_outputStream);
Logger.Info?.Print(LogClass.Audio,
Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}"); $"New audio stream setup with a target sample count of {_sampleCount}");
} }
} }
} }
private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength, int total_amount)
private unsafe void Update(nint userdata, IntPtr stream, int streamLength, int total_amount)
{ {
Console.WriteLine("call"); Console.WriteLine("call");
Console.WriteLine(SDL_GetAudioDeviceName(SDL_GetAudioStreamDevice(stream))); Console.WriteLine(SDL_GetAudioDeviceName(SDL_GetAudioStreamDevice(stream)));
@ -95,16 +100,20 @@ namespace Ryujinx.Audio.Backends.SDL3
_ringBuffer.Read(samples, 0, samples.Length); _ringBuffer.Read(samples, 0, samples.Length);
fixed (byte* p = samples) // fixed (byte* p = samples)
{ // {
nint pStreamSrc = (nint)p; // 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);
// }
// 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 sampleCount = GetSampleCount(samples.Length);
@ -117,7 +126,8 @@ namespace Ryujinx.Audio.Backends.SDL3
ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed); ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount); ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount); ulong currentSamplePlayed =
Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
availaibleSampleCount -= playedAudioBufferSampleCount; availaibleSampleCount -= playedAudioBufferSampleCount;
if (currentSamplePlayed == driverBuffer.SampleCount) if (currentSamplePlayed == driverBuffer.SampleCount)
@ -152,12 +162,27 @@ namespace Ryujinx.Audio.Backends.SDL3
public override void QueueBuffer(AudioBuffer buffer) public override void QueueBuffer(AudioBuffer buffer)
{ {
EnsureAudioStreamSetup(buffer); EnsureAudioStreamSetup(buffer);
if (_outputStream != 0) if (_outputStream != 0)
{ {
SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer)); SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); _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)
{
unsafe
{
fixed (byte* samplesPtr = buffer.Data)
{
IntPtr buffer2 = (IntPtr)samplesPtr;
SDL_PutAudioStreamData(_outputStream, buffer2, buffer.Data.Length);
}
}
}
_queuedBuffers.Enqueue(driverBuffer); _queuedBuffers.Enqueue(driverBuffer);
} }
@ -180,7 +205,7 @@ namespace Ryujinx.Audio.Backends.SDL3
{ {
if (_outputStream != 0) if (_outputStream != 0)
{ {
SDL_ResumeAudioDevice(_outputStream); SDL_ResumeAudioStreamDevice(_outputStream);
} }
_started = true; _started = true;
@ -193,7 +218,7 @@ namespace Ryujinx.Audio.Backends.SDL3
{ {
if (_outputStream != 0) if (_outputStream != 0)
{ {
SDL_PauseAudioDevice(_outputStream); SDL_PauseAudioStreamDevice(_outputStream);
} }
_started = false; _started = false;
@ -221,7 +246,7 @@ namespace Ryujinx.Audio.Backends.SDL3
if (_outputStream != 0) if (_outputStream != 0)
{ {
SDL_CloseAudioDevice(_outputStream); SDL_DestroyAudioStream(_outputStream);
} }
} }
} }

View File

@ -58,7 +58,7 @@ namespace Ryujinx.Common.Configuration.Hid
return backendType switch return backendType switch
{ {
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardKeyboardInputConfig), InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardKeyboardInputConfig),
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig), InputBackendType.GamepadSDL3 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig),
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
}; };
} }
@ -70,7 +70,7 @@ namespace Ryujinx.Common.Configuration.Hid
case InputBackendType.WindowKeyboard: case InputBackendType.WindowKeyboard:
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, _serializerContext.StandardKeyboardInputConfig); JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, _serializerContext.StandardKeyboardInputConfig);
break; break;
case InputBackendType.GamepadSDL2: case InputBackendType.GamepadSDL3:
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, _serializerContext.StandardControllerInputConfig); JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, _serializerContext.StandardControllerInputConfig);
break; break;
default: default:

View File

@ -968,7 +968,7 @@ public static unsafe partial class SDL
[LibraryImport(nativeLibName)] [LibraryImport(nativeLibName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
public static partial IntPtr SDL_OpenAudioDeviceStream(uint devid, ref SDL_AudioSpec spec, SDL_AudioStreamCallback callback, IntPtr userdata); public static partial IntPtr SDL_OpenAudioDeviceStream(uint devid, ref SDL_AudioSpec spec, SDL_AudioStreamCallback? callback, IntPtr userdata);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SDL_AudioPostmixCallback(IntPtr userdata, SDL_AudioSpec* spec, float* buffer, int buflen); public delegate void SDL_AudioPostmixCallback(IntPtr userdata, SDL_AudioSpec* spec, float* buffer, int buflen);

View File

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