sdl3 audio
This commit is contained in:
parent
b01dcd1963
commit
0bd62888a0
@ -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()
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user